import os import random from typing import List from docx import Document from docx.enum.table import WD_ALIGN_VERTICAL from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.oxml import OxmlElement from docx.shared import Pt, RGBColor, Mm, Cm, Inches from collections import defaultdict from docxtpl import DocxTemplate, InlineImage, Listing, R, RichText import jinja2 # from tool.screen import * class PartDetail1_1: def __init__(self): self.time = '?' self.name = '?' self.num = '?' self.tp = '?' self.type = '?' self.stop_times = '?' self.result = '?' self.detail = '' self.devail_value = [] class PartDetail: def __init__(self): self.item1 = '?' self.item2 = '?' self.item3 = '?' self.item4 = '?' self.item5 = '?' self.item6 = '?' self.item7 = '?' self.item8 = '-' self.item9 = '?' self.item10 = '?' self.item11 = '?' self.item12 = '?' self.item13 = '?' self.item14 = '?' self.item15 = '?' self.item16 = '?' self.item17 = '?' self.item18 = '?' self.item19 = '?' self.item20 = '?' self.item21 = '?' self.item22 = '?' self.detail = '' self.detail_value = [] self.item_list = [] self.table = [] self.table_head = [] self.head = [] self.table1_head = [] self.table2_head = [] self.table1 = [] self.table2 = [] self.image = '' self.images = [] self.in_detail = '' self.in_detail_value = [] self.in_image = ImageDetail() self.out_detail = '' self.out_detail_value = [] self.out_image = ImageDetail() self.result = [] self.optimize = [] self.reason = [] self.cut_image = ImageDetail() class ImageDetail: def __init__(self): self.wave_id = '' self.wave_name = '' self.time_list = [] self.tp = '' self.high = 0 #高频停车路口,0否,1是 self.odi = 0 #odi,0否,1是 self.turn_ts = 0 #分流转向比,1正向,2反向 self.in_ts = 0 #汇入转向比,1正向,2反向 self.week_or_day = 0, #1日,2周 self.date_name = '' #日:2024-10-15,周:2024-48 self.tp_start = '' self.tp_end = '' class DocDocumentContrastCtx: """路口优化对比报告""" def __init__(self, nodeid): self.nodeid = nodeid self.doc = DocxTemplate(self.get_template_file_path()) self.title = None self.cut_image = 0 self.cut_image_userid = '15836903493' self.cut_image_role = 'manager' self.tpl_data = { # 总体概述 'part1': self.tpl_paragraph_map['part1'], 'part1_1': PartDetail(), 'part1_key': 'part1', # 优化前现状分析 'part2': self.tpl_paragraph_map['part2'], 'part2_1': PartDetail(), # 用户传入 'part2_2': PartDetail(), # 用户传入 'part2_3': PartDetail(), # 用户传入 'part2_key': 'part2', # 诊断问题 'part3': self.tpl_paragraph_map['part3'], 'part3_1': PartDetail(), # 用户传入 'part3_key': 'part3', # 优化方案 'part4': self.tpl_paragraph_map['part4'], 'part4_1': PartDetail(), 'part4_2': PartDetail(), 'part4_key': 'part4', # 优化后效果对比 'part5': self.tpl_paragraph_map['part5'], 'part5_1': PartDetail(), 'part5_key': 'part5', } self.tpl_paragraph = { 'part1': {'visible': 0}, 'part2': {'visible': 0, 'part2_1': 0, 'part2_2': 0, 'part2_3': 0}, 'part3': {'visible': 0}, 'part4': {'visible': 0, 'part4_1': 0, 'part4_2': 0}, 'part5': {'visible': 0} } return def get_template_file_path(self): dir = os.path.dirname(os.path.abspath(__file__)) return f"{dir}/compare_report.docx" def get_static_file_path(self): dir = os.path.dirname(os.path.abspath(__file__)) return f"{dir}/../temp" tpl_paragraph_map = { 'part1': '总体概述', 'part2': '优化前现状分析', 'part2_1': '基本信息', 'part2_2': '延误分析', 'part2_3': '流量占比及分流转向比分析', 'part3': '诊断问题', 'part4': '优化方案', 'part4_1': '改善措施', 'part4_2': '配时方案', 'part5': '优化后效果对比', } def build_rich_text(self, detail, detail_value=None): item_rt = RichText('') item_details = detail.split('?') for k, v in enumerate(item_details): item_rt.add(f"{item_details[k]}") if k != len(item_details) - 1: item_rt.add(f"{detail_value[k]}", color="#FF0000", font=detail_value) continue return item_rt def build_rich_list(self, detail, detail_value): result = [] item_details = detail.split('?') for k, v in enumerate(item_details): item_rt = RichText('') item_rt.add(f"{item_details[k]}", color="#000000", size=18) result.append(item_rt) if k != len(item_details) - 1: item_rt = RichText('') item_rt.add(f"{detail_value[k]}", color="#ff0000", size=18) result.append(item_rt) continue return result def build_rich_list_bg(self, detail, detail_value): result = [] item_details = detail.split('?') for k, v in enumerate(item_details): item_rt = RichText('') item_rt.add(f"{item_details[k]}", color="#000000", size=18, style="background-color: #FFFF00;") result.append(item_rt) if k != len(item_details) - 1: item_rt = RichText('') item_rt.add(f"{detail_value[k]}", color="#ff0000", size=18, style="background-color: #FFFF00;") result.append(item_rt) continue return result def new_rich_text(self, text, color="#000000", font="仿宋_GB2312", size=None): rt = RichText('') if size: rt.add(f"{text}", color=color, font=font, size=size) if not size: rt.add(f"{text}", color=color, font=font) return rt def new_rich_text_list(self, texts): rt_list = [] for item_text in texts: rt = RichText('') rt.add(f"{item_text}", color="#ff0000") rt_list.append(rt) return rt_list def build_template(self, file_name): context = {} screen_image_list = [] context['title'] = self.title i = 0 for part_key, value in self.tpl_data.items(): match part_key: case 'part1': if self.tpl_paragraph[part_key]['visible'] == 0: continue i += 1 table_sort = 0 table_data = [] context[part_key] = table_data context['part1_key'] = str(i) context['part1_name'] = value item_data = { 'part_name': '', 'part_n': 'part1_1', 'detail': [], 'table_data': [], } item_data['detail'].append(self.tpl_data['part1_1'].item1) item_data['detail'].append(self.tpl_data['part1_1'].item2) item_data['detail'].append(self.tpl_data['part1_1'].item3) item_data['detail'].append(self.tpl_data['part1_1'].item4) # item_data['detail'].append(self.new_rich_text(self.tpl_data['part1_1'].item5, "#FF0000")) # item_data['detail'].append(self.build_rich_text(self.tpl_data['part1_1'].detail, self.tpl_data['part1_1'].detail_value) if self.tpl_data['part1_1'].detail != '' else '') if len(self.tpl_data['part1_1'].table) > 0: for item_table in self.tpl_data['part1_1'].table: item_data['table_data'].append(self.build_rich_text(item_table.detail, item_table.detail_value) if item_table.detail != '' else '') table_data.append(item_data) # for item_image in item_part_detail.images: # image_sort += 1 # if self.cut_image == 1: # screen_image_list.append(CutReportImgColumn( # wave_id=item_image.wave_id, # wave_name=item_image.wave_name, # time_list=item_image.time_list, # tp_name=item_image.tp, # high=item_image.high # )) # item_data_detail['images'].append({ # 'sort': image_sort, # 'name': item_image.wave_name, # 'image': len(screen_image_list) - 1, # }) # item_data['detail'].append(item_data_detail) # table_data.append(item_data) case 'part2': if self.tpl_paragraph[part_key]['visible'] == 0: continue i += 1 table_sort = 0 image_sort = 0 table_data = [] context[part_key] = table_data context['part2_key'] = str(i) context['part2_name'] = value if self.tpl_paragraph[part_key]['part2_1'] == 1: item_data = { 'part_name': self.tpl_paragraph_map['part2_1'], 'part_n': 'part2_1', 'detail': [], } item_data['detail'].append(self.tpl_data['part2_1'].item1) item_data['detail'].append(self.tpl_data['part2_1'].item2) item_data['detail'].append(self.tpl_data['part2_1'].item3) item_data['detail'].append(self.tpl_data['part2_1'].item4) table_data.append(item_data) if self.tpl_paragraph[part_key]['part2_2'] == 1: item_data = { 'part_name': self.tpl_paragraph_map['part2_2'], 'part_n': 'part2_2', 'detail': [], } for item in self.tpl_data['part2_2'].table: item_data['detail'].append( self.build_rich_text(item.detail, item.detail_value) if item.detail != '' else '') table_data.append(item_data) if self.tpl_paragraph[part_key]['part2_3'] == 1: item_data = { 'part_name': self.tpl_paragraph_map['part2_3'], 'part_n': 'part2_3', 'detail': [], } for item in self.tpl_data['part2_3'].table: item_data['detail'].append( self.build_rich_text(item.detail, item.detail_value) if item.detail != '' else '') table_data.append(item_data) case 'part3': if self.tpl_paragraph[part_key]['visible'] == 0: continue i += 1 table_data = [] context[part_key] = table_data context['part3_key'] = str(i) context['part3_name'] = value item_loop = 0 loop = 0 item_data = { 'part_name': "", 'part_n': 'part3_1', 'detail': [], } for item_data_list in self.tpl_data['part3_1'].table: item_data_detail = { 'title': item_data_list.item1, 'item_detail': [] } for item_data_table in item_data_list.table: item_item_data_detail = { 'title': item_data_table.item1, 'item_detail': [] } for item_data_table_value in item_data_table.table: item_item_data_detail['item_detail'].append( self.build_rich_text(item_data_table_value.detail, item_data_table_value.detail_value) if item_data_table_value.detail != '' else '') item_data_detail['item_detail'].append(item_item_data_detail) item_data['detail'].append(item_data_detail) table_data.append(item_data) case 'part4': if self.tpl_paragraph[part_key]['visible'] == 0: continue i += 1 table_sort = 0 table_sort += 1 table_data = [] context[part_key] = table_data context['part4_key'] = str(i) context['part4_name'] = value if self.tpl_paragraph[part_key]['part4_1'] == 1: item_data = { 'part_name': self.tpl_paragraph_map['part4_1'], 'part_n': 'part4_1', 'data': [], } table_data.append(item_data) if self.tpl_paragraph[part_key]['part4_2'] == 1: item_data = { 'part_name': self.tpl_paragraph_map['part4_2'], 'part_n': 'part4_1', 'data': [], } table_data.append(item_data) case 'part5': if self.tpl_paragraph[part_key]['visible'] == 0: continue i += 1 table_sort = 0 table_sort += 1 table_data = [] context[part_key] = table_data context['part5_key'] = str(i) context['part5_name'] = value item_data = { 'part_name': "", 'part_n': 'part5_1', 'detail': '', 'data': [], } item_data['detail'] = self.tpl_data['part5_1'].item1 for item_table in self.tpl_data['part5_1'].table: item_date_detail = { 'title': item_table.item1, 'item_detail': [] } for item_item_table in item_table.table: item_date_detail['item_detail'].append(self.build_rich_text(item_item_table.detail, item_item_table.detail_value) if item_item_table.detail != '' else '') item_data['data'].append(item_date_detail) table_data.append(item_data) if len(screen_image_list) > 0: cut_images, _ = cut_report_img(screen_image_list, self.cut_image_userid, self.cut_image_role) if context.get('part1'): for item_part in context['part1']: if item_part['part_n'] == 'part1_3': for item_detail in item_part['detail']: if len(item_detail['images']) > 0: for item_image in item_detail['images']: item_image['image'] = InlineImage(self.doc, cut_images[item_image['image']], width=Mm(146)) if cut_images[ item_image['image']] else '' self.doc.render(context, autoescape=True) # # 渲染模板 # for table in self.doc.tables: # for row in table.rows: # for cell in row.cells: # for para in cell.paragraphs: # set_font(para, para.text, '仿宋_GB2312', 30,font_color=(255, 0, 0), bg_color='FFFF00') # for para in self.doc.paragraphs: # set_font(para, para.text, '仿宋', 14) # self.doc.save(self.get_static_file_path() + f"/{file_name}.docx") # return f"/api/phase_template_download?nodeid={self.nodeid}&type=report&file_name={file_name}" def set_font(para, text_to_find, font_name='仿宋', font_size=12, font_color=(0, 0, 0), bg_color='FFFFFF'): for run in para.runs: run.text = text_to_find # if text_to_find in run.text: run.font.name = font_name run.font.size = Pt(font_size) run.font.color.rgb = RGBColor(*font_color) rPr = run._element.rPr if rPr is None: rPr = OxmlElement('w:rPr') run._element.append(rPr) rFonts = OxmlElement('w:rFonts') rFonts.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}ascii', font_name) rFonts.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}eastAsia', font_name) rFonts.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}hAnsi', font_name) rPr.append(rFonts) shading_elm = OxmlElement('w:shd') shading_elm.set('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}fill', bg_color) pPr = para._element.find('{http://schemas.openxmlformats.org/wordprocessingml/2006/main}pPr') if pPr is None: pPr = OxmlElement('w:pPr') para._element.insert(0, pPr) pPr.append(shading_elm) def doc_demo_contrast(): doc1 = DocDocumentContrastCtx('9660') #获取模版对象 doc1.tpl_paragraph['part1']['visible'] = 1 doc1.tpl_paragraph['part2']['visible'] = 1 doc1.tpl_paragraph['part2']['part2_1'] = 1 doc1.tpl_paragraph['part2']['part2_2'] = 1 doc1.tpl_paragraph['part2']['part2_3'] = 1 doc1.tpl_paragraph['part3']['visible'] = 1 doc1.tpl_paragraph['part4']['visible'] = 1 doc1.tpl_paragraph['part4']['part4_1'] = 1 doc1.tpl_paragraph['part4']['part4_2'] = 1 doc1.tpl_paragraph['part5']['visible'] = 1 doc1.cut_image = 0 doc1.cut_image_userid = '15836903493' doc1.cut_image_role = 'manager' doc1.title = '清盛大道与龙江路交叉口' # =========1总体概论=========== doc1.tpl_data['part1_1'].item1 = '2024年10月8日' doc1.tpl_data['part1_1'].item2 = '2025年10月8日' doc1.tpl_data['part1_1'].item3 = '清盛大道与龙江路交叉口' doc1.tpl_data['part1_1'].item4 = '09:00-17:00' detail1 = PartDetail() detail1.detail = "路口服务水平由?级提升为?级,提升?个等级;" detail1.detail_value = ['B', 'A', '1'] detail2 = PartDetail() detail2.detail = "路口拥堵指数由?下降为?,减少?,优化率为?;" detail2.detail_value = ['0.33', '0.56', '5.31', '3.21%'] detail3 = PartDetail() detail3.detail = "路口停车次数由?下降为?,减少?,优化率为?;" detail3.detail_value = ['2.331', '0.563', '2.31', '6.21%'] doc1.tpl_data['part1_1'].table = [detail1, detail2, detail3] #==========2.1优化前现状分析========== doc1.tpl_data['part2_1'].item1 = '清盛大道与龙江路交叉口' doc1.tpl_data['part2_1'].item2 = '东城区' doc1.tpl_data['part2_1'].item3 = '主干路与次干路相交的十字路口' doc1.tpl_data['part2_1'].item4 = '海信' #===========2.2延误分析============ detail1 = PartDetail() detail1.detail = "根据各进口道车辆延误时间分析,服务水平较低的进口道有?个。东南进口的延误时间为?s,西北进口的延误时间为?s。" detail1.detail_value = ['3', '5', '7'] detail2 = PartDetail() detail2.detail = "根据各进口道拥堵指数分析,服务水平有?个。东南进口的延误时间为?s,西北进口的延误时间为?s。" detail2.detail_value = ['2', '6', '9'] doc1.tpl_data['part2_2'].table = [detail1, detail2] #===========2.3延误分析============ detail1 = PartDetail() detail1.detail = "根据车辆分流转向比分析,西南进口道流量占比最大为?,东南进口道流量占比最小为?。路口的主要车流方向为东北直行和西南直行,东北进口道以直行车流为主,占比为?;西南进口道以直行车流为主,占比为?。" detail1.detail_value = ['5%', '7%', '8%', '12%'] detail2 = PartDetail() detail2.detail = "根据车辆分流转向比分析,西南进口道流量占比最大为?,东南进口道流量占比最小为?。路口的主要车流方向为东北直行和西南直行,东北进口道以直行车流为主,占比为?;西南进口道以直行车流为主,占比为?。" detail2.detail_value = ['5%', '7%', '8%', '12%'] doc1.tpl_data['part2_3'].table = [detail1, detail2] #==========3诊断问题============ detail1_1 = PartDetail() detail1_1.item1 = '多次排队' detail1_1_1 = PartDetail() detail1_1_1.detail = "西北进口道直行车辆多次停车率过大(?),车辆需要多次排队才能通过。" detail1_1_1.detail_value = ['16%'] detail1_1_2 = PartDetail() detail1_1_2.detail = "东北进口道直行车辆多次停车率过大(?),车辆需要多次排队才能通过。" detail1_1_2.detail_value = ['20%'] detail1_1.table = [detail1_1_1, detail1_1_2] detail1_2 = PartDetail() detail1_2.item1 = '停车较多' detail1_2_1 = PartDetail() detail1_2_1.detail = "路口停车次数较高(2.1),整体通行效率不高。" detail1_2_1.detail_value = ['2.1'] detail1_2.table = [detail1_2_1] detail1 = PartDetail() detail1.item1 = '运行效果' detail1.table = [detail1_1, detail1_2] detail2_1 = PartDetail() detail2_1.item1 = '多次排队1' detail2_1_1 = PartDetail() detail2_1_1.detail = "西北进口道直行车辆多次停车率过大(?),车辆需要多次排队才能通过。" detail2_1_1.detail_value = ['16%'] detail2_1_2 = PartDetail() detail2_1_2.detail = "东北进口道直行车辆多次停车率过大(?),车辆需要多次排队才能通过。" detail2_1_2.detail_value = ['20%'] detail2_1.table = [detail2_1_1, detail2_1_2] detail2_2 = PartDetail() detail2_2.item1 = '停车较多2' detail2_2_1 = PartDetail() detail2_2_1.detail = "路口停车次数较高(?),整体通行效率不高。" detail2_2_1.detail_value = ['2.1'] detail2_2.table = [detail2_2_1] detail2 = PartDetail() detail2.item1 = '均衡调控' detail2.table = [detail1_1, detail1_2] doc1.tpl_data['part3_1'].table = [detail1, detail2] #==========4优化方案============= #==========5优化后效果对比========== detail5_1_1 = PartDetail() detail5_1_1.detail = '路口服务水平由?级提升为?级,提升?个等级;' detail5_1_1.detail_value = ['A', 'B', '6'] detail5_1_2 = PartDetail() detail5_1_2.detail = '路口拥堵指数由?下降为?,减少?,优化率为?;' detail5_1_2.detail_value = ['3', '2', '1','5%'] detail5_1_3 = PartDetail() detail5_1_3.detail = '路口停车次数由?下降为?,减少?,优化率为?;' detail5_1_3.detail_value = ['3.13', '4.1334' , '3.13', '3.13%'] detail5_1 = PartDetail() detail5_1.item1 = '路口方面' detail5_1.table = [detail5_1_1, detail5_1_2,detail5_1_3] detail5_2_1 = PartDetail() detail5_2_1.detail = '路口服务水平由?级提升为?级,提升?个等级;' detail5_2_1.detail_value = ['A', 'B', '6'] detail5_2_2 = PartDetail() detail5_2_2.detail = '路口拥堵指数由?下降为?,减少?,优化率为?;' detail5_2_2.detail_value = ['3', '2', '1','5%'] detail5_2_3 = PartDetail() detail5_2_3.detail = '路口停车次数由?下降为?,减少?,优化率为?;' detail5_2_3.detail_value = ['3.13', '4.1334' , '3.13', '3.13%'] detail5_2 = PartDetail() detail5_2.item1 = 'X进口方面' detail5_2.table = [detail5_2_1, detail5_2_2,detail5_2_3] doc1.tpl_data['part5_1'].item1 = '方案下发后通过平台对路口优化效果进行对比,优化后路口及进口道效果提升较明显的指标如下:' doc1.tpl_data['part5_1'].table = [detail5_1, detail5_2] return doc1.build_template("对比报告") # 生成word if __name__ == '__main__': doc_demo_contrast()