create_monitoring_architecture_ppt.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. # -*- coding: utf-8 -*-
  2. from __future__ import annotations
  3. import html
  4. import os
  5. import shutil
  6. import zipfile
  7. from datetime import datetime, timezone
  8. from pathlib import Path
  9. BASE = Path(__file__).resolve().parent
  10. OUT = BASE / "订单与供应监测模块架构汇报.pptx"
  11. BUILD = BASE / "pptx-build-py"
  12. EMU = 9525
  13. def emu(px: float) -> int:
  14. return int(px * EMU)
  15. shape_id = 1
  16. def next_id() -> int:
  17. global shape_id
  18. shape_id += 1
  19. return shape_id
  20. def rgb(value: str) -> str:
  21. return value.strip("#").upper()
  22. def write(path: Path, content: str) -> None:
  23. path.parent.mkdir(parents=True, exist_ok=True)
  24. path.write_text(content, encoding="utf-8")
  25. def text_shape(text: str, x: int, y: int, w: int, h: int, size: int, color: str, bold: bool = False) -> str:
  26. sid = next_id()
  27. bold_attr = ' b="1"' if bold else ""
  28. paragraphs = []
  29. for line in str(text).splitlines() or [""]:
  30. paragraphs.append(
  31. f'<a:p><a:r><a:rPr lang="zh-CN" sz="{size * 100}"{bold_attr}>'
  32. f'<a:solidFill><a:srgbClr val="{rgb(color)}"/></a:solidFill>'
  33. f'<a:latin typeface="Microsoft YaHei"/><a:ea typeface="Microsoft YaHei"/></a:rPr>'
  34. f'<a:t>{html.escape(line)}</a:t></a:r>'
  35. f'<a:endParaRPr lang="zh-CN" sz="{size * 100}"/></a:p>'
  36. )
  37. return f"""
  38. <p:sp>
  39. <p:nvSpPr><p:cNvPr id="{sid}" name="Text {sid}"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr>
  40. <p:spPr><a:xfrm><a:off x="{emu(x)}" y="{emu(y)}"/><a:ext cx="{emu(w)}" cy="{emu(h)}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/><a:ln><a:noFill/></a:ln></p:spPr>
  41. <p:txBody><a:bodyPr wrap="square" rtlCol="0"><a:spAutoFit/></a:bodyPr><a:lstStyle/>{''.join(paragraphs)}</p:txBody>
  42. </p:sp>
  43. """
  44. def rect_shape(x: int, y: int, w: int, h: int, fill: str, line: str = "FFFFFF", rounded: bool = False) -> str:
  45. sid = next_id()
  46. geom = "roundRect" if rounded else "rect"
  47. return f"""
  48. <p:sp>
  49. <p:nvSpPr><p:cNvPr id="{sid}" name="Box {sid}"/><p:cNvSpPr/><p:nvPr/></p:nvSpPr>
  50. <p:spPr><a:xfrm><a:off x="{emu(x)}" y="{emu(y)}"/><a:ext cx="{emu(w)}" cy="{emu(h)}"/></a:xfrm><a:prstGeom prst="{geom}"><a:avLst/></a:prstGeom><a:solidFill><a:srgbClr val="{rgb(fill)}"/></a:solidFill><a:ln w="9525"><a:solidFill><a:srgbClr val="{rgb(line)}"/></a:solidFill></a:ln></p:spPr>
  51. </p:sp>
  52. """
  53. def card(title: str, body: str, x: int, y: int, w: int, h: int, accent: str) -> str:
  54. return (
  55. rect_shape(x, y, w, h, "FFFFFF", "DDDDDD", True)
  56. + rect_shape(x, y, 8, h, accent, accent, False)
  57. + text_shape(title, x + 22, y + 16, w - 35, 34, 17, "17304A", True)
  58. + text_shape(body, x + 22, y + 58, w - 35, h - 68, 13, "555555", False)
  59. )
  60. def node(text: str, x: int, y: int, w: int, h: int, fill: str, line: str) -> str:
  61. return rect_shape(x, y, w, h, fill, line, True) + text_shape(text, x + 10, y + 15, w - 20, h - 20, 14, "17304A", True)
  62. def title(text: str, subtitle: str = "") -> str:
  63. body = text_shape(text, 70, 42, 980, 52, 30, "17304A", True)
  64. if subtitle:
  65. body += text_shape(subtitle, 72, 92, 980, 30, 14, "666666")
  66. body += rect_shape(70, 128, 1140, 3, "2B74B1", "2B74B1")
  67. return body
  68. slides = [
  69. {
  70. "bg": "FBFAF7",
  71. "body": text_shape("订单监测与供应监测", 90, 170, 900, 58, 38, "17304A", True)
  72. + text_shape("模块架构、功能逻辑与系统关联汇报", 92, 238, 880, 38, 20, "666666")
  73. + text_shape("面向管理层的简版说明", 94, 300, 420, 28, 15, "2B74B1", True)
  74. + card("汇报重点", "1. 两个模块分别解决什么问题\n2. 数据如何进入系统并形成分析结果\n3. 与库存、产品、生命周期等模块如何关联", 760, 170, 390, 250, "2B74B1"),
  75. },
  76. {
  77. "bg": "FFFFFF",
  78. "body": title("一页总览", "两个模块都采用:数据接入 -> 数据入库 -> 后端聚合 -> 前端看板展示")
  79. + card("订单监测", "关注销售侧:订单金额、商品贡献、渠道/平台表现、支付效率、退款漏损、连带购买。\n\n当前数据库已有核心表,具备较完整的数据分析基础。", 80, 170, 500, 320, "2B74B1")
  80. + card("供应监测", "关注采购侧:供应商履约、采购订单、入库金额、账期、准交率、付款计划。\n\n当前数据库只有部分基础数据,需补齐采购订单、账期和交付匹配数据。", 700, 170, 500, 320, "449B7A"),
  81. },
  82. {
  83. "bg": "FFFFFF",
  84. "body": title("整体模块架构", "前端页面保持稳定,后端逐步将文件依赖切换为数据库驱动")
  85. + node("数据文件上传\nCSV / Excel", 70, 220, 180, 78, "DAE7F4", "2B74B1")
  86. + text_shape("→", 268, 234, 50, 50, 28, "666666", True)
  87. + node("导入服务\n解析 / 清洗 / 校验", 330, 220, 210, 78, "EAF5EC", "449B7A")
  88. + text_shape("→", 558, 234, 50, 50, 28, "666666", True)
  89. + node("业务数据库\n订单 / 供应 / 库存", 620, 220, 210, 78, "F7F1E8", "E88B3A")
  90. + text_shape("→", 848, 234, 50, 50, 28, "666666", True)
  91. + node("分析服务\nMapper / 聚合逻辑", 910, 220, 210, 78, "F5F5F5", "666666")
  92. + text_shape("↓", 995, 305, 50, 50, 26, "666666", True)
  93. + node("管理看板\n图表 / 报表 / 对比", 910, 370, 210, 78, "FFFFFF", "2B74B1")
  94. + card("架构原则", "接口尽量保持不变,减少前端改造;数据统一入库,避免依赖本地路径;分析逻辑集中在 Service 和 Mapper 层。", 120, 500, 1030, 105, "2B74B1"),
  95. },
  96. {
  97. "bg": "FFFFFF",
  98. "body": title("订单监测:功能逻辑", "订单监测已经具备数据库化基础,重点是持续完善导入和统计口径")
  99. + card("核心功能", "订单价值分析:GMV、成交金额、退款金额\n商品分析:Top 商品、连带购买\n渠道分析:平台、渠道、业务单元贡献\n支付分析:平均支付耗时、支付漏斗\n经营报表:按日/月/年聚合", 70, 165, 360, 370, "2B74B1")
  100. + card("实现逻辑", "1. 订单和店铺价值数据进入数据库\n2. Mapper 查询订单主表和店铺价值明细表\n3. Service 聚合指标并返回统一结构\n4. 前端看板按日期范围展示结果", 460, 165, 360, 370, "449B7A")
  101. + card("当前基础", "dtm_order_main:订单主表\ndtm_shop_value_main:店铺价值明细表\n\n当前数据库中两张核心表已有较多数据,可支撑订单监测主要分析。", 850, 165, 360, 370, "E88B3A"),
  102. },
  103. {
  104. "bg": "FFFFFF",
  105. "body": title("供应监测:功能逻辑", "供应监测目标是从 Excel 文件读取升级为数据库驱动")
  106. + card("核心功能", "供应商统计:订单金额、入库金额、完成率、准交率\n供应商对比:多个供应商横向比较\n付款计划:账期 + 验收/入库日期计算预计付款\n数据导入:账期、订单、入库、匹配结果", 70, 165, 360, 370, "449B7A")
  107. + card("实现逻辑", "1. 上传供应监测相关 Excel\n2. 解析供应商、订单、入库、账期字段\n3. 写入供应监测数据表\n4. Service 从数据库聚合供应商指标\n5. 前端继续使用现有查询接口", 460, 165, 360, 370, "2B74B1")
  108. + card("当前缺口", "数据库已有:供应商基础表、采购入库基础表。\n\n仍缺:采购订单、供应商账期、计划交货日期、实际验收日期、订单入库匹配结果。", 850, 165, 360, 370, "E88B3A"),
  109. },
  110. {
  111. "bg": "FFFFFF",
  112. "body": title("模块关联关系", "两个监测模块不是孤立看板,而是与基础数据和分析模块共享数据资产")
  113. + card("订单监测关联", "产品模块:通过 SKU 关联商品、SPU、产品信息。\n生命周期模块:复用订单销售数据做热销、趋势、生命周期分析。\n店铺模块:复用平台、渠道、业务单元维度。", 90, 170, 500, 280, "2B74B1")
  114. + card("供应监测关联", "库存模块:复用采购入库和库存流转数据。\n产品模块:通过 SKU 关联采购产品。\n供应商资料:复用供应商编号和名称,扩展账期和履约指标。", 690, 170, 500, 280, "449B7A")
  115. + card("通用关联", "系统权限和操作日志可统一管理导入权限、导入记录和异常追踪;导入批次设计可在订单和供应两个模块复用。", 190, 495, 900, 95, "E88B3A"),
  116. },
  117. {
  118. "bg": "FFFFFF",
  119. "body": title("数据入库建设重点", "管理层需要关注的是数据完整性、稳定性和可追踪性")
  120. + card("订单监测", "建设重点:完善导入批次、重复数据处理、异常行反馈。\n\n收益:订单看板稳定、历史数据可追溯、跨模块复用能力强。", 90, 170, 500, 300, "2B74B1")
  121. + card("供应监测", "建设重点:补齐采购订单、账期、计划交货日期、实际验收日期、匹配结果。\n\n收益:供应商履约分析和付款计划可以稳定落地。", 690, 170, 500, 300, "449B7A"),
  122. },
  123. {
  124. "bg": "FFFFFF",
  125. "body": title("推进路径", "建议分阶段推进,先稳定数据,再扩展分析能力")
  126. + node("阶段一\n确认数据源和字段口径", 80, 220, 210, 85, "DAE7F4", "2B74B1")
  127. + text_shape("→", 308, 240, 50, 50, 28, "666666", True)
  128. + node("阶段二\n建设导入和入库表", 370, 220, 210, 85, "EAF5EC", "449B7A")
  129. + text_shape("→", 598, 240, 50, 50, 28, "666666", True)
  130. + node("阶段三\n切换 Service 查询数据库", 660, 220, 230, 85, "F7F1E8", "E88B3A")
  131. + text_shape("→", 908, 240, 50, 50, 28, "666666", True)
  132. + node("阶段四\n完善看板和管理报表", 970, 220, 210, 85, "FFFFFF", "2B74B1")
  133. + card("结论", "订单监测已有较好的数据库基础,重点是规范化导入和追踪。供应监测需要先补齐核心数据表,再将现有 Excel 分析迁移到数据库查询。", 120, 430, 1040, 130, "2B74B1"),
  134. },
  135. ]
  136. def slide_xml(slide: dict[str, str]) -> str:
  137. return f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  138. <p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
  139. <p:cSld>
  140. <p:bg><p:bgPr><a:solidFill><a:srgbClr val="{rgb(slide["bg"])}"/></a:solidFill><a:effectLst/></p:bgPr></p:bg>
  141. <p:spTree>
  142. <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
  143. <p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr>
  144. {slide["body"]}
  145. </p:spTree>
  146. </p:cSld>
  147. <p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
  148. </p:sld>
  149. '''
  150. def build() -> None:
  151. if OUT.exists():
  152. OUT.unlink()
  153. if BUILD.exists():
  154. shutil.rmtree(BUILD)
  155. for folder in [
  156. "_rels",
  157. "docProps",
  158. "ppt/_rels",
  159. "ppt/slides/_rels",
  160. "ppt/slideMasters/_rels",
  161. "ppt/slideLayouts/_rels",
  162. "ppt/theme",
  163. ]:
  164. (BUILD / folder).mkdir(parents=True, exist_ok=True)
  165. overrides = [
  166. '<Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>',
  167. '<Override PartName="/ppt/slideMasters/slideMaster1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"/>',
  168. '<Override PartName="/ppt/slideLayouts/slideLayout1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"/>',
  169. '<Override PartName="/ppt/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/>',
  170. '<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>',
  171. '<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>',
  172. ]
  173. for index, slide in enumerate(slides, start=1):
  174. overrides.append(f'<Override PartName="/ppt/slides/slide{index}.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>')
  175. write(BUILD / f"ppt/slides/slide{index}.xml", slide_xml(slide))
  176. write(
  177. BUILD / f"ppt/slides/_rels/slide{index}.xml.rels",
  178. '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  179. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  180. <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
  181. </Relationships>
  182. ''',
  183. )
  184. write(
  185. BUILD / "[Content_Types].xml",
  186. f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  187. <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  188. <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
  189. <Default Extension="xml" ContentType="application/xml"/>
  190. {' '.join(overrides)}
  191. </Types>
  192. ''',
  193. )
  194. write(
  195. BUILD / "_rels/.rels",
  196. '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  197. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  198. <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
  199. <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
  200. <Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
  201. </Relationships>
  202. ''',
  203. )
  204. slide_ids = []
  205. rels = ['<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="slideMasters/slideMaster1.xml"/>']
  206. for index in range(1, len(slides) + 1):
  207. rid = index + 1
  208. slide_ids.append(f'<p:sldId id="{255 + index}" r:id="rId{rid}"/>')
  209. rels.append(f'<Relationship Id="rId{rid}" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide{index}.xml"/>')
  210. write(
  211. BUILD / "ppt/presentation.xml",
  212. f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  213. <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
  214. <p:sldMasterIdLst><p:sldMasterId id="2147483648" r:id="rId1"/></p:sldMasterIdLst>
  215. <p:sldIdLst>{''.join(slide_ids)}</p:sldIdLst>
  216. <p:sldSz cx="12192000" cy="6858000" type="wide"/>
  217. <p:notesSz cx="6858000" cy="9144000"/>
  218. <p:defaultTextStyle/>
  219. </p:presentation>
  220. ''',
  221. )
  222. write(BUILD / "ppt/_rels/presentation.xml.rels", f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  223. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  224. {' '.join(rels)}
  225. </Relationships>
  226. ''')
  227. write(BUILD / "ppt/slideMasters/slideMaster1.xml", '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  228. <p:sldMaster xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
  229. <p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld>
  230. <p:clrMap bg1="lt1" tx1="dk1" bg2="lt2" tx2="dk2" accent1="accent1" accent2="accent2" accent3="accent3" accent4="accent4" accent5="accent5" accent6="accent6" hlink="hlink" folHlink="folHlink"/>
  231. <p:sldLayoutIdLst><p:sldLayoutId id="2147483649" r:id="rId1"/></p:sldLayoutIdLst>
  232. <p:txStyles><p:titleStyle/><p:bodyStyle/><p:otherStyle/></p:txStyles>
  233. </p:sldMaster>
  234. ''')
  235. write(BUILD / "ppt/slideMasters/_rels/slideMaster1.xml.rels", '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  236. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  237. <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Target="../slideLayouts/slideLayout1.xml"/>
  238. <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="../theme/theme1.xml"/>
  239. </Relationships>
  240. ''')
  241. write(BUILD / "ppt/slideLayouts/slideLayout1.xml", '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  242. <p:sldLayout xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main" type="blank" preserve="1">
  243. <p:cSld name="Blank"><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld>
  244. <p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
  245. </p:sldLayout>
  246. ''')
  247. write(BUILD / "ppt/slideLayouts/_rels/slideLayout1.xml.rels", '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  248. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  249. <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster" Target="../slideMasters/slideMaster1.xml"/>
  250. </Relationships>
  251. ''')
  252. write(BUILD / "ppt/theme/theme1.xml", '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  253. <a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="DTM"><a:themeElements><a:clrScheme name="DTM"><a:dk1><a:srgbClr val="17304A"/></a:dk1><a:lt1><a:srgbClr val="FFFFFF"/></a:lt1><a:dk2><a:srgbClr val="666666"/></a:dk2><a:lt2><a:srgbClr val="FBFAF7"/></a:lt2><a:accent1><a:srgbClr val="2B74B1"/></a:accent1><a:accent2><a:srgbClr val="449B7A"/></a:accent2><a:accent3><a:srgbClr val="E88B3A"/></a:accent3><a:accent4><a:srgbClr val="8A8A8A"/></a:accent4><a:accent5><a:srgbClr val="DAE7F4"/></a:accent5><a:accent6><a:srgbClr val="F7F1E8"/></a:accent6><a:hlink><a:srgbClr val="2B74B1"/></a:hlink><a:folHlink><a:srgbClr val="449B7A"/></a:folHlink></a:clrScheme><a:fontScheme name="DTM"><a:majorFont><a:latin typeface="Microsoft YaHei"/><a:ea typeface="Microsoft YaHei"/></a:majorFont><a:minorFont><a:latin typeface="Microsoft YaHei"/><a:ea typeface="Microsoft YaHei"/></a:minorFont></a:fontScheme><a:fmtScheme name="DTM"><a:fillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:fillStyleLst><a:lnStyleLst><a:ln w="9525"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln></a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:bgFillStyleLst></a:fmtScheme></a:themeElements></a:theme>
  254. ''')
  255. stamp = datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
  256. write(BUILD / "docProps/core.xml", f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  257. <cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>订单与供应监测模块架构汇报</dc:title><dc:creator>Codex</dc:creator><cp:lastModifiedBy>Codex</cp:lastModifiedBy><dcterms:created xsi:type="dcterms:W3CDTF">{stamp}</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">{stamp}</dcterms:modified></cp:coreProperties>
  258. ''')
  259. write(BUILD / "docProps/app.xml", f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  260. <Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Application>Codex OpenXML Generator</Application><PresentationFormat>Widescreen</PresentationFormat><Slides>{len(slides)}</Slides><Company>DTM</Company></Properties>
  261. ''')
  262. with zipfile.ZipFile(OUT, "w", zipfile.ZIP_DEFLATED) as zf:
  263. for root, _, files in os.walk(BUILD):
  264. for file in files:
  265. path = Path(root) / file
  266. zf.write(path, path.relative_to(BUILD).as_posix())
  267. shutil.rmtree(BUILD)
  268. if __name__ == "__main__":
  269. build()
  270. print(OUT)