# 上海市服务月报 PPT 自动更新指南

本目录用于按页更新 `ppt_templates/上海市服务月报.pptx`。目前 9 个页面均已完成独立脚本设计，每一页由一个单文件 Python 脚本负责读取 FastAPI 数据、计算页面口径，并写回 PPTX 中对应的文本、表格、原生图表和内嵌 workbook。

## 核心原则

- 每一页脚本都是独立文件，位于 `ppt_update/shanghai_city/page_updates/P{页码}_update.py`。
- 所有数据只来自本机 FastAPI，不读取旧 `scripts/`，不使用 JSON fallback。
- 默认 FastAPI 地址为 `http://127.0.0.1:8000`。
- 任一必要接口、字段、占位符、表格、图表或 workbook 缺失时，脚本直接停止并抛出清晰错误。
- 脚本只更新自己负责的页面，其余页面和模板样式尽量保持不变。
- 脚本不额外处理字体、字号、文本框位置或模板版式；需要适配空间时优先压缩生成文案。
- 无法完全自动分析的内容保留双花括号事实占位符，格式为 `{{数据事实；请基于数据进行概括总结分析，凸显数据特征}}` 或同义短提示。
- 可比较字段为 `/` 时，表格单元格显示 `/`；分析描述保留数据事实，并说明可比较数据暂不可得或只写事实占位符。
- 支持输入 PPTX 与输出 PPTX 为同一路径，脚本会用临时文件安全替换。

## 目录结构

```text
market_api/
  market_api/                  FastAPI 服务
  ppt_templates/上海市服务月报.pptx   当前 PPT 模板
  ppt_update/
    README.md                  PPT 自动更新模块总入口
    shanghai_city/
      README.md                本指南
      page_updates/
        P1_update.py
        ...
        P9_update.py
    administrative_district/   行政区服务月报模块，后续建设
  output/                      建议输出目录
```

## 前置条件

1. 启动 FastAPI 服务，并确保本机 `8000` 端口可访问。

2. 用接口探活确认服务正常：

```bash
curl -I http://127.0.0.1:8000/docs
```

3. 建议在项目根目录执行脚本：

```bash
cd /Users/kun/PycharmProjects/market_api
```

## 单页更新用法

每个页面脚本参数一致：

```bash
python3 ppt_update/shanghai_city/page_updates/P1_update.py \
  --pptx ppt_templates/上海市服务月报.pptx \
  --output output/上海市服务月报-P1更新.pptx \
  --year 2026 \
  --month 4月 \
  --api-base http://127.0.0.1:8000
```

参数说明：

| 参数 | 说明 |
| --- | --- |
| `--pptx` | 输入 PPTX，可以是模板，也可以是上一页更新后的 PPTX |
| `--output` | 输出 PPTX |
| `--year` | 报告年份，例如 `2026` |
| `--month` | 报告月份，例如 `4月` |
| `--api-base` | FastAPI 地址，通常为 `http://127.0.0.1:8000` |

## 整份 PPT 串行更新

如果要得到一份 9 页都更新后的 PPT，可以先更新 P1 输出到一个总文件，再用同一个文件作为后续页面的输入和输出：

```bash
python3 ppt_update/shanghai_city/page_updates/P1_update.py --pptx ppt_templates/上海市服务月报.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
python3 ppt_update/shanghai_city/page_updates/P2_update.py --pptx output/上海市服务月报-自动更新.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
python3 ppt_update/shanghai_city/page_updates/P3_update.py --pptx output/上海市服务月报-自动更新.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
python3 ppt_update/shanghai_city/page_updates/P4_update.py --pptx output/上海市服务月报-自动更新.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
python3 ppt_update/shanghai_city/page_updates/P5_update.py --pptx output/上海市服务月报-自动更新.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
python3 ppt_update/shanghai_city/page_updates/P6_update.py --pptx output/上海市服务月报-自动更新.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
python3 ppt_update/shanghai_city/page_updates/P7_update.py --pptx output/上海市服务月报-自动更新.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
python3 ppt_update/shanghai_city/page_updates/P8_update.py --pptx output/上海市服务月报-自动更新.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
python3 ppt_update/shanghai_city/page_updates/P9_update.py --pptx output/上海市服务月报-自动更新.pptx --output output/上海市服务月报-自动更新.pptx --year 2026 --month 4月 --api-base http://127.0.0.1:8000
```

## 逐页更新逻辑

| 页面 | 主题 | FastAPI 数据源 | 主要更新内容 |
| --- | --- | --- | --- |
| P1 | 招标总览驾驶舱 | `/tender/overall`, `/tender/industry`, `/tender/region`, `/tender/major-buyers`, `/tender/class`, `/tender/time`, `/tender/customer-139` | 指标卡、增长行业/规模行业、重点区域图表与 workbook、政府/企业 Top5 客户、经营主线事实占位符、季度节奏表 |
| P2 | 累计行业招标分析 | `/tender/industry` | 按累计金额排序的行业表格、行业图表、行业分析文案 |
| P3 | 累计区域招标分析 | `/tender/region` | 按累计金额排序的区域表格、区域图表、区域分析文案 |
| P4 | 单月招标节奏、行业、区域 | `/tender/overall`, `/tender/industry`, `/tender/region` | 单月金额/个数、环比文案、月度行业与区域 Top3 |
| P5 | 政府侧与企业侧招采 Top5 客户、139 客户重点项目 | `/tender/major-buyers`, `/tender/customer-139`, `/tender/customer-139/key-projects` | 政府侧/企业侧两张 Top5 客户表、139 月度摘要、139 重点项目 Top5 表、项目共性事实占位符，标题同步改为 Top5 |
| P6 | 运营商市场中标总览 | `/award/overall`, `/award/industry`, `/award/region` | 累计/单月中标指标、同比/环比、运营商/行业/区域图表 |
| P7 | 累计行业中标分析 | `/award/industry` | 按累计中标金额排序的行业表格、`chart10/chart11`、Top3 行业文案 |
| P8 | 累计区域中标分析 | `/award/region` | 按累计中标金额排序的属地表格、`chart12/chart13`、Top3 区域文案 |
| P9 | 当月友商中标大单 | `/award/competitor-large-deals` | 电信大单与联通大单两张 Top8 明细表 |

## 关键口径

### 招标侧 P1-P5

- `scope=上海`，统计全上海公开市场招标数据。
- P1 使用累计总览、行业、属地、主要客户、项目类型、季度节奏和 139 客户数据；经营主线类内容保留事实占位符，供后置概括。
- P2/P3 使用累计数据，并按金额降序排序；同比字段为 `/` 时，表格保留 `/`，分析描述写事实与待分析提示。
- P4 使用月度数据，月度环比字段为 `/` 时，表格显示 `/`，描述中改为事实描述或“可比较环比数据暂不可得”。
- P5 使用 `/tender/major-buyers`，政府侧与企业侧各取有效 Top5；同时使用 139 客户月度数据和重点项目数据。
- P1/P5 会过滤无意义客户名称，避免 `未知单位`、`某单位`、`某公司` 等信息进入 Top 客户表述。

### 中标侧 P6-P9

- CT 口径固定为 `ct=是`。
- P6-P8 使用运营商市场中标数据，统计方式固定为 `scope=上海`。
- P6 同时读取当前月、去年同期和上月运营商总览，用于累计同比与单月环比；基准为 0 或可比较字段不足时，指标显示 `/` 或在事实占位符中提示可比不足。
- P7 使用 `/award/industry.累计数据`，按三家运营商合计中标金额排序。
- P8 使用 `/award/region.累计数据`，按三家运营商合计中标金额排序。
- P9 只使用当月数据，不做累计、同比或环比；接口返回 `电信大单` 与 `联通大单`，阈值为金额大于 300 万元，每张表取 Top8。

## 后置占位符规则

部分内容需要后续智能体基于事实进行概括总结，不在脚本里直接生成最终判断。脚本会保留双花括号，但会先填入数据事实：

```text
{{行业Top3：党政12.16/商客5.47/医卫1.89亿，占比82.7%；请基于数据进行概括总结分析，凸显数据特征}}
```

模板文本框空间较紧的页面会使用短提示或纯事实占位符，例如：

```text
{{移动累计：85个/6.89亿，29.2%}}
{{2026年4月：单月104个/9.79亿，环项+19.54%、额+115.29%；请概括，凸显特征}}
```

这种双花括号不是未更新错误，而是给后续人工或智能体二次修订保留的明确标记。检查残留时应关注 `{{P1...}}` 到 `{{P9...}}` 这类模板占位符是否仍存在。

## 模板更新对象

脚本直接写 PPTX 内部 OOXML，主要更新以下对象：

- 文本占位符：替换为脚本生成的摘要或分析文案。
- 原生表格：逐格写入最新字段值，保留原表格样式。
- 原生图表 XML：更新分类、系列名称、数值缓存和图表标题。
- 内嵌 workbook：同步更新图表对应的 Excel 数据。

P9 没有图表，只更新两张表和右侧标题。

## 错误策略

所有页面均采用严格错误策略：

- FastAPI 连接失败、HTTP 非 200、返回值不是 JSON：直接报错。
- 必填数据节点缺失：直接报错。
- 必填字段为空或不是数字：直接报错。
- 排序所需数据不足：直接报错。
- 模板缺少占位符、表格、图表或 workbook：直接报错。
- 不自动补默认值，不静默跳过，不降级到旧数据。
- 同比、环比、份额变化等可比较字段允许为 `/` 的页面，会在表格中原样显示 `/`，在描述中转为事实说明；其他必算字段仍按缺失处理。

这种策略是为了让智能体在数据或模板异常时停下来，而不是生成看似完整但口径错误的月报。

## 测试与验证

运行当前上海市页面测试：

```bash
python3 -m unittest discover -s tests
```

单页测试示例：

```bash
python3 -m unittest tests/test_shanghai_p6_update.py
python3 -m unittest tests/test_shanghai_p7_p8_p9_update.py
```

脚本编译检查示例：

```bash
python3 -m py_compile ppt_update/shanghai_city/page_updates/P{1,2,3,4,5,6,7,8,9}_update.py
```

建议每次修改模板或脚本后至少执行：

1. 对应单页测试或脚本编译检查。
2. P1-P9 全量回归测试，若测试目录存在。
3. 用 FastAPI 真实生成输出 PPTX。
4. 拆包检查无 `{{P1...}}` 到 `{{P9...}}` 模板占位符和 `XXXX` 残留；事实占位符可按设计保留。
5. 使用 LibreOffice 或 PowerPoint 渲染核验重点页，确认文本未溢出、表格未扩展、图表/workbook 与表格内容同步。

## 维护建议

- 新增或调整模板占位符时，先改对应测试，再改模板与脚本。
- 页面口径变更时，优先在对应 `P{页码}_update.py` 文件顶部注释和本 README 中同步说明。
- 不要把跨页公共逻辑抽到旧 `scripts/` 中；当前设计要求每页脚本自包含，便于智能体按页理解和执行。
- 如果 FastAPI 接口字段发生变化，先更新页面测试中的样例响应，再修改脚本解析逻辑。
- 如果模板页内图表编号、workbook 编号或表格数量变化，必须同步更新对应页面测试，防止脚本写错对象。
