report_generate.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. # -*- coding: utf-8 -*-
  2. """
  3. @author: yq
  4. @time: 2024/11/8
  5. @desc:
  6. """
  7. import os
  8. from typing import Dict
  9. from docx import Document
  10. from docx.enum.text import WD_ALIGN_PARAGRAPH
  11. from docx.oxml import OxmlElement
  12. from docx.oxml.ns import qn
  13. from docx.shared import Inches
  14. from commom import GeneralException, f_get_datetime
  15. from entitys import MetricFucEntity
  16. from enums import ResultCodesEnum, PlaceholderPrefixEnum
  17. class Report():
  18. # 设置 table 的边框,用法与 cell 类似
  19. @staticmethod
  20. def _set_table_boarder(table, **kwargs):
  21. """
  22. Set table`s border
  23. Usage:
  24. set_table_border(
  25. cell,
  26. top={"sz": 12, "val": "single", "color": "#FF0000"},
  27. bottom={"sz": 12, "color": "#00FF00", "val": "single"},
  28. left={"sz": 24, "val": "dashed"},
  29. right={"sz": 12, "val": "dashed"},
  30. )
  31. """
  32. borders = OxmlElement('w:tblBorders')
  33. for tag in ('bottom', 'top', 'left', 'right', 'insideV', 'insideH'):
  34. edge_data = kwargs.get(tag)
  35. if edge_data:
  36. any_border = OxmlElement(f'w:{tag}')
  37. for key in ["sz", "val", "color", "space", "shadow"]:
  38. if key in edge_data:
  39. any_border.set(qn(f'w:{key}'), str(edge_data[key]))
  40. borders.append(any_border)
  41. table._tbl.tblPr.append(borders)
  42. # 将table 的所有单元格四个边设置为 0.5 镑, 黑色, 实线
  43. @staticmethod
  44. def _set_table_singleBoard(table):
  45. return Report._set_table_boarder(
  46. table,
  47. top={"sz": 4, "val": "single", "color": "#000000"},
  48. bottom={"sz": 4, "val": "single", "color": "#000000"},
  49. left={"sz": 4, "val": "single", "color": "#000000"},
  50. right={"sz": 4, "val": "single", "color": "#000000"},
  51. insideV={"sz": 4, "val": "single", "color": "#000000"},
  52. insideH={"sz": 4, "val": "single", "color": "#000000"}
  53. )
  54. @staticmethod
  55. def _get_placeholder(placeholder_prefix_enum: PlaceholderPrefixEnum, metric_code: str):
  56. return "{{" + f"{placeholder_prefix_enum.value}{metric_code}" + "}}"
  57. @staticmethod
  58. def _fill_value_placeholder(doc: Document, metric_value_dict: Dict[str, MetricFucEntity]):
  59. # 替换指标
  60. for paragraph in doc.paragraphs:
  61. text = paragraph.text
  62. for metric_code, metric_fuc_entity in metric_value_dict.items():
  63. placeholder = Report._get_placeholder(PlaceholderPrefixEnum.VALUE, metric_code)
  64. metric_value = metric_fuc_entity.value
  65. if metric_value is None:
  66. continue
  67. text = text.replace(placeholder, metric_value)
  68. # 段落中多个runs时执行,最后一个run改成替换好的文本,其他run置空
  69. if len(paragraph.runs[:-1]) > 0:
  70. for run in paragraph.runs[:-1]:
  71. run.text = ''
  72. paragraph.runs[-1].text = text
  73. @staticmethod
  74. def _fill_table_placeholder(doc: Document, metric_value_dict: Dict[str, MetricFucEntity]):
  75. # 替换表格
  76. for paragraph in doc.paragraphs:
  77. for metric_code, metric_fuc_entity in metric_value_dict.items():
  78. placeholder = Report._get_placeholder(PlaceholderPrefixEnum.TABLE, metric_code)
  79. metric_table = metric_fuc_entity.table
  80. if metric_table is None:
  81. continue
  82. if not placeholder in paragraph.text:
  83. continue
  84. # 清除占位符
  85. for run in paragraph.runs:
  86. run.text = run.text.replace(placeholder, "")
  87. table = doc.add_table(rows=metric_table.shape[0] + 1, cols=metric_table.shape[1])
  88. table.alignment = WD_ALIGN_PARAGRAPH.CENTER
  89. paragraph._element.addnext(table._element)
  90. # 列名
  91. for column_idx, column_name in enumerate(metric_table.columns):
  92. cell = table.cell(0, column_idx)
  93. cell.text = str(column_name)
  94. for run in cell.paragraphs[0].runs:
  95. run.bold = True
  96. cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
  97. # 值
  98. for row_idx, row in metric_table.iterrows():
  99. for column_idx, value in enumerate(row):
  100. cell = table.cell(row_idx + 1, column_idx)
  101. cell.text = str(value)
  102. cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
  103. Report._set_table_singleBoard(table)
  104. @staticmethod
  105. def _fill_image_placeholder(doc: Document, metric_value_dict: Dict[str, MetricFucEntity]):
  106. # 替换图片
  107. for paragraph in doc.paragraphs:
  108. for metric_code, metric_fuc_entity in metric_value_dict.items():
  109. placeholder = Report._get_placeholder(PlaceholderPrefixEnum.IMAGE, metric_code)
  110. image_path = metric_fuc_entity.image_path
  111. if image_path is None:
  112. continue
  113. if not placeholder in paragraph.text:
  114. continue
  115. if not os.path.exists(image_path):
  116. raise GeneralException(ResultCodesEnum.NOT_FOUND, message=f"文件【{image_path}】不存在")
  117. # 清除占位符
  118. for run in paragraph.runs:
  119. if placeholder not in run.text:
  120. continue
  121. run.text = run.text.replace(placeholder, "")
  122. run.add_picture(image_path, width=Inches(5))
  123. @staticmethod
  124. def generate_report(metric_value_dict: Dict[str, MetricFucEntity], template_path: str):
  125. if os.path.exists(template_path):
  126. doc = Document(template_path)
  127. else:
  128. raise GeneralException(ResultCodesEnum.NOT_FOUND, message=f"监控模板文件【{template_path}】不存在")
  129. Report._fill_value_placeholder(doc, metric_value_dict)
  130. Report._fill_table_placeholder(doc, metric_value_dict)
  131. Report._fill_image_placeholder(doc, metric_value_dict)
  132. new_path = template_path.replace(".docx", f"{f_get_datetime()}.docx")
  133. doc.save(f"./{new_path}")
  134. if __name__ == "__main__":
  135. pass