#!/usr/bin/env python3
"""Build detailed template-style workbooks for three easier-to-crawl projects."""

from __future__ import annotations

from pathlib import Path
from typing import Any

from openpyxl import Workbook
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
from openpyxl.utils import get_column_letter


ROOT = Path(__file__).resolve().parents[1]
OUTPUT_DIR = ROOT / "docs" / "deliverables" / "20260615-full-candidate-tender-details"
TEMPLATE = ROOT / "docs" / "templates" / "tender-detail-template" / "【2025】海淀区校园食安项目.xlsx"

FONT = "Microsoft YaHei"
BLUE = "1F4E79"
LIGHT_BLUE = "D9EAF7"
GREEN = "E2F0D9"
YELLOW = "FFF2CC"
RED = "FCE4D6"
WHITE = "FFFFFF"
BORDER = Border(
    left=Side(style="thin", color="B7C9DA"),
    right=Side(style="thin", color="B7C9DA"),
    top=Side(style="thin", color="B7C9DA"),
    bottom=Side(style="thin", color="B7C9DA"),
)


SOURCES = {
    "nj_bid_notice": "https://njggzy.nanjing.gov.cn/njweb/gchw/070001/20241009/643b0162-4a8c-40ad-9d3e-d2cadbfe538d.html",
    "nj_candidate": "https://njggzy.nanjing.gov.cn/njweb/gchw/070003/20241101/b5cbf419-d1d5-48cb-93ac-1161cd26280b.html",
    "nj_result": "https://njggzy.nanjing.gov.cn/njweb/gchw/070004/20241210/34070c31-fcac-441c-89c2-ad9706ecef1c.html",
    "nj_metro_notice": "https://www.njmetro.com.cn/njdtweb/portal/main-article-detail.do?rowId=4028d48191538d4c0192704098e201d3&columnId=8a808007651c972001651d88ebe20001&columnName=search",
    "xnzn_nanjing_metro_case": "https://www.xnzn.net/qiye/142.html",
    "xnzn_ln508ca": "https://www.xnzn.net/xiaofeijishoufanji/renlianshibiexiaofeiji.html",
    "xnzn_bw0115": "https://www.xnzn.net/cantai/baowentai.html",
    "citic_bid": "https://ebid.cfhc.citic/cms/default/webfile/ywgg1/20250523/1110611760571744256.html",
    "citic_candidate": "https://ebid.cfhc.citic/cms/default/webfile/ywgg2/20250605/1115365101285670912.html",
    "abchina_bid": "https://e.sinochemitc.com/cms/channel/ywgg1fw/154689.htm",
    "abchina_candidate": "https://e.sinochemitc.com/cms/channel/ywgg2fw/155952.htm",
    "xnzn_about": "https://www.xnzn.net/about",
    "smartopshub_xnzn": "https://www.smartopshub.net/enterprise_library/showimg.php?id=118",
    "jiangsu_mobile_highbay": "https://highbay.cn/merchant/jiangsuyidongxinxixitongjichengyouxiangongsi/",
    "yunpeng_site": "https://www.yunpengai.com/",
    "yunpeng_intro": "https://www.yunpengai.com/55.html",
    "yunpeng_appstore": "https://apps.apple.com/cn/app/ai%E8%86%B3%E9%A3%9F%E8%90%A5%E5%85%BB%E5%B8%88/id6446716692",
    "yunpeng_zjnu": "https://zcgs.zjnu.edu.cn/2025/0428/c12035a516677/page.htm",
}


def style(cell, fill: str | None = None, bold: bool = False, color: str = "1F1F1F") -> None:
    cell.font = Font(name=FONT, size=10, bold=bold, color=color)
    cell.alignment = Alignment(wrap_text=True, vertical="top")
    cell.border = BORDER
    if fill:
        cell.fill = PatternFill("solid", fgColor=fill)


def title_row(ws, row: int, title: str, cols: int) -> None:
    ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=cols)
    cell = ws.cell(row, 1, title)
    cell.font = Font(name=FONT, size=15, bold=True, color=WHITE)
    cell.fill = PatternFill("solid", fgColor=BLUE)
    cell.alignment = Alignment(horizontal="center", vertical="center")
    cell.border = BORDER
    ws.row_dimensions[row].height = 32


def write_table(ws, start_row: int, title: str, headers: list[str], rows: list[list[Any]]) -> int:
    title_row(ws, start_row, title, len(headers))
    header_row = start_row + 1
    for col, header in enumerate(headers, 1):
        cell = ws.cell(header_row, col, header)
        style(cell, LIGHT_BLUE, True, "14344A")
        cell.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
    row_num = header_row
    for row in rows:
        row_num += 1
        for col, value in enumerate(row, 1):
            fill = None
            if value == "✓":
                fill = GREEN
            elif value == "△":
                fill = YELLOW
            elif value == "✗":
                fill = RED
            cell = ws.cell(row_num, col, value)
            style(cell, fill)
    ws.auto_filter.ref = f"A{header_row}:{get_column_letter(len(headers))}{row_num}"
    return row_num + 2


def set_widths(ws, widths: list[int]) -> None:
    for idx, width in enumerate(widths, 1):
        ws.column_dimensions[get_column_letter(idx)].width = width
    for row in range(1, ws.max_row + 1):
        ws.row_dimensions[row].height = 42
    ws.freeze_panes = "A3"


def common_software_rows(project_context: str, disclosed: str) -> list[list[Any]]:
    modules = [
        ("智慧食堂管理平台", "账户/权限、基础档案、餐厅/档口、设备、菜单、交易和报表管理。"),
        ("智能结算/收银", "支持人脸识别、刷卡、二维码、称重计量、芯片识别、AI识别等结算场景。"),
        ("设备管理", "终端注册、设备状态、版本升级、故障报修、维修保养和远程运维。"),
        ("交易与对账", "订单流水、补贴/扣款、对账、退款、营业报表和异常交易核查。"),
        ("数据报表", "按食堂、餐线、设备、人员、时间段统计消费、客流、销售和设备在线率。"),
        ("接口集成", "对接银行、校园/企业一卡通、支付平台、门禁/闸机、第三方餐饮系统。"),
        ("移动端/管理端", "支持用户点餐、消费查询、管理审批、运维工单和消息通知。"),
        ("安全与权限", "角色权限、日志审计、数据安全、接口鉴权、供应商账号隔离。"),
    ]
    return [[i, name, desc, disclosed, project_context] for i, (name, desc) in enumerate(modules, 1)]


def competition_ranks(values: list[float], reverse: bool = False) -> list[int]:
    sorted_values = sorted(set(values), reverse=reverse)
    return [sorted_values.index(value) + 1 for value in values]


def format_number(value: float) -> str:
    if float(value).is_integer():
        return str(int(value))
    return f"{value:.2f}".rstrip("0").rstrip(".")


def nj_score_detail_rows(scores: list[list[Any]], quotes: dict[str, float]) -> list[list[Any]]:
    totals = [float(row[5]) for row in scores]
    total_ranks = competition_ranks(totals, reverse=True)
    price_scores = [float(row[3]) for row in scores]
    price_score_ranks = competition_ranks(price_scores, reverse=True)

    rows: list[list[Any]] = []
    for idx, row in enumerate(scores):
        bidder, business, technical, price_score, other, total = row
        calculated = float(business) + float(technical) + float(price_score) + float(other)
        quote = quotes.get(str(bidder))
        quote_note = "前三名披露投标报价" if quote is not None else "公示得分表披露该投标人得分，但未在前三名表中披露报价金额"
        rows.append(
            [
                idx + 1,
                bidder,
                business,
                technical,
                price_score,
                other,
                total,
                f"{business:.2f}+{technical:.2f}+{price_score:.2f}+{other:.2f}={calculated:.2f}",
                "一致" if round(calculated, 2) == round(float(total), 2) else "需复核",
                total_ranks[idx],
                price_score_ranks[idx],
                f"{quote:.6f}万元" if quote is not None else "未披露",
                quote_note,
            ]
        )
    return rows


def nj_top3_quote_rows(quotes: dict[str, float], scores: list[list[Any]]) -> list[list[Any]]:
    score_map = {str(row[0]): row for row in scores}
    quote_values = list(quotes.values())
    quote_ranks = competition_ranks(quote_values)
    rows: list[list[Any]] = []
    for idx, (bidder, quote) in enumerate(quotes.items()):
        score_row = score_map[bidder]
        total = float(score_row[5])
        rows.append(
            [
                idx + 1,
                bidder,
                f"{quote:.6f}万元",
                quote_ranks[idx],
                float(score_row[3]),
                float(score_row[4]),
                total,
                "最低报价未中标" if quote_ranks[idx] == 1 and total != max(float(row[5]) for row in scores) else "综合得分决定排序",
            ]
        )
    return rows


def nj_technical_zero_rows(scores: list[list[Any]]) -> list[list[Any]]:
    factors = [
        "对投标人技术服务和质保期服务能力的评价",
        "对投标设备整体评价",
        "投标设备技术性能指标的响应程度",
    ]
    rows: list[list[Any]] = []
    index = 0
    for bidder, *_ in scores:
        for factor in factors:
            index += 1
            rows.append([index, bidder, factor, "7名评委均为0.00", "公示页技术标部分显示该因素所有评委分值均为0.00"])
    return rows


def nj_performance_rows() -> list[list[Any]]:
    return [
        [1, "南京小牛智能科技有限公司", "资审业绩", "南京外国语学校南部新城校区智能化系统工程项目", "江苏锦沃智能科技有限公司", "1200000元"],
        [2, "南京小牛智能科技有限公司", "评分业绩", "南京地铁运营有限责任公司食堂智能化系统移动支付场景合作项目", "南京地铁运营有限责任公司", "3415852元"],
        [3, "南京小牛智能科技有限公司", "评分业绩", "南京市政府大院信息化改造项目二期", "中国移动通信集团江苏有限公司南京分公司", "1329000元"],
        [4, "南京小牛智能科技有限公司", "评分业绩", "南京卷烟厂用餐智能化建设项目", "江苏中烟工业有限责任公司", "1167170元"],
        [5, "南京小牛智能科技有限公司", "评分业绩", "南京钢铁智慧食堂项目", "江苏金恒信息科技股份有限公司", "1450000元"],
        [6, "江苏移动信息系统集成有限公司", "资审业绩", "中国工商银行股份有限公司连云港分行连云港职业技术学院银校合作项目-学校智慧校园采购建设", "中国工商银行股份有限公司连云港分行", "5140000元"],
        [7, "江苏移动信息系统集成有限公司", "评分业绩", "中国工商银行股份有限公司连云港分行连云港职业技术学院银校合作项目-学校智慧校园采购建设", "中国工商银行股份有限公司连云港分行", "5140000元"],
        [8, "浙江云澎科技有限公司", "资审业绩", "金义东轨道交通有限公司智慧园区项目", "中国银行股份有限公司金华市分行", "101万元"],
        [9, "浙江云澎科技有限公司", "评分业绩", "金义东轨道交通有限公司智慧园区项目", "中国银行股份有限公司金华市分行", "101万元"],
        [10, "浙江云澎科技有限公司", "评分业绩", "永康市龙川学校校园一卡通项目", "中国银行股份有限公司永康市支行", "50万元"],
        [11, "浙江云澎科技有限公司", "评分业绩", "中数科谷（清远）科技发展有限公司智能设备销售", "中数科谷（清远）科技发展有限公司", "54万元"],
        [12, "浙江云澎科技有限公司", "评分业绩", "上海泽琳信息科技有限公司智能设备采购", "上海泽琳信息科技有限公司", "81.97万元"],
    ]


def nj_bid_notice_rows() -> list[list[Any]]:
    return [
        [1, "招标编号", "NJHW-230123-8", "招标公告", SOURCES["nj_bid_notice"]],
        [2, "招标人", "南京地铁运营有限责任公司", "招标公告", SOURCES["nj_bid_notice"]],
        [3, "项目名称", "南京地铁5号线工程", "招标公告", SOURCES["nj_bid_notice"]],
        [4, "采购设备", "食堂智能结算设备，一批", "招标公告", SOURCES["nj_bid_notice"]],
        [5, "建设/交货地点", "南京地铁5号线、6号线、9号线、10号线、11号线及宁马线食堂", "招标公告", SOURCES["nj_bid_notice"]],
        [6, "公告交货期", "10天；候选/结果页第一名为接招标人送货通知5天内供货", "招标公告+候选公示", f"{SOURCES['nj_bid_notice']}\n{SOURCES['nj_candidate']}"],
        [7, "资格：营业执照", "境内注册，具有独立法人资格，不接受以分支机构为代理人的申请，能提供本次招标服务的企业。", "招标公告", SOURCES["nj_bid_notice"]],
        [8, "资格：财务审计", "须提供2023年度经会计师事务所或审计机构审计的财务审计报告，含资产负债表、现金流量表、利润表和财务情况说明书/附注。", "招标公告", SOURCES["nj_bid_notice"]],
        [9, "资格：类似业绩门槛", "须具有2021年1月1日以来单项合同金额不低于40万元的类似食堂智能结算设备采购项目业绩。", "招标公告", SOURCES["nj_bid_notice"]],
        [10, "资格：信誉承诺", "重要内容无失实或弄虚作假；未被责令停业、取消投标资格、财产接管/冻结/破产；无骗取中标、严重违约、重大质量/安全事故等暂停投标资格情形。", "招标公告", SOURCES["nj_bid_notice"]],
        [11, "资格：关联限制", "法定代表人为同一人、集团公司与全资/控股子公司等情形不得参加同一标段投标。", "招标公告", SOURCES["nj_bid_notice"]],
        [12, "资格：失信限制", "不得被列入失信被执行人名单，以信用中国网站严重失信主体名单查询结果为准。", "招标公告", SOURCES["nj_bid_notice"]],
        [13, "联合体", "不接受联合体投标。", "招标公告", SOURCES["nj_bid_notice"]],
        [14, "招标文件获取方式", "登录南京市公共资源交易中心货物网上交易平台（http://221.226.86.168:8081）免费下载电子招标文件。公共网页附件接口返回空。", "招标公告+接口复核", SOURCES["nj_bid_notice"]],
        [15, "投标截止时间", "2024-10-31 09:30", "招标公告", SOURCES["nj_bid_notice"]],
    ]


def nj_business_inference_rows() -> list[list[Any]]:
    return [
        [1, "南京小牛智能科技有限公司", 7, "4项", "南京地铁运营食堂智能化、南京市政府大院信息化二期、南京卷烟厂用餐智能化、南京钢铁智慧食堂", "7分与4项评分业绩高度对应，疑似基础分3分+评分业绩每项1分、最高加4分。", "反推，待招标文件确认"],
        [2, "江苏移动信息系统集成有限公司", 4, "1项", "连云港职业技术学院银校合作项目-学校智慧校园采购建设", "4分与1项评分业绩高度对应；这能解释为什么江苏移动不是7分。", "反推，待招标文件确认"],
        [3, "浙江云澎科技有限公司", 7, "4项", "金义东轨道交通智慧园区、永康市龙川学校校园一卡通、中数科谷智能设备销售、上海泽琳智能设备采购", "7分与4项评分业绩高度对应。", "反推，待招标文件确认"],
        [4, "南京宁融信网络科技有限公司", 3, "未公开", "候选公示未列该投标人业绩明细", "若按上述反推，3分可能是商务基础分、无加分业绩；但不能确认。", "缺业绩明细"],
        [5, "南京宝华智能科技有限公司", 3, "未公开", "候选公示未列该投标人业绩明细", "若按上述反推，3分可能是商务基础分、无加分业绩；但不能确认。", "缺业绩明细"],
        [6, "湖南孚谷时代科技有限公司", 4, "未公开", "候选公示未列该投标人业绩明细", "若按上述反推，4分可能是1项加分业绩；但不能确认。", "缺业绩明细"],
        [7, "广州市大唐智讯电子技术有限公司", 6, "未公开", "候选公示未列该投标人业绩明细", "若按上述反推，6分可能是3项加分业绩；但不能确认。", "缺业绩明细"],
        [8, "北京新中新诚通信息技术股份有限公司", 6, "未公开", "候选公示未列该投标人业绩明细", "若按上述反推，6分可能是3项加分业绩；但不能确认。", "缺业绩明细"],
        [9, "杭州雄伟科技开发股份有限公司", 7, "未公开", "候选公示未列该投标人业绩明细", "若按上述反推，7分可能是4项或以上加分业绩；但不能确认。", "缺业绩明细"],
    ]


def nj_price_formula_trace_rows(scores: list[list[Any]], quotes: dict[str, float]) -> list[list[Any]]:
    score_map = {str(row[0]): row for row in scores}
    rows = [
        ["公开事实", "招标公告/候选公示披露评标办法为综合评估法，但公共HTML未披露投标报价评分公式。", "无法直接确认评标基准价、扣分率、K值或是否有异常报价处理。", "需登录货物网上交易平台下载招标文件。"],
        ["同类南京工程货物规则", "同类南京工程货物公告常见写法：投标报价等于评标基准价得满分；每高于评标基准价1%扣若干分；每低于评标基准价1%扣若干分；偏离不足1%按插入法计算。", "这解释了为什么低价不必然高分：报价过低也会因偏离评标基准价被扣分。", "本项目原文仍需招标文件确认。"],
        ["本项目可反推公式", "按前三名报价与报价分反推，价格满分很可能为45分；评标基准价约在290.35万-291.12万元之间；高于基准价每1%约扣0.6分，低于基准价每1%约扣0.4分。", "浙江云澎220.811万元约低于基准价24%，因此扣约9.58分，45-9.58=35.42。", "这是强反推，不是招标文件原文。"],
        ["为什么云澎220万只有35.42分", "若以评标基准价约290.35万元、低于基准价每1%扣0.4分估算：偏离率=(220.811-290.35)/290.35=-23.95%，扣分=23.95×0.4=9.58，报价分=45-9.58=35.42。", "云澎不是因为报价低被奖励，而是因为低于基准价过多被扣分。", "计算与公示报价分完全对应，但基准价精确值需招标文件/评标报告。"],
        ["价格分满分/最高分", "有效投标人中最高投标报价得分为南京宁融信网络科技有限公司42.44分。", "南京宁融信报价金额未公开，不能反推完整公式。", "需开标记录或评标报告。"],
    ]
    for bidder, quote in quotes.items():
        row = score_map[bidder]
        rows.append(
            [
                bidder,
                f"投标报价{quote:.6f}万元；投标报价分{float(row[3]):.2f}；最终总分{float(row[5]):.2f}",
                f"其他得分{float(row[4]):.2f}；商务{float(row[1]):.2f}；技术{float(row[2]):.2f}",
                "报价金额与报价分均来自候选公示；公式需招标文件补齐。",
            ]
        )
    return rows


def nj_price_formula_inference_rows(scores: list[list[Any]], quotes: dict[str, float]) -> list[list[Any]]:
    score_map = {str(row[0]): row for row in scores}
    full_score = 45.0
    high_deduction = 0.6
    low_deduction = 0.4
    rows: list[list[Any]] = [
        [
            "公式口径",
            "报价分=45-扣分；高于评标基准价时扣分=高出百分比×0.6；低于评标基准价时扣分=低出百分比×0.4；偏离不足1%按插入法计算。",
            "该口径可解释前三名报价分，但不是本项目招标文件原文。",
            "需登录南京货物网上交易平台下载招标文件确认。",
            "",
            "",
            "",
            "",
        ]
    ]
    for bidder, quote in quotes.items():
        price_score = float(score_map[bidder][3])
        deficit = full_score - price_score
        if bidder == "浙江云澎科技有限公司":
            deviation_pct = deficit / low_deduction
            benchmark = quote / (1 - deviation_pct / 100)
            side = "低于基准价"
            formula = f"45-(({benchmark:.3f}-{quote:.3f})/{benchmark:.3f}×100×0.4)={price_score:.2f}"
        else:
            deviation_pct = deficit / high_deduction
            benchmark = quote / (1 + deviation_pct / 100)
            side = "高于基准价"
            formula = f"45-(({quote:.3f}-{benchmark:.3f})/{benchmark:.3f}×100×0.6)≈{price_score:.2f}"
        rows.append(
            [
                bidder,
                f"{quote:.6f}",
                price_score,
                f"{deficit:.2f}",
                side,
                f"{deviation_pct:.2f}%",
                f"{benchmark:.3f}",
                formula,
            ]
        )
    rows.append(
        [
            "反推结论",
            "三家公司分别反推出的基准价约为290.350万、290.626万、291.118万，差异来自公示分数四舍五入、可能的评标价修正或未公开的全体有效投标报价。",
            "浙江云澎低价未得高分，是因为本项目价格分看起来按偏离评标基准价扣分，不是最低价满分法。",
            "要得到精确基准价，必须取得招标文件评分办法和开标/评标报告。",
            "",
            "",
            "",
            "",
        ]
    )
    return rows


def nj_supplier_public_qualification_rows() -> list[list[Any]]:
    return [
        ["南京小牛智能科技有限公司", "基础工商信息", "法定代表人马勋；注册资本1018万元；成立时间2015年6月3日；注册地址南京江北新区丽景路2号研创园研发大厦A座8层。", "第三方企业库公开页", SOURCES["smartopshub_xnzn"], "非天眼查；需天眼查/国家企业信用系统复核证书编号和最新状态。"],
        ["南京小牛智能科技有限公司", "体系认证", "ISO9001、ISO14001、ISO45001；另官网荣誉区列示CMMI3、信息安全管理体系认证、知识产权管理体系认证、售后服务认证、高新技术企业等。", "企业官网/第三方企业库", f"{SOURCES['xnzn_about']}\n{SOURCES['smartopshub_xnzn']}", "官网/企业库披露，证书编号和有效期需登录查询。"],
        ["南京小牛智能科技有限公司", "知识产权", "官网披露三大研发中心、51项专利、56项著作权；企业发展历程处披露50项专利、56项软著。", "企业官网", SOURCES["xnzn_about"], "不同页面数字略有差异，按官网最新口径记录为51项专利、56项著作权。"],
        ["南京小牛智能科技有限公司", "产品/能力", "RFID、菜品识别、智慧餐台、消费终端、食安监管、供应链管理等智慧食堂软硬件产品。", "企业官网", SOURCES["xnzn_about"], "与本项目食堂智能结算设备高度相关。"],
        ["江苏移动信息系统集成有限公司", "基础工商信息", "企查查搜索结果披露成立于2010-02-08，法定代表人张海忠，注册资本50000万元，统一社会信用代码91320000551171586G，地址南京市虎踞路59号。", "公开搜索结果/企业页", "https://m.qcc.com/firm/0c60cd1de830933894d7d7029649dbf4.html", "移动页抓取受限；需天眼查/企查查登录复核。"],
        ["江苏移动信息系统集成有限公司", "能力/资质", "公开资料显示其为江苏移动相关信息系统集成主体；页面披露CMMI-DEV v1.3成熟度5级；安防平台搜索结果披露拥有62项自主软件产品著作权证书。", "第三方公开页/搜索结果", SOURCES["jiangsu_mobile_highbay"], "证书编号、证书有效期和软著清单需登录查询。"],
        ["江苏移动信息系统集成有限公司", "本项目业绩", "中国工商银行连云港分行连云港职业技术学院银校合作项目-学校智慧校园采购建设，合同金额5140000元。", "候选公示", SOURCES["nj_candidate"], "同时作为资审业绩和评分业绩披露。"],
        ["浙江云澎科技有限公司", "基础工商信息", "App Store开发者信息披露统一社会信用代码91330701MA29NXWF0P；官网披露金华、杭州地址。", "App Store/企业官网", f"{SOURCES['yunpeng_appstore']}\n{SOURCES['yunpeng_site']}", "法定代表人、股权和证书状态需天眼查复核。"],
        ["浙江云澎科技有限公司", "能力/资质", "公开介绍披露2017年9月7日成立、注册资金1000万元；国家高新技术企业；早期报道披露12项软件著作权、4项发明专利、5项实用新型专利、6项外观专利、ISO9001、3A级信用企业、浙江省质量诚信双优企业、政府采购优秀供应商。", "企业官网/公开报道", SOURCES["yunpeng_intro"], "报道时间较早，最新数量需天眼查或知识产权库复核。"],
        ["浙江云澎科技有限公司", "最新荣誉", "浙师大科技园2025年报道披露云澎科技已获批国家高新技术企业、浙江省专精特新中小企业、浙江省服务业领军企业，服务政府机关、学校及养老机构等超千余家。", "高校科技园公开报道", SOURCES["yunpeng_zjnu"], "可作为资质背景，不等于本项目评分项。"],
        ["浙江云澎科技有限公司", "本项目业绩", "金义东轨道交通智慧园区项目101万元；永康市龙川学校校园一卡通50万元；中数科谷智能设备销售54万元；上海泽琳智能设备采购81.97万元。", "候选公示", SOURCES["nj_candidate"], "4项评分业绩与商务7分高度对应。"],
    ]


def nj_login_gap_rows() -> list[list[Any]]:
    return [
        [1, "招标文件原文", "需登录南京市公共资源交易中心货物网上交易平台 http://221.226.86.168:8081 下载。公共招标公告无附件；附件接口返回空。", "待用户提供平台登录/CA或原始招标文件"],
        [2, "商务分细则", "当前只能通过候选公示中前三名评分业绩数量反推：3分基础+每项评分业绩1分、最高7分；必须以招标文件为准。", "待招标文件确认"],
        [3, "投标报价分公式", "当前公共页未披露评标基准价、K值、扣分率、异常报价处理；仅能确认报价分结果。", "待招标文件/评标报告/开标记录确认"],
        [4, "其他45.46等分项", "候选公示只给“其他”总分，不拆分评委评分项。", "待评标报告或招标文件评分表确认"],
        [5, "资质证书编号和有效期", "公开网页可确认部分资质名称和荣誉，但证书编号、发证机构、有效期、证书扫描件需天眼查/企查查/国家企业信用等登录查询。", "可由用户扫码登录天眼查后继续补齐"],
    ]


def nj_equipment_tracking_rows() -> list[list[Any]]:
    return [
        [1, "南京市公共资源交易中心", "招标公告、候选公示、结果公示", "可确认标段为新线食堂智能结算设备采购；建设/交货地点为5号线、6号线、9号线、10号线、11号线及宁马线食堂；数量为一批；技术规格写明详见招标文件。", "未披露逐项设备清单", SOURCES["nj_bid_notice"]],
        [2, "南京地铁官网招标投标栏目", "南京地铁官网同名招标公告", "与公共资源公告一致，披露项目编号、采购范围、资格要求、招标文件下载入口。", "未披露逐项设备清单", SOURCES["nj_metro_notice"]],
        [3, "南京市公共资源交易中心货物网上交易平台", "招标公告指定下载入口 http://221.226.86.168:8081", "公告写明电子招标文件需登录该平台免费下载；当前公开网页和附件接口无法直接取得招标文件。", "需要平台登录/CA或原始招标文件", "http://221.226.86.168:8081"],
        [4, "企查查", "项目关键词搜索", "公开搜索结果能命中本项目和相关公司招投标信息，但未公开逐项设备清单。", "需登录企业平台进一步核验", "https://www.qcc.com/"],
        [5, "天眼查", "项目关键词搜索", "公开搜索结果未检出本项目逐项设备清单；更多招投标详情通常需登录。", "需用户扫码登录后继续查证", "https://www.tianyancha.com/"],
        [6, "爱企查", "项目关键词搜索", "公开搜索结果未检出本项目逐项设备清单；更多招投标详情通常需登录。", "需登录后继续查证", "https://aiqicha.baidu.com/"],
        [7, "南京小牛官网南京地铁案例", "供应商案例页", "披露南京地铁集团引进南京小牛智慧餐台、双屏双触消费机等设备；案例页标注南京、500+用餐人数、100+设备，并列示采用设备LN-508CA双屏消费机、LN-BW01-15智能保温售饭台。", "可作为南京地铁案例设备线索；不是招标文件设备清单", SOURCES["xnzn_nanjing_metro_case"]],
    ]


def nj_confirmed_procurement_scope_rows() -> list[list[Any]]:
    return [
        [1, "官方确认", "采购对象", "新线食堂智能结算设备采购", "来自南京公共资源/南京地铁招标公告", "可确认"],
        [2, "官方确认", "采购数量", "一批", "公告未列具体型号、数量、单价明细", "可确认但不完整"],
        [3, "官方确认", "交货/实施地点", "南京地铁5号线、6号线、9号线、10号线、11号线及宁马线食堂", "公告原文披露", "可确认"],
        [4, "官方确认", "技术规格", "详见招标文件", "公开HTML没有技术参数表", "缺招标文件"],
        [5, "官方确认", "中标范围金额", "南京小牛智能科技有限公司中标价305.010000万元", "中标结果公示披露", "可确认"],
        [6, "供应商案例确认", "南京地铁历史/案例设备", "智慧餐台、双屏双触消费机等；案例页标注100+设备", "南京小牛官网案例页披露", "不能直接等同本标清单"],
        [7, "供应商案例确认", "案例页列示采用设备", "LN-508CA双屏消费机；LN-BW01-15智能保温售饭台", "南京小牛官网案例页披露", "不能直接等同本标清单"],
    ]


def nj_xnzn_case_equipment_rows() -> list[list[Any]]:
    return [
        [1, "智慧餐台", "南京小牛官网南京地铁案例页项目概述", "案例页写明南京地铁集团引进南京小牛智慧餐台、双屏双触消费机等设备。", "未披露具体型号", "南京地铁案例确认；非招标文件逐项清单", SOURCES["xnzn_nanjing_metro_case"]],
        [2, "双屏双触消费机", "南京小牛官网南京地铁案例页项目概述", "案例页写明通过互联网技术提升食堂支付效率、缓解高峰拥堵并减少前场人工成本。", "未披露具体型号；同页采用设备列示LN-508CA双屏消费机", "南京地铁案例确认；非招标文件逐项清单", SOURCES["xnzn_nanjing_metro_case"]],
        [3, "LN-508CA 双屏消费机-售饭机-消费机", "南京小牛官网南京地铁案例页采用设备", "用于大型食堂场景，提升支付效率、避免结算失误；产品页披露刷卡、刷脸、扫码支付和双15.6英寸屏等参数。", "LN-508CA", "案例页明确列示采用设备；仍需招标文件确认是否属于本标", SOURCES["xnzn_ln508ca"]],
        [4, "LN-BW01-15 售饭保温台-智能售饭台", "南京小牛官网南京地铁案例页采用设备", "智能保温台应用于小碗菜自选餐线，实现无感出品、智能控温、营养推荐。", "LN-BW01-15", "案例页明确列示采用设备；仍需招标文件确认是否属于本标", SOURCES["xnzn_bw0115"]],
    ]


def nj_xnzn_case_parameter_rows() -> list[list[Any]]:
    return [
        [1, "LN-508CA 双屏消费机", "支付与识别", "支持刷卡、刷脸、扫码支付；顶部红外双目摄像头支持活体识别；内置高速处理器，1秒内完成用户识别。", "南京小牛产品页", SOURCES["xnzn_ln508ca"], "供应商产品参数，非招标文件技术响应表"],
        [2, "LN-508CA 双屏消费机", "显示与交互", "用户显示屏15.6英寸电容触摸屏，分辨率1920*1080，支持多点触控；操作触摸屏15.6英寸电容触摸屏，分辨率1920*1080，支持多点触控。", "南京小牛产品页", SOURCES["xnzn_ln508ca"], "供应商产品参数，非招标文件技术响应表"],
        [3, "LN-508CA 双屏消费机", "容量与性能", "人脸模板容量6万人可扩展，准确率大于等于99.9%，识别时间小于0.2s；读卡距离30mm以内，识别时间小于0.2s；扫码距离60mm以内，识别时间小于0.2s。", "南京小牛产品页", SOURCES["xnzn_ln508ca"], "供应商产品参数，非招标文件技术响应表"],
        [4, "LN-508CA 双屏消费机", "结构与环境", "尺寸385*260*440mm；材质高强度高韧性ABS树脂；重量7.6kg；电源12V/5A；通讯WLAN标配、Wi-Fi/4G选配；使用环境-10℃至40℃、湿度5%-95%。", "南京小牛产品页", SOURCES["xnzn_ln508ca"], "供应商产品参数，非招标文件技术响应表"],
        [5, "LN-BW01-15 智能无感出品保温台", "核心功能", "智能保温台应用于小碗菜自选餐线，由保温模块、出品模块、营养价签、操作模块、指示模块构成，实现无感出品、智能控温、营养推荐。", "南京小牛产品页", SOURCES["xnzn_bw0115"], "供应商产品参数，非招标文件技术响应表"],
        [6, "LN-BW01-15 智能无感出品保温台", "材质与出品", "采用水保温方式，304不锈钢材质与钢化玻璃面板，配置加热灯，上下保温；餐盘通过出品感应区即可赋价出品。", "南京小牛产品页", SOURCES["xnzn_bw0115"], "供应商产品参数，非招标文件技术响应表"],
        [7, "LN-BW01-15 智能无感出品保温台", "技术参数", "尺寸1500*750*1600mm；识别高度0-80mm；储存菜品100个；识别时间≤0.2s；内嵌7英寸IPS营养显示屏；CAN通讯；1.3英寸OLED出品模块；电压12V。", "南京小牛产品页", SOURCES["xnzn_bw0115"], "供应商产品参数，非招标文件技术响应表"],
    ]


def citic_device_detail_rows(devices: list[list[Any]]) -> list[list[Any]]:
    suppliers = [
        "常州勤网信息科技有限公司",
        "南京小牛智能科技有限公司",
        "杭州青橄榄网络技术有限公司",
        "浙江云澎科技有限公司",
        "正元智慧集团股份有限公司",
    ]
    rows: list[list[Any]] = []
    for idx, row in enumerate(devices, 1):
        device = row[0]
        prices = [float(value) for value in row[1:]]
        min_price = min(prices)
        max_price = max(prices)
        min_suppliers = "、".join(supplier for supplier, price in zip(suppliers, prices) if price == min_price)
        rows.append(
            [
                idx,
                device,
                *[format_number(value) for value in prices],
                format_number(min_price),
                min_suppliers,
                format_number(max_price),
                format_number(max_price - min_price),
                "并列最低" if min_suppliers.count("、") else "唯一最低",
            ]
        )
    return rows


def citic_supplier_summary_rows(devices: list[list[Any]]) -> list[list[Any]]:
    suppliers = [
        ("第一中标候选人", "常州勤网信息科技有限公司"),
        ("第二中标候选人", "南京小牛智能科技有限公司"),
        ("第三中标候选人", "杭州青橄榄网络技术有限公司"),
        ("第四中标候选人", "浙江云澎科技有限公司"),
        ("第五中标候选人", "正元智慧集团股份有限公司"),
    ]
    totals = [0.0 for _ in suppliers]
    lowest_counts = [0 for _ in suppliers]
    lowest_items = [[] for _ in suppliers]
    for row in devices:
        prices = [float(value) for value in row[1:]]
        min_price = min(prices)
        for idx, price in enumerate(prices):
            totals[idx] += price
            if price == min_price:
                lowest_counts[idx] += 1
                lowest_items[idx].append(str(row[0]))
    rows: list[list[Any]] = []
    for idx, ((official_rank, supplier), total, count, items) in enumerate(zip(suppliers, totals, lowest_counts, lowest_items), 1):
        rows.append(
            [
                idx,
                official_rank,
                supplier,
                format_number(total),
                count,
                "；".join(items) if items else "无",
                "该合计为16项公开单价的非加权简单相加，只用于价格画像，不等于官方评标价或总分。",
            ]
        )
    return rows


def abchina_price_rows() -> list[list[Any]]:
    candidates = [
        ("第一中标候选人", "广电运通集团股份有限公司", 2105.30),
        ("第二中标候选人", "深圳怡化电脑股份有限公司", 2610.25),
        ("第三中标候选人", "广州市派客朴食信息科技有限责任公司", 2872.54),
        ("第四中标候选人", "佳都科技集团股份有限公司", 2875.20),
        ("第五中标候选人", "杭州海康威视数字技术股份有限公司", 2848.70),
        ("第六中标候选人", "杭州企智互联科技有限公司", 2865.30),
    ]
    prices = [price for _, _, price in candidates]
    ranks = competition_ranks(prices)
    lowest = min(prices)
    rows: list[list[Any]] = []
    for idx, ((official_rank, supplier, price), price_rank) in enumerate(zip(candidates, ranks), 1):
        official_num = idx
        rows.append(
            [
                idx,
                official_rank,
                supplier,
                f"{price:.2f}",
                price_rank,
                f"{price - lowest:.2f}",
                "候选排序与价格排序一致" if official_num == price_rank else "候选排序不完全按价格排序，说明还有技术/商务等综合因素未公开",
            ]
        )
    return rows


def abchina_limit_rows(devices: list[list[Any]]) -> list[list[Any]]:
    rows: list[list[Any]] = []
    for idx, (device, ceiling, weight) in enumerate(devices, 1):
        price = float(str(ceiling).replace("元/台", "").replace("元/张", ""))
        weight_value = float(str(weight).replace("%", "")) / 100
        rows.append([idx, device, ceiling, weight, f"{price * weight_value:.2f}", "加权限价贡献=最高限价数值×权重，仅用于理解价格权重，不代表采购数量。"])
    return rows


def source_channel_rows(project_key: str) -> list[list[Any]]:
    """Return the channel checklist learned from the user's Yifangbao examples."""
    common = [
        [
            "乙方宝/千里马等聚合站",
            "项目名称、项目编号、招标单位、中标单位、预算/中标金额、页面底部附件",
            "用项目全名、项目编号、采购人、第一中标候选人组合检索；优先找“下载附件”“查看联系人”“中标公告”入口。",
            "常见需要微信/手机号扫码登录；登录后下载附件，保存到项目资料目录。",
            "若只能拿到截图，也要把截图字段结构化写入 Excel，并标注为截图线索。",
        ],
        [
            "天眼查/企查查/爱企查",
            "中标/候选公司的工商信息、资质证书、专利、软著、经营风险、历史中标项目",
            "先用公司全名检索，再进入“招投标/知识产权/资质证书/行政许可/司法风险”等栏目。",
            "企业详情和证书编号常需要登录；用户可扫码后继续抓。",
            "只把可见字段写为已确认；证书编号、有效期、原件截图未取得时写入缺口。",
        ],
        [
            "官方采购/公共资源平台",
            "招标公告、采购文件、开标记录、中标候选人公示、中标结果、合同公告",
            "按项目编号优先，其次按项目全名；政府采购项目优先财政/政府采购网，工程货物优先公共资源交易中心。",
            "招标文件、评标报告、开标记录经常需要交易系统登录或 CA。",
            "官方文件优先级最高；与聚合站冲突时，以官方附件为准。",
        ],
        [
            "行业/业主招标站",
            "银行、地铁、央企/国企自有招采平台的公告和附件",
            "按业主名称进入自有招采站，搜索项目编号、项目简称、供应商名称。",
            "银行/央企平台常要求平台注册、购买标书或报名审核。",
            "能解释官方公共资源站没有附件时的下载入口。",
        ],
        [
            "供应商官网/案例页",
            "产品型号、案例设备、解决方案、客户案例、产品参数",
            "只在官方招标文件缺设备明细时作为辅助线索；必须标注“非招标文件清单”。",
            "一般不需登录，但信息可能偏营销口径。",
            "用于补产品功能和硬件参数，不直接作为中标设备最终清单。",
        ],
    ]
    project_specific = {
        "nanjing": [
            [
                "南京市公共资源交易中心货物网上交易平台",
                "招标文件原件、评分标准、设备清单、开标记录",
                "入口为公告中的 http://221.226.86.168:8081；此前访问出现 502/下载入口不可用，需复测。",
                "可能需要平台登录/CA。",
                "重点补齐：商务分细则、报价分公式、其他45.46分拆项、逐项设备清单。",
            ],
            [
                "南京小牛官网案例/产品页",
                "南京地铁历史案例设备、LN-508CA、LN-BW01-15 参数",
                "已抓到案例设备线索；继续保存网页快照和参数截图。",
                "一般无需登录。",
                "只能作为案例设备，不等同本标设备清单。",
            ],
        ],
        "citic": [
            [
                "中信金控采购共享平台",
                "招标文件、评分办法、入围服务要求、合同模板",
                "公告写明需提交资料、购买标书并上传购买凭证后下载。",
                "需要平台注册/报名/购买标书。",
                "重点补齐：评分细则、软件模块级参数、服务 SLA、每类设备权重。",
            ],
            [
                "五家候选供应商企业库",
                "常州勤网、南京小牛、杭州青橄榄、浙江云澎、正元智慧的资质和案例",
                "逐家公司查企查查/天眼查/爱企查，按证书、专利、软著、银行/食堂案例归档。",
                "证书编号通常需要登录。",
                "重点补齐：为什么常州勤网排第一、五家入围差异。",
            ],
        ],
        "abchina": [
            [
                "农银 e 采平台/中化商务数字化服务平台",
                "招标文件、评分细则、分项报价、评标报告",
                "公告写明需在中化平台注册并到农银 e 采电子招采中心报名审核后下载。",
                "需要平台注册、报名审核或支付平台使用费。",
                "重点补齐：六家候选人的商务/技术/报价分、四家最终中标逻辑。",
            ],
            [
                "候选供应商企业库",
                "广电运通、深圳怡化、派客朴食、佳都科技、海康威视、企智互联的资质与银行场景案例",
                "逐家公司查招投标历史、专利软著、智慧食堂/金融机具/场景设备案例。",
                "部分需要登录。",
                "重点补齐：报价不是唯一排序因素时的技术/商务支撑。",
            ],
        ],
    }
    return common + project_specific[project_key]


def build_specs() -> list[dict[str, Any]]:
    nj_scores = [
        ["南京小牛智能科技有限公司", 7.00, 0.00, 42.03, 45.46, 94.49],
        ["江苏移动信息系统集成有限公司", 4.00, 0.00, 41.93, 41.76, 87.69],
        ["浙江云澎科技有限公司", 7.00, 0.00, 35.42, 42.52, 84.94],
        ["广州市大唐智讯电子技术有限公司", 6.00, 0.00, 39.65, 37.54, 83.19],
        ["杭州雄伟科技开发股份有限公司", 7.00, 0.00, 41.29, 28.82, 77.11],
        ["南京宁融信网络科技有限公司", 3.00, 0.00, 42.44, 27.56, 73.00],
        ["北京新中新诚通信息技术股份有限公司", 6.00, 0.00, 41.65, 25.32, 72.97],
        ["南京宝华智能科技有限公司", 3.00, 0.00, 41.88, 27.34, 72.22],
        ["湖南孚谷时代科技有限公司", 4.00, 0.00, 41.53, 20.30, 65.83],
    ]
    citic_devices = [
        ["人脸识别双面屏终端（大尺寸）", 3500, 3800, 3500, 4000, 4200],
        ["人脸识别双面屏终端（小尺寸）", 3000, 2800, 3000, 3000, 3200],
        ["称重计量一体机（含保温炉）", 5000, 5500, 5500, 5000, 7200],
        ["绑盘机", 3000, 3450, 2800, 3400, 3200],
        ["智慧餐台（芯片识别）", 16000, 20000, 21000, 18000, 26000],
        ["智慧餐台（AI识别）", 21000, 30000, 30000, 40000, 38000],
        ["餐具（带芯片）碗碟盘均价", 9, 9, 13, 12, 12],
        ["餐具（不带芯片）碗碟盘均价", 4, 7, 8, 6, 9],
        ["智能保温备餐台", 11000, 11500, 9800, 11500, 11000],
        ["食堂速通门（单机芯、摆闸）", 5000, 6000, 6000, 5000, 8000],
        ["食堂速通门（单机芯、翼闸）", 5000, 7000, 5700, 5500, 8500],
        ["食堂速通门（双机芯，摆闸）", 6000, 8000, 8000, 10000, 11000],
        ["食堂速通门（双机芯，翼闸）", 6000, 9500, 7500, 11000, 11500],
        ["人脸识别机-食堂闸机用", 2500, 2800, 2800, 3500, 3900],
        ["智慧餐卡", 3, 3.5, 3, 3, 8],
        ["托盘", 8, 12, 15, 10, 16],
    ]
    abchina_devices = [
        ["智慧食堂设备高配机（桌面式）", "4800元/台", "25%"],
        ["智慧食堂设备高配机（壁挂式）", "4700元/台", "25%"],
        ["智慧食堂设备基础机（桌面式）", "3700元/台", "15%"],
        ["智慧食堂设备基础机（壁挂式）", "3700元/台", "15%"],
        ["智慧食堂设备低配机（手持式）", "2300元/台", "15%"],
        ["空白卡", "6元/张", "5%"],
    ]
    nj_quotes = {
        "南京小牛智能科技有限公司": 305.010000,
        "江苏移动信息系统集成有限公司": 306.013500,
        "浙江云澎科技有限公司": 220.811000,
    }
    return [
        {
            "filename": "01-南京地铁5号线工程新线食堂智能结算设备采购.xlsx",
            "source_channel_rows": source_channel_rows("nanjing"),
            "overview": [
                ["项目名称", "南京地铁5号线工程新线食堂智能结算设备采购", "✓", SOURCES["nj_candidate"]],
                ["招标编号", "NJHW-230123-8", "✓", SOURCES["nj_candidate"]],
                ["评标办法", "综合评估法", "✓", SOURCES["nj_candidate"]],
                ["中标人", "南京小牛智能科技有限公司", "✓", SOURCES["nj_result"]],
                ["中标价格", "305.010000万元", "✓", SOURCES["nj_result"]],
                ["中标工期", "接招标人送货通知5天内供货", "✓", SOURCES["nj_result"]],
                ["全量程度结论", "9项中确认抓到6项，3项为部分抓到；设备清单已补充公开渠道追踪和供应商南京地铁案例设备，但招标文件逐项设备清单仍未公开。", "△", "本次抽取判断"],
            ],
            "products": [
                [1, "新线食堂智能结算设备", "公开候选/结果页未列设备型号；可确认标段为食堂智能结算设备采购。", "南京小牛智能科技有限公司", "中国", "未披露", "未披露", "未披露", "未披露", "305.010000万元", "一批", "5天内供货"],
                [2, "智慧餐台", "南京小牛南京地铁案例页写明引进智慧餐台、双屏双触消费机等设备；标注100+设备。", "南京小牛智能科技有限公司", "中国", "未披露", "未披露", "乐牛/小牛", "未披露", "未披露", "100+设备为案例页口径", "供应商案例确认，非招标文件清单"],
                [3, "LN-508CA 双屏消费机", "双15.6英寸触摸屏、红外双目活体识别、刷卡/刷脸/扫码支付、人脸模板容量6万人可扩展。", "南京小牛智能科技有限公司", "中国", "未披露", "未披露", "乐牛/小牛", "LN-508CA", "未披露", "未披露", "南京地铁案例页列示采用设备"],
                [4, "LN-BW01-15 智能无感出品保温台", "1500*750*1600mm，识别高度0-80mm，储存菜品100个，识别时间≤0.2s，7英寸营养显示屏。", "南京小牛智能科技有限公司", "中国", "未披露", "未披露", "乐牛/小牛", "LN-BW01-15", "未披露", "未披露", "南京地铁案例页列示采用设备"],
            ],
            "software": common_software_rows("食堂智能结算设备采购场景；模块为智慧结算项目通用拆解", "△ 公开页未披露软件参数，需招标文件补齐"),
            "hardware": [
                [1, "智能结算终端/设备", "未披露型号", "一批", "305.010000万元", "公开页未披露详细型号；从标段名可确认智能结算设备采购。", "△"],
                [2, "智慧餐台", "南京小牛南京地铁案例确认使用；未列型号", "案例页100+设备口径", "未披露", "供应商案例确认，不能直接等同本次招标文件清单。", "△"],
                [3, "LN-508CA 双屏消费机", "385*260*440mm；双15.6英寸触摸屏；200万像素红外双目摄像头；人脸模板容量6万人可扩展；识别时间小于0.2s。", "未披露", "未披露", "南京小牛南京地铁案例页列示采用设备，产品页披露参数。", "△"],
                [4, "LN-BW01-15 智能无感出品保温台", "1500*750*1600mm；识别高度0-80mm；储存菜品100个；识别时间≤0.2s；7英寸IPS液晶屏；304不锈钢板。", "未披露", "未披露", "南京小牛南京地铁案例页列示采用设备，产品页披露参数。", "△"],
            ],
            "qualifications": [
                [1, "资格能力条件", "三名候选人均显示满足招标文件要求。", "✓", SOURCES["nj_candidate"]],
                [2, "无效投标判定", "南京华成厨具、南京乐创厨房设备因类似食堂智能结算设备业绩不够40万未通过；江苏龙拓智能科技因未提供2023审计报告未通过。", "✓", SOURCES["nj_candidate"]],
                [3, "资审/评分业绩", "候选公示披露前三名资审业绩和评分业绩。", "✓", SOURCES["nj_candidate"]],
            ],
            "scores": [[i + 1] + row for i, row in enumerate(nj_scores)],
            "extra_sheets": [
                {
                    "name": "招标公告原文要点",
                    "title": "南京项目招标公告原文要点",
                    "headers": ["序号", "字段", "抓取内容", "来源类型", "来源链接"],
                    "rows": nj_bid_notice_rows(),
                    "widths": [8, 26, 94, 22, 72],
                },
                {
                    "name": "南京得分逐项校验",
                    "title": "南京项目有效投标人得分逐项校验",
                    "headers": ["序号", "投标人", "商务", "技术", "投标报价", "其他", "最终总得分", "公式校验", "校验结果", "总分排名", "报价分排名", "投标报价金额", "报价金额说明"],
                    "rows": nj_score_detail_rows(nj_scores, nj_quotes),
                    "widths": [8, 42, 12, 12, 14, 12, 14, 36, 14, 12, 12, 20, 48],
                },
                {
                    "name": "南京报价与排名对应",
                    "title": "南京前三名报价金额与综合得分对应",
                    "headers": ["候选排序", "投标人", "投标报价金额", "前三名报价金额排名", "投标报价得分", "其他得分", "最终总得分", "解释"],
                    "rows": nj_top3_quote_rows(nj_quotes, nj_scores),
                    "widths": [10, 42, 20, 18, 16, 14, 16, 58],
                },
                {
                    "name": "南京技术标0分明细",
                    "title": "南京项目技术标公示明细",
                    "headers": ["序号", "投标人", "评审因素", "公示分值", "说明"],
                    "rows": nj_technical_zero_rows(nj_scores),
                    "widths": [8, 42, 42, 22, 68],
                },
                {
                    "name": "南京候选人业绩明细",
                    "title": "南京前三名资审业绩与评分业绩",
                    "headers": ["序号", "候选人", "业绩类型", "项目名称", "买方名称", "合同金额"],
                    "rows": nj_performance_rows(),
                    "widths": [8, 38, 14, 72, 42, 18],
                },
                {
                    "name": "商务分反推说明",
                    "title": "南京项目商务分反推说明",
                    "headers": ["序号", "投标人", "商务分", "公开评分业绩数量", "公开评分业绩", "反推解释", "可信状态"],
                    "rows": nj_business_inference_rows(),
                    "widths": [8, 42, 12, 20, 86, 72, 22],
                },
                {
                    "name": "报价分公式追踪",
                    "title": "南京项目投标报价分公式追踪",
                    "headers": ["项目/投标人", "公开数据", "对应分析", "结论/缺口"],
                    "rows": nj_price_formula_trace_rows(nj_scores, nj_quotes),
                    "widths": [34, 72, 76, 68],
                },
                {
                    "name": "报价分反推计算",
                    "title": "南京项目报价分反推计算",
                    "headers": ["对象", "投标报价（万元）", "报价分", "距45分扣分", "偏离方向", "反推偏离率", "反推基准价（万元）", "计算式"],
                    "rows": nj_price_formula_inference_rows(nj_scores, nj_quotes),
                    "widths": [42, 22, 12, 16, 18, 16, 22, 86],
                },
                {
                    "name": "供应商公开资质证书",
                    "title": "南京项目三家供应商公开资质证书与能力资料",
                    "headers": ["供应商", "资料类型", "公开抓取内容", "来源类型", "来源链接", "审慎说明"],
                    "rows": nj_supplier_public_qualification_rows(),
                    "widths": [38, 22, 96, 24, 72, 56],
                },
                {
                    "name": "设备清单追踪",
                    "title": "南京项目设备清单公开渠道追踪",
                    "headers": ["序号", "渠道", "检索对象", "抓取结果", "设备清单状态", "来源/入口"],
                    "rows": nj_equipment_tracking_rows(),
                    "widths": [8, 30, 42, 92, 34, 72],
                },
                {
                    "name": "可确认采购范围",
                    "title": "南京项目可确认采购范围与缺口",
                    "headers": ["序号", "证据层级", "字段", "内容", "说明", "状态"],
                    "rows": nj_confirmed_procurement_scope_rows(),
                    "widths": [8, 20, 26, 74, 62, 22],
                },
                {
                    "name": "南京小牛案例设备",
                    "title": "南京小牛官网南京地铁案例设备线索",
                    "headers": ["序号", "设备名称", "来源位置", "公开描述", "型号", "可信边界", "来源链接"],
                    "rows": nj_xnzn_case_equipment_rows(),
                    "widths": [8, 34, 38, 84, 24, 48, 72],
                },
                {
                    "name": "案例设备参数",
                    "title": "南京小牛案例设备公开技术参数",
                    "headers": ["序号", "设备", "参数类别", "详细参数", "来源类型", "来源链接", "审慎说明"],
                    "rows": nj_xnzn_case_parameter_rows(),
                    "widths": [8, 34, 22, 94, 24, 72, 44],
                },
                {
                    "name": "需登录补齐清单",
                    "title": "南京项目需登录平台补齐的信息",
                    "headers": ["序号", "缺口", "当前状态", "下一步"],
                    "rows": nj_login_gap_rows(),
                    "widths": [8, 30, 98, 44],
                },
                {
                    "name": "得分解释",
                    "title": "南京项目得分解释",
                    "headers": ["问题", "解释", "对应数据", "结论"],
                    "rows": [
                        ["最终总得分是什么意思", "公示表中最终总得分等于商务、技术、投标报价、其他四列相加。", "南京小牛：7.00+0.00+42.03+45.46=94.49", "可直接校验"],
                        ["为什么最低价不是第一名", "浙江云澎前三名中报价最低，为220.811000万元，但投标报价得分35.42、其他得分42.52，最终84.94；南京小牛最终94.49。", "报价金额排名不等于综合评分排名", "综合评估法以总分第一为准"],
                        ["技术得分为什么全是0", "公开页面技术标部分将三个技术评审因素、七名评委分值均列为0.00；本表只能按公示结果记录，不能擅自推断隐藏技术分。", "技术列=0.00，技术标明细=0.00", "公开信息不足以拆出技术响应优劣"],
                        ["其他得分是什么", "公示只给出字段名“其他”，没有公开进一步拆分项；南京小牛其他45.46，是其总分领先的关键。", "其他45.46高于第二名41.76、第三名42.52", "需招标文件/评标报告才能拆分"],
                    ],
                    "widths": [28, 86, 54, 34],
                },
            ],
            "package_rows": [
                ["新线食堂智能结算设备", "官方公告：未披露分项型号", "官方公告：一批", "中标价305.010000万元", "交货期5天/接通知5天内供货", "需招标文件补齐设备清单"],
                ["智慧餐台", "供应商案例：未披露型号", "南京小牛案例页：100+设备总体口径", "未披露", "南京地铁案例页确认使用", "非招标文件清单"],
                ["LN-508CA 双屏消费机", "供应商案例设备", "未披露", "未披露", "双15.6英寸屏、人脸/刷卡/扫码", "非招标文件清单"],
                ["LN-BW01-15 智能无感出品保温台", "供应商案例设备", "未披露", "未披露", "智能控温、无感出品、营养价签", "非招标文件清单"],
            ],
            "scope": [
                [1, "南京地铁5号线工程新线食堂", "南京地铁", "未披露具体食堂点位", "智能结算设备采购，交货期5天/接通知5天内供货。", "✓", SOURCES["nj_candidate"]],
                [2, "5号线、6号线、9号线、10号线、11号线及宁马线食堂", "南京地铁", "公告未披露具体食堂门牌或站点", "招标公告披露建设/交货地点为上述线路食堂。", "✓", SOURCES["nj_bid_notice"]],
                [3, "南京地铁集团食堂案例", "南京", "南京小牛官网案例页未披露具体食堂点位", "案例页披露500+用餐人数、100+设备。", "△", SOURCES["xnzn_nanjing_metro_case"]],
            ],
            "bidders": [
                ["第一名", "南京小牛智能科技有限公司", "305.010000万元", "94.49", "拟定中标人/中标人", "价格分42.03，其他45.46，总分第一。"],
                ["第二名", "江苏移动信息系统集成有限公司", "306.013500万元", "87.69", "中标候选人", "商务4.00，报价41.93，其他41.76。"],
                ["第三名", "浙江云澎科技有限公司", "220.811000万元", "84.94", "中标候选人", "报价低但总分低于前两名，其他得分42.52。"],
            ],
            "reason": [
                ["中标原因", "南京小牛智能科技有限公司最终总得分94.49，为所有有效投标人最高；商务得分7.00、投标报价42.03、其他45.46。"],
                ["价格因素", "中标价305.010000万元，报价分42.03；并非最低报价，但综合评分最高。"],
                ["业绩因素", "披露评分业绩包括南京地铁运营食堂智能化系统移动支付场景合作项目、南京钢铁智慧食堂项目等。"],
                ["风险/缺口", "公开页未披露招标文件的逐项设备清单；目前只能确认官方采购范围和南京小牛官网案例设备线索，不能把案例设备直接当作本标最终清单。"],
            ],
            "sources": [SOURCES["nj_bid_notice"], SOURCES["nj_metro_notice"], SOURCES["nj_candidate"], SOURCES["nj_result"], SOURCES["xnzn_nanjing_metro_case"], SOURCES["xnzn_ln508ca"], SOURCES["xnzn_bw0115"]],
        },
        {
            "filename": "02-中信银行杭州分行智慧食堂软硬件服务商集采入围项目.xlsx",
            "source_channel_rows": source_channel_rows("citic"),
            "overview": [
                ["项目名称", "中信银行杭州分行智慧食堂软硬件服务商集采入围项目", "✓", SOURCES["citic_bid"]],
                ["项目编号", "CTZB-2025050401", "✓", SOURCES["citic_bid"]],
                ["采购预算", "1000万元", "✓", SOURCES["citic_bid"]],
                ["服务期", "2年", "✓", SOURCES["citic_bid"]],
                ["入围供应商数量", "5家", "✓", SOURCES["citic_bid"]],
                ["工作内容", "为银行客户智慧食堂系统提供软硬件设备采购、安装、调试、升级、维修及保养。", "✓", SOURCES["citic_bid"]],
                ["全量程度结论", "9项中确认抓到6项，3项为部分抓到；主要缺评分细则和软件模块级招标参数。", "△", "本次抽取判断"],
            ],
            "products": [[i + 1, row[0], "中标单价（元）按5家候选人披露", "五家入围候选", "中国", "未披露", "未披露", "未披露", row[0], "见分包与设备清单", "集采入围", "公开候选公示披露"] for i, row in enumerate(citic_devices)],
            "software": common_software_rows("智慧食堂综合服务，软硬件采购、安装、调试、升级、维修及保养", "△ 招标公告披露服务范围，未披露模块级参数"),
            "hardware": [[i + 1, row[0], "详见各候选人单价", "集采入围", "单价见分包与设备清单", "候选公示披露16类软硬件/耗材价格。", "✓"] for i, row in enumerate(citic_devices)],
            "qualifications": [
                [1, "独立承担民事责任", "投标人须具备独立承担民事责任能力。", "✓", SOURCES["citic_bid"]],
                [2, "信誉和财务制度", "须具有良好商业信誉和健全财务会计制度。", "✓", SOURCES["citic_bid"]],
                [3, "履约能力", "须具有履行合同所必需的设备和专业技术能力。", "✓", SOURCES["citic_bid"]],
                [4, "税收社保", "须有依法缴纳税收和社会保障资金的良好记录。", "✓", SOURCES["citic_bid"]],
                [5, "信用限制", "未被信用中国、中国政府采购网列入相关失信或严重违法记录。", "✓", SOURCES["citic_bid"]],
                [6, "联合体", "不接受联合体投标。", "✓", SOURCES["citic_bid"]],
            ],
            "scores": [
                [1, "常州勤网信息科技有限公司", "第一中标候选人", "未披露总分", "设备单价明细已披露"],
                [2, "南京小牛智能科技有限公司", "第二中标候选人", "未披露总分", "设备单价明细已披露"],
                [3, "杭州青橄榄网络技术有限公司", "第三中标候选人", "未披露总分", "设备单价明细已披露"],
                [4, "浙江云澎科技有限公司", "第四中标候选人", "未披露总分", "设备单价明细已披露"],
                [5, "正元智慧集团股份有限公司", "第五中标候选人", "未披露总分", "设备单价明细已披露"],
            ],
            "extra_sheets": [
                {
                    "name": "设备单价逐项对比",
                    "title": "中信银行杭州分行16类设备单价逐项对比",
                    "headers": ["序号", "设备", "常州勤网", "南京小牛", "杭州青橄榄", "浙江云澎", "正元智慧", "最低价", "最低供应商", "最高价", "价差", "价格结论"],
                    "rows": citic_device_detail_rows(citic_devices),
                    "widths": [8, 36, 14, 14, 16, 14, 14, 14, 48, 14, 14, 24],
                },
                {
                    "name": "候选公司价格画像",
                    "title": "中信银行杭州分行候选公司价格画像",
                    "headers": ["序号", "官方排序", "供应商", "16项单价简单合计", "最低/并列最低项数", "最低价设备清单", "说明"],
                    "rows": citic_supplier_summary_rows(citic_devices),
                    "widths": [8, 18, 40, 20, 18, 86, 58],
                },
                {
                    "name": "得分解释",
                    "title": "中信银行杭州分行得分解释",
                    "headers": ["问题", "解释", "对应数据", "结论"],
                    "rows": [
                        ["有没有公开总分", "候选公示只披露五家中标候选人排序和16类设备中标单价，没有披露商务分、技术分、报价分或总分。", "第一至第五候选人排序；16类设备单价", "不能把单价直接解释为得分"],
                        ["价格和排名怎么对应", "第一候选人常州勤网在多项设备上为最低或并列最低，但官方未披露权重和评分公式。", "设备单价逐项对比、候选公司价格画像", "只能做价格画像，不能还原官方总分"],
                        ["为什么要保留16项简单合计", "简单合计用于快速看供应商整体报价水平，不代表官方评标价，因为招标可能按设备权重、场景数量、服务能力等综合评分。", "候选公司价格画像", "作为辅助判断"],
                    ],
                    "widths": [28, 86, 54, 34],
                },
            ],
            "package_rows": citic_devices,
            "scope": [
                [1, "中信银行杭州分行客户", "杭州", "银行客户智慧食堂", "软硬件设备采购、安装、调试、升级、维修及保养。", "✓", SOURCES["citic_bid"]],
            ],
            "bidders": [
                ["第一名", "常州勤网信息科技有限公司", "详见16类设备单价", "未披露", "中标候选人", "多项设备价格较低，例如称重计量一体机5000元、智慧餐台AI识别21000元。"],
                ["第二名", "南京小牛智能科技有限公司", "详见16类设备单价", "未披露", "中标候选人", "智慧食堂领域高频供应商，价格体系完整披露。"],
                ["第三名", "杭州青橄榄网络技术有限公司", "详见16类设备单价", "未披露", "中标候选人", "部分设备价格有优势，例如绑盘机2800元、智能保温备餐台9800元。"],
            ],
            "reason": [
                ["入围原因", "候选公示只披露候选排序和单价，未披露评分细则/总分；可判断候选公司满足采购要求并进入前五。"],
                ["价格因素", "公开16类设备单价，可横向比较各候选人报价。"],
                ["缺口", "需招标文件或评标报告补齐评分标准、技术响应得分和最终总分。"],
            ],
            "sources": [SOURCES["citic_bid"], SOURCES["citic_candidate"]],
        },
        {
            "filename": "03-中国农业银行广东省分行智慧食堂场景建设设备项目.xlsx",
            "source_channel_rows": source_channel_rows("abchina"),
            "overview": [
                ["项目名称", "中国农业银行股份有限公司广东省分行智慧食堂场景建设设备项目", "✓", SOURCES["abchina_bid"]],
                ["招标编号", "A44H999M25015000/0747-2560SCCGD011", "✓", SOURCES["abchina_bid"]],
                ["预计采购金额", "3800万元（不含增值税）", "✓", SOURCES["abchina_bid"]],
                ["采购有效期", "4年，首期服务期2年，累计续签不超过2年。", "✓", SOURCES["abchina_bid"]],
                ["中标人数量", "不多于4家供应商", "✓", SOURCES["abchina_bid"]],
                ["全量程度结论", "9项中确认抓到6项，3项为部分抓到；主要缺评分细则和单设备技术参数。", "△", "本次抽取判断"],
            ],
            "products": [[i + 1, row[0], f"最高限价{row[1]}，权重{row[2]}", "候选供应商", "中国", "未披露", "未披露", "未披露", row[0], row[1], "集采有效期内按需采购", f"权重{row[2]}"] for i, row in enumerate(abchina_devices)],
            "software": common_software_rows("智慧食堂场景建设设备及相关服务", "△ 招标公告披露设备类别，未披露软件模块参数"),
            "hardware": [[i + 1, row[0], f"最高限价：{row[1]}", "按需采购", row[2], "招标公告披露设备类别、最高限价和权重。", "✓"] for i, row in enumerate(abchina_devices)],
            "qualifications": [
                [1, "独立承担民事责任", "须提供营业执照复印件。", "✓", SOURCES["abchina_bid"]],
                [2, "信用限制", "截至投标截止日，未被信用中国列入失信被执行人、重大税收违法失信主体、政府采购严重违法失信行为记录名单。", "✓", SOURCES["abchina_bid"]],
                [3, "农行禁入限制", "投标人或其所投产品/服务未被列入《中国农业银行集中采购禁入名录》。", "✓", SOURCES["abchina_bid"]],
                [4, "关联关系限制", "法定代表人、控股股东或实际控制人与招标人相关人员无禁止关系；存在控股/管理等关联关系不得同时参与。", "✓", SOURCES["abchina_bid"]],
                [5, "重大违法违规限制", "涉嫌向本行有关部门或人员行贿、输送其他不正当利益等重大违法违规行为的不得参与。", "✓", SOURCES["abchina_bid"]],
                [6, "联合体", "不接受联合体参与投标。", "✓", SOURCES["abchina_bid"]],
            ],
            "scores": [
                [1, "广电运通集团股份有限公司", "第一中标候选人", "投标单价2105.30元（不含增值税）", "未披露总分"],
                [2, "深圳怡化电脑股份有限公司", "第二中标候选人", "投标单价2610.25元（不含增值税）", "未披露总分"],
                [3, "广州市派客朴食信息科技有限责任公司", "第三中标候选人", "投标单价2872.54元（不含增值税）", "未披露总分"],
                [4, "佳都科技集团股份有限公司", "第四中标候选人", "投标单价2875.20元（不含增值税）", "未披露总分"],
                [5, "杭州海康威视数字技术股份有限公司", "第五中标候选人", "投标单价2848.70元（不含增值税）", "未披露总分"],
                [6, "杭州企智互联科技有限公司", "第六中标候选人", "投标单价2865.30元（不含增值税）", "未披露总分"],
            ],
            "extra_sheets": [
                {
                    "name": "限价权重明细",
                    "title": "农行广东省分行设备最高限价与权重",
                    "headers": ["序号", "设备", "最高限价", "权重", "加权限价贡献", "说明"],
                    "rows": abchina_limit_rows(abchina_devices),
                    "widths": [8, 44, 18, 12, 18, 72],
                },
                {
                    "name": "候选报价排序对应",
                    "title": "农行广东省分行候选排序与报价排序对应",
                    "headers": ["序号", "官方候选排序", "供应商", "投标单价（不含税，元）", "价格排名", "较最低价高出（元）", "对应解释"],
                    "rows": abchina_price_rows(),
                    "widths": [8, 18, 46, 24, 12, 22, 72],
                },
                {
                    "name": "得分解释",
                    "title": "农行广东省分行得分解释",
                    "headers": ["问题", "解释", "对应数据", "结论"],
                    "rows": [
                        ["有没有公开总分", "中标候选人公示只披露第一至第六候选人及投标单价，未披露商务分、技术分、报价分或总分。", "候选报价排序对应", "不能还原官方总分"],
                        ["价格和排名怎么对应", "第一候选人广电运通报价最低且排名第一；但第三、第四候选人的报价高于第五、第六候选人，说明候选排序不是单纯按价格升序。", "派客朴食2872.54元排第三，海康威视2848.70元排第五", "存在未公开的综合评审因素"],
                        ["权重是什么意思", "招标公告披露每类设备的最高限价和价格权重，权重合计100%，用于价格评审或采购模型理解；但没有披露每家对每类设备的分项报价。", "限价权重明细", "只能解释价格结构，不能拆出技术参数得分"],
                    ],
                    "widths": [28, 86, 54, 34],
                },
            ],
            "package_rows": [[row[0], row[1], row[2]] for row in abchina_devices],
            "scope": [
                [1, "广东省分行智慧食堂场景", "广东省", "分行及客户场景", "集中采购不多于4家供应商，提供智慧食堂场景建设设备及相关服务。", "✓", SOURCES["abchina_bid"]],
            ],
            "bidders": [
                ["第一名", "广电运通集团股份有限公司", "2105.30元", "未披露", "中标候选人", "投标单价最低且排名第一。"],
                ["第二名", "深圳怡化电脑股份有限公司", "2610.25元", "未披露", "中标候选人", "排名第二，单价低于第三至第六名。"],
                ["第三名", "广州市派客朴食信息科技有限责任公司", "2872.54元", "未披露", "中标候选人", "排名第三，进入不多于4家中标供应商候选范围。"],
            ],
            "reason": [
                ["候选原因", "候选公示只披露排序和投标单价，未披露总分；第一候选人同时具有最低投标单价。"],
                ["价格因素", "广电运通2105.30元为候选人中最低单价，明显低于第二候选人2610.25元。"],
                ["缺口", "需农银 e采平台招标文件或评标报告补齐评分标准、技术响应和最终得分。"],
            ],
            "sources": [SOURCES["abchina_bid"], SOURCES["abchina_candidate"]],
        },
    ]


def add_sheet(wb: Workbook, name: str, title: str, headers: list[str], rows: list[list[Any]], widths: list[int]) -> None:
    ws = wb.create_sheet(name)
    write_table(ws, 1, title, headers, rows)
    set_widths(ws, widths)


def write_workbook(spec: dict[str, Any]) -> Path:
    wb = Workbook()
    wb.remove(wb.active)
    add_sheet(wb, "项目概览", "项目基本信息", ["字段", "值", "状态", "来源"], spec["overview"], [28, 72, 14, 72])
    add_sheet(wb, "产品清单-模板", "产品清单（按模板口径）", ["序号", "分项名称", "技术参数", "制造商/供应商", "产地", "统一信用代码", "制造商规模", "品牌", "规格型号", "单价/金额", "数量/范围", "备注"], spec["products"], [8, 28, 74, 28, 12, 24, 14, 18, 28, 18, 20, 42])
    add_sheet(wb, "软件端详细技术参数表", "软件端详细技术参数表", ["序号", "功能模块", "功能要求", "披露状态", "来源/判断"], spec["software"], [8, 28, 82, 26, 48])
    add_sheet(wb, "硬件设备参数", "硬件设备参数", ["序号", "设备/范围", "型号/参数", "数量/范围", "价格/权重", "说明", "状态"], spec["hardware"], [8, 30, 48, 20, 22, 68, 14])
    add_sheet(wb, "公司资质", "公司/供应商资质", ["序号", "资质/条件", "详细要求", "状态", "来源"], spec["qualifications"], [8, 30, 86, 14, 72])
    add_sheet(wb, "评分情况", "评分/候选情况", ["序号", "投标人/供应商", "商务/角色", "技术/报价", "投标报价/其他", "最终总分/备注"], spec["scores"], [8, 42, 18, 18, 28, 28])
    for sheet in spec.get("extra_sheets", []):
        add_sheet(wb, sheet["name"], sheet["title"], sheet["headers"], sheet["rows"], sheet["widths"])
    if "package_rows" in spec:
        headers = ["设备/货物", "候选1/限价", "候选2/权重", "候选3", "候选4", "候选5"]
        rows = spec["package_rows"]
        if rows and len(rows[0]) == 3:
            headers = ["设备/货物", "最高限价/候选1", "权重/候选2"]
        add_sheet(wb, "分包与设备清单", "分包与设备清单", headers, rows, [34, 24, 24, 24, 24, 24])
    add_sheet(wb, "建设范围", "建设范围", ["序号", "范围", "区域", "地址/点位", "建设内容", "状态", "来源"], spec["scope"], [8, 32, 18, 28, 72, 14, 72])
    add_sheet(wb, "3家投标公司分析", "3家投标公司分析", ["排名", "公司", "报价/金额", "得分", "角色", "分析"], spec["bidders"], [12, 42, 24, 18, 22, 72])
    add_sheet(wb, "中标原因", "中标原因/缺口", ["类型", "结论"], spec["reason"], [24, 110])
    add_sheet(
        wb,
        "渠道抓取与补全计划",
        "渠道抓取与补全计划（由乙方宝截图/附件经验沉淀）",
        ["渠道", "要抓的信息", "操作方式", "登录/扫码状态", "写入Excel规则"],
        spec["source_channel_rows"],
        [30, 48, 70, 34, 60],
    )
    add_sheet(wb, "来源与缺口", "来源与缺口", ["序号", "来源链接"], [[i + 1, src] for i, src in enumerate(spec["sources"])], [8, 120])
    output = OUTPUT_DIR / spec["filename"]
    wb.save(output)
    return output


def main() -> None:
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    for spec in build_specs():
        print(write_workbook(spec))


if __name__ == "__main__":
    main()
