diff --git a/Dt_report.py b/Dt_report.py new file mode 100644 index 0000000..784c7b9 --- /dev/null +++ b/Dt_report.py @@ -0,0 +1,567 @@ +# 文档处理工具 +from tools.document_tools import ( + create_document, add_documents,add_table_and_replace, + add_table_to_document,add_dynamic_table, + process_server_images_table,add_header +) + +# 内容处理工具 +from tools.content_tools import ( + add_picture,split_table_by_row_content, + search_and_replace +) + +from tools.get_pictures import ( + process_picture_data,get_records_with_pic +) + +from tools.Get_Json import ( + get_project_info,get_jizu_info, + get_jizu_shigong_info,get_defect_detail, + get_part_picture,get_yepian_xiangqing, + check_pic_url,get_full_picture_url, + get_defect_record_list +) + +from tools.dataproccess import ( + caculate_work_days,get_year_month, + merge_info,get_defect_str, + safe_get,get_resource_path +) + + + +from core.tables import fill_tables + +from tools.defines import * +import os, re, datetime + + +async def generate_dt_report(base_info, baogao_info): + #获取模板编号、模板名称 + num_to_chinese = {1 : '一', 2 : '二', 3 : '三', 4 : '四', 5 : '五', 6 : '六', 7 : '七', 8 : '八', 9 : '九', 10 : '十', 11 : '十一', 12 : '十二'} + cover_encode = "encode" + cover_project = "project" + baogao_name1 = "baogaoname1" + baogao_name2 = "baogaoname2" + company_name_yi = "company_name_yi" + cover_date = "time" + TITLE_OF_REPORT = "companyencode" + jiegou_xuhao = 'num' + + print(f"获取到参数:基本信息:{base_info}\n\n报告信息:{baogao_info}") + + try: + base_info = merge_info(base_info, DEFAULT_BASE_INFO) + turbine_id = base_info['turbine_id'] + + jizu_data = get_jizu_info(turbine_id) + project_data = get_project_info(jizu_data['projectId']) + shigong_data = get_jizu_shigong_info(turbine_id) + + fengchang_name = project_data['farmName'] + Yi_company = project_data['inspectionUnit'] + yi_fuzeren = project_data['inspectionContact'] + yi_phone = project_data['inspectionPhone'] + fengchang_location = project_data['farmAddress'] + Jia_company = project_data['client'] + jia_fuzeren = project_data['clientContact'] + jia_phone = project_data['clientPhone'] + jizu_num = project_data['scale'] + jizu_xinghao = project_data['turbineModel'] + project_name = project_data['projectName'] + jizu_bianhao = jizu_data["turbineName"] + start_date = project_data['startDate'] + end_date = project_data['endDate'] + cover_url = project_data['coverUrl'] + gongqi = caculate_work_days(start_date, end_date) + except Exception as e: + print(f"数据库的项目-机组基本信息获取失败:{e}") + return + + try: + baogao_date = datetime.datetime.now().strftime("%Y年%m月%d日 %H:%M") #现在的时间 + date_year_month = get_year_month(baogao_date) + + #前端信息 + baogao_info = merge_info(baogao_info, DEFAULT_BAOGAO_INFO) + + key_words= re.compile('|'.join(map(re.escape, baogao_info['key_words'].split(',')))) #关键字 + + shengcheng_dir = baogao_info['shengcheng_dir'] #路径 + if shengcheng_dir == "": + print("未配置生成路径,请检查配置") + return + if_waibu = baogao_info["if_waibu"] + if_neibu = baogao_info["if_neibu"] + if_fanglei = baogao_info["if_fanglei"] + + quexian_type = baogao_info['quexian_enum'] + + dianxing_type = baogao_info['dianxing_enum'] + + other_type = baogao_info["other_enum"] + + jiancha_renyuan = baogao_info['jiancha_renyuan'] #检查人员,目前是从命令行参数获取,需要完善 + check_date = baogao_info['check_date'] + if check_date == None: + check_date = "未获取" + baogao_bianzhi = baogao_info["userName"] + baogao_shenghe = baogao_info["baogaoCheck"] + Jiancha_date = baogao_info["check_date"] + data_processor = baogao_info["data_processor"] + coverurl = baogao_info["coverurl"] + + + #数据库拉取信息 + # Jiancha_date = shigong_data["startTime"].replace("T", " ") #检查日期 + # image_count = shigong_data['imageCount'] #从施工方案获取的图片数量,待定!!! + # temperature = shigong_data['temperature'] #温度 + # wind_speed = shigong_data['windSpeed'] #风速 + # weather = get_weather(shigong_data["weatherCode"]) #天气 不从此接口获取,待定!!! + + #拉取部件、图片数据 + part_data, picture_data, Yepians = get_part_picture(turbine_id) + + print(Yepians) + Y1_info = get_yepian_xiangqing(Yepians[0]["partId"]) + Y_Code = [yepian["partCode"] for yepian in Yepians] + partManufacturer = Y1_info["partManufacturer"] + print(f"找到叶片号{Y_Code},厂商{partManufacturer}") + + image_source_to_find = [] + baogao_label = [] + renyuan_peizhi = [] + gongzuo_neirong = [] + shigong_fangan = [] + shebei_peizhi = [] + beizhu = [] + jiancha = [] + neirong = [] + neirong2 = [] + #获取对应枚举字段 + if if_waibu: + baogao_label.append("外观") + image_source_to_find.append(baogao_info['waibu_enum']) + if baogao_info["shigong_fangan"] == None: + print("未传入施工方案,使用已有枚举") + renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.WAIBU.RENYUAN_PEIZHI) + gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.WAIBU.GONGZUO_NEIRONG) + shebei_peizhi.append(SHIGONG_FANGAN_ENUM.WAIBU.SHEBEI_PEIZHI) + shigong_fangan.append(SHIGONG_FANGAN_ENUM.WAIBU.SHIGONG_FANGAN) + else: + pass #待添加如果从平台传入枚举,但目前没必要,如果这种枚举标准信息库有更好的优化则可以实现 + jiancha.append("无人机近距离外观检查") + neirong.append(f"、".join(Y_Code) + f"共{len(Y_Code)}支叶片的前缘、后缘、迎风面、背风面。") + neirong2.append("前缘、后缘、迎风面、背风面。") + if if_neibu: + baogao_label.append("内部") + image_source_to_find.append(baogao_info['neibu_enum']) + if baogao_info["shigong_fangan"] == None: + renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.NEIBU.RENYUAN_PEIZHI) + gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.NEIBU.GONGZUO_NEIRONG) + shebei_peizhi.append(SHIGONG_FANGAN_ENUM.NEIBU.SHEBEI_PEIZHI) + shigong_fangan.append(SHIGONG_FANGAN_ENUM.NEIBU.SHIGONG_FANGAN) + else: + pass + jiancha.append("人工内部拍摄") + neirong.append(f"、".join(Y_Code) + f"共{len(Y_Code)}支叶片的内部导雷卡、腹板、透光、人孔盖版、叶根盖板...") + neirong2.append("内部导雷卡、腹板、透光、人孔盖版、叶根盖板...") + if if_fanglei: + baogao_label.append("防雷") + image_source_to_find.append(baogao_info['fanglei_enum']) + if baogao_info["shigong_fangan"] == None: + renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.RENYUAN_PEIZHI) + gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.GONGZUO_NEIRONG) + shebei_peizhi.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.SHEBEI_PEIZHI) + shigong_fangan.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.SHIGONG_FANGAN) + else: + pass + jiancha.append("人工防雷") + neirong.append(f"轮毂至塔基导通、内部导线线阻、外部导线线阻...") + neirong2.append("轮毂至塔基导通、内部导线线阻、外部导线线阻...") + + #获取缺陷图列表和典型图列表 + filtered_picture_data, total_picture_num = process_picture_data(picture_data, image_source_to_find) + #获取所有缺陷记录 + defect_records = get_defect_record_list() + #获取缺陷记录中对应图片的记录 + defect_records_with_pic, error_pic_with_no_record = get_records_with_pic(defect_records, filtered_picture_data, quexian_type) + if len(error_pic_with_no_record) > 0: + print(f"!!!有部分缺陷图片没有对应的缺陷记录,将不会生成这些图片的缺陷表:{error_pic_with_no_record} ") + print(f"对应缺陷图的缺陷记录列表:{defect_records_with_pic}") + except Exception as e: + print(f"报告基本信息获取失败:{e}") + return + + #检查参数合法性 + if not if_fanglei or not if_neibu or not if_waibu: + print("请至少选择一种检查项目") + return + if not os.path.exists(shengcheng_dir): + print(f"生成路径{shengcheng_dir}不存在") + return + + output_doc = None + head_num = 1 + ###封面创建### + cover_dirs = [get_resource_path("muban/fengmian1.docx"),get_resource_path("muban/fengmian.jpg"),get_resource_path("muban/fengmian2.docx")] + #输出目录 + baogao_name = "叶片" + "、".join(baogao_label) + "检查报告" + output_dir = os.path.normpath(f"{shengcheng_dir}/{project_name}项目{baogao_name}{jizu_bianhao}{baogao_date.split(' ')[0]}版.docx") + + version = 1 + while os.path.exists(output_dir): + if version != 1: + output_dir = output_dir.replace(f"版{version - 1}",f"版{version}") + else: + output_dir = output_dir.replace("版",f"版{version}") + version += 1 + + mianzhe_shengming = f"本报告仅涵盖{'、'.join(baogao_label)}检测内容" + + print(await create_document(output_dir)) + if baogao_info["if_docx_fengmian"] : + #创建文档、添加封面 + print(add_documents(output_dir, cover_dirs[0])) + if check_pic_url(coverurl): #手动导入封面图片测试用 + print(add_picture(output_dir, get_full_picture_url(coverurl), width = 6.41, height = 4)) + elif check_pic_url(cover_url): + print(add_picture(output_dir, get_full_picture_url(cover_url), width = 6.41, height = 4)) + else: + print(add_picture(output_dir, cover_dirs[1])) + print(add_documents(output_dir, cover_dirs[2])) + print("封面创建成功") + #YYYY年MM月DD日 HH:MM:SS + #更改文档信息 + print(search_and_replace(output_dir, TITLE_OF_REPORT, jizu_bianhao)) + print(search_and_replace(output_dir, baogao_name1, baogao_name)) + print(search_and_replace(output_dir, company_name_yi, Yi_company)) + print(search_and_replace(output_dir, cover_project, fengchang_name)) + print(search_and_replace(output_dir, cover_encode, jizu_bianhao)) + print(search_and_replace(output_dir, cover_date, date_year_month)) + print(search_and_replace(output_dir, 'bianzhi', baogao_bianzhi)) + print(search_and_replace(output_dir, 'shenghe', baogao_shenghe)) + print(search_and_replace(output_dir, 'mianzhe_shengming', mianzhe_shengming)) + + + add_header(output_dir, TEMPLATE_HEADER.DT_HEADER.ENUM) + + total_table_num = 0 + if baogao_info["if_docx_project_overview"]: + #项目概况表 + print("开始添加项目概况表") + XIANG_MU_GAI_KUANG = get_resource_path("muban/xiangmugaikuo.docx") + print(f"查找模板,找到模板:{XIANG_MU_GAI_KUANG}") + project_location = fengchang_location + company_name_jia = Jia_company + fuzeren = yi_fuzeren + phone_fuzeren = yi_phone + xiangmuguige = jizu_num + Yi_company = Yi_company + XIANGMU_GAIKUO = list(list("" for i in range(6)) for j in range(5)) + XIANGMU_GAIKUO[0][1] = fengchang_name + XIANGMU_GAIKUO[0][4] = project_location + XIANGMU_GAIKUO[1][1] = company_name_jia + XIANGMU_GAIKUO[1][4] = Yi_company + XIANGMU_GAIKUO[2][1] = jia_fuzeren + XIANGMU_GAIKUO[2][4] = fuzeren + XIANGMU_GAIKUO[3][2] = jia_phone + XIANGMU_GAIKUO[3][5] = phone_fuzeren + XIANGMU_GAIKUO[4][1] = jizu_xinghao + XIANGMU_GAIKUO[4][3] = xiangmuguige + XIANGMU_GAIKUO[4][5] = gongqi + print("建立表结构完毕,开始插入模板") + #添加项目概况表 + print(f"输出路径:{output_dir},模板路径:{XIANG_MU_GAI_KUANG},插入数据:{XIANGMU_GAIKUO}") + output_doc, message = add_table_to_document(output_dir, XIANG_MU_GAI_KUANG,5,5,total_table_num,XIANGMU_GAIKUO) + print(message) + print("模板插入完毕,开始替换内容") + total_table_num += 1 + print(search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) + print(search_and_replace(output_dir, TITLE_OF_REPORT, jizu_bianhao)) + print(search_and_replace(output_dir, baogao_name2, baogao_name)) + head_num += 1 + + if baogao_info['if_docx_inspection_method']: + #检查方案描述 + FANGAN_JIANCHA_DIR = get_resource_path("muban/checkmethod.docx") + list_to_replace = { + 'renyuan_peizhi' : "\n".join(renyuan_peizhi), + 'shebei_peizhi' : "\n".join(shebei_peizhi), + 'shigong_fangan' : "\n".join(shigong_fangan), + 'gongzuo_neirong' : "\n".join(gongzuo_neirong), + 'beizhu' : beizhu, + 'num' : num_to_chinese[head_num], + } + print(add_table_and_replace(output_dir, FANGAN_JIANCHA_DIR, 0, list_to_replace)) + print(split_table_by_row_content(output_dir, output_dir, total_table_num)) + total_table_num += 1 + head_num += 1 + + if baogao_info['if_docx_inspection_info']: + #检查信息 + JIANCHA_XINGXI_DIR = get_resource_path("muban/checkinfo.docx") + JIANCHA_XINGXI = list(list("" for i in range(4)) for j in range(9)) + JIANCHA_XINGXI[0][1] = jiancha_renyuan + try: + JIANCHA_XINGXI[1][1] = Jiancha_date.split('T')[0] + except: + JIANCHA_XINGXI[1][1] = "格式不对或无数据" + JIANCHA_XINGXI[1][3] = jizu_bianhao + JIANCHA_XINGXI[2][1] = "风力发电机组" + baogao_name + JIANCHA_XINGXI[2][3] = "、".join(jiancha) + JIANCHA_XINGXI[3][2] = partManufacturer + JIANCHA_XINGXI[4][1] = '叶片型号:' + jizu_xinghao + JIANCHA_XINGXI[5][1] = Y_Code[0] if len(Y_Code) > 0 else "无" + JIANCHA_XINGXI[6][1] = Y_Code[1] if len(Y_Code) > 1 else "无" + JIANCHA_XINGXI[7][1] = Y_Code[2] if len(Y_Code) > 2 else "无" + JIANCHA_XINGXI[8][0] = "本次" + "、".join(_ for _ in jiancha) + f"检查,采集叶片图片{total_picture_num}张,内容覆盖" + ";".join(_ for _ in neirong) + #新建检查信息表 + output_doc, message = add_table_to_document(output_dir, JIANCHA_XINGXI_DIR,9,4,total_table_num ,JIANCHA_XINGXI,False) + print(message) + print(search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) + head_num += 1 + total_table_num += 1 + + if baogao_info['if_docx_chengguo_sub']: + # 添加成果递交表 + CHENGGUO_DIJIAO_DIR = get_resource_path("muban/chengguo_sub.docx") + CHENGGUO_DIJIAO = list(list("" for i in range(4)) for j in range(6)) + CHENGGUO_DIJIAO[0][1] = jiancha_renyuan + CHENGGUO_DIJIAO[1][1] = jia_fuzeren + try: + CHENGGUO_DIJIAO[2][1] = Jiancha_date.split('T')[0] + except: + CHENGGUO_DIJIAO[2][1] = "格式不对或无数据" + CHENGGUO_DIJIAO[3][1] = data_processor + CHENGGUO_DIJIAO[4][1] = baogao_bianzhi + CHENGGUO_DIJIAO[5][1] = baogao_shenghe + try: + CHENGGUO_DIJIAO[2][3] = Jiancha_date.split('T')[1] + except: + CHENGGUO_DIJIAO[2][3] = "格式不对或无数据" + CHENGGUO_DIJIAO[3][3] = baogao_date.split(' ')[0] + CHENGGUO_DIJIAO[4][3] = baogao_date.split(' ')[0] + CHENGGUO_DIJIAO[5][3] = "未审核" + + output_doc, message = add_table_to_document(output_dir, CHENGGUO_DIJIAO_DIR,5,5,total_table_num,CHENGGUO_DIJIAO,True,0.04) + print(message) + print(search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) + head_num += 1 + total_table_num += 1 + + if baogao_info['if_docx_inspection_text'] and if_waibu: + #检查情况汇总表(文字信息) + + try: + #获取缺陷信息 + """ + 需要获取: + Y1、Y2、Y3叶片的缺陷数量,缺陷字典{描述:图片路径} + 和数据库连接需要的新增异常处理: + 目前逻辑为找到图片后,找缺陷类型的图片,通过图片id查找对应缺陷记录 + 如果没有找到,要返回对应异常,即已选出(标注)缺陷图,但未填写对应缺陷信息的异常返回。 + 最后将无异常的图片入队缺陷字典(同数量),有异常的图片再另存。 + """ + + Y1_quexian_list = [] + Y2_quexian_list = [] + Y3_quexian_list = [] + Y1_quexian_list = defect_records_with_pic["叶片1"]["DEFECT"] if "叶片1" in defect_records_with_pic else {} + Y2_quexian_list = defect_records_with_pic["叶片2"]["DEFECT"] if "叶片2" in defect_records_with_pic else {} + Y3_quexian_list = defect_records_with_pic["叶片3"]["DEFECT"] if "叶片3" in defect_records_with_pic else {} + + Y1_quexian_num = len(Y1_quexian_list) + Y2_quexian_num = len(Y2_quexian_list) + Y3_quexian_num = len(Y3_quexian_list) + no_defect_found = "未发现明显缺陷" + weak_num_Y1 = f"{Y_Code[0] if len(Y_Code) > 0 else '无'}叶片" + f"共发现缺陷{Y1_quexian_num}处" if Y1_quexian_num > 0 else no_defect_found + weak_num_Y2 = f"{Y_Code[1] if len(Y_Code) > 1 else '无'}叶片" + f"共发现缺陷{Y2_quexian_num}处" if Y1_quexian_num > 0 else no_defect_found + weak_num_Y3 = f"{Y_Code[2] if len(Y_Code) > 2 else '无'}叶片" + f"共发现缺陷{Y3_quexian_num}处" if Y1_quexian_num > 0 else no_defect_found + except Exception as e: + print(f"缺陷图获取失败:{e}") + return + + #添加检查情况汇总表 + JIANCHA_HUIZONG_DIR = get_resource_path("muban/total_check.docx") + JIANCHA_HUIZONG = list(list("" for i in range(3)) for j in range(4)) + + JIANCHA_HUIZONG[1][0] = weak_num_Y1 + JIANCHA_HUIZONG[2][0] = weak_num_Y2 + JIANCHA_HUIZONG[3][0] = weak_num_Y3 + JIANCHA_HUIZONG[1][1] = "\n".join(neirong2) + JIANCHA_HUIZONG[2][1] = "\n".join(neirong2) + JIANCHA_HUIZONG[3][1] = "\n".join(neirong2) + JIANCHA_HUIZONG[1][2] = "\n".join([f"{i+1}.{s}" for i, s in enumerate(get_defect_str(Y1_quexian_list))]) if Y1_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷' + JIANCHA_HUIZONG[2][2] = "\n".join([f"{i+1}.{s}" for i, s in enumerate(get_defect_str(Y2_quexian_list))]) if Y2_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷' + JIANCHA_HUIZONG[3][2] = "\n".join([f"{i+1}.{s}" for i, s in enumerate(get_defect_str(Y3_quexian_list))]) if Y3_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷' + output_doc, message = add_table_to_document(output_dir, JIANCHA_HUIZONG_DIR,4,3,total_table_num,JIANCHA_HUIZONG,False,ALIGMENT='LEFT') + print(message) + print(search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) + total_table_num += 1 + head_num += 1 + + if baogao_info['if_docx_inspection_picture']: + #主要部位图片展示表/检查内容表 + #获取典型图信息 + try: + Y1_list = safe_get(filtered_picture_data, "叶片1", dianxing_type, default=[]) + Y2_list = safe_get(filtered_picture_data, "叶片2", dianxing_type, default=[]) + Y3_list = safe_get(filtered_picture_data, "叶片3", dianxing_type, default=[]) + picture_Y1_num = len(Y1_list) + picture_Y2_num = len(Y2_list) + picture_Y3_num = len(Y3_list) + except Exception as e: + print(f"获取缺陷图失败:{e}") + print(f"图片、文字数量:{picture_Y1_num} {picture_Y2_num} {picture_Y3_num}") + JIANCHA_NEIRONG_TOTAL_NUM = picture_Y1_num+ picture_Y2_num + picture_Y3_num + if JIANCHA_NEIRONG_TOTAL_NUM <= 0: + print("无典型图片数据,无法生成典型图表") + else: + col ,row = 3, 0 + JIANCHA_NEIRONG_PICTURES_TABLE = get_resource_path("muban/check2.docx") + JIANCHA_NEIRONG_Y1_DIR = get_resource_path("muban/check_content.docx") + JIANCHA_NEIRONG_Y1 = list(list("" for _ in range(3)) for j in range(1)) + Y1_code = Y_Code[0] if len(Y_Code) > 0 else "无" + Y2_code = Y_Code[1] if len(Y_Code) > 1 else "无" + Y3_code = Y_Code[2] if len(Y_Code) > 2 else "无" + JIANCHA_NEIRONG_Y1[0][0] = f"叶片1:{Y1_code}检查内容" + print(f"Y1标题内容:{JIANCHA_NEIRONG_Y1}") + JIANCHA_NEIRONG_Y2_DIR = get_resource_path("muban/check3.docx") + JIANCHA_NEIRONG_Y2 = list(list("" for _ in range(3)) for j in range(1)) + JIANCHA_NEIRONG_Y2[0][0] = f"叶片2:{Y2_code}检查内容" + print(f"Y2标题内容:{JIANCHA_NEIRONG_Y2}") + JIANCHA_NEIRONG_Y3_DIR = get_resource_path("muban/check3.docx") + JIANCHA_NEIRONG_Y3 = list(list("" for _ in range(3)) for j in range(1)) + JIANCHA_NEIRONG_Y3[0][0] = f"叶片3:{Y3_code}检查内容" + print(f"Y3标题内容:{JIANCHA_NEIRONG_Y3}") + print(f"当前表格序号为 {total_table_num}") + print(key_words) + output_doc, message = add_table_to_document(output_dir, JIANCHA_NEIRONG_Y1_DIR,1,3,total_table_num,JIANCHA_NEIRONG_Y1,True, 1) + print(message) + total_table_num += 1 + + total_table_num = await process_server_images_table(Y1_list, image_source_to_find, output_dir, total_table_num, JIANCHA_NEIRONG_PICTURES_TABLE, key_words) + + output_doc, message = add_table_to_document(output_dir, JIANCHA_NEIRONG_Y2_DIR,1,3,total_table_num,JIANCHA_NEIRONG_Y2,True, 1) + print(message) + total_table_num += 1 + + total_table_num = await process_server_images_table(Y2_list, image_source_to_find, output_dir, total_table_num, JIANCHA_NEIRONG_PICTURES_TABLE, key_words) + + output_doc, message = add_table_to_document(output_dir, JIANCHA_NEIRONG_Y3_DIR,1,3,total_table_num,JIANCHA_NEIRONG_Y3,True, 1) + print(message) + total_table_num += 1 + + total_table_num = await process_server_images_table(Y3_list, image_source_to_find, output_dir, total_table_num, JIANCHA_NEIRONG_PICTURES_TABLE, key_words) + print(search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) + head_num += 1 + + if baogao_info['if_docx_defect_picture'] and if_waibu: + # #缺陷详情 + QUEXIAN_XIANGQING_DIR = get_resource_path("muban/check_check.docx") + QUEXIAN_XIANGQING_TITLE_DIR = get_resource_path("muban/check_check_title.docx") + + Y_tables = [Y1_quexian_list,Y2_quexian_list,Y3_quexian_list] + Y1_table_list = [] + Y2_table_list = [] + Y3_table_list = [] + table_lists = [Y1_table_list, Y2_table_list, Y3_table_list] + + for i, (table_list, Y_lists) in enumerate(zip(table_lists, Y_tables)): + for Y_list in Y_lists: + image_defect_record = Y_list["record"] + image_path = Y_list["imagePath"] + + image_defect_record = get_defect_detail(image_defect_record["defectId"]) + + # 从imagedefectrecord中获取各个字段 + defect_type = image_defect_record.get('defectTypeLabel', '未知缺陷类型') + defect_location = f"{image_defect_record.get('partName', '')}{image_defect_record.get('defectPosition', '')}" + + # 使用axial和chordwise作为缺陷尺寸信息 + axial = image_defect_record.get('axial', '') + chordwise = image_defect_record.get('chordwise', '') + defect_size = f"轴向:{axial} 弦向:{chordwise}" if axial or chordwise else '' + + visibility = "暂无此项" + + urgency = "暂无此项" + + severity = image_defect_record.get('defectLevelLabel', '未知严重等级') + repair_suggestion = image_defect_record.get('repairIdea', '无维修建议') + + print(f"获取第{i + 1}个叶片的缺陷图: {image_path}") + + table_list.append({ + "QueXianLeiXing": defect_type, + "QueXianWeiZhi": defect_location, + "QueXianChiCun": defect_size, + "WeiZongDengJi": severity, + "Tupian_Dir": get_full_picture_url(image_path) if check_pic_url(image_path) else None, + "visibility": visibility, + "urgency": urgency, + "repair_suggestion": repair_suggestion, # 新增维修建议字段 + }) + + # for i, (table_list, Y_dict) in enumerate(zip(table_lists, Y_tables)): + # for image_defect_record, image_path in Y_dict.items(): + # # 从图片名解析各个字段 + # parts = image_name.split('_') + # if len(parts) >= 8: # 确保有7个部分 + # defect_type = parts[1] + # defect_location = parts[2] + # defect_size = parts[3] + # visibility = parts[4] + # urgency = parts[5] + # severity = parts[6] + # repair_suggestion = parts[7] + + # print(f"获取第{i+1}个叶片的缺陷图: {image_path}") + + # table_list.append({ + # "QueXianLeiXing": defect_type, + # "QueXianWeiZhi": defect_location, + # "QueXianChiCun": defect_size, + # "WeiZongDengJi": severity, + # "Tupian_Dir": image_path, + # "visibility": visibility, + # "urgency": urgency, + # "repair_suggestion": repair_suggestion.split('.')[0], # 新增维修建议字段 + # }) + # else: + # table_list.append({ + # "QueXianLeiXing": "图片命名有误", + # "QueXianWeiZhi": "图片命名有误", + # "QueXianChiCun": "图片命名有误", + # "WeiZongDengJi": "图片命名有误", + # "Tupian_Dir": image_path, + # "visibility": "图片命名有误", + # "urgency": "图片命名有误", + # "repair_suggestion": "图片命名有误", # 新增维修建议字段 + # }) + + + Y1_TABLES, Y1_TABLES_PICTURES = fill_tables(table_lists[0],4,5,len(table_lists[0]),Y_Code[0] if len(Y_Code) > 0 else "无") + Y2_TABLES, Y2_TABLES_PICTURES = fill_tables(table_lists[1],4,5,len(table_lists[1]),Y_Code[1] if len(Y_Code) > 1 else "无") + Y3_TABLES, Y3_TABLES_PICTURES = fill_tables(table_lists[2],4,5,len(table_lists[2]),Y_Code[2] if len(Y_Code) > 2 else "无") + print(add_documents(output_dir, QUEXIAN_XIANGQING_TITLE_DIR)) + print(search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) + head_num += 1 + table_num = 0 + Xu_Hao = 0 + total_table_num,table_num,Xu_Hao = await add_dynamic_table(output_doc,output_dir,table_num,Y1_TABLES,QUEXIAN_XIANGQING_DIR,Y1_TABLES_PICTURES,4,5,total_table_num,False,xuhao=Xu_Hao) + total_table_num,table_num,Xu_Hao = await add_dynamic_table(output_doc,output_dir,table_num,Y2_TABLES,QUEXIAN_XIANGQING_DIR,Y2_TABLES_PICTURES,4,5,total_table_num,False,xuhao=Xu_Hao) + total_table_num,table_num,Xu_Hao = await add_dynamic_table(output_doc,output_dir,table_num,Y3_TABLES,QUEXIAN_XIANGQING_DIR,Y3_TABLES_PICTURES,4,5,total_table_num,False,xuhao=Xu_Hao) + + if baogao_info['if_docx_conclusion']: + #总结 + ZONG_JIE_DIR = get_resource_path("muban/result.docx") + ZONG_JIE_BEFORE = "result" + ZONG_JIE = baogao_info['conclusion'] + print(add_documents(output_dir, ZONG_JIE_DIR)) + print(search_and_replace(output_dir, ZONG_JIE_BEFORE, ZONG_JIE)) + print(search_and_replace(output_dir, 'company_yi', Yi_company)) + print(search_and_replace(output_dir, 'baogao_date', baogao_date.split(' ')[0])) + print(search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) diff --git a/Generate_Report.py b/Generate_Report.py index 588f7ed..b501cea 100644 --- a/Generate_Report.py +++ b/Generate_Report.py @@ -1,569 +1,9 @@ -# 文档处理工具 -from tools.document_tools import ( - create_document, add_documents,add_table_and_replace, - add_table_to_document,add_dynamic_table, - process_server_images_table -) - -# 内容处理工具 -from tools.content_tools import ( - add_picture,split_table_by_row_content, - search_and_replace -) - -from tools.get_pictures import ( - process_picture_data,get_records_with_pic -) - -from tools.Get_Json import ( - get_project_info,get_jizu_info, - get_jizu_shigong_info,get_defect_detail, - get_part_picture,get_yepian_xiangqing, - check_pic_url,get_full_picture_url, - get_defect_record_list -) - -from tools.dataproccess import ( - caculate_work_days,get_year_month, - merge_info,get_defect_str, - safe_get,get_resource_path -) - import asyncio - -from core.tables import fill_tables - -from tools.defines import * -import os, re, datetime from pathlib import Path - -async def generate_report(base_info, baogao_info): - #获取模板编号、模板名称 - num_to_chinese = {1 : '一', 2 : '二', 3 : '三', 4 : '四', 5 : '五', 6 : '六', 7 : '七', 8 : '八', 9 : '九', 10 : '十', 11 : '十一', 12 : '十二'} - cover_encode = "encode" - cover_project = "project" - baogao_name1 = "baogaoname1" - baogao_name2 = "baogaoname2" - company_name_yi = "company_name_yi" - cover_date = "time" - TITLE_OF_REPORT = "companyencode" - jiegou_xuhao = 'num' - - print(f"获取到参数:基本信息:{base_info}\n\n报告信息:{baogao_info}") - - try: - base_info = merge_info(base_info, DEFAULT_BASE_INFO) - turbine_id = base_info['turbine_id'] - - jizu_data = get_jizu_info(turbine_id) - project_data = get_project_info(jizu_data['projectId']) - shigong_data = get_jizu_shigong_info(turbine_id) - - fengchang_name = project_data['farmName'] - Yi_company = project_data['inspectionUnit'] - yi_fuzeren = project_data['inspectionContact'] - yi_phone = project_data['inspectionPhone'] - fengchang_location = project_data['farmAddress'] - Jia_company = project_data['client'] - jia_fuzeren = project_data['clientContact'] - jia_phone = project_data['clientPhone'] - jizu_num = project_data['scale'] - jizu_xinghao = project_data['turbineModel'] - project_name = project_data['projectName'] - jizu_bianhao = jizu_data["turbineName"] - start_date = project_data['startDate'] - end_date = project_data['endDate'] - cover_url = project_data['coverUrl'] - gongqi = caculate_work_days(start_date, end_date) - except Exception as e: - print(f"数据库的项目-机组基本信息获取失败:{e}") - return - - try: - baogao_date = datetime.datetime.now().strftime("%Y年%m月%d日 %H:%M") #现在的时间 - date_year_month = get_year_month(baogao_date) - - #前端信息 - baogao_info = merge_info(baogao_info, DEFAULT_BAOGAO_INFO) - - key_words= re.compile('|'.join(map(re.escape, baogao_info['key_words'].split(',')))) #关键字 - - shengcheng_dir = baogao_info['shengcheng_dir'] #路径 - if shengcheng_dir == "": - print("未配置生成路径,请检查配置") - return - if_waibu = baogao_info["if_waibu"] - if_neibu = baogao_info["if_neibu"] - if_fanglei = baogao_info["if_fanglei"] - - quexian_type = baogao_info['quexian_enum'] - - dianxing_type = baogao_info['dianxing_enum'] - - other_type = baogao_info["other_enum"] - - jiancha_renyuan = baogao_info['jiancha_renyuan'] #检查人员,目前是从命令行参数获取,需要完善 - check_date = baogao_info['check_date'] - if check_date == None: - check_date = "未获取" - baogao_bianzhi = baogao_info["userName"] - baogao_shenghe = baogao_info["baogaoCheck"] - Jiancha_date = baogao_info["check_date"] - data_processor = baogao_info["data_processor"] - coverurl = baogao_info["coverurl"] - - - #数据库拉取信息 - # Jiancha_date = shigong_data["startTime"].replace("T", " ") #检查日期 - # image_count = shigong_data['imageCount'] #从施工方案获取的图片数量,待定!!! - # temperature = shigong_data['temperature'] #温度 - # wind_speed = shigong_data['windSpeed'] #风速 - # weather = get_weather(shigong_data["weatherCode"]) #天气 不从此接口获取,待定!!! - - #拉取部件、图片数据 - part_data, picture_data, Yepians = get_part_picture(turbine_id) - - print(Yepians) - Y1_info = get_yepian_xiangqing(Yepians[0]["partId"]) - Y_Code = [yepian["partCode"] for yepian in Yepians] - partManufacturer = Y1_info["partManufacturer"] - print(f"找到叶片号{Y_Code},厂商{partManufacturer}") - - image_source_to_find = [] - baogao_label = [] - renyuan_peizhi = [] - gongzuo_neirong = [] - shigong_fangan = [] - shebei_peizhi = [] - beizhu = [] - jiancha = [] - neirong = [] - neirong2 = [] - #获取对应枚举字段 - if if_waibu: - baogao_label.append("外观") - image_source_to_find.append(baogao_info['waibu_enum']) - if baogao_info["shigong_fangan"] == None: - print("未传入施工方案,使用已有枚举") - renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.WAIBU.RENYUAN_PEIZHI) - gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.WAIBU.GONGZUO_NEIRONG) - shebei_peizhi.append(SHIGONG_FANGAN_ENUM.WAIBU.SHEBEI_PEIZHI) - shigong_fangan.append(SHIGONG_FANGAN_ENUM.WAIBU.SHIGONG_FANGAN) - else: - pass #待添加如果从平台传入枚举,但目前没必要,如果这种枚举标准信息库有更好的优化则可以实现 - jiancha.append("无人机近距离外观检查") - neirong.append(f"、".join(Y_Code) + f"共{len(Y_Code)}支叶片的前缘、后缘、迎风面、背风面。") - neirong2.append("前缘、后缘、迎风面、背风面。") - if if_neibu: - baogao_label.append("内部") - image_source_to_find.append(baogao_info['neibu_enum']) - if baogao_info["shigong_fangan"] == None: - renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.NEIBU.RENYUAN_PEIZHI) - gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.NEIBU.GONGZUO_NEIRONG) - shebei_peizhi.append(SHIGONG_FANGAN_ENUM.NEIBU.SHEBEI_PEIZHI) - shigong_fangan.append(SHIGONG_FANGAN_ENUM.NEIBU.SHIGONG_FANGAN) - else: - pass - jiancha.append("人工内部拍摄") - neirong.append(f"、".join(Y_Code) + f"共{len(Y_Code)}支叶片的内部导雷卡、腹板、透光、人孔盖版、叶根盖板...") - neirong2.append("内部导雷卡、腹板、透光、人孔盖版、叶根盖板...") - if if_fanglei: - baogao_label.append("防雷") - image_source_to_find.append(baogao_info['fanglei_enum']) - if baogao_info["shigong_fangan"] == None: - renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.RENYUAN_PEIZHI) - gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.GONGZUO_NEIRONG) - shebei_peizhi.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.SHEBEI_PEIZHI) - shigong_fangan.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.SHIGONG_FANGAN) - else: - pass - jiancha.append("人工防雷") - neirong.append(f"轮毂至塔基导通、内部导线线阻、外部导线线阻...") - neirong2.append("轮毂至塔基导通、内部导线线阻、外部导线线阻...") - - #获取缺陷图列表和典型图列表 - filtered_picture_data, total_picture_num = process_picture_data(picture_data, image_source_to_find) - #获取所有缺陷记录 - defect_records = get_defect_record_list() - #获取缺陷记录中对应图片的记录 - defect_records_with_pic, error_pic_with_no_record = get_records_with_pic(defect_records, filtered_picture_data, quexian_type) - if len(error_pic_with_no_record) > 0: - print(f"!!!有部分缺陷图片没有对应的缺陷记录,将不会生成这些图片的缺陷表:{error_pic_with_no_record} ") - print(f"对应缺陷图的缺陷记录列表:{defect_records_with_pic}") - except Exception as e: - print(f"报告基本信息获取失败:{e}") - return - - #检查参数合法性 - if not if_fanglei or not if_neibu or not if_waibu: - print("请至少选择一种检查项目") - return - if not os.path.exists(shengcheng_dir): - print(f"生成路径{shengcheng_dir}不存在") - return - - output_doc = None - head_num = 1 - ###封面创建### - cover_dirs = [get_resource_path("muban/fengmian1.docx"),get_resource_path("muban/fengmian.jpg"),get_resource_path("muban/fengmian2.docx")] - #输出目录 - baogao_name = "叶片" + "、".join(baogao_label) + "检查报告" - output_dir = os.path.normpath(f"{shengcheng_dir}/{project_name}项目{baogao_name}{jizu_bianhao}{baogao_date.split(' ')[0]}版.docx") - - version = 1 - while os.path.exists(output_dir): - if version != 1: - output_dir = output_dir.replace(f"版{version - 1}",f"版{version}") - else: - output_dir = output_dir.replace("版",f"版{version}") - version += 1 - - mianzhe_shengming = f"本报告仅涵盖{'、'.join(baogao_label)}检测内容" - - print(await create_document(output_dir)) - if baogao_info["if_docx_fengmian"] : - #创建文档、添加封面 - print(add_documents(output_dir, cover_dirs[0])) - if check_pic_url(coverurl): #手动导入封面图片测试用 - print(await add_picture(output_dir, get_full_picture_url(coverurl), width = 6.41, height = 4)) - elif check_pic_url(cover_url): - print(await add_picture(output_dir, get_full_picture_url(cover_url), width = 6.41, height = 4)) - else: - print(await add_picture(output_dir, cover_dirs[1])) - print(add_documents(output_dir, cover_dirs[2])) - print("封面创建成功") - #YYYY年MM月DD日 HH:MM:SS - #更改文档信息 - print(await search_and_replace(output_dir, TITLE_OF_REPORT, jizu_bianhao)) - print(await search_and_replace(output_dir, baogao_name1, baogao_name)) - print(await search_and_replace(output_dir, baogao_name2, baogao_name)) - print(await search_and_replace(output_dir, company_name_yi, Yi_company)) - print(await search_and_replace(output_dir, cover_project, fengchang_name)) - print(await search_and_replace(output_dir, cover_encode, jizu_bianhao)) - print(await search_and_replace(output_dir, cover_date, date_year_month)) - print(await search_and_replace(output_dir, 'bianzhi', baogao_bianzhi)) - print(await search_and_replace(output_dir, 'shenghe', baogao_shenghe)) - print(await search_and_replace(output_dir, 'mianzhe_shengming', mianzhe_shengming)) - - total_table_num = 0 - if baogao_info["if_docx_project_overview"]: - #项目概况表 - print("开始添加项目概况表") - XIANG_MU_GAI_KUANG = get_resource_path("muban/xiangmugaikuo.docx") - print(f"查找模板,找到模板:{XIANG_MU_GAI_KUANG}") - project_location = fengchang_location - company_name_jia = Jia_company - fuzeren = yi_fuzeren - phone_fuzeren = yi_phone - xiangmuguige = jizu_num - Yi_company = Yi_company - XIANGMU_GAIKUO = list(list("" for i in range(6)) for j in range(5)) - XIANGMU_GAIKUO[0][1] = fengchang_name - XIANGMU_GAIKUO[0][4] = project_location - XIANGMU_GAIKUO[1][1] = company_name_jia - XIANGMU_GAIKUO[1][4] = Yi_company - XIANGMU_GAIKUO[2][1] = jia_fuzeren - XIANGMU_GAIKUO[2][4] = fuzeren - XIANGMU_GAIKUO[3][2] = jia_phone - XIANGMU_GAIKUO[3][5] = phone_fuzeren - XIANGMU_GAIKUO[4][1] = jizu_xinghao - XIANGMU_GAIKUO[4][3] = xiangmuguige - XIANGMU_GAIKUO[4][5] = gongqi - print("建立表结构完毕,开始插入模板") - #添加项目概况表 - print(f"输出路径:{output_dir},模板路径:{XIANG_MU_GAI_KUANG},插入数据:{XIANGMU_GAIKUO}") - output_doc, message = await add_table_to_document(output_dir, XIANG_MU_GAI_KUANG,5,5,total_table_num,XIANGMU_GAIKUO) - print(message) - print("模板插入完毕,开始替换内容") - total_table_num += 1 - print(await search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) - head_num += 1 - - if baogao_info['if_docx_inspection_method']: - #检查方案描述 - FANGAN_JIANCHA_DIR = get_resource_path("muban/checkmethod.docx") - list_to_replace = { - 'renyuan_peizhi' : "\n".join(renyuan_peizhi), - 'shebei_peizhi' : "\n".join(shebei_peizhi), - 'shigong_fangan' : "\n".join(shigong_fangan), - 'gongzuo_neirong' : "\n".join(gongzuo_neirong), - 'beizhu' : beizhu, - 'num' : num_to_chinese[head_num], - } - print(await add_table_and_replace(output_dir, FANGAN_JIANCHA_DIR, 0, list_to_replace)) - print(split_table_by_row_content(output_dir, output_dir, total_table_num)) - total_table_num += 1 - head_num += 1 - - if baogao_info['if_docx_inspection_info']: - #检查信息 - JIANCHA_XINGXI_DIR = get_resource_path("muban/checkinfo.docx") - JIANCHA_XINGXI = list(list("" for i in range(4)) for j in range(9)) - JIANCHA_XINGXI[0][1] = jiancha_renyuan - try: - JIANCHA_XINGXI[1][1] = Jiancha_date.split('T')[0] - except: - JIANCHA_XINGXI[1][1] = "格式不对或无数据" - JIANCHA_XINGXI[1][3] = jizu_bianhao - JIANCHA_XINGXI[2][1] = "风力发电机组" + baogao_name - JIANCHA_XINGXI[2][3] = "、".join(jiancha) - JIANCHA_XINGXI[3][2] = partManufacturer - JIANCHA_XINGXI[4][1] = '叶片型号:' + jizu_xinghao - JIANCHA_XINGXI[5][1] = Y_Code[0] if len(Y_Code) > 0 else "无" - JIANCHA_XINGXI[6][1] = Y_Code[1] if len(Y_Code) > 1 else "无" - JIANCHA_XINGXI[7][1] = Y_Code[2] if len(Y_Code) > 2 else "无" - JIANCHA_XINGXI[8][0] = "本次" + "、".join(_ for _ in jiancha) + f"检查,采集叶片图片{total_picture_num}张,内容覆盖" + ";".join(_ for _ in neirong) - #新建检查信息表 - output_doc, message = await add_table_to_document(output_dir, JIANCHA_XINGXI_DIR,9,4,total_table_num ,JIANCHA_XINGXI,False) - print(message) - print(await search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) - head_num += 1 - total_table_num += 1 - - if baogao_info['if_docx_chengguo_sub']: - # 添加成果递交表 - CHENGGUO_DIJIAO_DIR = get_resource_path("muban/chengguo_sub.docx") - CHENGGUO_DIJIAO = list(list("" for i in range(4)) for j in range(6)) - CHENGGUO_DIJIAO[0][1] = jiancha_renyuan - CHENGGUO_DIJIAO[1][1] = jia_fuzeren - try: - CHENGGUO_DIJIAO[2][1] = Jiancha_date.split('T')[0] - except: - CHENGGUO_DIJIAO[2][1] = "格式不对或无数据" - CHENGGUO_DIJIAO[3][1] = data_processor - CHENGGUO_DIJIAO[4][1] = baogao_bianzhi - CHENGGUO_DIJIAO[5][1] = baogao_shenghe - try: - CHENGGUO_DIJIAO[2][3] = Jiancha_date.split('T')[1] - except: - CHENGGUO_DIJIAO[2][3] = "格式不对或无数据" - CHENGGUO_DIJIAO[3][3] = baogao_date.split(' ')[0] - CHENGGUO_DIJIAO[4][3] = baogao_date.split(' ')[0] - CHENGGUO_DIJIAO[5][3] = "未审核" - - output_doc, message = await add_table_to_document(output_dir, CHENGGUO_DIJIAO_DIR,5,5,total_table_num,CHENGGUO_DIJIAO,True,0.04) - print(message) - print(await search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) - head_num += 1 - total_table_num += 1 - - if baogao_info['if_docx_inspection_text'] and if_waibu: - #检查情况汇总表(文字信息) - - try: - #获取缺陷信息 - """ - 需要获取: - Y1、Y2、Y3叶片的缺陷数量,缺陷字典{描述:图片路径} - 和数据库连接需要的新增异常处理: - 目前逻辑为找到图片后,找缺陷类型的图片,通过图片id查找对应缺陷记录 - 如果没有找到,要返回对应异常,即已选出(标注)缺陷图,但未填写对应缺陷信息的异常返回。 - 最后将无异常的图片入队缺陷字典(同数量),有异常的图片再另存。 - """ - - Y1_quexian_list = [] - Y2_quexian_list = [] - Y3_quexian_list = [] - Y1_quexian_list = defect_records_with_pic["叶片1"]["DEFECT"] if "叶片1" in defect_records_with_pic else {} - Y2_quexian_list = defect_records_with_pic["叶片2"]["DEFECT"] if "叶片2" in defect_records_with_pic else {} - Y3_quexian_list = defect_records_with_pic["叶片3"]["DEFECT"] if "叶片3" in defect_records_with_pic else {} - - Y1_quexian_num = len(Y1_quexian_list) - Y2_quexian_num = len(Y2_quexian_list) - Y3_quexian_num = len(Y3_quexian_list) - no_defect_found = "未发现明显缺陷" - weak_num_Y1 = f"{Y_Code[0] if len(Y_Code) > 0 else '无'}叶片" + f"共发现缺陷{Y1_quexian_num}处" if Y1_quexian_num > 0 else no_defect_found - weak_num_Y2 = f"{Y_Code[1] if len(Y_Code) > 1 else '无'}叶片" + f"共发现缺陷{Y2_quexian_num}处" if Y1_quexian_num > 0 else no_defect_found - weak_num_Y3 = f"{Y_Code[2] if len(Y_Code) > 2 else '无'}叶片" + f"共发现缺陷{Y3_quexian_num}处" if Y1_quexian_num > 0 else no_defect_found - except Exception as e: - print(f"缺陷图获取失败:{e}") - return - - #添加检查情况汇总表 - JIANCHA_HUIZONG_DIR = get_resource_path("muban/total_check.docx") - JIANCHA_HUIZONG = list(list("" for i in range(3)) for j in range(4)) - - JIANCHA_HUIZONG[1][0] = weak_num_Y1 - JIANCHA_HUIZONG[2][0] = weak_num_Y2 - JIANCHA_HUIZONG[3][0] = weak_num_Y3 - JIANCHA_HUIZONG[1][1] = "\n".join(neirong2) - JIANCHA_HUIZONG[2][1] = "\n".join(neirong2) - JIANCHA_HUIZONG[3][1] = "\n".join(neirong2) - JIANCHA_HUIZONG[1][2] = "\n".join([f"{i+1}.{s}" for i, s in enumerate(get_defect_str(Y1_quexian_list))]) if Y1_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷' - JIANCHA_HUIZONG[2][2] = "\n".join([f"{i+1}.{s}" for i, s in enumerate(get_defect_str(Y2_quexian_list))]) if Y2_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷' - JIANCHA_HUIZONG[3][2] = "\n".join([f"{i+1}.{s}" for i, s in enumerate(get_defect_str(Y3_quexian_list))]) if Y3_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷' - output_doc, message = await add_table_to_document(output_dir, JIANCHA_HUIZONG_DIR,4,3,total_table_num,JIANCHA_HUIZONG,False,ALIGMENT='LEFT') - print(message) - print(await search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) - total_table_num += 1 - head_num += 1 - - if baogao_info['if_docx_inspection_picture']: - #主要部位图片展示表/检查内容表 - #获取典型图信息 - try: - Y1_list = safe_get(filtered_picture_data, "叶片1", dianxing_type, default=[]) - Y2_list = safe_get(filtered_picture_data, "叶片2", dianxing_type, default=[]) - Y3_list = safe_get(filtered_picture_data, "叶片3", dianxing_type, default=[]) - picture_Y1_num = len(Y1_list) - picture_Y2_num = len(Y2_list) - picture_Y3_num = len(Y3_list) - except Exception as e: - print(f"获取缺陷图失败:{e}") - print(f"图片、文字数量:{picture_Y1_num} {picture_Y2_num} {picture_Y3_num}") - JIANCHA_NEIRONG_TOTAL_NUM = picture_Y1_num+ picture_Y2_num + picture_Y3_num - if JIANCHA_NEIRONG_TOTAL_NUM <= 0: - print("无典型图片数据,无法生成典型图表") - else: - col ,row = 3, 0 - JIANCHA_NEIRONG_PICTURES_TABLE = get_resource_path("muban/check2.docx") - JIANCHA_NEIRONG_Y1_DIR = get_resource_path("muban/check_content.docx") - JIANCHA_NEIRONG_Y1 = list(list("" for _ in range(3)) for j in range(1)) - Y1_code = Y_Code[0] if len(Y_Code) > 0 else "无" - Y2_code = Y_Code[1] if len(Y_Code) > 1 else "无" - Y3_code = Y_Code[2] if len(Y_Code) > 2 else "无" - JIANCHA_NEIRONG_Y1[0][0] = f"叶片1:{Y1_code}检查内容" - print(f"Y1标题内容:{JIANCHA_NEIRONG_Y1}") - JIANCHA_NEIRONG_Y2_DIR = get_resource_path("muban/check3.docx") - JIANCHA_NEIRONG_Y2 = list(list("" for _ in range(3)) for j in range(1)) - JIANCHA_NEIRONG_Y2[0][0] = f"叶片2:{Y2_code}检查内容" - print(f"Y2标题内容:{JIANCHA_NEIRONG_Y2}") - JIANCHA_NEIRONG_Y3_DIR = get_resource_path("muban/check3.docx") - JIANCHA_NEIRONG_Y3 = list(list("" for _ in range(3)) for j in range(1)) - JIANCHA_NEIRONG_Y3[0][0] = f"叶片3:{Y3_code}检查内容" - print(f"Y3标题内容:{JIANCHA_NEIRONG_Y3}") - print(f"当前表格序号为 {total_table_num}") - print(key_words) - output_doc, message = await add_table_to_document(output_dir, JIANCHA_NEIRONG_Y1_DIR,1,3,total_table_num,JIANCHA_NEIRONG_Y1,True, 1) - print(message) - total_table_num += 1 - - total_table_num = await process_server_images_table(Y1_list, image_source_to_find, output_dir, total_table_num, JIANCHA_NEIRONG_PICTURES_TABLE, key_words) - - output_doc, message = await add_table_to_document(output_dir, JIANCHA_NEIRONG_Y2_DIR,1,3,total_table_num,JIANCHA_NEIRONG_Y2,True, 1) - print(message) - total_table_num += 1 - - total_table_num = await process_server_images_table(Y2_list, image_source_to_find, output_dir, total_table_num, JIANCHA_NEIRONG_PICTURES_TABLE, key_words) - - output_doc, message = await add_table_to_document(output_dir, JIANCHA_NEIRONG_Y3_DIR,1,3,total_table_num,JIANCHA_NEIRONG_Y3,True, 1) - print(message) - total_table_num += 1 - - total_table_num = await process_server_images_table(Y3_list, image_source_to_find, output_dir, total_table_num, JIANCHA_NEIRONG_PICTURES_TABLE, key_words) - print(await search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) - head_num += 1 - - if baogao_info['if_docx_defect_picture'] and if_waibu: - # #缺陷详情 - QUEXIAN_XIANGQING_DIR = get_resource_path("muban/check_check.docx") - QUEXIAN_XIANGQING_TITLE_DIR = get_resource_path("muban/check_check_title.docx") - - Y_tables = [Y1_quexian_list,Y2_quexian_list,Y3_quexian_list] - Y1_table_list = [] - Y2_table_list = [] - Y3_table_list = [] - table_lists = [Y1_table_list, Y2_table_list, Y3_table_list] - - for i, (table_list, Y_lists) in enumerate(zip(table_lists, Y_tables)): - for Y_list in Y_lists: - image_defect_record = Y_list["record"] - image_path = Y_list["imagePath"] - - image_defect_record = get_defect_detail(image_defect_record["defectId"]) - - # 从imagedefectrecord中获取各个字段 - defect_type = image_defect_record.get('defectTypeLabel', '未知缺陷类型') - defect_location = f"{image_defect_record.get('partName', '')}{image_defect_record.get('defectPosition', '')}" - - # 使用axial和chordwise作为缺陷尺寸信息 - axial = image_defect_record.get('axial', '') - chordwise = image_defect_record.get('chordwise', '') - defect_size = f"轴向:{axial} 弦向:{chordwise}" if axial or chordwise else '' - - visibility = "暂无此项" - - urgency = "暂无此项" - - severity = image_defect_record.get('defectLevelLabel', '未知严重等级') - repair_suggestion = image_defect_record.get('repairIdea', '无维修建议') - - print(f"获取第{i + 1}个叶片的缺陷图: {image_path}") - - table_list.append({ - "QueXianLeiXing": defect_type, - "QueXianWeiZhi": defect_location, - "QueXianChiCun": defect_size, - "WeiZongDengJi": severity, - "Tupian_Dir": get_full_picture_url(image_path) if check_pic_url(image_path) else None, - "visibility": visibility, - "urgency": urgency, - "repair_suggestion": repair_suggestion, # 新增维修建议字段 - }) - - # for i, (table_list, Y_dict) in enumerate(zip(table_lists, Y_tables)): - # for image_defect_record, image_path in Y_dict.items(): - # # 从图片名解析各个字段 - # parts = image_name.split('_') - # if len(parts) >= 8: # 确保有7个部分 - # defect_type = parts[1] - # defect_location = parts[2] - # defect_size = parts[3] - # visibility = parts[4] - # urgency = parts[5] - # severity = parts[6] - # repair_suggestion = parts[7] - - # print(f"获取第{i+1}个叶片的缺陷图: {image_path}") - - # table_list.append({ - # "QueXianLeiXing": defect_type, - # "QueXianWeiZhi": defect_location, - # "QueXianChiCun": defect_size, - # "WeiZongDengJi": severity, - # "Tupian_Dir": image_path, - # "visibility": visibility, - # "urgency": urgency, - # "repair_suggestion": repair_suggestion.split('.')[0], # 新增维修建议字段 - # }) - # else: - # table_list.append({ - # "QueXianLeiXing": "图片命名有误", - # "QueXianWeiZhi": "图片命名有误", - # "QueXianChiCun": "图片命名有误", - # "WeiZongDengJi": "图片命名有误", - # "Tupian_Dir": image_path, - # "visibility": "图片命名有误", - # "urgency": "图片命名有误", - # "repair_suggestion": "图片命名有误", # 新增维修建议字段 - # }) - - - Y1_TABLES, Y1_TABLES_PICTURES = fill_tables(table_lists[0],4,5,len(table_lists[0]),Y_Code[0] if len(Y_Code) > 0 else "无") - Y2_TABLES, Y2_TABLES_PICTURES = fill_tables(table_lists[1],4,5,len(table_lists[1]),Y_Code[1] if len(Y_Code) > 1 else "无") - Y3_TABLES, Y3_TABLES_PICTURES = fill_tables(table_lists[2],4,5,len(table_lists[2]),Y_Code[2] if len(Y_Code) > 2 else "无") - print(add_documents(output_dir, QUEXIAN_XIANGQING_TITLE_DIR)) - print(await search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) - head_num += 1 - table_num = 0 - Xu_Hao = 0 - total_table_num,table_num,Xu_Hao = await add_dynamic_table(output_doc,output_dir,table_num,Y1_TABLES,QUEXIAN_XIANGQING_DIR,Y1_TABLES_PICTURES,4,5,total_table_num,False,xuhao=Xu_Hao) - total_table_num,table_num,Xu_Hao = await add_dynamic_table(output_doc,output_dir,table_num,Y2_TABLES,QUEXIAN_XIANGQING_DIR,Y2_TABLES_PICTURES,4,5,total_table_num,False,xuhao=Xu_Hao) - total_table_num,table_num,Xu_Hao = await add_dynamic_table(output_doc,output_dir,table_num,Y3_TABLES,QUEXIAN_XIANGQING_DIR,Y3_TABLES_PICTURES,4,5,total_table_num,False,xuhao=Xu_Hao) - - if baogao_info['if_docx_conclusion']: - #总结 - ZONG_JIE_DIR = get_resource_path("muban/result.docx") - ZONG_JIE_BEFORE = "result" - ZONG_JIE = baogao_info['conclusion'] - print(add_documents(output_dir, ZONG_JIE_DIR)) - print(await search_and_replace(output_dir, ZONG_JIE_BEFORE, ZONG_JIE)) - print(await search_and_replace(output_dir, 'company_yi', Yi_company)) - print(await search_and_replace(output_dir, 'baogao_date', baogao_date.split(' ')[0])) - print(await search_and_replace(output_dir, jiegou_xuhao, num_to_chinese[head_num])) - from tools.argtool import (load_config, parse_arguments, get_default_config, merge_configs) +from Dt_report import generate_dt_report +from Jf_report import generate_jf_report def main(): args = parse_arguments() @@ -586,7 +26,12 @@ def main(): Path(merged['json2']['shengcheng_dir']).mkdir(parents=True, exist_ok=True) - asyncio.run(generate_report(merged['json1'], merged['json2'])) + if merged['json2']['choose_template'] == 'DT': + asyncio.run(generate_dt_report(merged['json1'], merged['json2'])) + elif merged['json2']['choose_template'] == 'JF': + asyncio.run(generate_jf_report(merged['json1'], merged['json2'])) + else: + print('指定了不存在的,请检查配置文件') print('文档生成完毕') if __name__ == '__main__': diff --git a/Jf_report.py b/Jf_report.py new file mode 100644 index 0000000..066cb9d --- /dev/null +++ b/Jf_report.py @@ -0,0 +1,284 @@ +# 文档处理工具 +from tools.document_tools import ( + create_document, add_documents,add_table_and_replace, + add_table_to_document,add_dynamic_table, + process_server_images_table,add_header,add_landscape_section, + merge_documents,add_table,add_defect_info_table +) + +# 内容处理工具 +from tools.content_tools import ( + add_picture,split_table_by_row_content, + search_and_replace +) + +from tools.get_pictures import ( + process_picture_data,get_records_with_pic +) + +from tools.Get_Json import ( + get_project_info,get_jizu_info, + get_jizu_shigong_info,get_defect_detail, + get_part_picture,get_yepian_xiangqing, + check_pic_url,get_full_picture_url, + get_defect_record_list +) + +from tools.dataproccess import ( + caculate_work_days,get_year_month, + merge_info,get_defect_str,get_defect_json, + safe_get,get_resource_path,get_defedct_info, +) + +from tools.json_to_docx import json_to_docx + + +from core.tables import fill_tables + +from tools.defines import * +import os, re, datetime + +async def generate_jf_report(base_info, baogao_info): + #获取模板编号、模板名称 + num_to_chinese = {1 : '一', 2 : '二', 3 : '三', 4 : '四', 5 : '五', 6 : '六', 7 : '七', 8 : '八', 9 : '九', 10 : '十', 11 : '十一', 12 : '十二'} + cover_encode = "encode" + cover_project = "project" + baogao_name1 = "baogaoname1" + baogao_name2 = "baogaoname2" + company_name_yi = "company_name_yi" + cover_date = "time" + TITLE_OF_REPORT = "companyencode" + jiegou_xuhao = 'num' + + print(f"获取到参数:基本信息:{base_info}\n\n报告信息:{baogao_info}") + + try: + base_info = merge_info(base_info, DEFAULT_BASE_INFO) + turbine_id = base_info['turbine_id'] + + jizu_data = get_jizu_info(turbine_id) + project_data = get_project_info(jizu_data['projectId']) + shigong_data = get_jizu_shigong_info(turbine_id) + + fengchang_name = project_data['farmName'] + Yi_company = project_data['inspectionUnit'] + yi_fuzeren = project_data['inspectionContact'] + yi_phone = project_data['inspectionPhone'] + fengchang_location = project_data['farmAddress'] + Jia_company = project_data['client'] + jia_fuzeren = project_data['clientContact'] + jia_phone = project_data['clientPhone'] + jizu_num = project_data['scale'] + jizu_xinghao = project_data['turbineModel'] + jizu_manufacture = project_data.get('turbineManufacturer', "") + project_name = project_data['projectName'] + jizu_bianhao = jizu_data["turbineName"] + start_date = project_data['startDate'] + end_date = project_data['endDate'] + cover_url = project_data['coverUrl'] + gongqi = caculate_work_days(start_date, end_date) + except Exception as e: + print(f"数据库的项目-机组基本信息获取失败:{e}") + return + + try: + baogao_date = datetime.datetime.now().strftime("%Y年%m月%d日 %H:%M") #现在的时间 + date_year_month = get_year_month(baogao_date) + + #前端信息 + baogao_info = merge_info(baogao_info, DEFAULT_BAOGAO_INFO) + + key_words= re.compile('|'.join(map(re.escape, baogao_info['key_words'].split(',')))) #关键字 + + shengcheng_dir = baogao_info['shengcheng_dir'] #路径 + if shengcheng_dir == "": + print("未配置生成路径,请检查配置") + return + if_waibu = baogao_info["if_waibu"] + if_neibu = baogao_info["if_neibu"] + if_fanglei = baogao_info["if_fanglei"] + + quexian_type = baogao_info['quexian_enum'] + + dianxing_type = baogao_info['dianxing_enum'] + + other_type = baogao_info["other_enum"] + + jiancha_renyuan = baogao_info['jiancha_renyuan'] #检查人员,目前是从命令行参数获取,需要完善 + check_date = baogao_info['check_date'] + if check_date == None: + check_date = "未获取" + baogao_bianzhi = baogao_info["userName"] + baogao_shenghe = baogao_info["baogaoCheck"] + Jiancha_date = baogao_info["check_date"] + data_processor = baogao_info["data_processor"] + coverurl = baogao_info["coverurl"] + + + #数据库拉取信息 + # Jiancha_date = shigong_data["startTime"].replace("T", " ") #检查日期 + # image_count = shigong_data['imageCount'] #从施工方案获取的图片数量,待定!!! + # temperature = shigong_data['temperature'] #温度 + # wind_speed = shigong_data['windSpeed'] #风速 + # weather = get_weather(shigong_data["weatherCode"]) #天气 不从此接口获取,待定!!! + + #拉取部件、图片数据 + part_data, picture_data, Yepians = get_part_picture(turbine_id) + + print(Yepians) + Y1_info = get_yepian_xiangqing(Yepians[0]["partId"]) + Y_Code = [yepian["partCode"] for yepian in Yepians] + partType = Y1_info["partType"] + partManufacturer = Y1_info["partManufacturer"] + print(f"找到叶片号{Y_Code},厂商{partManufacturer}") + + image_source_to_find = [] + baogao_label = [] + renyuan_peizhi = [] + gongzuo_neirong = [] + shigong_fangan = [] + shebei_peizhi = [] + beizhu = [] + jiancha = [] + neirong = [] + neirong2 = [] + #获取对应枚举字段 + if if_waibu: + baogao_label.append("外观") + image_source_to_find.append(baogao_info['waibu_enum']) + if baogao_info["shigong_fangan"] == None: + print("未传入施工方案,使用已有枚举") + renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.WAIBU.RENYUAN_PEIZHI) + gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.WAIBU.GONGZUO_NEIRONG) + shebei_peizhi.append(SHIGONG_FANGAN_ENUM.WAIBU.SHEBEI_PEIZHI) + shigong_fangan.append(SHIGONG_FANGAN_ENUM.WAIBU.SHIGONG_FANGAN) + else: + pass #待添加如果从平台传入枚举,但目前没必要,如果这种枚举标准信息库有更好的优化则可以实现 + jiancha.append("无人机外观检查") + neirong.append(f"、".join(Y_Code) + f"共{len(Y_Code)}支叶片的前缘、后缘、迎风面、背风面。") + neirong2.append("前缘、后缘、迎风面、背风面。") + if if_neibu: + baogao_label.append("内部") + image_source_to_find.append(baogao_info['neibu_enum']) + if baogao_info["shigong_fangan"] == None: + renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.NEIBU.RENYUAN_PEIZHI) + gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.NEIBU.GONGZUO_NEIRONG) + shebei_peizhi.append(SHIGONG_FANGAN_ENUM.NEIBU.SHEBEI_PEIZHI) + shigong_fangan.append(SHIGONG_FANGAN_ENUM.NEIBU.SHIGONG_FANGAN) + else: + pass + jiancha.append("叶片内部检查") + neirong.append(f"、".join(Y_Code) + f"共{len(Y_Code)}支叶片的内部导雷卡、腹板、透光、人孔盖版、叶根盖板...") + neirong2.append("内部导雷卡、腹板、透光、人孔盖版、叶根盖板...") + if if_fanglei: + baogao_label.append("防雷") + image_source_to_find.append(baogao_info['fanglei_enum']) + if baogao_info["shigong_fangan"] == None: + renyuan_peizhi.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.RENYUAN_PEIZHI) + gongzuo_neirong.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.GONGZUO_NEIRONG) + shebei_peizhi.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.SHEBEI_PEIZHI) + shigong_fangan.append(SHIGONG_FANGAN_ENUM.FANGLEI.YEPIAN.SHIGONG_FANGAN) + else: + pass + jiancha.append("叶片导通测试") + neirong.append(f"轮毂至塔基导通、内部导线线阻、外部导线线阻...") + neirong2.append("轮毂至塔基导通、内部导线线阻、外部导线线阻...") + + #获取缺陷图列表和典型图列表 + filtered_picture_data, total_picture_num = process_picture_data(picture_data, image_source_to_find) + #获取所有缺陷记录 + defect_records = get_defect_record_list() + #获取缺陷记录中对应图片的记录 + defect_records_with_pic, error_pic_with_no_record = get_records_with_pic(defect_records, filtered_picture_data, quexian_type) + if len(error_pic_with_no_record) > 0: + print(f"!!!有部分缺陷图片没有对应的缺陷记录,将不会生成这些图片的缺陷表:{error_pic_with_no_record} ") + print(f"对应缺陷图的缺陷记录列表:{defect_records_with_pic}") + except Exception as e: + print(f"报告基本信息获取失败:{e}") + return + + #检查参数合法性 + if not if_fanglei or not if_neibu or not if_waibu: + print("请至少选择一种检查项目") + return + if not os.path.exists(shengcheng_dir): + print(f"生成路径{shengcheng_dir}不存在") + return + + baogao_name = "叶片" + "、".join(baogao_label) + "检查报告" + output_dir = os.path.normpath(f"{shengcheng_dir}/{project_name}项目{baogao_name}{jizu_bianhao}{baogao_date.split(' ')[0]}版.docx") + + version = 1 + while os.path.exists(output_dir): + if version != 1: + output_dir = output_dir.replace(f"版{version - 1}",f"版{version}") + else: + output_dir = output_dir.replace("版",f"版{version}") + version += 1 + add_list = [ + get_resource_path(MUBAN_DIR + '/jf_fengmian.png'), + get_resource_path(MUBAN_DIR + '/jf_fengmian1.png'), + get_resource_path(MUBAN_DIR + '/jinfeng_fengmian_name.docx'), + get_resource_path(MUBAN_DIR + '/jf_fengmian2.png'), + get_resource_path(MUBAN_DIR + '/jinfeng_fengmian_renyuan.docx'), + get_resource_path(MUBAN_DIR + '/jf_fengmian2.png'), + get_resource_path(MUBAN_DIR + '/jinfeng_fengmian_riqi.docx'), + get_resource_path(MUBAN_DIR + '/jf_fengmian1.png'), + get_resource_path(MUBAN_DIR + '/jinfeng_fengmian_luokuan.docx'), + get_resource_path(MUBAN_DIR + '/jf_fengmian3.png'), + ] + print(await create_document(output_dir, section_args={ + "page_height" : JF_HEIGHT, + "page_width" : JF_WIDTH, + "left_margin" : JF_L_MARGIN, + "right_margin" : JF_R_MARGIN, + "top_margin" : JF_T_MARGIN, + "bottom_margin" : JF_B_MARGIN, + })) + merge_documents(output_dir, add_list) + + add_header(output_dir, TEMPLATE_HEADER.JINFENG_HEADER.ENUM) + + print(add_documents(output_dir, get_resource_path(MUBAN_DIR + '/jf_table_title.docx'))) + total_table_num = 0 + print(add_table(output_dir, get_resource_path(MUBAN_DIR + '/jinfeng_table.docx'))) + total_picture_num += 1 + list_to_replace = { + 'jia_company_name' : Jia_company, + 'fengchang_name' : fengchang_name, + 'jizu_hao' : jizu_bianhao, + 'bianzhi_renyuan': baogao_bianzhi, + 'bianzhi_riqi': baogao_date, + 'fengchang_name' : fengchang_name, + 'jizu_hao' : jizu_bianhao, + 'fengchang_location' : fengchang_location, + 'kehu_company' : Yi_company, + 'xiangmuguige' : jizu_num, + 'kehu_fuzeren' : yi_fuzeren, + 'yezhu_phone' : yi_phone, + 'shigong_company' : JINFENG_COMPANY, + 'shigong_date' : "未填写",#shigong_date, + 'shigong_fuzeren' : "未填写", + 'shigong_phone' : "未填写", + 'zhengji_changjia' : jizu_manufacture, + 'yepian_changjia' : partManufacturer, + 'jizu_type' : jizu_xinghao, + 'yepian_type' : partType, + 'jiancha_fangshi' : '、'.join(jiancha), + 'jiancha_renyuan' : jiancha_renyuan + } + for find_text, replace_text in list_to_replace.items(): + print(search_and_replace(output_dir, find_text, replace_text)) + print(f"静态内容生成完毕,开始生成动态内容") + + defect_info = get_defedct_info(defect_records_with_pic) + + total_picture_num = add_defect_info_table(output_dir, defect_info, get_resource_path(MUBAN_DIR + '/quexian_liebiao.docx'), total_table_num) + + # defect_json = get_defect_json(defect_info, Y_Code, jizu_bianhao) + + # print(f"{defect_json} {type(defect_json)}") + + # doc = json_to_docx(defect_json) + + # doc.save(output_dir) \ No newline at end of file diff --git a/__pycache__/Dt_report.cpython-310.pyc b/__pycache__/Dt_report.cpython-310.pyc new file mode 100644 index 0000000..b29a7cc Binary files /dev/null and b/__pycache__/Dt_report.cpython-310.pyc differ diff --git a/__pycache__/Jf_report.cpython-310.pyc b/__pycache__/Jf_report.cpython-310.pyc new file mode 100644 index 0000000..e0184b3 Binary files /dev/null and b/__pycache__/Jf_report.cpython-310.pyc differ diff --git a/build.spec b/build.spec index 802dc2d..a8f7c7e 100644 --- a/build.spec +++ b/build.spec @@ -7,7 +7,8 @@ a = Analysis( pathex=[], binaries=[], datas=[('./muban/*.docx', 'muban'), - ('./muban/*.jpg', 'muban')], + ('./muban/*.jpg', 'muban'), + ('./muban/*.png', 'muban')], hiddenimports=[], hookspath=[], hooksconfig={}, diff --git a/core/__pycache__/tables.cpython-310.pyc b/core/__pycache__/tables.cpython-310.pyc index 13f866b..6a3dfa4 100644 Binary files a/core/__pycache__/tables.cpython-310.pyc and b/core/__pycache__/tables.cpython-310.pyc differ diff --git a/core/tables.py b/core/tables.py index abeb0b8..bb19736 100644 --- a/core/tables.py +++ b/core/tables.py @@ -102,9 +102,7 @@ def apply_table_style(table, has_header_row=False, border_style=None, shading=No return True except Exception: return False - - - + def copy_table(source_table, target_doc, ifadjustheight=True, height = 1): """ Copy a table from one document to another. @@ -118,7 +116,6 @@ def copy_table(source_table, target_doc, ifadjustheight=True, height = 1): """ # Create a new table with the same dimensions new_table = target_doc.add_table(rows=len(source_table.rows), cols=len(source_table.columns)) - # Try to apply the same style try: if source_table.style: @@ -130,8 +127,9 @@ def copy_table(source_table, target_doc, ifadjustheight=True, height = 1): except: pass from docx.enum.table import WD_TABLE_ALIGNMENT - from docx.enum.table import WD_ALIGN_VERTICAL + from docx.enum.table import WD_ALIGN_VERTICAL, WD_ROW_HEIGHT_RULE from docx.shared import Pt, Inches, Cm, RGBColor + # Copy cell contents for i, row in enumerate(source_table.rows): for j, cell in enumerate(row.cells): @@ -144,18 +142,21 @@ def copy_table(source_table, target_doc, ifadjustheight=True, height = 1): new_table.cell(i,j).paragraphs[0].runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋') #设置中文字体 new_table.cell(i,j).paragraphs[0].paragraph_format.alignment = WD_TABLE_ALIGNMENT.CENTER new_table.cell(i,j).vertical_alignment = WD_ALIGN_VERTICAL.CENTER + #new_table.cell(i,j).width = cell.width + """ 待添加:如何让表格自适应大小(autofit目前不知为何没有作用) """ - if ifadjustheight: - new_table.rows[i].height = Cm(height) + #new_table.rows[i].height = row.height + #new_table.rows[i].height_rule = WD_ROW_HEIGHT_RULE.EXACTLY + + if not ifadjustheight: + new_table.auto_fit = True try: new_table = merge_tables(new_table) except Exception as e: print(f"合并表格失败:{e}") - from docx.shared import Inches - - return new_table + return target_doc from collections import deque diff --git a/muban/dt_header.png b/muban/dt_header.png new file mode 100644 index 0000000..21ac0d9 Binary files /dev/null and b/muban/dt_header.png differ diff --git a/muban/fengmian1.docx b/muban/fengmian1.docx index fc0e701..742527b 100644 Binary files a/muban/fengmian1.docx and b/muban/fengmian1.docx differ diff --git a/muban/fengmian2.docx b/muban/fengmian2.docx index c12fbf8..99dc4d6 100644 Binary files a/muban/fengmian2.docx and b/muban/fengmian2.docx differ diff --git a/muban/jf_fengmian.png b/muban/jf_fengmian.png new file mode 100644 index 0000000..d5b6f5d Binary files /dev/null and b/muban/jf_fengmian.png differ diff --git a/muban/jf_fengmian1.png b/muban/jf_fengmian1.png new file mode 100644 index 0000000..7d82804 Binary files /dev/null and b/muban/jf_fengmian1.png differ diff --git a/muban/jf_fengmian2.png b/muban/jf_fengmian2.png new file mode 100644 index 0000000..e644c2c Binary files /dev/null and b/muban/jf_fengmian2.png differ diff --git a/muban/jf_fengmian3.png b/muban/jf_fengmian3.png new file mode 100644 index 0000000..63314e8 Binary files /dev/null and b/muban/jf_fengmian3.png differ diff --git a/muban/jf_header.png b/muban/jf_header.png new file mode 100644 index 0000000..fc111b4 Binary files /dev/null and b/muban/jf_header.png differ diff --git a/muban/jf_hengxiang.doc b/muban/jf_hengxiang.doc new file mode 100644 index 0000000..b4f8dca Binary files /dev/null and b/muban/jf_hengxiang.doc differ diff --git a/muban/jf_table_title.docx b/muban/jf_table_title.docx new file mode 100644 index 0000000..c706585 Binary files /dev/null and b/muban/jf_table_title.docx differ diff --git a/muban/jinfeng_fengmian_luokuan.docx b/muban/jinfeng_fengmian_luokuan.docx new file mode 100644 index 0000000..b43b905 Binary files /dev/null and b/muban/jinfeng_fengmian_luokuan.docx differ diff --git a/muban/jinfeng_fengmian_name.docx b/muban/jinfeng_fengmian_name.docx new file mode 100644 index 0000000..1648349 Binary files /dev/null and b/muban/jinfeng_fengmian_name.docx differ diff --git a/muban/jinfeng_fengmian_renyuan.docx b/muban/jinfeng_fengmian_renyuan.docx new file mode 100644 index 0000000..afd6c4e Binary files /dev/null and b/muban/jinfeng_fengmian_renyuan.docx differ diff --git a/muban/jinfeng_fengmian_riqi.docx b/muban/jinfeng_fengmian_riqi.docx new file mode 100644 index 0000000..e26e16f Binary files /dev/null and b/muban/jinfeng_fengmian_riqi.docx differ diff --git a/muban/jinfeng_table.docx b/muban/jinfeng_table.docx new file mode 100644 index 0000000..927d1e2 Binary files /dev/null and b/muban/jinfeng_table.docx differ diff --git a/muban/quexian_liebiao.docx b/muban/quexian_liebiao.docx new file mode 100644 index 0000000..655fdf5 Binary files /dev/null and b/muban/quexian_liebiao.docx differ diff --git a/muban/wechat_2025-07-29_123404_038.png b/muban/wechat_2025-07-29_123404_038.png new file mode 100644 index 0000000..2250528 Binary files /dev/null and b/muban/wechat_2025-07-29_123404_038.png differ diff --git a/muban/xiangmugaikuo.docx b/muban/xiangmugaikuo.docx index 5662b94..6592bf1 100644 Binary files a/muban/xiangmugaikuo.docx and b/muban/xiangmugaikuo.docx differ diff --git a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版1.docx b/output/test.docx similarity index 84% rename from output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版1.docx rename to output/test.docx index 7bc2304..338a7fe 100644 Binary files a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版1.docx and b/output/test.docx differ diff --git a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版.docx b/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版.docx deleted file mode 100644 index a26a301..0000000 Binary files a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版.docx and /dev/null differ diff --git a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版2.docx b/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版2.docx deleted file mode 100644 index 0a6af68..0000000 Binary files a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版2.docx and /dev/null differ diff --git a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版6.docx b/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版6.docx deleted file mode 100644 index 421c0b3..0000000 Binary files a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月25日版6.docx and /dev/null differ diff --git a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月29日版35.docx b/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月29日版35.docx new file mode 100644 index 0000000..bd70562 Binary files /dev/null and b/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月29日版35.docx differ diff --git a/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月29日版36.docx b/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月29日版36.docx new file mode 100644 index 0000000..a6ad2b3 Binary files /dev/null and b/output/三峡能源阿城万兴风电场防雷通道检测项目项目叶片外观、内部、防雷检查报告一期012号2025年07月29日版36.docx differ diff --git a/tools/Spiretool.py b/tools/Spiretool.py new file mode 100644 index 0000000..5b3c7a3 --- /dev/null +++ b/tools/Spiretool.py @@ -0,0 +1,20 @@ +from spire.doc import * +from spire.doc.common import * + +# 加载第一个文档 +doc1 = Document() +doc1.LoadFromFile("./muban/jingfeng_1.docx") + +# 加载第二个文档 +doc2 = Document() +doc2.LoadFromFile("./muban/jf_hengxiang.doc") + +# 遍历 doc2 的所有节(使用 GetSections()) +for i in range(doc2.Sections.Count): + section = doc2.Sections.get_Item(i) # 使用 GetItem 获取 Section + doc1.Sections.Add(section.Clone()) # 克隆并添加到 doc1 + +# 保存合并后的文档 +doc1.SaveToFile("./output/MergedDocument.docx", FileFormat.Docx2019) +doc1.Close() +doc2.Close() \ No newline at end of file diff --git a/tools/__pycache__/argtool.cpython-310.pyc b/tools/__pycache__/argtool.cpython-310.pyc index a9f06d1..d430016 100644 Binary files a/tools/__pycache__/argtool.cpython-310.pyc and b/tools/__pycache__/argtool.cpython-310.pyc differ diff --git a/tools/__pycache__/content_tools.cpython-310.pyc b/tools/__pycache__/content_tools.cpython-310.pyc index 31dd9c4..ef43d10 100644 Binary files a/tools/__pycache__/content_tools.cpython-310.pyc and b/tools/__pycache__/content_tools.cpython-310.pyc differ diff --git a/tools/__pycache__/dataproccess.cpython-310.pyc b/tools/__pycache__/dataproccess.cpython-310.pyc index 76cfbdb..a9f5afa 100644 Binary files a/tools/__pycache__/dataproccess.cpython-310.pyc and b/tools/__pycache__/dataproccess.cpython-310.pyc differ diff --git a/tools/__pycache__/defines.cpython-310.pyc b/tools/__pycache__/defines.cpython-310.pyc index 528ad42..40c2c27 100644 Binary files a/tools/__pycache__/defines.cpython-310.pyc and b/tools/__pycache__/defines.cpython-310.pyc differ diff --git a/tools/__pycache__/document_tools.cpython-310.pyc b/tools/__pycache__/document_tools.cpython-310.pyc index a87900a..8eae654 100644 Binary files a/tools/__pycache__/document_tools.cpython-310.pyc and b/tools/__pycache__/document_tools.cpython-310.pyc differ diff --git a/tools/__pycache__/esay_docx_func.cpython-310.pyc b/tools/__pycache__/esay_docx_func.cpython-310.pyc new file mode 100644 index 0000000..c879edb Binary files /dev/null and b/tools/__pycache__/esay_docx_func.cpython-310.pyc differ diff --git a/tools/__pycache__/get_pictures.cpython-310.pyc b/tools/__pycache__/get_pictures.cpython-310.pyc index afc78ad..670ae6b 100644 Binary files a/tools/__pycache__/get_pictures.cpython-310.pyc and b/tools/__pycache__/get_pictures.cpython-310.pyc differ diff --git a/tools/__pycache__/json_to_docx.cpython-310.pyc b/tools/__pycache__/json_to_docx.cpython-310.pyc new file mode 100644 index 0000000..288ea61 Binary files /dev/null and b/tools/__pycache__/json_to_docx.cpython-310.pyc differ diff --git a/tools/argtool.py b/tools/argtool.py index 53440dd..5398be1 100644 --- a/tools/argtool.py +++ b/tools/argtool.py @@ -41,6 +41,8 @@ def parse_arguments(): help='封面图片 URL') parser.add_argument('--conclusion', dest='conclusion', help='报告总结') + parser.add_argument('--choose_template', '--ct', dest='choose_template', + help='选择模板,默认为迪特模板,目前支持枚举参数:DT,JF') return parser.parse_args() def merge_configs(default_cfg: Dict[str, Any], @@ -60,7 +62,8 @@ def merge_configs(default_cfg: Dict[str, Any], merged['json2']['shengcheng_dir'] = cli.output_dir for key in ('if_waibu', 'if_neibu', 'if_fanglei', 'userName', 'baogaoCheck', 'key_words', 'data_processor', - 'jiancha_renyuan', 'check_date', 'coverurl', 'conclusion'): + 'jiancha_renyuan', 'check_date', 'coverurl', 'conclusion' + 'choose_template'): val = getattr(cli, key, None) if val is not None: merged['json2'][key] = val @@ -101,6 +104,6 @@ def get_default_config() -> Dict[str, Dict[str, Any]]: "if_docx_inspection_picture": True, "if_docx_defect_picture": True, "if_docx_conclusion": True, + "choose_template" : "JF" } } - diff --git a/tools/content_tools.py b/tools/content_tools.py index a5b8905..4c76a88 100644 --- a/tools/content_tools.py +++ b/tools/content_tools.py @@ -396,8 +396,10 @@ async def add_picture_to_table( import requests from io import BytesIO from PIL import Image +from docx.enum.text import WD_ALIGN_PARAGRAPH -async def add_picture(filename: str, image_path: str, width: Optional[float] = None, height: Optional[float] = None) -> str: + +def add_picture(filename: str, image_path: str, width: Optional[float] = None, height: Optional[float] = None, is_center: Optional[bool] = False) -> str: """添加一个图片到文档中(支持本地路径或 URL) Args: @@ -416,7 +418,8 @@ async def add_picture(filename: str, image_path: str, width: Optional[float] = N try: doc = Document(abs_filename) - + para = doc.add_paragraph() + run = para.add_run() # 处理 URL 图片 if is_url: try: @@ -430,10 +433,11 @@ async def add_picture(filename: str, image_path: str, width: Optional[float] = N # 添加到文档 if width: - doc.add_picture(image_bytes, width=Inches(width), height=Inches(height)) + run.add_picture(image_bytes, width=Inches(width), height=Inches(height)) else: - doc.add_picture(image_bytes) - + run.add_picture(image_bytes) + if is_center: + para.alignment = WD_ALIGN_PARAGRAPH.CENTER doc.save(abs_filename) return f"Picture from URL {image_path} added to {filename}" except Exception as url_error: @@ -456,9 +460,11 @@ async def add_picture(filename: str, image_path: str, width: Optional[float] = N # 添加到文档 try: if width: - doc.add_picture(abs_image_path, width=Inches(width), height=Inches(height)) + run.add_picture(abs_image_path, width=Inches(width), height=Inches(height)) else: - doc.add_picture(abs_image_path) + run.add_picture(abs_image_path) + if is_center: + para.alignment = WD_ALIGN_PARAGRAPH.CENTER doc.save(abs_filename) return f"Picture {image_path} added to {filename}" except Exception as inner_error: @@ -619,7 +625,7 @@ async def delete_paragraph(filename: str, paragraph_index: int) -> str: return f"Failed to delete paragraph: {str(e)}" -async def search_and_replace(filename: str, find_text: str, replace_text: str) -> str: +def search_and_replace(filename: str, find_text: str, replace_text: str) -> str: """替换所有find_text为replace_text Args: diff --git a/tools/dataproccess.py b/tools/dataproccess.py index a3342ec..8e964b6 100644 --- a/tools/dataproccess.py +++ b/tools/dataproccess.py @@ -130,13 +130,226 @@ def safe_get(data, *keys, default=None): else: return default +def is_frozen(): + return hasattr(sys, 'frozen') or hasattr(sys, '_MEIPASS') + def get_resource_path(relative_path): - """ 获取打包后资源的绝对路径 """ - try: - # PyInstaller创建的临时文件夹 + """ 获取资源的路径 """ + if is_frozen(): base_path = sys._MEIPASS - except AttributeError: - # 正常开发环境 + else: base_path = os.path.abspath(".") - return os.path.join(base_path, relative_path) \ No newline at end of file + return os.path.join(base_path, relative_path) + +def get_defedct_info(defect_dict): + """获取按缺陷类型分类的缺陷信息 + + Args: + defect_dict (dict): + {'叶片1': + {'DEFECT':[{'record': + { + 'defectId': '02d892a178a82561bb565559102c7a58', + 'imageId': '41543f531be24522b7bec741b9a483a2', + 'defectName': '手动添加的缺陷1', + 'defectCode': None, + 'partName': '叶片1', + 'defectTypeLabel': '表面裂纹', + 'defectType': 'bmlw', + 'defectLevelLabel': '轻微缺陷', + 'defectLevel': 'SLIGHT', + 'defectPosition': '', + 'description': '手动添加的缺陷,请填写详细描述', + 'repairIdea': '建议进行进一步检查', + 'labelInfo': None, + 'markInfo': {'label': None, 'clsId': None, 'bbox': None, 'confidence': 0.0}, + 'imagePath': '/static/image/temp/out-work/12bc30fb209f3af3bf530541c5b062bd/F02_机组10060_叶片,叶片PS面距叶根15.5米处轴向裂纹,轴向:260mm,弦向:20mm。DJI_20250526090227_0052_Z.png' + }, + ... + },] + } + }, + ... + Returns: + result (dict): + { + '叶片1': { #按叶片分记录 + '表面裂纹': { 'SLIGHT': #按类型分等级,等级从'defectLevel'获取 + [ #按等级分记录 + { + record:{...} + }, #一张图片一个记录 + ... + ] + ... + } + } + """ + result = {} + for part_name, part_info in defect_dict.items(): + if part_name not in result: + result[part_name] = {} + + for defect_type, defect_list in part_info.items(): + for defect_dict in defect_list: + record = defect_dict['record'] + type_label = record['defectTypeLabel'] + level = record['defectLevel'] + + if type_label not in result[part_name]: + result[part_name][type_label] = {} + + if level not in result[part_name][type_label]: + result[part_name][type_label][level] = [] + + result[part_name][type_label][level].append(defect_dict) + + return result + +def get_defect_json(defect_info: dict, Y_code: list[str], turbine_code: str) -> dict: + """将缺陷信息转换为表格形式的JSON格式 + + Args: + defect_info: 结构为 {叶片1/2/3: {缺陷类型: [{'record': {...}}, ...]}} + Y_code: 叶片编号列表,如 ['1001', '1002', '1003'] + turbine_code: 机组编号,如 'G01' + + Returns: + { + "type": "table", + "content": { + "rows": 总行数, + "cols": 4, + "merged_cells": [合并信息], + "cells": [表格内容] + } + } + """ + # 初始化表格 + cells = [] + merged_cells = [] + + # 默认设置 + default_border = { + "top": {"style": "single", "size": "4", "color": "000000"}, + "left": {"style": "single", "size": "4", "color": "000000"}, + "bottom": {"style": "single", "size": "4", "color": "000000"}, + "right": {"style": "single", "size": "4", "color": "000000"} + } + + # 1. 计算总行数(先计算再创建表头) + total_defect_rows = sum(len(defects) for blade_defects in defect_info.values() + for defects in blade_defects.values()) + total_rows = 1 + total_defect_rows # 表头 + 数据行 + + # 2. 创建表头行(第0行) + header_row = [ + create_cell(0, 0, "机组号", default_border), + create_cell(0, 1, "叶片号", default_border), + create_cell(0, 2, "损坏描述", default_border), + create_cell(0, 3, "缺陷面积", default_border) + ] + cells.append(header_row) + + # 3. 填充数据行(从第1行开始) + current_data_row = 1 + + # 合并机组号(如果有多行数据) + if total_defect_rows > 1: + merged_cells.append({ + "start_row": 1, # 第2行(0-based中的1) + "start_col": 0, + "end_row": total_rows - 1, # 最后一行(0-based) + "end_col": 0 + }) + + # 填充机组号(仅第一行数据行) + if total_defect_rows > 0: + first_data_row = [ + create_cell(1, 0, turbine_code, default_border, + is_merged=total_defect_rows>1, is_primary=True), + None, None, None + ] + cells.append(first_data_row) + + # 按叶片顺序填充数据 + for blade_idx, (blade_key, blade_defects) in enumerate(defect_info.items(), 1): + blade_code = Y_code[blade_idx - 1] + blade_defect_count = sum(len(defects) for defects in blade_defects.values()) + + if blade_defect_count > 1: + merged_cells.append({ + "start_row": current_data_row, # 当前行(0-based) + "start_col": 1, + "end_row": current_data_row + blade_defect_count - 1, # 结束行(0-based) + "end_col": 1 + }) + + # 填充缺陷数据 + for defect_type, records in blade_defects.items(): + for record in records: + # 确保不超过总行数 + if current_row >= total_rows: + break + + description = record.get('record', {}).get('损坏描述', '待填写') + area = record.get('record', {}).get('缺陷面积', '待填写') + + is_primary = (not merged_cells or + current_row == merged_cells[-1]["start_row"]) + + row_data = [ + None, # 机组号(已合并) + create_cell(current_row, 1, blade_code, default_border, + is_merged=blade_defect_count>1, is_primary=is_primary), + create_cell(current_row, 2, str(description), default_border), + create_cell(current_row, 3, str(area), default_border) + ] + cells.append(row_data) + current_row += 1 + + # 填充空单元格 + for row_idx, row in enumerate(cells): + for col_idx in range(4): + if row[col_idx] is None: + row[col_idx] = create_cell(row_idx, col_idx, "", default_border) + + # 最终行数检查(调试用) + print(f"调试信息: 预期行数={total_rows}, 实际行数={len(cells)}") + print(f"缺陷信息统计: 叶片数={len(defect_info)}, 总缺陷数={total_defect_rows}") + + return [{ + "type": "table", + "content": { + "rows": len(cells), # 使用实际行数而非计算行数 + "cols": 4, + "merged_cells": merged_cells, + "cells": cells + } + }] + +def create_cell(row, col, text, border, is_merged=False, is_primary=False): + """创建标准化的单元格结构""" + return { + "row": row, + "col": col, + "is_merged": is_merged, + "alignment": "center", + "border": border, + "merge_info": {"is_primary": is_primary}, + "content": [{ + "alignment": "center", + "runs": [{ + "text": text, + "font": { + "name": "宋体", + "size": 12, + "bold": False, + "italic": False, + "underline": False, + "color": {"r": 0, "g": 0, "b": 0} + } + }] + }] + } \ No newline at end of file diff --git a/tools/defines.py b/tools/defines.py index 92f5b1f..e04190d 100644 --- a/tools/defines.py +++ b/tools/defines.py @@ -1,21 +1,7 @@ -""" -缺陷图目录格式: - -缺陷图期望格式 _隔开 - -外部内部命名格式都如下: -图片名:xuhao_缺陷类型_缺陷位置_缺陷尺寸_可见程度_紧急程度_危重等级_维修建议 -例:涂层损伤_叶片ps面距叶根3m处_缺陷尺寸弦向100mm,轴向800mm_轻微_紧急_重要_建议打磨维修 -每个的选项:见我发的图 - -防雷: -例:轮毂至塔基导通阻值_169mΩ -缺陷例:轮毂至塔基未导通 #即标明未导通即可 -""" - DEFAULT_BASE_INFO = { #项目基本信息 "turbine_id" : None, } + DEFAULT_BAOGAO_INFO = { #目录 'shengcheng_dir': "", #报告生成的路径 @@ -38,11 +24,6 @@ DEFAULT_BAOGAO_INFO = { "check_date" : None, } -class JIANCHA_ENUM : - class WAIBU: - PART = "无人机外部高精度飞行" - #NEIRONG = - class SHIGONG_FANGAN_ENUM : class WAIBU: GONGZUO_NEIRONG = "无人机叶片外观巡检" @@ -72,87 +53,55 @@ class SHIGONG_FANGAN_ENUM : SHIGONG_FANGAN = "" FEISHOURENYUAN_PEIZHI = "1人;主检飞手1人" LUNGUZUOYERENYUAN_PEIZHI = "2人;轮毂作业检查2人" -oneproject = { - "status": 200, - "data": { - "projectId": "96e0debf78187300f144d7f3450a2477", - "projectName": "三峡能源阿城万兴风电场防雷通道检测项目", - "coverUrl": "", - "farmName": "三峡能源阿城万兴风电场", - "farmAddress": "哈尔滨市阿城区", - "client": "辽宁信达检测有限公司", - "clientContact": "李经理", - "clientPhone": "13504783720", - "inspectionUnit": "武汉市迪特影像科技有限公司", - "inspectionContact": "吴名州", - "inspectionPhone": "18807109269", - "scale": "", - "turbineModel": "", - "constructorIds": "5709ccfece2685090ff700a3469f2539,a76d78f1325deda1790a12bdad4aad4e", - "auditorId": "ca37c4337df8673a5c045b6c25acf74a", - "qualityOfficerId": "862e027910c2562d2b67d88ec33d77ba", - "projectManagerId": "fbaa9e0aecf2ce287138c38a4b654085", - "constructionTeamLeaderId": "", - "status": 0, - "startDate": "", - "endDate": "", - "constructorName": "", - "auditorName": "李四", - "qualityOfficerName": "辛奇", - "projectManagerName": "张三", - "constructionTeamLeaderName": "", - "statusLabel": "待施工" + +from docx.oxml.ns import qn +from docx.shared import Pt + + +class TEMPLATE_HEADER: + + class JINFENG_HEADER: + ENUM = 'JF' + PIC_DIR = './muban/jf_header.png' + PARA =' Q/GW 3LC-FJFW21-2022-BY14' + QN = qn('w:eastAsia') + FONT = 'Arial' + PT = Pt(9) + + class DT_HEADER: + ENUM = 'DT' + PIC_DIR = './muban/dt_header.png' + PARA = '' + QN = qn('w:eastAsia') + FONT = '宋体(中文正文)' + PT = Pt(9) + +MUBAN_DIR = 'muban' + +JF_HEIGHT = 297 +JF_WIDTH = 210 +JF_L_MARGIN = 20 +JF_R_MARGIN = 20 +JF_T_MARGIN = 10 +JF_B_MARGIN = 10 + +JINFENG_COMPANY = "金风科技股份有限公司" + +#json表格样式宏定义 +STYLE_CONFIG = { + "alignment": "center", + "font": { + "name": "宋体", + "size": 9, + "bold": False, }, - "msg": "", - "code": 200, - "success": True - } -onejizu = { - "status": 200, - "data": [ - { - "turbineId": "183463dbf40d9278549a76b82b175dd9", - "projectId": "96e0debf78187300f144d7f3450a2477", - "projectName": "三峡能源阿城万兴风电场防雷通道检测项目", - "turbineName": "一期012号", - "turbineCode": "00000", - "turbineDesc": "一期012号,全新设备", - "turbineManufacturer": "", - "turbineModel": "", - "turbineCoverUrl": "" - } - ], - "msg": "", - "code": 200, - "success": True -} -yepian = { - "status": 200, - "data": [ - { - "partId": "12bc30fb209f3af3bf530541c5b062bc", - "projectId": "96e0debf78187300f144d7f3450a2477", - "projectName": "三峡能源阿城万兴风电场防雷通道检测项目", - "turbineId": "183463dbf40d9278549a76b82b175dd9", - "turbineName": "一期012号", - "partName": "叶片2", - "partCode": "0001", - "partType": "VANE-2", - "partTypeLabel": "叶片2" + "border": { + "top": {"style": "single", "size": "4", "color": "000000"}, + "bottom": {"style": "single", "size": "4", "color": "000000"}, + "left": {"style": "single", "size": "4", "color": "000000"}, + "right": {"style": "single", "size": "4", "color": "000000"} }, - { - "partId": "12bc30fb209f3af3bf530541c5b062bd", - "projectId": "96e0debf78187300f144d7f3450a2477", - "projectName": "三峡能源阿城万兴风电场防雷通道检测项目", - "turbineId": "183463dbf40d9278549a76b82b175dd9", - "turbineName": "一期012号", - "partName": "叶片1", - "partCode": "0000", - "partType": "VANE-1", - "partTypeLabel": "叶片1" + "shading": { + "color": "FFFFFF" } - ], - "msg": "", - "code": 200, - "success": True -} +} \ No newline at end of file diff --git a/tools/document_tools.py b/tools/document_tools.py deleted file mode 100644 index 119835f..0000000 --- a/tools/document_tools.py +++ /dev/null @@ -1,729 +0,0 @@ -""" -Document creation and manipulation tools for Word Document Server. -""" -import os -import json, re -from typing import Dict, List, Optional, Any -from docx import Document - -from utils.file_utils import check_file_writeable, ensure_docx_extension, create_document_copy -from utils.document_utils import get_document_properties, extract_document_text, get_document_structure -from core.styles import ensure_heading_style, ensure_table_style -from docx.oxml.shared import qn -from docx.oxml import OxmlElement -from tools.content_tools import search_and_replace,add_picture_to_table -from tools.Get_Json import get_full_picture_url -from tools.get_pictures import resize_and_reduce_quality - -async def create_document(filename: str, title: Optional[str] = None, author: Optional[str] = None) -> str: - """创建一个包含可选元数据的新Word文档。 - - 参数: - filename: 要创建的文档名称(带或不带.docx扩展名) - title: 可选标题 - author: 可选作者 - """ - filename = ensure_docx_extension(filename) - - # Check if file is writeable - is_writeable, error_message = check_file_writeable(filename) - if not is_writeable: - return f"Cannot create document: {error_message}" - - try: - doc = Document() - - # Set properties if provided - if title: - doc.core_properties.title = title - if author: - doc.core_properties.author = author - - # Ensure necessary styles exist - ensure_heading_style(doc) - ensure_table_style(doc) - # 更改纸张大小为A4 - from docx.shared import Mm, Inches - sections = doc.sections - for section in sections: - section.page_height = Mm(297) - section.page_width = Mm(210) - section.left_margin = Inches(0.94) - section.right_margin = Inches(0.94) - # Save the document - doc.save(filename) - - return f"Document {filename} created successfully" - except Exception as e: - return f"Failed to create document: {str(e)}" - - -async def get_document_info(filename: str) -> str: - """获得文档信息 - - Args: - filename: 目标文档 - """ - filename = ensure_docx_extension(filename) - - if not os.path.exists(filename): - return f"Document {filename} does not exist" - - try: - properties = get_document_properties(filename) - return json.dumps(properties, indent=2) - except Exception as e: - return f"Failed to get document info: {str(e)}" - - -async def get_document_text(filename: str) -> str: - """获得文档的所有文本 - - Args: - filename: 目标文档 - """ - filename = ensure_docx_extension(filename) - - return extract_document_text(filename) - - -async def get_document_outline(filename: str) -> str: - """获得文档的所有结构信息 - - Args: - filename: 目标文档 - """ - filename = ensure_docx_extension(filename) - - structure = get_document_structure(filename) - return json.dumps(structure, indent=2) - - -async def list_available_documents(directory: str = ".") -> str: - """列出目录下所有Word文档 - - Args: - directory: 目录 - """ - try: - if not os.path.exists(directory): - return f"Directory {directory} does not exist" - - docx_files = [f for f in os.listdir(directory) if f.endswith('.docx')] - - if not docx_files: - return f"No Word documents found in {directory}" - - result = f"Found {len(docx_files)} Word documents in {directory}:\n" - for file in docx_files: - file_path = os.path.join(directory, file) - size = os.path.getsize(file_path) / 1024 # KB - result += f"- {file} ({size:.2f} KB)\n" - - return result - except Exception as e: - return f"Failed to list documents: {str(e)}" - - -async def copy_document(source_filename: str, destination_filename: Optional[str] = None) -> str: - """创建文档的副本 - - Args: - source_filename: 源文档路径 - destination_filename: 目标文档路径,为空则为当前目录 - """ - source_filename = ensure_docx_extension(source_filename) - - if destination_filename: - destination_filename = ensure_docx_extension(destination_filename) - - success, message, new_path = create_document_copy(source_filename, destination_filename) - if success: - return message - else: - return f"Failed to copy document: {message}" - -def add_documents(target_filename: str, source_filename: str) -> str: - """将源文档(文本)添加到目标文档尾部 - Args: - target_doc: 目标文档 - source_filename: 源文档路径 - """ - target_doc = Document(target_filename) - source_filename = ensure_docx_extension(source_filename) - source_doc = Document(source_filename) - for source_paragraph in source_doc.paragraphs: - new_paragraph = target_doc.add_paragraph(source_paragraph.text) - new_paragraph.style = target_doc.styles['Normal'] # Default style - - #获取合并等样式2025427 - new_paragraph.alignment = source_paragraph.alignment - print(f"Source paragraph alignment: {source_paragraph.alignment}") - - # Try to match the style if possible - try: - if source_paragraph.style and source_paragraph.style.name in target_doc.styles: - new_paragraph.style = target_doc.styles[source_paragraph.style.name] - except Exception as e: - print(f"Failed to apply style: {e}") - - # Copy run formatting - for i, run in enumerate(source_paragraph.runs): - if i < len(new_paragraph.runs): - new_run = new_paragraph.runs[i] - # Copy basic formatting - new_run.bold = run.bold - new_run.italic = run.italic - new_run.underline = run.underline - #添加同时合并字体2025427 - new_run.font.name = run.font.name - rPr = new_run.element.get_or_add_rPr() - rFonts = rPr.get_or_add_rFonts() - # 检查 run.font.name 是否为 None - if run.font.name is None: - # 设置默认的中文字体名称 - run.font.name = '宋体 (中文正文)' # 或者使用其他你喜欢的中文字体 - rFonts.set(qn('w:eastAsia'), run.font.name) - new_run.font.color.rgb = run.font.color.rgb - - # Font size if specified - if run.font.size: - new_run.font.size = run.font.size - target_doc.save(target_filename) - return f"{target_filename}添加{source_filename}成功" - - - -def write_table(target_filename: str, rows: int, cols: int, table_num: int, data: Optional[List[List[str]]] = None, ifadjustheight: Optional[bool] = True, height: Optional[float] = 1, key_words: re.Pattern[str] = None, ALIGMENT: Optional[str] = 'CENTER') -> Document: - """填写word文档里的表格,返回填写后的文档 - - Args: - target_filename: 目标文档路径 - rows: 表格行数 - cols: 表格列数 - table_num: 表格序号 - data: 表格数据,二维列表,每个单元格为字符串 - ifadjustheight: bool,为真则表格行高自动调整 - """ - target_filename = ensure_docx_extension(target_filename) - # Check if target file is writeable - is_writeable, error_message = check_file_writeable(target_filename) - if not is_writeable: - return f"Cannot create target document: {error_message}" - - try: - target_filename = ensure_docx_extension(target_filename) - target_doc = Document(target_filename) - except Exception as e: - print(f"获取{target_filename}失败:{str(e)}") - - # Try to set the table style - try: - target_doc.tables[table_num].style = 'Table Grid' - except KeyError as k: - pass - except Exception as e: - print(f"{target_doc}最后一个表格更改样式失败: {str(e)}") - - print("开始写入表格") - from docx.enum.table import WD_TABLE_ALIGNMENT - from docx.enum.table import WD_ALIGN_VERTICAL - from docx.shared import Pt, Inches, Cm, RGBColor - try: - if data: - for i, row_data in enumerate(data): - if i >= rows + 1: - break - for j, cell_text in enumerate(row_data): - if j >= cols + 1: - break - if str(cell_text) == "": continue - print(f"在[{i},{j}]处写入{str(cell_text)}") - target_doc.tables[table_num].cell(i,j).text = str(cell_text) - print(key_words, cell_text) - if key_words and key_words.search(str(cell_text)): - print(f'{cell_text}包含关键之,已置红') - target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 0, 0) - target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0].font.name = "Times New Roman" #设置英文字体 - target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0].font.size = Pt(10.5) # 字体大小 - target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋') #设置中文字体 - if ALIGMENT == 'CENTER': - target_doc.tables[table_num].cell(i,j).paragraphs[0].paragraph_format.alignment = WD_TABLE_ALIGNMENT.CENTER - elif ALIGMENT == 'LEFT': - target_doc.tables[table_num].cell(i,j).paragraphs[0].paragraph_format.alignment = WD_TABLE_ALIGNMENT.LEFT - target_doc.tables[table_num].cell(i,j).vertical_alignment = WD_ALIGN_VERTICAL.CENTER - if ifadjustheight: - target_doc.tables[table_num].rows[i].height = Cm(height) - except Exception as e: - print(f"写入{target_filename}tables.cell({i},{j})失败:{str(e)}") - print("表格写入完成") - return target_doc - -def set_document_para(target_doc: Document) -> Document: - """设置文档的段落格式 - """ - paragraphs_to_remove = [] - for i, paragraph in enumerate(target_doc.paragraphs): - if i <= 11: - continue - if not paragraph.text.strip(): - paragraphs_to_remove.append(paragraph) - - for paragraph in paragraphs_to_remove: - p = paragraph._element - p.getparent().remove(p) - - return target_doc - - -async def add_table_to_document(target_filename: str, source_filename: str, rows: int, cols: int, table_num: int, data: Optional[List[List[str]]] = None, ifadjustheight: Optional[bool] = True, height: Optional[float] = 1, key_words: re.Pattern[str] = None, ALIGMENT: Optional[str] = 'CENTER') -> str: - """复制源文件中的文字与表格(先文字后表格格式)到目标文档 - Args: - target_filename: 目标文档路径 - source_doc: 源文档路径 - rows: 表格行数 - cols: 表格列数 - table_num: 表格序号 - data: 表格数据,二维列表,每个单元格为字符串 - ifadjustheight: bool,为真则表格行高自动调整 - key_words: list, 关键字 - """ - target_filename = ensure_docx_extension(target_filename) - source_filename = ensure_docx_extension(source_filename) - source_doc = Document(source_filename) - - target_doc = Document(target_filename) - try: - # Copy all paragraphs - for paragraph in source_doc.paragraphs: - # Create a new paragraph with the same text and style - new_paragraph = target_doc.add_paragraph(paragraph.text) - new_paragraph.style = target_doc.styles['Normal'] # Default style - #获取合并等样式2025427 - new_paragraph.alignment = paragraph.alignment - - # 复制段落分页属性 - new_paragraph.paragraph_format.page_break_before = paragraph.paragraph_format.page_break_before - # Try to match the style if possible - try: - if paragraph.style and paragraph.style.name in target_doc.styles: - new_paragraph.style = target_doc.styles[paragraph.style.name] - except: - pass - - - # Copy run formatting - for i, run in enumerate(paragraph.runs): - if i < len(new_paragraph.runs): - new_run = new_paragraph.runs[i] - # Copy basic formatting - new_run.bold = run.bold - new_run.italic = run.italic - new_run.underline = run.underline - #添加同时合并字体2025427 - new_run.font.name = run.font.name - rPr = new_run.element.get_or_add_rPr() - rFonts = rPr.get_or_add_rFonts() - # 检查 run.font.name 是否为 None - if run.font.name is None: - # 设置默认的中文字体名称 - run.font.name = '宋体(中文正文)' # 或者使用其他你喜欢的中文字体 - rFonts.set(qn('w:eastAsia'), run.font.name) - new_run.font.color.rgb = run.font.color.rgb - - - # Font size if specified - if run.font.size: - new_run.font.size = run.font.size - - # 复制分页符(处理w:br标签) - for element in run._element: - if element.tag.endswith('br'): - br_type = element.get(qn('type'), '') - if br_type == 'page': - new_br = OxmlElement('w:br') - new_br.set(qn('type'), 'page') - new_run._element.append(new_br) - - except Exception as e: - print(f"添加表格前文章失败:{str(e)}") - - try:# Copy all tables - from core.tables import copy_table - copy_table(source_doc.tables[0], target_doc, ifadjustheight, height) - except Exception as e: - print(f"添加表格失败:{str(e)}") - print(f"{target_doc}写入表格{source_doc.tables[0]}成功") - target_doc = set_document_para(target_doc) - target_doc.save(target_filename) - target_doc = Document(target_filename) - try: - target_doc = write_table(target_filename, rows, cols, table_num, data, ifadjustheight, height, key_words, ALIGMENT) - except Exception as e: - print(f"{target_filename}写入{data}失败:{str(e)}") - target_doc.save(target_filename) - return target_doc,f"{target_filename}添加表格{source_doc}成功" - -async def add_table_and_replace(target_filename: str, source_filename: str, ifadjustheight: Optional[bool] = True, list_to_replace: dict = {}, height: Optional[float] = 1): - """复制源文件中的文字与表格(先文字后表格格式)到目标文档 - Args: - target_filename: 目标文档路径 - source_doc: 源文档路径 - ifadjustheight: bool,为真则表格行高自动调整 - list_to_replace: dict, 待替换内容和替换内容 - """ - target_filename = ensure_docx_extension(target_filename) - source_filename = ensure_docx_extension(source_filename) - source_doc = Document(source_filename) - - target_doc = Document(target_filename) - try: - # Copy all paragraphs - for paragraph in source_doc.paragraphs: - # Create a new paragraph with the same text and style - new_paragraph = target_doc.add_paragraph(paragraph.text) - new_paragraph.style = target_doc.styles['Normal'] # Default style - #获取合并等样式2025427 - new_paragraph.alignment = paragraph.alignment - - # 复制段落分页属性 - new_paragraph.paragraph_format.page_break_before = paragraph.paragraph_format.page_break_before - # Try to match the style if possible - try: - if paragraph.style and paragraph.style.name in target_doc.styles: - new_paragraph.style = target_doc.styles[paragraph.style.name] - except: - pass - # Copy run formatting - for i, run in enumerate(paragraph.runs): - if i < len(new_paragraph.runs): - new_run = new_paragraph.runs[i] - # Copy basic formatting - new_run.bold = run.bold - new_run.italic = run.italic - new_run.underline = run.underline - #添加同时合并字体2025427 - new_run.font.name = run.font.name - rPr = new_run.element.get_or_add_rPr() - rFonts = rPr.get_or_add_rFonts() - # 检查 run.font.name 是否为 None - if run.font.name is None: - # 设置默认的中文字体名称 - run.font.name = '宋体(中文正文)' # 或者使用其他你喜欢的中文字体 - rFonts.set(qn('w:eastAsia'), run.font.name) - new_run.font.color.rgb = run.font.color.rgb - - - # Font size if specified - if run.font.size: - new_run.font.size = run.font.size - - # 复制分页符(处理w:br标签) - for element in run._element: - if element.tag.endswith('br'): - br_type = element.get(qn('type'), '') - if br_type == 'page': - new_br = OxmlElement('w:br') - new_br.set(qn('type'), 'page') - new_run._element.append(new_br) - except Exception as e: - print(f"添加表格前文章失败:{str(e)}") - try:# Copy all tables - from core.tables import copy_table - copy_table(source_doc.tables[0], target_doc, ifadjustheight, height) - target_doc.save(target_filename) - except Exception as e: - print(f"添加表格失败:{str(e)}") - for find_text, replace_text in list_to_replace.items(): - print(await search_and_replace(target_filename, find_text, replace_text)) - -async def merge_documents(target_filename: str, source_filenames: List[str], add_page_breaks: bool = True) -> str: - """合并文档(文本) 表格会添加到最后 - - Args: - target_filename: 合并后文档路径 - source_filenames: 源文档路径(列表) - add_page_breaks: bool,为真则每个源文档中间加入分页符 - """ - from core.tables import copy_table - - target_filename = ensure_docx_extension(target_filename) - - # Check if target file is writeable - is_writeable, error_message = check_file_writeable(target_filename) - if not is_writeable: - return f"Cannot create target document: {error_message}" - - # Validate all source documents exist - missing_files = [] - for filename in source_filenames: - doc_filename = ensure_docx_extension(filename) - if not os.path.exists(doc_filename): - missing_files.append(doc_filename) - - if missing_files: - return f"Cannot merge documents. The following source files do not exist: {', '.join(missing_files)}" - - try: - # Create a new document for the merged result - target_doc = Document() - - # Process each source document - for i, filename in enumerate(source_filenames): - doc_filename = ensure_docx_extension(filename) - source_doc = Document(doc_filename) - - # Add page break between documents (except before the first one) - if add_page_breaks and i > 0: - target_doc.add_page_break() - - # Copy all paragraphs - for paragraph in source_doc.paragraphs: - # Create a new paragraph with the same text and style - new_paragraph = target_doc.add_paragraph(paragraph.text) - new_paragraph.style = target_doc.styles['Normal'] # Default style - #获取合并等样式2025427 - new_paragraph.alignment = paragraph.alignment - - # Try to match the style if possible - try: - if paragraph.style and paragraph.style.name in target_doc.styles: - new_paragraph.style = target_doc.styles[paragraph.style.name] - except: - pass - - - # Copy run formatting - for i, run in enumerate(paragraph.runs): - if i < len(new_paragraph.runs): - new_run = new_paragraph.runs[i] - # Copy basic formatting - new_run.bold = run.bold - new_run.italic = run.italic - new_run.underline = run.underline - #添加同时合并字体2025427 - new_run.font.name = run.font.name - rPr = new_run.element.get_or_add_rPr() - rFonts = rPr.get_or_add_rFonts() - # 检查 run.font.name 是否为 None - if run.font.name is None: - # 设置默认的中文字体名称 - run.font.name = '宋体(中文正文)' # 或者使用其他你喜欢的中文字体 - rFonts.set(qn('w:eastAsia'), run.font.name) - new_run.font.color.rgb = run.font.color.rgb - - - # Font size if specified - if run.font.size: - new_run.font.size = run.font.size - - # Copy all tables - for table in source_doc.tables: - copy_table(table, target_doc) - - # Save the merged document - target_doc.save(target_filename) - return f"Successfully merged {len(source_filenames)} documents into {target_filename}" - except Exception as e: - return f"Failed to merge documents: {str(e)}" - - -async def right_align_last_three_para(target_filename: str) -> str: - """右对齐最后三个段落 - - Args: - target_filename: 目标文档路径 - """ - target_filename = ensure_docx_extension(target_filename) - - # Check if target file is writeable - is_writeable, error_message = check_file_writeable(target_filename) - if not is_writeable: - return f"Cannot right align paragraphs: {error_message}" - - try: - # Open the target document - target_doc = Document(target_filename) - - # Get the last three paragraphs - paragraphs = target_doc.paragraphs[-3:] - - # Set the alignment of each paragraph to right - from docx.enum.text import WD_ALIGN_PARAGRAPH - for paragraph in paragraphs: - paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT - - # Save the modified document - target_doc.save(target_filename) - return f"Successfully right aligned the last three paragraphs in {target_filename}" - except Exception as e: - return f"Failed to right align paragraphs: {str(e)}" - -async def add_dynamic_table(output_doc, output_dir, table_num, TABLES, JIANCHA_XIANGQING_DIR, PICTURES, row, col, i, FLAG, xuhao): - """创建动态表 - - Args: - output_doc (Document): 文档对象 - output_dir (str): 输出目录 - table_num (int): 表格序号 - TABLES (list): 表格数据 - JIANCHA_XIANGQING_DIR (str): 检查详情表目录 - PICTURES (dict): 图片数据字典,键为表索引,值为图片路径列表 - row (int): 行数 - col (int): 列数 - i (int): 表格序号 - FLAG: 其他标志 - - Returns: - tuple: (i, table_num) 更新后的表格序号和表格数量 - """ - for table_idx, Table in enumerate(TABLES): - print(Table) - output_doc, message = await add_table_to_document(output_dir, JIANCHA_XIANGQING_DIR, row, col, i, Table, FLAG) - print(message) - # 获取当前表格对应的图片 - current_table_pictures = PICTURES.get(table_idx, []) - print(f"开始处理图片列表: {current_table_pictures}") - - for picturedir in current_table_pictures: - if picturedir is None: - print(f"图片路径为空,跳过") - continue - try: - print(f"添加 {picturedir} {type(picturedir)}到表格{table_idx}") - resize_and_reduce_quality(picturedir, picturedir) - await add_picture_to_table(output_doc, output_dir, 4, 0, picturedir, i, 4.7232) - except Exception as e: - print(f"添加图片失败:{e}") - - print(await search_and_replace(output_dir, 'tupian_xuhao', f'{xuhao}')) - table_num += 1 - i += 1 - xuhao += 1 - return i, table_num, xuhao - -async def process_images_table(data_dict, output_dir, start_i, JIANCHA_NEIRONG_PICTURES_TABLE, key_words = None): - """添加对应表格且填写图片名与插入图片 - - Args: - data_dict (dict): dict内容,图片:图片路径 - output_dir (str): 输出路径 - start_i (int): 总表格数量 - JIANCHA_NEIRONG_PICTURES_TABLE (str): 二维表模板路径 - - Returns: - int: 最后使用的表格序号 - """ - items = list(data_dict.items()) - picture_num = len(items) - line_index = 0 - picture_index = 0 - i = start_i - - for content_row in range(((picture_num + 2) // 3) * 2): - if content_row % 2 == 1: - # 文字行(从 items 取图片名) - JIANCHA_NEIRONG_TEXT = [["" for _ in range(3)] for _ in range(1)] # 1行3列 - for k in range(1): # 只有1行 - for l in range(3): - if line_index >= picture_num: - break - JIANCHA_NEIRONG_TEXT[k][l] = items[line_index][0] # 图片名 - print(f'当前为文字表格,在({k},{l})位置插入文字: {items[line_index][0]}') - line_index += 1 - print(f"当前待插入表格: {JIANCHA_NEIRONG_TEXT}") - print(f"当前表格序号为 {i}") - output_doc, message = await add_table_to_document( - output_dir, JIANCHA_NEIRONG_PICTURES_TABLE, 1, 3, i, JIANCHA_NEIRONG_TEXT, False, None, key_words - ) - i += 1 - else: - # 图片行(从 items 取图片路径) - print(f"当前表格序号为 {i}") - output_doc, message = await add_table_to_document( - output_dir, JIANCHA_NEIRONG_PICTURES_TABLE, 1, 3, i, None, False - ) - for k in range(3): - if picture_index < picture_num: - pic_path = items[picture_index][1] # 图片路径 - print(f"当前为图片表格,在(0,{k})位置插入图片: {pic_path}") - print(await add_picture_to_table(output_doc, output_dir, 0, k, pic_path, i, 1.8898)) - picture_index += 1 - i += 1 - print(message) - return i # 返回最后使用的表格序号 - -async def process_server_images_table(data_list, image_source_list, output_dir, start_i, JIANCHA_NEIRONG_PICTURES_TABLE, key_words=None): - """从服务器获取的图片数据添加对应表格且填写图片名与插入图片 - - 逻辑:按来源排序,添加 - Args: - data_list (list): 图片数据列表,每个元素为图片路径 - [ - {'imageId': '41543f531be24522b7bec741b9a483a2', 'imageName': 'F02_机组10060_叶片,叶片PS面距叶根15.5米处轴向裂纹,轴向:260mm,弦向:20mm。DJI_20250526090227_0052_Z.png', 'imagePath': '/static/image/temp/out-work/12bc30fb209f3af3bf530541c5b062bd/F02_机组10060_叶片,叶片PS面距叶根15.5米处轴向裂纹,轴向:260mm,弦向:20mm。DJI_20250526090227_0052_Z.png', 'partId': '12bc30fb209f3af3bf530541c5b062bd', 'partName': '叶片1', 'imageResolution': '8000x6000', 'focalDistance': None, 'shootingTime': None, 'cameraManufacturer': None, 'cameraModel': None, 'weather': None, 'weatherLabel': None, 'humidness': None, 'temperature': None, 'windLevel': None, 'shootingMethod': 'UAV', 'shootingMethodLabel': '无人机航拍', 'shootingDistance': None, 'collectorName': '程启谦', 'imageSource': 'out-work', 'imageSourceLabel': '外部工作', 'imageType': 'DEFECT', 'imageTypeLabel': '缺陷影像', 'audioList': None, 'preImagePath': None, 'preTreatment': False, 'projectId': None, 'gps': None}, - ... - ] - image_source_list (list): 来源列表,每个元素为来源名称 - ['out-work', 'in-work', 'in-field'] (来源可能会少于3个,pop出没有给出对应来源的图片) - output_dir (str): 输出路径 - start_i (int): 总表格数量 - JIANCHA_NEIRONG_PICTURES_TABLE (str): 二维表模板路径 - key_words (re) - - Returns: - int: 最后使用的表格序号 - """ - # 按来源对图片数据进行分组和排序 - sorted_data = {source: [] for source in image_source_list} - - # 将图片数据按来源分组 - for item in data_list: - source = item['imageSource'] - if source in sorted_data: - # 存储图片名和图片路径的元组,与非server版本保持一致 - sorted_data[source].append((item['imageTypeLabel'], item['imagePath'])) - - i = start_i - # 按照image_source_list的顺序处理每个来源的图片 - for source in image_source_list: - items = sorted_data[source] - if not items: - continue # 如果该来源没有图片则跳过 - - print(f"处理来源: {source}") - picture_num = len(items) - line_index = 0 - picture_index = 0 - - for content_row in range(((picture_num + 2) // 3) * 2): - if content_row % 2 == 1: - # 文字行(从 items 取图片名) - JIANCHA_NEIRONG_TEXT = [["" for _ in range(3)] for _ in range(1)] # 1行3列 - for k in range(1): # 只有1行 - for l in range(3): - if line_index >= picture_num: - break - JIANCHA_NEIRONG_TEXT[k][l] = items[line_index][0] # 图片名 - print(f'当前为文字表格,在({k},{l})位置插入文字: {items[line_index][0]}') - line_index += 1 - print(f"当前待插入表格: {JIANCHA_NEIRONG_TEXT}") - print(f"当前表格序号为 {i}") - output_doc, message = await add_table_to_document( - output_dir, JIANCHA_NEIRONG_PICTURES_TABLE, 1, 3, i, JIANCHA_NEIRONG_TEXT, False, None, key_words - ) - i += 1 - else: - # 图片行(从 items 取图片路径) - print(f"当前表格序号为 {i}") - output_doc, message = await add_table_to_document( - output_dir, JIANCHA_NEIRONG_PICTURES_TABLE, 1, 3, i, None, False - ) - for k in range(3): - if picture_index < picture_num: - pic_path = items[picture_index][1] # 图片路径 - print(f"当前为图片表格,在(0,{k})位置插入图片: {pic_path}") - print(await add_picture_to_table(output_doc, output_dir, 0, k, get_full_picture_url(pic_path), i, 1.8898)) - picture_index += 1 - i += 1 - print(message) - return i # 返回最后使用的表格序号 \ No newline at end of file diff --git a/tools/esay_docx_func.py b/tools/esay_docx_func.py index 322c5b9..79ef1da 100644 --- a/tools/esay_docx_func.py +++ b/tools/esay_docx_func.py @@ -1,7 +1,8 @@ -from content_tools import add_picture_to_table, search_and_replace +from content_tools import add_picture_to_table, search_and_replace,add_picture from get_pictures import resize_and_reduce_quality -from document_tools import add_table_to_document - +from document_tools import add_table_to_document,add_documents +from docx import Document +from typing import List def fill_tables(Y_table_list, row, col, Y_Table_num, Y): """根据前端返回json块填写表格list,并实时跟进已填写表格数量 目前只支持固定的缺陷图的填写 @@ -179,4 +180,3 @@ def merge_info(frontend_info, default_info): merged_info[key] = frontend_value return merged_info - diff --git a/tools/get_pictures.py b/tools/get_pictures.py index 597d70c..c6afbaf 100644 --- a/tools/get_pictures.py +++ b/tools/get_pictures.py @@ -2,6 +2,7 @@ import os import math from PIL import Image from concurrent.futures import ThreadPoolExecutor +from tools.dataproccess import get_resource_path def resize_and_reduce_quality(image_path, output_path, target_width = None): try: @@ -332,3 +333,9 @@ def get_records_with_pic(records: list[dict], filtered_picture_data: dict[str, d return records_with_defect_pic, defect_pic_with_no_records + + +def get_template_pic(pic_name : str): + """获取模板图片路径""" + return get_resource_path(pic_name) + \ No newline at end of file diff --git a/tools/json_to_docx.py b/tools/json_to_docx.py new file mode 100644 index 0000000..0348d51 --- /dev/null +++ b/tools/json_to_docx.py @@ -0,0 +1,375 @@ +from docx import Document +from docx.shared import Pt, RGBColor +from docx.enum.text import WD_ALIGN_PARAGRAPH +from docx.oxml.shared import qn, OxmlElement +import json + +def json_to_docx(json_data): + print(f"\n开始转换JSON到DOCX文档") + doc = Document() + total_elements = len(json_data) + print(f"文档包含 {total_elements} 个元素(段落和表格)") + + for i, element in enumerate(json_data, 1): + print(f"\n处理元素 {i}/{total_elements}: ", end="") + if element["type"] == "text": + print(f"段落 (长度: {len(element['content']['runs'])}个runs)") + add_paragraph_from_json(doc, element["content"]) + elif element["type"] == "table": + rows = element["content"]["rows"] + cols = element["content"]["cols"] + merges = len(element["content"].get("merged_cells", [])) + print(f"表格 ({rows}行×{cols}列, 包含 {merges} 个合并单元格)") + add_table_from_json(doc, element["content"], element.get("bold", False)) + + return doc + +def add_paragraph_from_json(doc, para_json): + paragraph = doc.add_paragraph() + print(f" 添加段落 (对齐: {para_json['alignment']})") + + # 设置段落对齐方式 + alignment_map = { + "left": WD_ALIGN_PARAGRAPH.LEFT, + "center": WD_ALIGN_PARAGRAPH.CENTER, + "right": WD_ALIGN_PARAGRAPH.RIGHT, + "justify": WD_ALIGN_PARAGRAPH.JUSTIFY + } + paragraph.alignment = alignment_map.get(para_json["alignment"], WD_ALIGN_PARAGRAPH.LEFT) + + # 添加文本运行(runs) + for run_idx, run_json in enumerate(para_json["runs"], 1): + run = paragraph.add_run(run_json["text"]) + try: + if run_json["has_page_break"]: + import docx + run.add_break(docx.enum.text.WD_BREAK.PAGE) + except: + pass + font = run.font + + print(f" 添加run {run_idx}: '{run_json['text']}' " + f"(字体: {run_json['font']['name']}, 大小: {run_json['font']['size']}, " + f"加粗: {run_json['font']['bold']}, 斜体: {run_json['font']['italic']})") + + # 设置字体样式 + if run_json["font"]["name"]: + font.name = run_json["font"]["name"] + run.element.rPr.rFonts.set(qn('w:eastAsia'), run_json["font"]["name"]) + + if run_json["font"]["size"]: + font.size = Pt(run_json["font"]["size"]) + + font.bold = run_json["font"]["bold"] + font.italic = run_json["font"]["italic"] + font.underline = run_json["font"]["underline"] + + # 设置字体颜色 + if run_json["font"]["color"]: + color = run_json["font"]["color"] + font.color.rgb = RGBColor(color["r"], color["g"], color["b"]) + print(f" 设置颜色: RGB({color['r']}, {color['g']}, {color['b']})") + +def add_table_from_json(doc, table_json, bold=False): + print(f" 创建表格: {table_json['rows']}行 × {table_json['cols']}列") + table = doc.add_table(rows=table_json["rows"], cols=table_json["cols"]) + + # 设置表格样式为无网格线(我们将自定义边框) + table.style = 'Table Grid' + + # 设置列宽 + if "col_widths" in table_json and any(table_json["col_widths"]): + print(" 设置列宽...") + for col_idx, width in enumerate(table_json["col_widths"]): + if width is not None: + # 将英寸转换为Twips(1英寸=1440 Twips) + twips_width = int(width * 1440) + for cell in table.columns[col_idx].cells: + tc = cell._tc + tcPr = tc.get_or_add_tcPr() + tcW = tcPr.first_child_found_in("w:tcW") + if tcW is None: + tcW = OxmlElement('w:tcW') + tcPr.append(tcW) + tcW.set(qn('w:w'), str(twips_width)) + tcW.set(qn('w:type'), 'dxa') # 使用绝对单位 + + # 设置行高 + if "row_heights" in table_json and any(table_json["row_heights"]): + print(" 设置行高...") + for row_idx, height in enumerate(table_json["row_heights"]): + if height is not None: + # 将英寸转换为Twips(1英寸=1440 Twips) + twips_height = int(height * 1440) + tr = table.rows[row_idx]._tr + trPr = tr.get_or_add_trPr() + trHeight = OxmlElement('w:trHeight') + trHeight.set(qn('w:val'), str(twips_height)) + trHeight.set(qn('w:hRule'), 'atLeast') # 或'exact'表示固定高度 + trPr.append(trHeight) + + # 处理合并单元格 + for merge_idx, merge_info in enumerate(table_json.get("merged_cells", []), 1): + start_row = merge_info["start_row"] + start_col = merge_info["start_col"] + end_row = merge_info["end_row"] + end_col = merge_info["end_col"] + + print(f" 合并单元格 #{merge_idx}: 从({start_row},{start_col})到({end_row},{end_col})") + + start_cell = table.cell(start_row, start_col) + end_cell = table.cell(end_row, end_col) + start_cell.merge(end_cell) + + # 填充表格内容 + for row_idx, row_data in enumerate(table_json["cells"]): + for col_idx, cell_data in enumerate(row_data): + # 跳过被合并的非主单元格 + if cell_data["is_merged"] and not cell_data["merge_info"]["is_primary"]: + print(f" 跳过被合并的单元格({row_idx},{col_idx})") + continue + + cell = table.cell(cell_data["row"], cell_data["col"]) + print(f" 处理单元格({row_idx},{col_idx}) - 对齐: {cell_data['alignment']}") + format_cell(cell, cell_data) # 统一设置单元格格式 + +def format_cell(cell, cell_data): + """设置单元格完整格式""" + # 清空原有内容 + for p in cell.paragraphs: + p._element.getparent().remove(p._element) + + # 添加内容 + for para in cell_data["content"]: + add_paragraph_from_json(cell, para) + + # 设置对齐方式 + set_cell_alignment(cell, cell_data) + + # 设置边框 + set_cell_border(cell, cell_data["border"]) + + # 设置背景色 + if cell_data.get("shading"): + set_cell_shading(cell, cell_data["shading"]) + + # 设置边距 + if cell_data.get("margins"): + set_cell_margins(cell, cell_data["margins"]) + +def set_cell_alignment(cell, cell_data): + """设置单元格对齐(水平和垂直)""" + # 水平对齐 + if cell.paragraphs: + align_map = { + "left": WD_ALIGN_PARAGRAPH.LEFT, + "center": WD_ALIGN_PARAGRAPH.CENTER, + "right": WD_ALIGN_PARAGRAPH.RIGHT, + "justify": WD_ALIGN_PARAGRAPH.JUSTIFY + } + cell.paragraphs[0].alignment = align_map.get(cell_data["alignment"], WD_ALIGN_PARAGRAPH.LEFT) + + # 垂直对齐设置 + tcPr = cell._tc.get_or_add_tcPr() + vAlign = OxmlElement('w:vAlign') + align_value = cell_data.get('vertical_align', 'top') + print(f" 设置垂直对齐: {align_value}") + + # 确保使用有效的对齐值 + valid_alignments = ['top', 'center', 'bottom'] + if align_value not in valid_alignments: + align_value = 'top' # 默认值 + + vAlign.set(qn('w:val'), align_value) + tcPr.append(vAlign) + +def set_cell_shading(cell, shading): + """设置单元格背景色""" + tcPr = cell._tc.get_or_add_tcPr() + shd = OxmlElement('w:shd') + shd.set(qn('w:fill'), shading["color"]) + if shading.get("theme"): + shd.set(qn('w:themeColor'), shading["theme"]) + tcPr.append(shd) + +def set_cell_margins(cell, margins): + """设置单元格边距""" + tcPr = cell._tc.get_or_add_tcPr() + tcMar = OxmlElement('w:tcMar') + + for side, margin in margins.items(): + side_el = OxmlElement(f'w:{side}') + side_el.set(qn('w:w'), margin["w"]) + side_el.set(qn('w:type'), margin["type"]) + tcMar.append(side_el) + + tcPr.append(tcMar) + +def set_cell_border(cell, border_data): + """ + 设置单元格边框 + :param cell: 单元格对象 + :param border_data: 边框数据 + """ + tc = cell._tc + tcPr = tc.get_or_add_tcPr() + + # 检查是否存在边框元素,不存在则创建 + tcBorders = tcPr.first_child_found_in("w:tcBorders") + if tcBorders is None: + tcBorders = OxmlElement('w:tcBorders') + tcPr.append(tcBorders) + + # 设置各边边框 + for side in ['top', 'left', 'bottom', 'right']: + if side in border_data: + border = border_data[side] + border_el = OxmlElement(f'w:{side}') + border_el.set(qn('w:val'), border.get('style', 'single')) + border_el.set(qn('w:sz'), str(border.get('size', 4))) + border_el.set(qn('w:color'), border.get('color', '000000')) + tcBorders.append(border_el) + +# 使用示例 +if __name__ == "__main__": + # 假设我们已经有了之前生成的JSON数据 + input_json = "output.json" + output_path = "restored.docx" + + print(f"从 {input_json} 读取JSON数据...") + with open(input_json, "r", encoding="utf-8") as f: + json_data = json.load(f) + + # 将JSON转换回DOCX + json_to_docx(json_data, output_path) + +from typing import List, Dict, Any +def list_to_json_with_merges( + table_data: List[List[str]], + style_config: Dict[str, Any] = None, + detect_merges: bool = True # 新增控制参数 +) -> Dict[str, Any]: + """ + 将二维列表转换为表格JSON,可选是否合并相邻相同单元格 + + 参数: + table_data: 二维字符串列表表示的表格数据 + style_config: 包含样式配置的字典(可选) + detect_merges: 是否检测并合并相邻相同单元格(默认为True) + + 返回: + 符合表格JSON结构的字典 + """ + if not table_data or not table_data[0]: + return {"type": "table", "content": {"rows": 0, "cols": 0, "cells": [], "merged_cells": []}} + + rows = len(table_data) + cols = len(table_data[0]) + + result = { + "type": "table", + "content": { + "rows": rows, + "cols": cols, + "merged_cells": [], + "cells": [[None for _ in range(cols)] for _ in range(rows)] + } + } + + for col in range(cols): + start_row = 0 + while start_row < rows: + current_value = table_data[start_row][col] + end_row = start_row + + # 只有开启合并检测时才查找可合并区域 + if detect_merges: + while end_row + 1 < rows and table_data[end_row + 1][col] == current_value: + end_row += 1 + + # 处理单元格(区分合并/非合并模式) + if detect_merges and end_row > start_row: # 合并模式 + merge_info = { + "start_row": start_row, + "start_col": col, + "end_row": end_row, + "end_col": col + } + result["content"]["merged_cells"].append(merge_info) + + for row in range(start_row, end_row + 1): + cell_data = create_cell_data( + row=row, + col=col, + value=current_value, + style_config=style_config, + is_merged=True, + is_primary=(row == start_row), + merge_range=merge_info + ) + result["content"]["cells"][row][col] = cell_data + else: # 非合并模式或无需合并的单单元格 + cell_data = create_cell_data( + row=start_row, + col=col, + value=current_value, + style_config=style_config, + is_merged=False + ) + result["content"]["cells"][start_row][col] = cell_data + + start_row = end_row + 1 + + return [result] + +def create_cell_data( + row: int, + col: int, + value: str, + style_config: Dict[str, Any], + is_merged: bool = False, + is_primary: bool = False, + merge_range: Dict[str, int] = None +) -> Dict[str, Any]: + """创建标准化单元格数据""" + cell = { + "row": row, + "col": col, + "is_merged": is_merged, + "content": create_cell_content(value, style_config), + "alignment": style_config.get("alignment", "left") if style_config else "left", + "border": style_config.get("border", {}) if style_config else {}, + "shading": style_config.get("shading", {}) if style_config else {}, + "margins": style_config.get("margins", {}) if style_config else {} + } + + if is_merged: + cell["merge_info"] = { + "is_primary": is_primary, + "start_row": merge_range["start_row"], + "start_col": merge_range["start_col"], + "end_row": merge_range["end_row"], + "end_col": merge_range["end_col"] + } + + return cell + +def create_cell_content(text: str, style_config: Dict[str, Any] = None) -> List[Dict]: + """创建单元格内容结构""" + font_config = style_config.get("font", {}) if style_config else {} + + return [{ + "alignment": style_config.get("alignment", "left") if style_config else "left", + "runs": [{ + "text": text, + "font": { + "name": font_config.get("name", "Calibri"), + "size": font_config.get("size", 11), + "bold": font_config.get("bold", False), + "italic": font_config.get("italic", False), + "underline": font_config.get("underline", False), + "color": font_config.get("color", {"r": 0, "g": 0, "b": 0}) + }, + "has_page_break": False + }] + }] \ No newline at end of file diff --git a/utils/__pycache__/document_utils.cpython-310.pyc b/utils/__pycache__/document_utils.cpython-310.pyc index 2343e63..9bc8a33 100644 Binary files a/utils/__pycache__/document_utils.cpython-310.pyc and b/utils/__pycache__/document_utils.cpython-310.pyc differ diff --git a/utils/document_utils.py b/utils/document_utils.py index d8a1e5b..d5ce8bf 100644 --- a/utils/document_utils.py +++ b/utils/document_utils.py @@ -165,3 +165,10 @@ def find_and_replace_text(doc, old_text, new_text): count += 1 return count + + +def clear_header(section): + for para in section.header.paragraphs: + para.clear() + + \ No newline at end of file