from copy import copy
from pathlib import Path

from openpyxl import load_workbook
from openpyxl.comments import Comment
from openpyxl.formatting.rule import FormulaRule
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
from openpyxl.utils import get_column_letter


ROOT = Path(__file__).resolve().parent
TEMPLATE = Path("/Users/jack/同步空间/cpt/05_经营管理与会议/005_日常管理/周例会（事业部层面）/2026年/周报模板.xlsx")
OUTPUT = ROOT / "output" / "多项目交付与运维周报_偏差与支持版_20260904.xlsx"
MINUTES = Path("/Users/jack/code/010-cpt/008-zhct/zhctprompt/work/2026-09-04-multi-project-delivery-ops-weekly-meeting/meeting-minutes.md")

NAVY = "17324D"
BLUE = "1D5FA7"
TEAL = "2F7D72"
ORANGE = "D87516"
RED = "B84638"
GREEN = "2F7D52"
WHITE = "FFFFFF"
PALE_BLUE = "EAF2F9"
PALE_ORANGE = "FFF2E5"
PALE_RED = "FDEBE8"
PALE_GREEN = "EAF5EE"
PALE_GRAY = "F4F6F8"
GRAY = "607387"
LINE = "D8E1E8"

thin = Side(style="thin", color=LINE)
border = Border(left=thin, right=thin, top=thin, bottom=thin)


def fill(color):
    return PatternFill("solid", fgColor=color)


def font(color=NAVY, bold=False, size=12):
    return Font(name="微软雅黑", size=size, color=color, bold=bold)


def set_title(ws, title, subtitle, end_col):
    ws.merge_cells(start_row=1, start_column=1, end_row=2, end_column=end_col)
    c = ws.cell(1, 1, title)
    c.font = font(WHITE, True, 22)
    c.fill = fill(NAVY)
    c.alignment = Alignment(horizontal="left", vertical="center")
    for row in ws.iter_rows(min_row=1, max_row=2, min_col=1, max_col=end_col):
        for cell in row:
            cell.fill = fill(NAVY)
    ws.merge_cells(start_row=3, start_column=1, end_row=3, end_column=end_col)
    c = ws.cell(3, 1, subtitle)
    c.font = font(GRAY, False, 11)
    c.fill = fill(PALE_BLUE)
    c.alignment = Alignment(vertical="center", wrap_text=True)
    ws.row_dimensions[1].height = 30
    ws.row_dimensions[2].height = 18
    ws.row_dimensions[3].height = 34


def section(ws, row, text, end_col, color=BLUE):
    ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=end_col)
    c = ws.cell(row, 1, text)
    c.font = font(WHITE, True, 14)
    c.fill = fill(color)
    c.alignment = Alignment(vertical="center")
    for cell in ws[row][:end_col]:
        cell.fill = fill(color)
    ws.row_dimensions[row].height = 28


def header_row(ws, row, headers, color=BLUE):
    for col, value in enumerate(headers, 1):
        c = ws.cell(row, col, value)
        c.font = font(WHITE, True, 12)
        c.fill = fill(color)
        c.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
        c.border = border
    ws.row_dimensions[row].height = 34


def body_row(ws, row, values, fill_color=None, height=42):
    for col, value in enumerate(values, 1):
        c = ws.cell(row, col, value)
        c.font = font()
        c.alignment = Alignment(vertical="top", wrap_text=True)
        c.border = border
        if fill_color:
            c.fill = fill(fill_color)
    ws.row_dimensions[row].height = height


def reset_sheet(ws):
    for merged in list(ws.merged_cells.ranges):
        ws.unmerge_cells(str(merged))
    for row in ws.iter_rows():
        for c in row:
            c.value = None
            c.comment = None
            c.hyperlink = None
    ws._images = []
    ws._charts = []
    ws.sheet_view.showGridLines = False


def normalize_fonts(wb):
    for ws in wb.worksheets:
        for row in ws.iter_rows():
            for c in row:
                if c.value is None:
                    continue
                old = c.font
                c.font = copy(old)
                c.font = Font(
                    name="微软雅黑",
                    size=12,
                    bold=old.bold,
                    italic=old.italic,
                    color=old.color,
                    underline=old.underline,
                    strike=old.strike,
                    vertAlign=old.vertAlign,
                )


def build_summary(ws):
    ws.sheet_view.showGridLines = False
    set_title(
        ws,
        "数字技术中心多项目交付与运维周报｜偏差与支持版",
        "周期：2026-W36（2026-08-31 至 2026-09-06）｜来源：9月4日多项目交付与运维周会｜口径：未提供验收、合同和回款台账的事项不计为完成",
        12,
    )
    ws.merge_cells("A5:L6")
    c = ws["A5"]
    c.value = "本周核心判断：项目数量多、关键节点密集，但当前主要矛盾不是工作量不足，而是日期口径、设备到货、服务器、现场安装资源、客户确认和需求边界尚未闭环。管理重点应从“继续推进”转为优先关闭 P0 偏差并在最晚决策日前提供资源。"
    c.font = font(NAVY, True, 14)
    c.fill = fill(PALE_ORANGE)
    c.alignment = Alignment(vertical="center", wrap_text=True)
    c.border = Border(left=Side(style="medium", color=ORANGE))
    ws.row_dimensions[5].height = 34
    ws.row_dimensions[6].height = 34

    cards = [
        (1, "P0偏差", '=COUNTIF(\'偏差与支持\'!B:B,"P0")', RED),
        (4, "P1偏差", '=COUNTIF(\'偏差与支持\'!B:B,"P1")', ORANGE),
        (7, "纳入项目", '=COUNTA(\'项目\'!D6:D24)', BLUE),
        (10, "待关闭支持", '=COUNTIF(\'偏差与支持\'!L6:L19,"<>已关闭")', TEAL),
    ]
    for start_col, label, formula, color in cards:
        ws.merge_cells(start_row=8, start_column=start_col, end_row=8, end_column=start_col + 2)
        ws.merge_cells(start_row=9, start_column=start_col, end_row=10, end_column=start_col + 2)
        c = ws.cell(8, start_col, label)
        c.font = font(WHITE, True, 14)
        c.fill = fill(color)
        c.alignment = Alignment(horizontal="center", vertical="center")
        fcell = ws.cell(9, start_col, formula)
        fcell.font = font(WHITE, True, 24)
        fcell.fill = fill(color)
        fcell.alignment = Alignment(horizontal="center", vertical="center")
        for row in ws.iter_rows(min_row=8, max_row=10, min_col=start_col, max_col=start_col + 2):
            for cell in row:
                cell.fill = fill(color)
        ws.row_dimensions[8].height = 26
        ws.row_dimensions[9].height = 34
        ws.row_dimensions[10].height = 18

    section(ws, 12, "一、本周管理层结论", 12)
    conclusions = [
        ("交付节奏", "哈尔滨、安徽体职院、富士康、江西体科医、四方达等项目节点集中在 9 月上中旬；设备、服务器和现场资源是当前关键路径。"),
        ("偏差判断", "会议材料能证明部分项目完成联调或具备运营条件，但不能证明团队已完成 20 个项目或 600 万元目标；验收、合同、金额和回款台账仍缺失。"),
        ("支持重点", "管理层需优先拍板金额目标、计入绩效人数和江西体科医日期；采购、商务、财务、产品与硬件需按最晚决策时间关闭资源缺口。"),
    ]
    for i, (topic, text) in enumerate(conclusions, 14):
        ws.merge_cells(start_row=i, start_column=1, end_row=i, end_column=2)
        ws.merge_cells(start_row=i, start_column=3, end_row=i, end_column=12)
        ws.cell(i, 1, topic).font = font(WHITE, True)
        ws.cell(i, 1).fill = fill(TEAL)
        ws.cell(i, 1).alignment = Alignment(horizontal="center", vertical="center")
        ws.cell(i, 3, text).font = font()
        ws.cell(i, 3).fill = fill(PALE_BLUE if i % 2 == 0 else WHITE)
        ws.cell(i, 3).alignment = Alignment(vertical="center", wrap_text=True)
        ws.row_dimensions[i].height = 45

    section(ws, 18, "二、需要管理层立即支持的 5 件事", 12, RED)
    header_row(ws, 19, ["优先级", "事项", "需要的支持", "建议决策/动作", "最晚时间", "责任方"], RED)
    widths = [1, 2, 4, 7, 10, 11, 13]
    support = [
        ("P0", "经营目标口径", "明确 600 万元/月与四个月 2200 万元哪个为准", "二选一：600万/月、四个月2400万；或四个月2200万、月均550万", "下发绩效前", "管理层/财务"),
        ("P0", "绩效适用人数", "明确每人4项/月适用于哪些岗位", "团队20项/月隐含5人；本次名单8人，需锁定计入范围", "下发绩效前", "管理层/HR/交付"),
        ("P0", "江西体科医日期", "确认9月24日与28日分别是什么节点", "明确试运行、正式上线或纠正日期", "发布排期前", "劲玮/客户"),
        ("P0", "哈尔滨关键路径", "采购、硬件和交付联合保障设备与现场资源", "按9月15日上线、15-22日跟餐、22日交付倒排", "立即", "采购/硬件/交付"),
        ("P0", "安徽体职院安装", "批准安装人力与费用路径", "锁定30台安装人员、约2000多元费用口径及验收方式", "9月8日前", "商务/财务/交付"),
    ]
    for idx, row_data in enumerate(support, 20):
        cells = []
        for value, start, end in zip(row_data, widths[:-1], widths[1:]):
            ws.merge_cells(start_row=idx, start_column=start, end_row=idx, end_column=end - 1)
            c = ws.cell(idx, start, value)
            c.font = font(RED if start == 1 else NAVY, True if start in (1, 2) else False)
            c.fill = fill(PALE_RED if idx % 2 == 0 else WHITE)
            c.alignment = Alignment(vertical="center", wrap_text=True)
            cells.append(c)
        ws.row_dimensions[idx].height = 48

    section(ws, 26, "三、经营目标算术核对（均为会议目标，不代表实际完成）", 12, ORANGE)
    header_row(ws, 27, ["指标", "原始口径", "公式核对", "管理含义"], ORANGE)
    kpis = [
        ("团队月项目目标", 20, "个/月", "与四个月80个一致"),
        ("人均月项目目标", 4, "个/人/月", "需明确计入考核岗位"),
        ("隐含计入人数", "=D28/D29", "人", "20个/月 ÷ 4个/人/月"),
        ("本次名单人数", 8, "人", "若全部计入，理论为32个/月"),
        ("按本次名单理论项目数", "=D29*D31", "个/月", "与团队20个/月不一致"),
        ("月金额目标", 600, "万元/月", "会议目标，实际待财务台账"),
        ("四个月声明目标", 2200, "万元", "会议目标"),
        ("按月目标推算四个月", "=D33*4", "万元", "应为2400万元"),
        ("金额口径差异", "=D35-D34", "万元", "需管理层确认"),
        ("若2200万元为准的月均", "=D34/4", "万元/月", "应为550万元/月"),
    ]
    for r, (metric, value, unit, meaning) in enumerate(kpis, 28):
        ws.merge_cells(start_row=r, start_column=1, end_row=r, end_column=3)
        ws.merge_cells(start_row=r, start_column=4, end_row=r, end_column=5)
        ws.merge_cells(start_row=r, start_column=6, end_row=r, end_column=7)
        ws.merge_cells(start_row=r, start_column=8, end_row=r, end_column=12)
        ws.cell(r, 1, metric)
        ws.cell(r, 4, value)
        ws.cell(r, 6, unit)
        ws.cell(r, 8, meaning)
        for c in (ws.cell(r, 1), ws.cell(r, 4), ws.cell(r, 6), ws.cell(r, 8)):
            c.font = font(RED if metric == "金额口径差异" else NAVY, metric in ("金额口径差异", "按月目标推算四个月"))
            c.fill = fill(PALE_RED if metric == "金额口径差异" else (PALE_GRAY if r % 2 == 0 else WHITE))
            c.alignment = Alignment(vertical="center", wrap_text=True)
            c.border = border
        ws.row_dimensions[r].height = 28
    ws["D33"].comment = Comment("来源：2026-09-04 多项目交付与运维周会，A/B级组合证据；金额为目标，不是实际收入。", "Codex")
    ws.freeze_panes = "A4"
    ws.sheet_properties.pageSetUpPr.fitToPage = True
    ws.page_setup.orientation = "landscape"
    ws.page_setup.fitToWidth = 1
    ws.page_setup.fitToHeight = 2
    ws.print_area = "A1:L37"
    for col in range(1, 13):
        ws.column_dimensions[get_column_letter(col)].width = 14


def build_deviations(ws):
    set_title(
        ws,
        "偏差与支持台账",
        "先处理影响上线、验收和经营口径的 P0 偏差；支持请求写到部门、动作和最晚时间，避免停留在“协调中”。",
        12,
    )
    headers = ["序号", "优先级", "项目/事项", "目标", "当前事实", "偏差/风险", "可能影响", "需要的支持", "支持部门/责任方", "最晚时间", "验收/关闭标准", "状态"]
    header_row(ws, 5, headers, RED)
    rows = [
        (1, "P0", "经营目标金额", "600万元/月；四个月2200万元", "目标已提出，未提供实际收入台账", "600×4=2400，与2200相差200万元", "绩效拆解和经营判断失真", "管理层二选一冻结口径", "管理层/财务", "下发绩效前", "月/四个月目标一致并书面下发", "待决策"),
        (2, "P0", "人均交付目标", "每人4项/月；团队20项/月", "本次名单8人", "20÷4隐含5人，适用人数不清", "重复计数或目标失真", "明确计入岗位及唯一项目主责", "管理层/HR/交付", "下发绩效前", "人员范围和计数规则唯一", "待决策"),
        (3, "P0", "江西体科医", "9月24日/28日上线", "两日期同时出现；医务侧提供服务器", "节点含义冲突，服务器交付日未锁定", "部署、测试和上线无法倒排", "客户确认试运行/正式上线及服务器到位日", "劲玮/客户/医务", "发布排期前", "日期唯一且服务器验收", "待确认"),
        (4, "P0", "哈尔滨", "9月15日上线；22日整体交付", "设备预计13日前后到货", "安装联调缓冲短，跟餐窗口固定", "延误将直接挤压7天运行观察", "采购、硬件、交付联合锁定到货和现场资源", "采购/硬件/交付", "立即", "15日上线、7天运行记录、22日交付确认", "进行中"),
        (5, "P0", "安徽体职院", "9月8日交付30台动感单车", "纯硬件安装；安装费约2000多元，厂家报价2400元", "现场安装人员与费用路径未确认", "无法按期安装或产生临时费用", "批准安装人力、费用和现场联系人", "商务/财务/交付", "9月8日前", "30台安装、通电测试、客户签收", "待支持"),
        (6, "P0", "金斯瑞", "到货后约3天完成安装", "取餐柜物流当天才发出，单号未同步", "提前出发可能形成空等和差旅浪费", "采购/物流提供单号和到货承诺", "采购/物流/赵野", "出发前", "到货可追踪且行程与到货匹配", "待核实"),
        (7, "P1", "富士康", "34个摄像头、留样秤，6天交付", "有交付时长，无明确起止日", "点位、网络、到货和验收标准未冻结", "6天目标无法执行和复盘", "客户与硬件共同确认点位图和进场条件", "海宇/硬件/客户", "进场前", "34点位联调、留样秤测试、客户确认", "待支持"),
        (8, "P1", "北体大人脸识别", "降低误识别并保持速度", "个别误识别与相似度、采集不规范相关", "提高阈值可能影响识别速度，客户未同意", "错误继续影响用餐体验", "产品/算法提供A/B测试和采集规范", "令颖/赵野/产品", "下周方案会", "误识别率、耗时和客户确认均有记录", "待验证"),
        (9, "P1", "城市副中心", "交付收尾并稳定运维", "PAD有功能未上线；设备黑屏；口水罩划手", "未定位具体设备和根因", "安全风险、客户投诉和运维反复", "硬件现场定位，产品给出PAD上线时间", "赵野/硬件/产品", "下周", "设备序列号、照片、措施、复测、客户确认", "待支持"),
        (10, "P1", "仙龙潭维修", "下周完成维修", "本周已去现场，部分配件未到", "维修受配件到货制约", "项目继续延期", "采购/供应商确认到货时间", "令颖/采购/供应商", "下周", "配件签收、维修复测、客户确认", "进行中"),
        (11, "P1", "小程序商户号能力", "交付团队可独立配置", "团队缺少注册、证书、密钥配置的系统文档", "重复问人、配置错误和交付依赖", "产品组织培训并输出标准检查单", "产品/前端/赵野", "下周", "文档、演示、问答和检查单齐全", "待排期"),
        (12, "P1", "需求变更管理", "需求边界、周期和费用同步受控", "重庆环卫、江苏国庆、三全均有变更或付费需求", "免费/付费/不支持边界未统一书面化", "交付周期和毛利失控", "产品、商务、交付统一变更单和报价规则", "产品/商务/交付", "下次需求确认前", "基线、变更、费用和客户确认可追溯", "待决策"),
        (13, "P2", "张家港", "形成正式交付排期", "当前尚未安排", "范围、资源和前置条件缺失", "进入月度目标但无法执行", "赵野与李潇完成项目启动信息", "李潇/赵野", "下次周会前", "开始日、完成日、资源和验收标准齐全", "待排期"),
        (14, "P2", "产品三版本", "形成运动员版、教学版、团餐版矩阵", "仅提出版本名称", "场景、功能、配置和不可承诺项未冻结", "售前承诺和交付范围不一致", "产品负责人组织版本评审", "劲玮/产品团队", "待确定", "三版本矩阵经产品与交付确认", "待排期"),
    ]
    for r, values in enumerate(rows, 6):
        body_row(ws, r, values, PALE_RED if values[1] == "P0" else (PALE_ORANGE if values[1] == "P1" else PALE_BLUE), 58)
        ws.cell(r, 1).alignment = Alignment(horizontal="center", vertical="center")
        ws.cell(r, 2).font = font(RED if values[1] == "P0" else ORANGE if values[1] == "P1" else BLUE, True)
        ws.cell(r, 2).alignment = Alignment(horizontal="center", vertical="center")
    ws.auto_filter.ref = f"A5:L{5 + len(rows)}"
    ws.freeze_panes = "A6"
    widths = [7, 9, 20, 24, 30, 31, 26, 32, 22, 16, 32, 12]
    for i, width in enumerate(widths, 1):
        ws.column_dimensions[get_column_letter(i)].width = width
    ws.sheet_properties.pageSetUpPr.fitToPage = True
    ws.page_setup.orientation = "landscape"
    ws.page_setup.fitToWidth = 1
    ws.page_setup.fitToHeight = 0
    ws.print_title_rows = "1:5"
    ws.print_area = f"A1:L{5 + len(rows)}"
    ws.conditional_formatting.add(f"B6:B{5 + len(rows)}", FormulaRule(formula=["B6=\"P0\""], fill=fill(PALE_RED)))


def build_projects(ws):
    reset_sheet(ws)
    set_title(
        ws,
        "数字技术中心周工作汇报（20260831-20260906）",
        "项目明细是偏差与支持台账的证据附表；未提供合同、金额、验收或回款证据的字段统一标记待确认。",
        18,
    )
    ws.merge_cells("A4:R4")
    ws["A4"] = "一、项目｜按“合同—采购—交付—上线—验收—回款”六节点管理，重点暴露偏差和支持需求"
    ws["A4"].font = font(WHITE, True, 14)
    ws["A4"].fill = fill(BLUE)
    ws["A4"].alignment = Alignment(vertical="center")
    header_row(ws, 5, ["序号", "项目类型", "项目阶段", "项目名称", "关键时间", "项目金额", "负责人", "合同", "采购", "交付", "上线", "验收", "回款", "本周进展/当前事实", "下周计划/后续动作", "问题与偏差", "需要的支持", "链接"], BLUE)
    rows = [
        (1, "数科", "交付", "哈尔滨项目", "9/15上线；9/22交付", "待商务确认", "海宇/赵野", "待确认", "设备预计9/13到货", "倒排中", "目标9/15", "目标9/22", "待财务确认", "已确认目标与到货节奏", "完成安装联调、跟餐和交付", "缓冲短，7天运行窗口固定", "采购、硬件和交付联合保障", "会议纪要"),
        (2, "数科", "需求评估", "大庆油田", "会议当日输出", "待商务确认", "海宇", "待确认", "待确认", "未触发", "未触发", "未触发", "未触发", "待发开发估时和定制化比例", "形成估时与偏差分析", "缺估时依据和范围冻结", "产品/研发确认工作量", "会议纪要"),
        (3, "硬件", "交付", "安徽体职院", "9/8交付", "待商务确认", "海宇", "待确认", "30台动感单车", "安装资源待确认", "目标9/8", "待确认", "待财务确认", "确认纯硬件安装需求", "完成30台安装与签收", "人员与约2000多元安装费未锁定", "商务/财务/交付批准资源", "会议纪要"),
        (4, "食安硬件", "交付", "富士康项目", "6天交付", "待商务确认", "海宇", "待确认", "34摄像头+留样秤", "待进场", "待确认", "待确认", "待财务确认", "已明确设备规模和周期", "形成6天倒排并执行", "无起止日、点位图和验收标准", "硬件/客户冻结进场条件", "会议纪要"),
        (5, "数科", "交付", "江西体科医", "9/24或9/28", "待商务确认", "劲玮", "待确认", "服务器由医务侧提供", "待部署", "日期待确认", "待确认", "待财务确认", "已提出上线节点和服务器责任", "确认日期并倒排部署", "两个上线日期冲突", "客户确认节点和服务器到位日", "会议纪要"),
        (6, "数科", "运维/培训", "北体大项目", "9/15后培训", "待商务确认", "令颖/赵野", "待确认", "不涉及", "运维中", "已上线/待确认", "待确认", "待财务确认", "定位偶发人脸误识别原因", "A/B测试并组织培训", "阈值与速度存在权衡", "产品/算法支持测试方案", "会议纪要"),
        (7, "团餐", "售前", "固安宣武医院", "9/14-9/18", "待商务确认", "令颖", "待确认", "未触发", "未触发", "未触发", "未触发", "未触发", "明确售前支持窗口", "准备方案、演示和问题清单", "范围和交付承诺待冻结", "业务/产品提供客户背景", "会议纪要"),
        (8, "运维", "维修", "仙龙潭维修", "下周完成", "待商务确认", "令颖", "待确认", "配件未齐", "维修进行中", "不涉及", "待确认", "待财务确认", "已到场一次，部分配件未到", "到货后维修复测", "配件制约完成时间", "采购/供应商确认到货", "会议纪要"),
        (9, "数科", "待排期", "张家港项目", "下次周会前排期", "待商务确认", "李潇/赵野", "待确认", "待确认", "未排期", "未触发", "未触发", "未触发", "会议确认尚未安排", "形成范围、资源和排期", "缺少启动信息", "项目负责人补齐前置条件", "会议纪要"),
        (10, "数科", "运维", "滨州项目", "本周收尾", "待商务确认", "世伟/金洋", "待确认", "已到场", "跟餐稳定", "主流程已跑通", "待确认", "待财务确认", "完成跟餐、账号和操作培训", "撤场并完成风险交接", "扫码器、人脸支付以重启规避", "硬件/产品给出根因方案", "会议纪要"),
        (11, "校园", "运维", "彩育3校", "本周恢复", "待商务确认", "世伟", "待确认", "不涉及", "已恢复", "已上线/待确认", "待确认", "待财务确认", "恢复海康平台并完成培训", "补网络配置和关闭证据", "新网络可能再次影响访问", "网络配置留档", "会议纪要"),
        (12, "数科", "试运营", "中央美院", "下周观察约3天", "待商务确认", "玉龙", "待确认", "20台410等已安装", "联调完成", "具备运营条件", "培训后触发", "待财务确认", "设备、服务器、AI盒子和消费主流程已联调", "完成培训和运营观察", "正式开餐门槛未完全关闭", "甲方协调培训与开餐准备", "会议纪要"),
        (13, "数科", "交付", "金斯瑞", "到货后约3天", "待商务确认", "金洋/赵野", "待确认", "取餐柜在途", "待安装", "待确认", "待确认", "待财务确认", "计划安装并处理遗留问题", "核实物流后确定行程", "单号和到货日未同步", "采购/物流提供到货承诺", "会议纪要"),
        (14, "数科", "试运行", "四方达", "下周三试运行", "待商务确认", "赵野", "待确认", "已完成/待确认", "测试完成", "下下周切换", "待确认", "待财务确认", "测试已完成，客户计划两部门试运行", "跟场2天并准备切换", "小程序等客户配合项待到位", "客户按期提供配合项", "会议纪要"),
        (15, "数科", "方案确认", "三全项目", "下周再次对齐", "待商务确认", "赵野", "待确认", "不涉及", "未触发", "未触发", "未触发", "未触发", "进销存和结算蓝皮书已提交", "冻结方案与变更边界", "客户内部尚未定案", "客户/产品完成书面确认", "会议纪要"),
        (16, "数科", "运维", "城市副中心", "下周闭环", "待商务确认", "赵野", "待确认", "PAD已交付", "交付类工作完成", "部分功能未上线", "待确认", "待财务确认", "进入运维；识别黑屏和口水罩问题", "定位设备并推动PAD功能上线", "具体设备和根因未定位", "硬件现场支持、产品给出日期", "会议纪要"),
        (17, "硬件", "返厂/运维", "上海崇明一体机", "费用确认后推进", "待商务确认", "赵野", "合同状态待确认", "已发厂商", "返厂中", "不涉及", "待确认", "待财务确认", "一体机已发往厂商", "确认费用和合同路径", "费用承担方不清", "商务/业务确认合同与采购", "会议纪要"),
        (18, "数科", "交付", "重庆环卫", "1周主要交付；1-2周整体", "待商务确认", "玉龙", "待确认", "待确认", "已达进场标准", "待上线", "待确认", "待财务确认", "持续处理需求池和变更", "冻结基线并完成交付", "需求频繁变化，周期和费用风险", "产品/商务统一变更规则", "会议纪要"),
        (19, "数科", "需求澄清", "上海项目", "待确定", "待商务确认", "令颖/王金洋", "待确认", "待确认", "未触发", "未触发", "未触发", "未触发", "需双方沟通", "明确项目问题和责任边界", "项目全称、问题和节点缺失", "业务补充完整项目背景", "会议纪要"),
    ]
    for r, values in enumerate(rows, 6):
        body_row(ws, r, values, PALE_GRAY if r % 2 == 0 else WHITE, 54)
        ws.cell(r, 1).alignment = Alignment(horizontal="center", vertical="center")
        if any(word in str(values[15]) for word in ("冲突", "未", "缺", "风险", "短")):
            ws.cell(r, 16).fill = fill(PALE_RED)
        ws.cell(r, 18).hyperlink = MINUTES.as_uri()
        ws.cell(r, 18).style = "Hyperlink"
        ws.cell(r, 18).font = font(BLUE, False)
    ws.auto_filter.ref = f"A5:R{5 + len(rows)}"
    ws.freeze_panes = "A6"
    widths = [7, 11, 13, 22, 20, 15, 16, 14, 17, 16, 16, 15, 15, 34, 34, 34, 32, 14]
    for i, width in enumerate(widths, 1):
        ws.column_dimensions[get_column_letter(i)].width = width
    ws.sheet_properties.pageSetUpPr.fitToPage = True
    ws.page_setup.orientation = "landscape"
    ws.page_setup.fitToWidth = 1
    ws.page_setup.fitToHeight = 0
    ws.print_title_rows = "1:5"
    ws.print_area = f"A1:R{5 + len(rows)}"


def build_products(ws):
    reset_sheet(ws)
    set_title(
        ws,
        "数字技术中心周工作汇报（20260831-20260906）",
        "产品与机制沉淀｜把本周暴露的共性偏差转化为可复用标准，降低后续交付对个人经验的依赖。",
        9,
    )
    ws.merge_cells("A4:I4")
    ws["A4"] = "二、产品与交付标准化｜本周事实 → 标准资产 → 下周可验收状态"
    ws["A4"].font = font(WHITE, True, 14)
    ws["A4"].fill = fill(TEAL)
    ws["A4"].alignment = Alignment(vertical="center")
    header_row(ws, 5, ["序号", "一级分类", "二级分类", "工作内容", "对应项目/场景", "时间节点", "负责人", "本周输出/目标", "问题与需要的支持"], TEAL)
    rows = [
        (1, "产品标准化", "交付周会机制", "统一目标—结果—偏差—方案—完成时间—下周目标模板", "全部交付项目", "下周周会", "赵野/交付团队", "每人按统一模板汇报，减少流水账", "需要管理层坚持同一口径并按证据验收"),
        (2, "产品标准化", "需求偏差评估", "开发估时、定制化比例、免费/付费/不支持分类", "大庆油田、重庆环卫、江苏国庆、三全", "需求确认前", "海宇/产品/商务", "形成书面偏差和变更单", "需要产品、研发、商务共同确认工时和报价"),
        (3, "产品标准化", "三版本矩阵", "梳理运动员版、教学版、团餐版场景与边界", "江西体科医等", "待确定", "劲玮/产品团队", "形成场景、功能、配置、不可承诺项矩阵", "需要产品负责人组织评审并冻结版本"),
        (4, "产品标准化", "小程序商户号SOP", "沉淀注册、证书、密钥配置培训和检查单", "哈尔滨及后续小程序项目", "下周", "产品/前端/赵野", "交付团队可按文档独立完成配置", "需要产品排期和前端提供配置基线"),
        (5, "产品标准化", "人脸识别规范", "形成采集规范、阈值A/B测试和异常复测方法", "北体大、滨州", "下周方案会", "令颖/赵野/产品", "误识别与速度有数据对比和客户确认", "需要算法/产品提供可调参数和测试支持"),
        (6, "产品标准化", "硬件交付检查单", "摄像头、动感单车、留样秤、410等按点位/数量/通电/联调/签收验收", "富士康、安徽体职院、中央美院", "进场前", "海宇/玉龙/硬件", "形成一套可复用现场验收清单", "需要硬件部门确认型号、点位和验收项"),
        (7, "AI赋能", "纪要到偏差台账", "将会议原文转换为负责人、偏差、支持和验收闭环", "交付周会", "本周", "交付管理", "已形成会议纪要和偏差支持台账", "下周需用真实关闭证据验证周会机制有效性"),
        (8, "其他", "经营目标看板", "统一项目数、金额、验收、回款和唯一主责口径", "月度与四个月经营目标", "下发绩效前", "管理层/财务/HR", "目标口径一致并可周度回看", "当前缺实际验收、收入和回款台账"),
    ]
    for r, values in enumerate(rows, 6):
        body_row(ws, r, values, PALE_GRAY if r % 2 == 0 else WHITE, 60)
        ws.cell(r, 1).alignment = Alignment(horizontal="center", vertical="center")
        ws.cell(r, 9).fill = fill(PALE_ORANGE if values[1] != "其他" else PALE_RED)
    ws.auto_filter.ref = f"A5:I{5 + len(rows)}"
    ws.freeze_panes = "A6"
    widths = [7, 16, 20, 36, 28, 17, 20, 38, 38]
    for i, width in enumerate(widths, 1):
        ws.column_dimensions[get_column_letter(i)].width = width
    ws.sheet_properties.pageSetUpPr.fitToPage = True
    ws.page_setup.orientation = "landscape"
    ws.page_setup.fitToWidth = 1
    ws.page_setup.fitToHeight = 0
    ws.print_title_rows = "1:5"
    ws.print_area = f"A1:I{5 + len(rows)}"


def build_sources(ws):
    set_title(ws, "来源索引与证据边界", "只记录本周周报实际使用的来源；材料中未出现的财务、验收和回款事实不补造。", 8)
    header_row(ws, 5, ["序号", "来源", "日期", "证据等级", "本周用途", "允许结论", "缺口/边界", "状态"], GRAY)
    rows = [
        (1, "Jack本次线程补充", "2026-09-04", "A", "人员、项目节点、经营目标", "可作为周报目标和责任映射", "未给正式验收、合同、收入和回款台账", "已使用"),
        (2, "原文_多项目交付与运维周会纪要.pdf", "2026-09-04", "B", "逐字转写、项目进展和管理点评", "可支持会议中陈述的事实与计划", "自动转写存在近音字；发言不等于已验收", "已使用"),
        (3, "纪要_多项目交付与运维周会纪要.pdf", "2026-09-04", "C", "机器摘要与待办辅助", "仅辅助结构化", "机器分析不作为正式决议", "已使用"),
        (4, "多项目交付与运维周会纪要", "2026-09-04", "A/B/C汇编", "偏差、行动、支持和算术核对", "可作为本周周报直接来源", "仍有9项待确认", "已使用"),
        (5, "9月周会连续性扫描", "2026-09-01至09-04", "覆盖检查", "检查同月既有会议/周报", "本次范围内仅发现9月4日会议", "W36保盘硬件周报是另一独立未跟踪任务，未并入本报告", "已说明"),
    ]
    for r, values in enumerate(rows, 6):
        body_row(ws, r, values, PALE_GRAY if r % 2 == 0 else WHITE, 48)
        ws.cell(r, 1).alignment = Alignment(horizontal="center", vertical="center")
    widths = [7, 35, 16, 14, 30, 34, 40, 12]
    for i, width in enumerate(widths, 1):
        ws.column_dimensions[get_column_letter(i)].width = width
    ws.freeze_panes = "A6"
    ws.auto_filter.ref = f"A5:H{5 + len(rows)}"
    ws.sheet_properties.pageSetUpPr.fitToPage = True
    ws.page_setup.orientation = "landscape"
    ws.page_setup.fitToWidth = 1
    ws.page_setup.fitToHeight = 1
    ws.print_area = f"A1:H{5 + len(rows)}"


def main():
    if not TEMPLATE.exists():
        raise FileNotFoundError(TEMPLATE)
    wb = load_workbook(TEMPLATE)
    project = wb["项目"]
    product = wb["产品"]
    build_projects(project)
    build_products(product)

    summary = wb.create_sheet("管理摘要", 0)
    deviations = wb.create_sheet("偏差与支持", 1)
    sources = wb.create_sheet("来源索引")
    build_summary(summary)
    build_deviations(deviations)
    build_sources(sources)

    wb.calculation.fullCalcOnLoad = True
    wb.calculation.forceFullCalc = True
    wb.calculation.calcMode = "auto"
    normalize_fonts(wb)
    OUTPUT.parent.mkdir(parents=True, exist_ok=True)
    wb.save(OUTPUT)
    print(OUTPUT)


if __name__ == "__main__":
    main()
