create_qa_score_ppt.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. from pathlib import Path
  2. from zipfile import ZipFile, ZIP_DEFLATED
  3. import html
  4. OUT = Path("docs/供应监测评分卡QA质量评分机制_两页汇报_修正版.pptx")
  5. OUT.parent.mkdir(parents=True, exist_ok=True)
  6. def esc(value):
  7. return html.escape(str(value), quote=True)
  8. shape_id = 10
  9. def next_id():
  10. global shape_id
  11. shape_id += 1
  12. return shape_id
  13. def text_box(x, y, cx, cy, text, size=1800, bold=False, color="1F2937"):
  14. sid = next_id()
  15. runs = []
  16. for line in text.split("\n"):
  17. b = ' b="1"' if bold else ""
  18. runs.append(
  19. f'<a:p><a:r><a:rPr lang="zh-CN" sz="{size}"{b}>'
  20. f'<a:solidFill><a:srgbClr val="{color}"/></a:solidFill>'
  21. f'<a:latin typeface="Microsoft YaHei"/><a:ea typeface="Microsoft YaHei"/></a:rPr>'
  22. f"<a:t>{esc(line)}</a:t></a:r></a:p>"
  23. )
  24. return (
  25. f'<p:sp><p:nvSpPr><p:cNvPr id="{sid}" name="TextBox {sid}"/>'
  26. f'<p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr><p:spPr>'
  27. f'<a:xfrm><a:off x="{x}" y="{y}"/><a:ext cx="{cx}" cy="{cy}"/></a:xfrm>'
  28. f'<a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/>'
  29. f'<a:ln><a:noFill/></a:ln></p:spPr><p:txBody><a:bodyPr wrap="square"/>'
  30. f"<a:lstStyle/>{''.join(runs)}</p:txBody></p:sp>"
  31. )
  32. def rect(x, y, cx, cy, fill="FFFFFF", line="D9E2F1", rounded=True):
  33. sid = next_id()
  34. prst = "roundRect" if rounded else "rect"
  35. return (
  36. f'<p:sp><p:nvSpPr><p:cNvPr id="{sid}" name="Shape {sid}"/>'
  37. f"<p:cNvSpPr/><p:nvPr/></p:nvSpPr><p:spPr>"
  38. f'<a:xfrm><a:off x="{x}" y="{y}"/><a:ext cx="{cx}" cy="{cy}"/></a:xfrm>'
  39. f'<a:prstGeom prst="{prst}"><a:avLst/></a:prstGeom>'
  40. f'<a:solidFill><a:srgbClr val="{fill}"/></a:solidFill>'
  41. f'<a:ln w="12700"><a:solidFill><a:srgbClr val="{line}"/></a:solidFill></a:ln>'
  42. f"</p:spPr><p:txBody><a:bodyPr/><a:lstStyle/><a:p/></p:txBody></p:sp>"
  43. )
  44. def card(x, y, cx, cy, title, body, accent="2563EB"):
  45. return (
  46. rect(x, y, cx, cy)
  47. + text_box(x + 180000, y + 130000, cx - 360000, 230000, title, 1500, True, accent)
  48. + text_box(x + 180000, y + 420000, cx - 360000, cy - 520000, body, 1120, False, "475569")
  49. )
  50. def slide(title, subtitle, shapes):
  51. return f'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  52. <p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
  53. xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
  54. xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
  55. <p:cSld>
  56. <p:bg><p:bgPr><a:solidFill><a:srgbClr val="F4F7FB"/></a:solidFill><a:effectLst/></p:bgPr></p:bg>
  57. <p:spTree>
  58. <p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr>
  59. <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>
  60. {rect(0, 0, 12192000, 760000, "1F4E79", "1F4E79", False)}
  61. {text_box(520000, 170000, 9500000, 340000, title, 2300, True, "FFFFFF")}
  62. {text_box(520000, 545000, 9800000, 200000, subtitle, 1050, False, "DBEAFE")}
  63. {shapes}
  64. </p:spTree>
  65. </p:cSld>
  66. <p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr>
  67. </p:sld>'''
  68. slide1_shapes = ""
  69. slide1_shapes += card(
  70. 520000,
  71. 1060000,
  72. 3500000,
  73. 1700000,
  74. "数据可以接入",
  75. "QA 表包含供应商、商品编码、验货日期、来货数量、抽检个数、不合格个数、大批次品和问题描述,可支撑供应商质量维度统计。",
  76. )
  77. slide1_shapes += card(
  78. 4300000,
  79. 1060000,
  80. 3500000,
  81. 1700000,
  82. "核心计算口径",
  83. "抽检总数 = SUM(抽检/个数)\n不合格总数 = SUM(不合格/个 + 大批次品)\nQA合格率 = 合格数 / 抽检总数",
  84. "059669",
  85. )
  86. slide1_shapes += card(
  87. 8080000,
  88. 1060000,
  89. 3500000,
  90. 1700000,
  91. "供应商匹配",
  92. "QA 表常用简称,如“顾舒家华”。系统供应商可能是公司全称。采用包含关系匹配:全称包含简称即可归属。",
  93. "7C3AED",
  94. )
  95. slide1_shapes += rect(520000, 3200000, 11150000, 2500000)
  96. slide1_shapes += text_box(820000, 3430000, 10300000, 260000, "落地数据流", 1600, True, "1F4E79")
  97. slide1_shapes += text_box(
  98. 850000,
  99. 3920000,
  100. 10200000,
  101. 850000,
  102. "QA Excel 多月份 Sheet → 读取所有月份 → 按供应商汇总抽检/不合格 → 宽松匹配供应商评分卡 → 输出 QA合格率、质量分、不合格数",
  103. 1400,
  104. False,
  105. "334155",
  106. )
  107. slide1_shapes += text_box(
  108. 850000,
  109. 5030000,
  110. 10200000,
  111. 450000,
  112. "无 QA 数据的供应商:QA合格率显示“暂无”,质量分按 80 分中性值处理,避免误伤。",
  113. 1250,
  114. False,
  115. "64748B",
  116. )
  117. slide2_shapes = ""
  118. slide2_shapes += rect(520000, 1040000, 11150000, 1200000)
  119. slide2_shapes += text_box(850000, 1260000, 10200000, 280000, "综合评分新权重", 1750, True, "1F4E79")
  120. slide2_shapes += text_box(
  121. 850000,
  122. 1680000,
  123. 10200000,
  124. 360000,
  125. "综合分 = 成本分 × 30% + 交付分 × 30% + 账期分 × 15% + 质量分 × 25%",
  126. 1550,
  127. True,
  128. "2563EB",
  129. )
  130. slide2_shapes += card(520000, 2550000, 2550000, 1900000, "页面新增字段", "质量分\nQA合格率\n不合格数\n可扩展:QA抽检数、QA来货数")
  131. slide2_shapes += card(3370000, 2550000, 2550000, 1900000, "质量预警规则", "QA抽检数 > 0\n且 QA合格率 < 95%\n则标记:质量预警", "DC2626")
  132. slide2_shapes += card(6220000, 2550000, 2550000, 1900000, "业务价值", "识别低价但质量不稳的供应商\n避免评分只被成本和交付主导\n支持整改、淘汰、谈判", "059669")
  133. slide2_shapes += card(9070000, 2550000, 2550000, 1900000, "后续建议", "将 QA Excel 入库\n建立质量检验明细表\n扩展质量趋势和问题类型分析", "7C3AED")
  134. slide2_shapes += rect(520000, 5050000, 11150000, 850000, "EFF6FF", "BFDBFE")
  135. slide2_shapes += text_box(
  136. 820000,
  137. 5290000,
  138. 10500000,
  139. 300000,
  140. "结论:QA 数据可以纳入供应监测评分卡,推荐以 QA 合格率形成质量分,并作为 25% 权重参与综合评分。",
  141. 1350,
  142. True,
  143. "1E3A8A",
  144. )
  145. slide1 = slide("供应监测评分卡新增 QA 质量维度", "数据来源与计算逻辑", slide1_shapes)
  146. slide2 = slide("评分机制与页面落地", "新增质量分、QA合格率与质量预警", slide2_shapes)
  147. content_types = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  148. <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  149. <Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
  150. <Default Extension="xml" ContentType="application/xml"/>
  151. <Override PartName="/ppt/presentation.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"/>
  152. <Override PartName="/ppt/slides/slide1.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>
  153. <Override PartName="/ppt/slides/slide2.xml" ContentType="application/vnd.openxmlformats-officedocument.presentationml.slide+xml"/>
  154. </Types>'''
  155. rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  156. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  157. <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="ppt/presentation.xml"/>
  158. </Relationships>'''
  159. pres = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  160. <p:presentation xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"
  161. xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
  162. xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main">
  163. <p:sldIdLst><p:sldId id="256" r:id="rId1"/><p:sldId id="257" r:id="rId2"/></p:sldIdLst>
  164. <p:sldSz cx="12192000" cy="6858000" type="screen4x3"/>
  165. <p:notesSz cx="6858000" cy="9144000"/>
  166. </p:presentation>'''
  167. pres_rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  168. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
  169. <Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide1.xml"/>
  170. <Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide" Target="slides/slide2.xml"/>
  171. </Relationships>'''
  172. empty_rels = '''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
  173. <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"/>'''
  174. with ZipFile(OUT, "w", ZIP_DEFLATED) as z:
  175. z.writestr("[Content_Types].xml", content_types.encode("utf-8"))
  176. z.writestr("_rels/.rels", rels.encode("utf-8"))
  177. z.writestr("ppt/presentation.xml", pres.encode("utf-8"))
  178. z.writestr("ppt/_rels/presentation.xml.rels", pres_rels.encode("utf-8"))
  179. z.writestr("ppt/slides/slide1.xml", slide1.encode("utf-8"))
  180. z.writestr("ppt/slides/slide2.xml", slide2.encode("utf-8"))
  181. z.writestr("ppt/slides/_rels/slide1.xml.rels", empty_rels.encode("utf-8"))
  182. z.writestr("ppt/slides/_rels/slide2.xml.rels", empty_rels.encode("utf-8"))
  183. print(OUT.resolve())