Compare commits
No commits in common. "main" and "dachuan" have entirely different histories.
|
@ -1,202 +0,0 @@
|
|||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[codz]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# UV
|
||||
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
#uv.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
#poetry.toml
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||
#pdm.lock
|
||||
#pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# pixi
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||
#pixi.lock
|
||||
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||
.pixi
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.envrc
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Abstra
|
||||
# Abstra is an AI-powered process automation framework.
|
||||
# Ignore directories containing user credentials, local state, and settings.
|
||||
# Learn more at https://abstra.io/docs
|
||||
.abstra/
|
||||
|
||||
# Visual Studio Code
|
||||
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||
# you could uncomment the following to ignore the entire vscode folder
|
||||
# .vscode/
|
||||
|
||||
# Ruff stuff:
|
||||
.ruff_cache/
|
||||
|
||||
# PyPI configuration file
|
||||
.pypirc
|
||||
|
||||
# Marimo
|
||||
marimo/_static/
|
||||
marimo/_lsp/
|
||||
__marimo__/
|
||||
|
||||
# Streamlit
|
||||
.streamlit/secrets.toml
|
575
Dt_report.py
|
@ -1,575 +0,0 @@
|
|||
# 文档处理工具
|
||||
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,change_heading
|
||||
)
|
||||
|
||||
# 内容处理工具
|
||||
from tools.content_tools import (
|
||||
add_picture,split_table_by_row_content,
|
||||
search_and_replace,add_heading
|
||||
)
|
||||
|
||||
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,merge_dicts
|
||||
)
|
||||
|
||||
|
||||
|
||||
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_data1, picture_data2, Yepians = get_part_picture(turbine_id)
|
||||
picture_data = merge_dicts(picture_data1, picture_data2)
|
||||
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))
|
||||
change_heading(output_dir, "Heading 1", DT_EADING_1_CONFIG)
|
||||
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}")
|
||||
print(add_heading(output_dir, f"一、项目概括", 1))
|
||||
output_doc, message = add_table_to_document(output_dir, XIANG_MU_GAI_KUANG,5,5,total_table_num,XIANGMU_GAIKUO, if_para=False)
|
||||
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_heading(output_dir, f"二、检查方案", 1))
|
||||
print(add_table_and_replace(output_dir, FANGAN_JIANCHA_DIR, 0, list_to_replace, no_para=True))
|
||||
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)
|
||||
#新建检查信息表
|
||||
print(add_heading(output_dir, f"三、检查信息", 1))
|
||||
output_doc, message = add_table_to_document(output_dir, JIANCHA_XINGXI_DIR,9,4,total_table_num ,JIANCHA_XINGXI,False,if_para=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] = "未审核"
|
||||
|
||||
print(add_heading(output_dir, f"四、成果递交", 1))
|
||||
output_doc, message = add_table_to_document(output_dir, CHENGGUO_DIJIAO_DIR,5,5,total_table_num,CHENGGUO_DIJIAO,True,0.04,if_para=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_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 '未发现明显影响风力发电机组正常运行的缺陷'
|
||||
print(add_heading(output_dir, f"五、检查情况汇总 ", 1))
|
||||
output_doc, message = add_table_to_document(output_dir, JIANCHA_HUIZONG_DIR,4,3,total_table_num,JIANCHA_HUIZONG,False,ALIGMENT='LEFT', if_para=False)
|
||||
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:
|
||||
print(add_heading(output_dir, f"六、检查内容", 1))
|
||||
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,if_para=False)
|
||||
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]))
|
|
@ -1,38 +1,525 @@
|
|||
# 文档处理工具
|
||||
from tools.document_tools import (
|
||||
create_document, add_documents,add_table_and_replace,
|
||||
add_table_to_document,process_images_table,
|
||||
)
|
||||
|
||||
# 内容处理工具
|
||||
from tools.content_tools import (
|
||||
add_picture,split_table_by_row_content,
|
||||
search_and_replace,add_picture_to_table
|
||||
)
|
||||
|
||||
from tools.get_pictures import (
|
||||
make_Thumbnail,resize_and_reduce_quality,
|
||||
get_picture_nums,find_image,collect_defect_data,
|
||||
process_picture_data
|
||||
)
|
||||
|
||||
from tools.Get_Json import (
|
||||
get_project_info,get_jizu_info,
|
||||
get_jizu_shigong_info,get_weather,
|
||||
get_part_picture,get_yepian_xiangqing
|
||||
)
|
||||
|
||||
from tools.dataproccess import (
|
||||
caculate_work_days,add_dynamic_table,
|
||||
get_year_month,merge_info,
|
||||
)
|
||||
|
||||
import asyncio
|
||||
|
||||
from core.tables import fill_tables
|
||||
|
||||
from tools.defines import *
|
||||
import os, re, datetime
|
||||
from pathlib import Path
|
||||
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
|
||||
|
||||
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'
|
||||
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)
|
||||
|
||||
try:
|
||||
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']
|
||||
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']
|
||||
muban_dir = baogao_info['muban_dir']
|
||||
if muban_dir == "" or 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_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 = get_part_picture(turbine_id)
|
||||
|
||||
#获取叶片信息
|
||||
print(part_data)
|
||||
Yepians = []
|
||||
yepian_to_find = ["叶片1","叶片2","叶片3"]
|
||||
#依次获取叶片1,2,3的信息(未考虑多个同叶片的信息情况,正常情况下不会发生)
|
||||
for name in yepian_to_find:
|
||||
# 找到第一个匹配的部件并添加到列表中
|
||||
for part in part_data:
|
||||
if part['partName'] == name:
|
||||
Yepians.append(part)
|
||||
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 = []
|
||||
#获取对应枚举字段
|
||||
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)}支叶片的前缘、后缘、迎风面、背风面。")
|
||||
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)}支叶片的内部导雷卡、腹板、透光、人孔盖版、叶根盖板...")
|
||||
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"轮毂至塔基导通、内部导线线阻、外部导线线阻...")
|
||||
|
||||
#获取缺陷图列表和典型图列表
|
||||
(defect_pictures, typical_pictures,
|
||||
other_pictures, normal_picture_num) = process_picture_data(picture_data, image_source_to_find,
|
||||
quexian_type, dianxing_type, other_type)
|
||||
print(f"\n\n\n缺陷图片列表:{defect_pictures}\n\n\n典型图片列表:{typical_pictures}\n\n\n")
|
||||
|
||||
#处理图片数据待完成,图片绑定施工后
|
||||
defect_pictures_id = [pic for pic in defect_pictures]
|
||||
|
||||
|
||||
baogao_bianzhi = baogao_info["userName"]
|
||||
baogao_shenghe = baogao_info["baogaoCheck"]
|
||||
|
||||
except Exception as e:
|
||||
print(f"报告基本信息获取失败:{e}")
|
||||
return
|
||||
|
||||
output_doc = None
|
||||
head_num = 1
|
||||
###封面创建###
|
||||
cover_dirs = [os.path.join(muban_dir,"fengmian1.docx"),os.path.join(muban_dir,"fengmian.jpg"),os.path.join(muban_dir,"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))
|
||||
print(add_documents(output_dir, cover_dirs[0]))
|
||||
print(await add_picture(output_dir, cover_dirs[1]))
|
||||
print(add_documents(output_dir, cover_dirs[2]))
|
||||
print("封面创建成功")
|
||||
|
||||
#更改文档信息
|
||||
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
|
||||
#项目概况表
|
||||
print("开始添加项目概况表")
|
||||
XIANG_MU_GAI_KUANG = os.path.join(muban_dir,"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][3]=XIANGMU_GAIKUO[0][4] = "盐城市滨海县"
|
||||
XIANGMU_GAIKUO[0][4] = project_location
|
||||
#XIANGMU_GAIKUO[1][1]=XIANGMU_GAIKUO[2,1]=XIANGMU_GAIKUO[3,1] = "国家电投集团滨海风力发电有限公司"
|
||||
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
|
||||
|
||||
#检查方案描述
|
||||
FANGAN_JIANCHA_DIR = os.path.join(muban_dir,"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
|
||||
|
||||
|
||||
JIANCHA_XINGXI_DIR = os.path.join(muban_dir,"checkinfo.docx")
|
||||
JIANCHA_XINGXI = list(list("" for i in range(4)) for j in range(9))
|
||||
JIANCHA_XINGXI[0][1] = ""#检查人员,需要完善
|
||||
JIANCHA_XINGXI[1][1] = Jiancha_date.split(' ')[0]
|
||||
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"检查,采集叶片图片{normal_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
|
||||
|
||||
# 添加成果递交表
|
||||
CHENGGUO_DIJIAO_DIR = os.path.join(muban_dir,"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
|
||||
CHENGGUO_DIJIAO[2][1] = Jiancha_date.split(' ')[0]
|
||||
CHENGGUO_DIJIAO[3][1] = "" #data_process
|
||||
CHENGGUO_DIJIAO[4][1] = baogao_bianzhi
|
||||
CHENGGUO_DIJIAO[5][1] = baogao_shenghe
|
||||
CHENGGUO_DIJIAO[2][3] = Jiancha_date.split(' ')[1]
|
||||
CHENGGUO_DIJIAO[3][3] = baogao_date.split(' ')[0]
|
||||
CHENGGUO_DIJIAO[4][3] = baogao_date.split(' ')[0]
|
||||
CHENGGUO_DIJIAO[5][3] = "" #shenghe_date.split(' ')[0]
|
||||
|
||||
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
|
||||
|
||||
#检查情况汇总表(文字信息)
|
||||
|
||||
try:
|
||||
#获取缺陷信息
|
||||
"""
|
||||
需要获取:
|
||||
Y1、Y2、Y3叶片的缺陷数量,缺陷字典{图片名:图片路径}
|
||||
和数据库连接需要的新增异常处理:
|
||||
目前逻辑为找到图片后,找缺陷类型的图片,通过图片id查找对应缺陷记录
|
||||
如果没有找到,要返回对应异常,即已选出(标注)缺陷图,但未填写对应缺陷信息的异常返回。
|
||||
最后将无异常的图片入队缺陷字典(同数量),有异常的图片再另存。
|
||||
"""
|
||||
|
||||
Y1_quexian_num, Y1_quexian_dict = 0, {}
|
||||
Y2_quexian_num, Y2_quexian_dict = 0, {}
|
||||
Y3_quexian_num, Y3_quexian_dict = 0, {}
|
||||
weak_num_Y1 = f"{Y_Code[0] if len(Y_Code) > 0 else '无'}叶片共发现缺陷{Y1_quexian_num}处"
|
||||
weak_num_Y2 = f"{Y_Code[1] if len(Y_Code) > 1 else '无'}叶片共发现缺陷{Y2_quexian_num}处"
|
||||
weak_num_Y3 = f"{Y_Code[2] if len(Y_Code) > 2 else '无'}叶片共发现缺陷{Y3_quexian_num}处"
|
||||
except Exception as e:
|
||||
print(f"缺陷图获取失败:{e}")
|
||||
return
|
||||
|
||||
#添加检查情况汇总表
|
||||
JIANCHA_HUIZONG_DIR = os.path.join(muban_dir,"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] = ""#Y1_jiancha
|
||||
JIANCHA_HUIZONG[2][1] = ""#Y2_jiancha
|
||||
JIANCHA_HUIZONG[3][1] = ""#Y3_jiancha
|
||||
JIANCHA_HUIZONG[1][2] = "/n".join([f"{i+1}.{name}" for i, (name, path) in enumerate(Y1_quexian_dict.items())]) if Y1_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷'
|
||||
JIANCHA_HUIZONG[2][2] = "/n".join([f"{i+1}.{name}" for i, (name, path) in enumerate(Y2_quexian_dict.items())]) if Y2_quexian_num else '未发现明显影响风力发电机组正常运行的缺陷'
|
||||
JIANCHA_HUIZONG[3][2] = "/n".join([f"{i+1}.{name}" for i, (name, path) in enumerate(Y3_quexian_dict.items())]) 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
|
||||
|
||||
#主要部位图片展示表/检查内容表
|
||||
#获取典型图信息
|
||||
search_file_list = ["外汇总","内汇总","防汇总"]
|
||||
try:
|
||||
picture_Y1_num, Y1_dict = collect_defect_data(Y1, Picture_dir, ifwaibu, ifneibu, search_file_list, iffanglei)
|
||||
picture_Y2_num, Y2_dict = collect_defect_data(Y2, Picture_dir, ifwaibu, ifneibu, search_file_list, iffanglei)
|
||||
picture_Y3_num, Y3_dict = collect_defect_data(Y3, Picture_dir, ifwaibu, ifneibu, search_file_list, iffanglei)
|
||||
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
|
||||
col ,row = 3, 0
|
||||
JIANCHA_NEIRONG_PICTURES_TABLE = os.path.join(muban_dir,"check2.docx")
|
||||
JIANCHA_NEIRONG_Y1_DIR = os.path.join(muban_dir,"check_content.docx")
|
||||
JIANCHA_NEIRONG_Y1 = list(list("" for _ in range(3)) for j in range(1))
|
||||
JIANCHA_NEIRONG_Y1[0][0] = f"叶片1:{Y1}检查内容"
|
||||
print(f"Y1标题内容:{JIANCHA_NEIRONG_Y1}")
|
||||
JIANCHA_NEIRONG_Y2_DIR = os.path.join(muban_dir,"check3.docx")
|
||||
JIANCHA_NEIRONG_Y2 = list(list("" for _ in range(3)) for j in range(1))
|
||||
JIANCHA_NEIRONG_Y2[0][0] = f"叶片2:{Y2}检查内容"
|
||||
print(f"Y2标题内容:{JIANCHA_NEIRONG_Y2}")
|
||||
JIANCHA_NEIRONG_Y3_DIR = os.path.join(muban_dir,"check3.docx")
|
||||
JIANCHA_NEIRONG_Y3 = list(list("" for _ in range(3)) for j in range(1))
|
||||
JIANCHA_NEIRONG_Y3[0][0] = f"叶片3:{Y3}检查内容"
|
||||
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_images_table(Y1_dict, 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_images_table(Y2_dict, 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_images_table(Y3_dict, 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
|
||||
|
||||
# #缺陷详情
|
||||
# QUEXIAN_XIANGQING_DIR = os.path.join(muban_dir,"check_check.docx")
|
||||
# QUEXIAN_XIANGQING_TITLE_DIR = os.path.join(muban_dir,"check_check_title.docx")
|
||||
|
||||
# Y_tables = [Y1_quexian_dict,Y2_quexian_dict,Y3_quexian_dict]
|
||||
# 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_dict) in enumerate(zip(table_lists, Y_tables)):
|
||||
# for image_name, 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]),Y1)
|
||||
# Y2_TABLES, Y2_TABLES_PICTURES = fill_tables(table_lists[1],4,5,len(table_lists[1]),Y2)
|
||||
# Y3_TABLES, Y3_TABLES_PICTURES = fill_tables(table_lists[2],4,5,len(table_lists[2]),Y3)
|
||||
# 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)
|
||||
|
||||
|
||||
#总结
|
||||
ZONG_JIE_DIR = os.path.join(muban_dir,"result.docx")
|
||||
ZONG_JIE_BEFORE = "result"
|
||||
ZONG_JIE = baogao_info['baogao_zongjie']
|
||||
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]))
|
||||
|
||||
def main():
|
||||
args = parse_arguments()
|
||||
|
||||
default_cfg = get_default_config()
|
||||
|
||||
# 读取配置文件(若存在)
|
||||
file_cfg = {}
|
||||
if Path(args.config_file).exists():
|
||||
try:
|
||||
file_cfg = load_config(args.config_file)
|
||||
except Exception as e:
|
||||
print(f'警告:加载配置文件失败 {e},使用默认配置')
|
||||
|
||||
merged = merge_configs(default_cfg, file_cfg, args)
|
||||
|
||||
# 补全必要目录
|
||||
if not merged['json2']['shengcheng_dir']:
|
||||
merged['json2']['shengcheng_dir'] = str(Path.cwd() / 'output')
|
||||
|
||||
Path(merged['json2']['shengcheng_dir']).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
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('指定了不存在的模板,请检查配置文件')
|
||||
json_data1 = {
|
||||
"turbine_id" : "183463dbf40d9278549a76b82b175dd9",
|
||||
}
|
||||
json_data2 = {
|
||||
'shengcheng_dir': r".\output",
|
||||
'muban_dir': r".\muban",
|
||||
# "shigong_fangan_enum" : { #可能放入标准信息库?但不论如何,修改了这个枚举的话,报告生成逻辑也要修改,待优化!
|
||||
# "SHIGONG_FANGAN_ENUM": {
|
||||
# "WAIBU": {
|
||||
# "GONGZUO_NEIRONG": "无人机叶片外观巡检",
|
||||
# "RENYUAN_PEIZHI": "1人;主检飞手1人",
|
||||
# "SHEBEI_PEIZHI": "1、大疆无人机1台(M350rtk,M300rtk,M30T,M30,精灵4PRO)2、大疆精灵4PRO+索尼A7R2机身+索尼200-600mm镜头/适马150-600mm镜头",
|
||||
# "SHIGONG_FANGAN": None
|
||||
# },
|
||||
# "NEIBU": {
|
||||
# "GONGZUO_NEIRONG": "叶片内部检查",
|
||||
# "RENYUAN_PEIZHI": "2人;轮毂作业检查2人",
|
||||
# "SHEBEI_PEIZHI": "1、人工检查:照明设备2套,视频记录手机2台,含氧量监测仪1台,电动扳手2套,卷尺1个。2、爬壁机器人检查:无人作业校车+视频图传1套,照明设备2套,含氧量监测仪1台,电动扳手2套,卷尺1个。",
|
||||
# "SHIGONG_FANGAN": None
|
||||
# },
|
||||
# "FANGLEI": {
|
||||
# "YEPIAN": {
|
||||
# "GONGZUO_NEIRONG": "无人机叶片防雷导通测试",
|
||||
# "RENYUAN_PEIZHI": "2人;主检飞手1人,副检抄表1人",
|
||||
# "SHEBEI_PEIZHI": "1四轴电阻无人机1套,电子微欧计1台,视频记录手机1台",
|
||||
# "SHIGONG_FANGAN": None
|
||||
# },
|
||||
# "DIAOLAN": {
|
||||
# "GONGZUO_NEIRONG": "无人吊篮叶片导通测试(含机舱设备、)",
|
||||
# "RENYUAN_PEIZHI": "3人,轮毂机舱作业1人,揽风绳作业1人,无人设备操作员及抄表1人",
|
||||
# "SHEBEI_PEIZHI": "无人吊篮系统1套(爬绳器+接触平台)、电子微欧计1套,视频记录手机1台,对讲机2台",
|
||||
# "SHIGONG_FANGAN": None
|
||||
# },
|
||||
# "SHESHI": {
|
||||
# "GONGZUO_NEIRONG": "风机基础、办公楼、变电站防雷接地检测及浪涌保护器测试",
|
||||
# "RENYUAN_PEIZHI": "1人;抄表人员1人,检测人员1人,监护1人。",
|
||||
# "SHEBEI_PEIZHI": "接地电阻测试仪1套、SPD测试仪1套、对讲机2个、",
|
||||
# "SHIGONG_FANGAN": None
|
||||
# },
|
||||
# "FEISHOURENYUAN_PEIZHI": "1人;主检飞手1人",
|
||||
# "LUNGUZUOYERENYUAN_PEIZHI": "2人;轮毂作业检查2人"
|
||||
# }
|
||||
# }
|
||||
# },
|
||||
}
|
||||
asyncio.run(generate_report(json_data1,json_data2))
|
||||
print('文档生成完毕')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
425
Jf_report.py
|
@ -1,425 +0,0 @@
|
|||
# 文档处理工具
|
||||
from tools.document_tools import (
|
||||
create_document, add_documents,add_table_and_replace,
|
||||
add_table_to_document,add_dynamic_table,add_section,
|
||||
process_server_images_table,add_header,add_landscape_section,
|
||||
merge_documents,add_table,add_defect_info_table,add_table_title,
|
||||
add_title,change_heading,add_auto_toc_at_end,add_jf_picture_table
|
||||
)
|
||||
|
||||
# 内容处理工具
|
||||
from tools.content_tools import (
|
||||
search_and_replace,add_heading
|
||||
)
|
||||
|
||||
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,
|
||||
tree_dict_to_table_data,merge_dicts,
|
||||
defect_list_addtitle
|
||||
)
|
||||
|
||||
from tools.json_to_docx import json_to_docx, list_to_json_with_merges
|
||||
|
||||
|
||||
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_data1, picture_data2, Yepians = get_part_picture(turbine_id)
|
||||
picture_data = merge_dicts(picture_data1, picture_data2)
|
||||
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 = []
|
||||
use_tool_table = []
|
||||
table_index = 1
|
||||
typical_picture_description = []
|
||||
#获取对应枚举字段
|
||||
if if_waibu:
|
||||
baogao_label.append("外观")
|
||||
image_source_to_find.append(baogao_info['waibu_enum'])
|
||||
use_tool_table.extend(
|
||||
[ [str(idx)] + item
|
||||
for idx, item in enumerate(USE_TOOL_ENUM.OUT.LIST, start=table_index) ]
|
||||
)
|
||||
table_index += len(USE_TOOL_ENUM.OUT.LIST)
|
||||
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("前缘、后缘、迎风面、背风面。")
|
||||
typical_picture_description.extend(TEMPLATE_HEADER.JINFENG_HEADER.WAIBU.TYPICAL_LIST)
|
||||
if if_neibu:
|
||||
baogao_label.append("内部")
|
||||
image_source_to_find.append(baogao_info['neibu_enum'])
|
||||
use_tool_table.extend(
|
||||
[ [str(idx)] + item
|
||||
for idx, item in enumerate(USE_TOOL_ENUM.IN.LIST, start=table_index) ]
|
||||
)
|
||||
table_index += len(USE_TOOL_ENUM.IN.LIST)
|
||||
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("内部导雷卡、腹板、透光、人孔盖版、叶根盖板...")
|
||||
typical_picture_description.extend(TEMPLATE_HEADER.JINFENG_HEADER.NEIBU.TYPICAL_LIST)
|
||||
if if_fanglei:
|
||||
baogao_label.append("防雷")
|
||||
image_source_to_find.append(baogao_info['fanglei_enum'])
|
||||
use_tool_table.extend(
|
||||
[ [str(idx)] + item
|
||||
for idx, item in enumerate(USE_TOOL_ENUM.LIGHT.LIST, start=table_index) ]
|
||||
)
|
||||
table_index += len(USE_TOOL_ENUM.LIGHT.LIST)
|
||||
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("轮毂至塔基导通、内部导线线阻、外部导线线阻...")
|
||||
typical_picture_description.extend(TEMPLATE_HEADER.JINFENG_HEADER.FANGLEI.TYPICAL_LIST)
|
||||
|
||||
#获取缺陷图列表和典型图列表
|
||||
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,
|
||||
}))
|
||||
#标题样式
|
||||
change_heading(output_dir, "Heading 1")
|
||||
change_heading(output_dir, "Heading 2", HEADING_2_CONFIG)
|
||||
change_heading(output_dir, "Heading 3", HEADING_3_CONFIG)
|
||||
#加封面
|
||||
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'), REPORT_ENUM=TEMPLATE_HEADER.JINFENG_HEADER.ENUM))
|
||||
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"静态内容生成完毕,开始生成动态内容")
|
||||
|
||||
#缺陷信息表格1
|
||||
defect_info, defect_part_type_list = 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,TEMPLATE_HEADER.JINFENG_HEADER.ENUM)
|
||||
|
||||
#缺陷信息表格2
|
||||
|
||||
|
||||
table_list = tree_dict_to_table_data(defect_part_type_list) # 服务器获取的树形结构转化为表格数据
|
||||
table_list = defect_list_addtitle(table_list, jizu_bianhao) # 增加每列标题,机组
|
||||
|
||||
add_table_title(output_dir, DEFECT_TABLE_TITLE) # 增加表格标题
|
||||
total_table_num += 1
|
||||
|
||||
table_json = list_to_json_with_merges(table_list, style_config=STYLE_CONFIG, merge_columns=2) #将list转换为json形式,前两列检查相同合并
|
||||
|
||||
json_to_docx(table_json, output_dir).save(output_dir) #jsontodocx
|
||||
total_table_num += 1
|
||||
|
||||
#使用器具记录表
|
||||
|
||||
add_table_title(output_dir, USE_TOOL_TABLE_TITLE)
|
||||
total_table_num += 1
|
||||
|
||||
json_to_docx(list_to_json_with_merges(use_tool_table, style_config=STYLE_CONFIG, detect_merges=False),output_dir).save(output_dir)
|
||||
total_table_num += 1
|
||||
|
||||
#添加目录节
|
||||
add_section(output_dir).save(output_dir)
|
||||
|
||||
#添加目录
|
||||
#add_auto_toc_at_end(output_dir)
|
||||
|
||||
#添加横向节(展示图片)
|
||||
add_landscape_section(output_dir).save(output_dir)
|
||||
|
||||
print(add_heading(output_dir, f"1. 机组号:{jizu_bianhao}", 1))
|
||||
print(add_heading(output_dir, f"1.1 叶片编号:{Y_Code[0]}", 2))
|
||||
#add_header(output_dir, TEMPLATE_HEADER.JINFENG_HEADER.ENUM, if_section=False, text = " " + TEMPLATE_HEADER.JINFENG_HEADER.PARA)
|
||||
"""
|
||||
typical_picture_list: #每列有两种情况
|
||||
[
|
||||
[str(图片描述), int(n), ...],
|
||||
[str(图片地址), str(缺陷类型), ...]
|
||||
]
|
||||
defect_picture_list: #只有一种情况
|
||||
[
|
||||
[str(图片描述), str(图片描述), ...],
|
||||
[str(图片地址), str(图片地址), ...]
|
||||
]
|
||||
"""
|
||||
typical_picture_list = []
|
||||
defect_picture_list = []
|
||||
typical_picture_list = [
|
||||
[
|
||||
"外观检查(迎、背风面是否有漆面脱落、裂纹等)1",
|
||||
"外观检查(前、后缘;如前缘漆面脱落、合模缝开裂等)2",
|
||||
"外观检查(迎、背风面是否有漆面脱落、裂纹等)3",
|
||||
"外观检查(前、后缘;如前缘漆面脱落、合模缝开裂等)4",
|
||||
"外观检查(迎、背风面是否有漆面脱落、裂纹等)5",
|
||||
"外观检查(前、后缘;如前缘漆面脱落、合模缝开裂等)6",
|
||||
"外观检查(迎、背风面是否有漆面脱落、裂纹等)7",
|
||||
"外观检查(前、后缘;如前缘漆面脱落、合模缝开裂等)8",
|
||||
"外观检查(迎、背风面是否有漆面脱落、裂纹等)9",
|
||||
"外观检查(前、后缘;如前缘漆面脱落、合模缝开裂等)10",
|
||||
],
|
||||
[
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
2,
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
2,
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
2,
|
||||
2,
|
||||
],
|
||||
[
|
||||
"",
|
||||
"图层脱落",
|
||||
"",
|
||||
"",
|
||||
"图层脱落",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"图层脱落",
|
||||
"图层脱落",
|
||||
]
|
||||
]
|
||||
defect_picture_list = [
|
||||
[
|
||||
"1",
|
||||
"2",
|
||||
"3456",
|
||||
"456",
|
||||
"56",
|
||||
"6",
|
||||
"12",
|
||||
"123",
|
||||
],
|
||||
[
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
"/home/dtyx/桌面/yhh/Report_Generate_Server/muban/wechat_2025-07-29_123404_038.png",
|
||||
]
|
||||
]
|
||||
|
||||
total_table_num = add_jf_picture_table(
|
||||
typical_picture_list,
|
||||
defect_picture_list,
|
||||
get_resource_path(MUBAN_DIR + '/typical_picture_table.docx'),
|
||||
get_resource_path(MUBAN_DIR + '/defect_picture_table.docx'),
|
||||
output_dir,
|
||||
total_table_num
|
||||
)
|
||||
|
|
@ -1,3 +1 @@
|
|||
# 报告生成接入服务器的测试开发(调用服务器的api测试)
|
||||
|
||||
## 在dist目录查看编译好的文件,和使用文档
|
||||
报告生成接入服务器的测试开发(调用服务器的api测试)
|
||||
|
|
44
build.spec
|
@ -1,44 +0,0 @@
|
|||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
block_cipher = None
|
||||
|
||||
a = Analysis(
|
||||
['Generate_Report.py'], # 替换为你的脚本文件名
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[('./muban/*.docx', 'muban'),
|
||||
('./muban/*.jpg', 'muban'),
|
||||
('./muban/*.png', 'muban')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='report_generator', # 可执行文件名称
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True, # 显示控制台窗口
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
|
@ -103,7 +103,9 @@ def apply_table_style(table, has_header_row=False, border_style=None, shading=No
|
|||
except Exception:
|
||||
return False
|
||||
|
||||
def copy_table(source_table, target_doc, ifadjustheight=True, height = 1, if_merge = True, REPORT_ENUM = 'DT'):
|
||||
|
||||
|
||||
def copy_table(source_table, target_doc, ifadjustheight=True, height = 1):
|
||||
"""
|
||||
Copy a table from one document to another.
|
||||
|
||||
|
@ -116,20 +118,6 @@ def copy_table(source_table, target_doc, ifadjustheight=True, height = 1, if_mer
|
|||
"""
|
||||
# Create a new table with the same dimensions
|
||||
new_table = target_doc.add_table(rows=len(source_table.rows), cols=len(source_table.columns))
|
||||
# 获取表格属性
|
||||
tbl_pr = new_table._tbl.tblPr
|
||||
|
||||
# 设置表格边框
|
||||
tbl_borders = OxmlElement('w:tblBorders')
|
||||
for border_name in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
|
||||
border = OxmlElement(f'w:{border_name}')
|
||||
border.set(qn('w:val'), 'single') # 边框类型
|
||||
border.set(qn('w:sz'), '4') # 边框粗细(1-96,8代表1磅)
|
||||
border.set(qn('w:space'), '0') # 边框间距
|
||||
border.set(qn('w:color'), 'auto') # 边框颜色
|
||||
tbl_borders.append(border)
|
||||
|
||||
tbl_pr.append(tbl_borders)
|
||||
|
||||
# Try to apply the same style
|
||||
try:
|
||||
|
@ -142,9 +130,8 @@ def copy_table(source_table, target_doc, ifadjustheight=True, height = 1, if_mer
|
|||
except:
|
||||
pass
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.enum.table import WD_ALIGN_VERTICAL, WD_ROW_HEIGHT_RULE
|
||||
from docx.enum.table import WD_ALIGN_VERTICAL
|
||||
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):
|
||||
|
@ -157,24 +144,18 @@ def copy_table(source_table, target_doc, ifadjustheight=True, height = 1, if_mer
|
|||
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
|
||||
if REPORT_ENUM == 'JF':
|
||||
new_table.cell(i,j).paragraphs[0].runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体') #设置中文字体
|
||||
new_table.cell(i,j).paragraphs[0].runs[0].font.size = Pt(8) # 字体大小
|
||||
"""
|
||||
待添加:如何让表格自适应大小(autofit目前不知为何没有作用)
|
||||
"""
|
||||
#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
|
||||
if if_merge:
|
||||
if ifadjustheight:
|
||||
new_table.rows[i].height = Cm(height)
|
||||
try:
|
||||
new_table = merge_tables(new_table)
|
||||
except Exception as e:
|
||||
print(f"合并表格失败:{e}")
|
||||
return target_doc
|
||||
from docx.shared import Inches
|
||||
|
||||
return new_table
|
||||
|
||||
|
||||
from collections import deque
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
# 报告生成工具使用文档
|
||||
|
||||
## 1. 功能简介
|
||||
本工具用于根据风机巡检数据自动生成 Word 报告。
|
||||
支持 **配置文件** 与 **命令行参数** 两种输入方式,命令行可覆盖配置文件中的同名字段。
|
||||
|
||||
## 2. 准备
|
||||
1. 获取可执行程序。
|
||||
2. 准备输出目录(默认 `./output`)。
|
||||
3. 获取要生成报告的机组id。
|
||||
4. 获取要生成的报告包含的检查来源(外部、内部、防雷)。
|
||||
|
||||
## 3. 配置文件启动
|
||||
|
||||
在项目根目录新建 `config.json`(或任意文件名,但需要在命令行指定),格式可如下,必填必须填写,选填则键也不需要,否则会覆盖默认值,下面是默认的配置文件:
|
||||
```json
|
||||
{
|
||||
"json1": {
|
||||
"turbine_id": "183463dbf40d9278549a76b82b175dd9" //机组id 必填
|
||||
},
|
||||
"json2": {
|
||||
"shengcheng_dir": "./output", //输出目录 必填
|
||||
|
||||
//选填项
|
||||
"if_waibu": true, //是否包含外部检测图片即对应信息
|
||||
"if_neibu": true, //是否包含内部检测图片即对应信息
|
||||
"if_fanglei": true, //是否包含防雷检测图片即对应信息
|
||||
|
||||
"userName": "admin", //报告编制人
|
||||
"baogaoCheck": "未审核", //报告审核人
|
||||
"key_words": "缺,损,裂,脱,污", //关键词(英文逗号分隔),用于汇总表标红
|
||||
"data_processor": "未获取", //数据处理人
|
||||
"jiancha_renyuan": "未获取", //检查人员
|
||||
"check_date": null, //检查日期,日和时必须用空格分隔 YYYY-MM-DD hh:mm:ss
|
||||
"coverurl": null, //封面图片 URL(不指定则获取project表中的封面url
|
||||
"conclusion": "未填写总结", //报告总结文字
|
||||
|
||||
//报告结构定制
|
||||
"if_docx_fengmian": true, //是否包含封面
|
||||
"if_docx_project_overview": true, //是否包含项目概览表
|
||||
"if_docx_inspection_method": true, //是否包含检查方案表
|
||||
"if_docx_inspection_info": true, //是否包含检查信息表
|
||||
"if_docx_chengguo_sub": true, //是否包含成果递交表
|
||||
"if_docx_inspection_text": true, //是否包含检查汇总文字表
|
||||
"if_docx_inspection_picture": true, //是否包含检查汇总图片表
|
||||
"if_docx_defect_picture": true, //是否包含缺陷图片表
|
||||
"if_docx_conclusion": true, //是否包含报告总结
|
||||
|
||||
//下面为枚举字段,程序通过这些字段在数据库中筛选、分类获取到的图片
|
||||
"dianxing_enum": "TYPICAL", //典型图片类型枚举
|
||||
"quexian_enum": "DEFECT", //缺陷图片类型枚举
|
||||
"other_enum": "OTHER", //其他图片类型枚举 (目前程序不会操作其它图片)
|
||||
"waibu_enum": "out-work", //外部图片来源枚举
|
||||
"neibu_enum": "in-work", //内部图片来源枚举
|
||||
"fanglei_enum": "lightning-protection-work", //防雷图片来源枚举
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 命令行参数
|
||||
|
||||
| 参数 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `--config` / `-c` | 指定配置文件 | `--config prod.json` |
|
||||
| `--turbine_id` / `--id` | 风机 ID | `--turbine_id 123456` |
|
||||
| `--output_dir` / `--out` | 报告输出目录 | `--output_dir ./result` |
|
||||
| `--template_dir` / `--tpl` | 模板目录 | `--template_dir ./tpl` |
|
||||
| `--if_waibu` | 是否包含外部作业图片及信息 | `true` / `false` |
|
||||
| `--if_neibu` | 是否包含内部作业图片及信息 | `true` / `false` |
|
||||
| `--if_fanglei` | 是否包含防雷作业图片及信息 | `true` / `false` |
|
||||
| `--userName` | 报告编制人 | `--userName 张三` |
|
||||
| `--baogaoCheck` | 报告审核人 | `--baogaoCheck 已审核` |
|
||||
| `--key_words` | 关键词(英文逗号分隔) | `--key_words 缺,损,裂` |
|
||||
| `--data_processor` | 数据处理人 | `--data_processor 李四` |
|
||||
| `--jiancha_renyuan` | 检查人员 | `--jiancha_renyuan 王五` |
|
||||
| `--check_date` | 检查日期(YYYY-MM-DD hh:mm:ss) | `--check_date 2024-06-01 12:00:00` |
|
||||
| `--coverurl` | 封面图片 URL | `--coverurl /stastic/path/to/pic` |
|
||||
| `--conclusion` | 报告总结 | `--conclusion 整体良好` |
|
||||
|
||||
## 5. 使用示例
|
||||
|
||||
### 5.1 仅使用配置文件
|
||||
根目录存放config.json后会加载config.json,或者使用命令行指定配置文件名和路径
|
||||
|
||||
#### Linux|bash
|
||||
```bash
|
||||
./report_generator
|
||||
```
|
||||
|
||||
#### Windows|cmd
|
||||
```cmd
|
||||
./report_generator.exe
|
||||
```
|
||||
|
||||
### 5.2 完全命令行
|
||||
|
||||
#### Linux|bash
|
||||
```bash
|
||||
./report_generator \
|
||||
--turbine_id 123456 \
|
||||
--output_dir ./my_out \
|
||||
--if_waibu false \
|
||||
--userName Alice \
|
||||
--check_date 2024-06-01 \
|
||||
--conclusion "检查完毕,无异常"
|
||||
```
|
||||
|
||||
#### Windows|cmd
|
||||
```cmd
|
||||
./report_generator.exe \
|
||||
--turbine_id 123456 \
|
||||
--output_dir ./my_out \
|
||||
--if_waibu false \
|
||||
--userName Alice \
|
||||
--check_date 2024-06-01 \
|
||||
--conclusion "检查完毕,无异常"
|
||||
```
|
||||
|
||||
### 5.3 混合方式
|
||||
|
||||
#### Linux|bash
|
||||
```bash
|
||||
./report_generator --config config.json --userName Bob --if_fanglei false
|
||||
```
|
Before Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 197 B |
Before Width: | Height: | Size: 138 B |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 104 KiB |
BIN
report_generator
117
test.py
|
@ -1,117 +0,0 @@
|
|||
from typing import Dict
|
||||
import os
|
||||
from docx import Document
|
||||
from tools.document_tools import add_picture_to_table, add_table_to_document
|
||||
def add_jf_picture_table(
|
||||
typical_picture_dict: Dict[str, str],
|
||||
defect_picture_dict: Dict[str, str],
|
||||
TYPICAL_MUBAN_DIR: str,
|
||||
DEFECT_MUBAN_DIR: str,
|
||||
output_dir: str,
|
||||
):
|
||||
"""添加金风版本的图片展示表格"""
|
||||
# 初始化文档对象
|
||||
doc = Document()
|
||||
target_filename = os.path.join(output_dir, "output.docx")
|
||||
|
||||
# 处理典型图
|
||||
typical_data = []
|
||||
defect_keys = [] # 记录有缺陷的典型图key
|
||||
|
||||
# 准备典型图数据
|
||||
for key, value in typical_picture_dict.items():
|
||||
if "损伤" in key or "损伤" in value:
|
||||
# 情况二:有缺陷
|
||||
defect_count = int(value.split("损伤类型")[1].split("处")[0])
|
||||
typical_data.append([key, "损伤{}处,详见下表".format(defect_count), value])
|
||||
defect_keys.append((key, defect_count))
|
||||
else:
|
||||
# 情况一:正常图片
|
||||
typical_data.append([key, value, ""]) # 第三行留空
|
||||
|
||||
# 添加典型图表格
|
||||
if typical_data:
|
||||
# 将数据转换为适合表格的格式(3行×n列)
|
||||
# 需要将数据从行优先转为列优先
|
||||
cols = len(typical_data)
|
||||
table_data = [[], [], []]
|
||||
for col_data in typical_data:
|
||||
for i in range(3):
|
||||
table_data[i].append(col_data[i] if i < len(col_data) else "")
|
||||
|
||||
# 添加典型图表格
|
||||
add_table_to_document(
|
||||
target_filename=target_filename,
|
||||
source_filename=TYPICAL_MUBAN_DIR,
|
||||
rows=3,
|
||||
cols=cols,
|
||||
table_num=0,
|
||||
data=table_data
|
||||
)
|
||||
|
||||
# 添加典型图中的图片
|
||||
doc = Document(target_filename)
|
||||
for col_idx, (key, value) in enumerate(typical_picture_dict.items()):
|
||||
if "损伤" not in key and "损伤" not in value: # 只有正常图片才添加
|
||||
add_picture_to_table(
|
||||
target_doc=doc,
|
||||
target_filename=target_filename,
|
||||
row=1, # 第二行是图片
|
||||
col=col_idx,
|
||||
image_path=value
|
||||
)
|
||||
doc.save(target_filename)
|
||||
|
||||
# 处理缺陷图
|
||||
if defect_keys:
|
||||
# 遍历所有有缺陷的典型图
|
||||
for typical_key, defect_count in defect_keys:
|
||||
# 计算需要多少个缺陷图表格(每表5列)
|
||||
table_count = (defect_count + 4) // 5 # 向上取整
|
||||
|
||||
for table_idx in range(table_count):
|
||||
# 计算当前表格的列数
|
||||
cols_in_table = min(5, defect_count - table_idx * 5)
|
||||
|
||||
# 准备缺陷图数据
|
||||
defect_data = []
|
||||
for i in range(cols_in_table):
|
||||
defect_idx = table_idx * 5 + i
|
||||
defect_key = list(defect_picture_dict.keys())[defect_idx]
|
||||
defect_value = list(defect_picture_dict.values())[defect_idx]
|
||||
defect_data.append([defect_key, defect_value])
|
||||
|
||||
# 将数据转换为适合表格的格式(2行×n列)
|
||||
table_data = [[], []]
|
||||
for col_data in defect_data:
|
||||
for i in range(2):
|
||||
table_data[i].append(col_data[i] if i < len(col_data) else "")
|
||||
|
||||
# 添加缺陷图表格
|
||||
add_table_to_document(
|
||||
target_filename=target_filename,
|
||||
source_filename=DEFECT_MUBAN_DIR,
|
||||
rows=2,
|
||||
cols=cols_in_table,
|
||||
table_num=0,
|
||||
data=table_data
|
||||
)
|
||||
|
||||
# 添加缺陷图中的图片
|
||||
doc = Document(target_filename)
|
||||
for col_idx in range(cols_in_table):
|
||||
defect_idx = table_idx * 5 + col_idx
|
||||
defect_value = list(defect_picture_dict.values())[defect_idx]
|
||||
add_picture_to_table(
|
||||
target_doc=doc,
|
||||
target_filename=target_filename,
|
||||
row=1, # 第二行是图片
|
||||
col=col_idx,
|
||||
image_path=defect_value
|
||||
)
|
||||
doc.save(target_filename)
|
||||
|
||||
return target_filename
|
||||
|
||||
|
||||
add_jf_picture_table({""})
|
86
tools/1.md
|
@ -1,86 +0,0 @@
|
|||
```python
|
||||
def tree_dict_to_table_data(tree: Dict) -> List[List[str]]:
|
||||
"""
|
||||
将树状字典转换为二维表格数据
|
||||
|
||||
参数:
|
||||
tree: 树状字典,结构为dict[dict[...[list]]]
|
||||
|
||||
返回:
|
||||
二维列表表示的表格数据
|
||||
"""
|
||||
if not tree:
|
||||
return []
|
||||
|
||||
# 首先确定树的深度
|
||||
depth = 0
|
||||
current = tree
|
||||
while isinstance(current, dict):
|
||||
depth += 1
|
||||
# 获取第一个子节点来继续探测深度
|
||||
if current:
|
||||
current = next(iter(current.values()))
|
||||
else:
|
||||
break
|
||||
|
||||
# 收集所有路径和叶子节点
|
||||
paths = []
|
||||
|
||||
def traverse(node, current_path):
|
||||
if isinstance(node, dict):
|
||||
for key, value in node.items():
|
||||
traverse(value, current_path + [str(key)])
|
||||
elif isinstance(node, list):
|
||||
paths.append((current_path, node))
|
||||
else:
|
||||
paths.append((current_path, [str(node)]))
|
||||
|
||||
traverse(tree, [])
|
||||
|
||||
# 确定最大深度(处理可能的不平衡树)
|
||||
max_depth = max(len(path) for path, _ in paths) if paths else 0
|
||||
max_leaf_length = max(len(leaf) for _, leaf in paths) if paths else 0
|
||||
|
||||
# 填充路径到最大深度
|
||||
filled_paths = []
|
||||
for path, leaf in paths:
|
||||
# 填充路径
|
||||
filled_path = path.copy()
|
||||
while len(filled_path) < max_depth:
|
||||
filled_path.append("") # 用空字符串填充不足的深度
|
||||
|
||||
# 填充叶子节点
|
||||
filled_leaf = leaf.copy()
|
||||
while len(filled_leaf) < max_leaf_length:
|
||||
filled_leaf.append("") # 用空字符串填充不足的叶子长度
|
||||
|
||||
filled_paths.append((filled_path, filled_leaf))
|
||||
|
||||
# 构建表格数据
|
||||
table_data = []
|
||||
|
||||
# 添加路径部分的行
|
||||
for i in range(max_depth):
|
||||
row = []
|
||||
for path, leaf in filled_paths:
|
||||
row.append(path[i])
|
||||
table_data.append(row)
|
||||
|
||||
# 添加叶子部分的行
|
||||
for i in range(max_leaf_length):
|
||||
row = []
|
||||
for path, leaf in filled_paths:
|
||||
row.append(leaf[i] if i < len(leaf) else "")
|
||||
table_data.append(row)
|
||||
|
||||
# 转置表格,使每个路径+叶子成为一列
|
||||
if table_data:
|
||||
# 获取最大列数
|
||||
max_cols = max(len(row) for row in table_data) if table_data else 0
|
||||
# 统一每行的列数
|
||||
table_data = [row + [""] * (max_cols - len(row)) for row in table_data]
|
||||
# 转置
|
||||
table_data = list(map(list, zip(*table_data)))
|
||||
|
||||
return table_data
|
||||
```
|
|
@ -6,5 +6,3 @@ GETWEATHERINFO = "/weather-type/{weatherCode}"
|
|||
GETPICTURELIST = "/image/list"
|
||||
GETPARTLIST = "/part/list"
|
||||
GETYEPIANINFO = "/part/detail/{partId}"
|
||||
DEFECTRECORDLIST = "/defect/list"
|
||||
DEFECTDETAIL = "/defect/detail/{defectId}"
|
|
@ -10,6 +10,7 @@ def get_data(url : str, data_type : str = "data", params : dict = None ) -> dict
|
|||
response = requests.get(url, headers=headers, params=params)
|
||||
if response.status_code == 200:
|
||||
data = json.loads(response.text)
|
||||
print(f"获取到数据:{data}")
|
||||
return data[data_type]
|
||||
else:
|
||||
print(f"请求数据失败,状态码:{response.status_code}")
|
||||
|
@ -40,19 +41,7 @@ def get_weather(weatherid : str) -> dict:
|
|||
weatherurl = DTURL + GETWEATHERINFO.format(weatherCode=weatherid)
|
||||
return get_data(weatherurl, "data")
|
||||
|
||||
def get_defect_record_list() -> list[dict]:
|
||||
url = DTURL + DEFECTRECORDLIST
|
||||
return get_data(url)
|
||||
|
||||
def get_part_list(turbineId : str) -> dict:
|
||||
"""获取对应机组所有部件信息
|
||||
Args:
|
||||
turbineId (str): 机组ID
|
||||
Return:
|
||||
list: 包含所有部件完整信息的列表
|
||||
|
||||
list: 包含所有部件id的列表
|
||||
"""
|
||||
parturl = DTURL + GETPARTLIST
|
||||
params = {
|
||||
"turbineId" : turbineId
|
||||
|
@ -61,42 +50,57 @@ def get_part_list(turbineId : str) -> dict:
|
|||
print(f"获取到部件{result}")
|
||||
return result, [item["partId"] for item in result]
|
||||
|
||||
def get_part_picture(turbineId : str) -> tuple[list[dict], dict[list], list[str]]:
|
||||
|
||||
|
||||
def get_part_picture(turbineId : str) -> tuple[list[dict], list[dict]]:
|
||||
"""获取对应机组所有图片
|
||||
|
||||
Args:
|
||||
turbineId (str): 机组ID
|
||||
Return:
|
||||
list: 包含所有对应机组的部件信息的列表
|
||||
dict[list]: 包含所有图片信息的列表(按部件分list)
|
||||
Yepians: 包含所有叶片信息的列表
|
||||
|
||||
list: 包含所有图片信息的列表(按部件分list)
|
||||
[
|
||||
{
|
||||
"imageId": "图片id",
|
||||
"imageName": "图片名",
|
||||
"imagePath": "图片路径",
|
||||
"partId": "部件id",
|
||||
"partName": "部件名",
|
||||
"imageResolution": "字符串",
|
||||
"focalDistance": "字符串/None",
|
||||
"shootingTime": "字符串/None",
|
||||
"cameraManufacturer": "字符串/None",
|
||||
"cameraModel": "字符串/None",
|
||||
"weather": "字符串/None",
|
||||
"weatherLabel": "字符串/None",
|
||||
"humidness": "数值/None",
|
||||
"temperature": "字符串/None",
|
||||
"windLevel": "数值/None",
|
||||
"shootingMethod": "字符串/None",
|
||||
"shootingMethodLabel": "字符串/None",
|
||||
"shootingDistance": "数值/None",
|
||||
"collectorName": "字符串/None",
|
||||
"imageType": "None",
|
||||
"imageTypeLabel": "None",
|
||||
"audioList": "None",
|
||||
"gps": "字符串/None"
|
||||
},
|
||||
...
|
||||
]
|
||||
"""
|
||||
picturerul = DTURL + GETPICTURELIST
|
||||
part_data, part_list = get_part_list(turbineId)
|
||||
#先按顺序找叶片123
|
||||
Yepians = []
|
||||
yepian_to_find = ["叶片1","叶片2","叶片3"]
|
||||
#依次获取叶片1,2,3的信息(未考虑多个同叶片的信息情况,正常情况下不会发生)
|
||||
try:
|
||||
part_result = {}
|
||||
yepian_part_result = {}
|
||||
for name in yepian_to_find:
|
||||
# 找到第一个匹配的部件并添加到列表中
|
||||
for part in part_data:
|
||||
if part['partName'] == name:
|
||||
Yepians.append(part)
|
||||
for image in get_data(picturerul, params = {"partId" : part["partId"]}):
|
||||
if part["partName"] not in yepian_part_result:
|
||||
yepian_part_result[part["partName"]] = []
|
||||
yepian_part_result[part["partName"]].append(image)
|
||||
elif part['partName'] not in part_result and part['partName'] not in yepian_to_find:
|
||||
for image in get_data(picturerul, params = {"partId" : part["partId"]}):
|
||||
if part["partName"] not in part_result:
|
||||
part_result[part["partName"]] = []
|
||||
part_result[part["partName"]].append(image)
|
||||
except Exception as e:
|
||||
print(f"获取叶片信息失败,异常:{e}")
|
||||
return None, None, None
|
||||
return part_data, yepian_part_result, part_result, Yepians
|
||||
result = []
|
||||
for part_id in part_list:
|
||||
params = {
|
||||
"partId" : part_id,
|
||||
}
|
||||
for images in get_data(picturerul, params = params):
|
||||
result.append(images)
|
||||
print(f"图片数据获取成功:{result}")
|
||||
return part_data, result
|
||||
|
||||
def get_yepian_xiangqing(yepian_id : str) -> dict:
|
||||
url = GETYEPIANINFO.format(partId=yepian_id)
|
||||
|
@ -108,60 +112,4 @@ def find_defect_record(defect_pictures : list[dict]):
|
|||
Args:
|
||||
defect_pictures (list[dict]): 缺陷图列表
|
||||
"""
|
||||
for pic in defect_pictures:
|
||||
pic_id = pic["imageId"]
|
||||
|
||||
def get_full_picture_url(pic_url : str) -> str:
|
||||
"""获取完整的图片URL
|
||||
|
||||
Args:
|
||||
pic_url (str): 图片URL
|
||||
|
||||
Returns:
|
||||
str: 完整的图片URL
|
||||
"""
|
||||
return DTURL + pic_url
|
||||
|
||||
def check_pic_url(pic_url: str, timeout: int = 5) -> bool:
|
||||
"""检查图片URL是否有效
|
||||
仅验证了头部和Content-Type
|
||||
|
||||
Args:
|
||||
pic_url: 图片URL
|
||||
timeout: 请求超时时间(秒)
|
||||
|
||||
Returns:
|
||||
bool: True 如果是有效图片,否则 False
|
||||
"""
|
||||
print(f"检查图片URL:{pic_url}")
|
||||
if pic_url is None:
|
||||
return False
|
||||
pic_url = get_full_picture_url(pic_url)
|
||||
try:
|
||||
# 发起HEAD请求(更快,仅获取头部信息)
|
||||
response = requests.head(pic_url, timeout=timeout, allow_redirects=True)
|
||||
if response.status_code != 200:
|
||||
print(f"请求图片失败,状态码:{response.status_code}")
|
||||
return False
|
||||
|
||||
# 检查Content-Type是否是图片类型
|
||||
content_type = response.headers.get("Content-Type", "").lower()
|
||||
if not content_type.startswith("image/"):
|
||||
print(f"图片类型错误:{content_type}")
|
||||
return False
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"请求图片失败, 未知错误{e}")
|
||||
return False
|
||||
|
||||
def get_defect_detail(defectId : str) -> dict:
|
||||
"""获取缺陷详情
|
||||
|
||||
Args:
|
||||
defectId (str): 缺陷ID
|
||||
|
||||
Returns:
|
||||
dict: 缺陷详情
|
||||
"""
|
||||
url = DTURL + DEFECTDETAIL.format(defectId=defectId)
|
||||
return get_data(url, "data")
|
|
@ -1,20 +0,0 @@
|
|||
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()
|
109
tools/argtool.py
|
@ -1,109 +0,0 @@
|
|||
from typing import Dict, Any
|
||||
import json
|
||||
import argparse
|
||||
def load_config(config_path: str) -> Dict[str, Any]:
|
||||
"""从JSON配置文件加载配置"""
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
def parse_arguments():
|
||||
parser = argparse.ArgumentParser(description='报告生成工具')
|
||||
|
||||
# 添加配置文件参数
|
||||
parser.add_argument('--config', '-c', dest='config_file',
|
||||
default='config.json',
|
||||
help='配置文件路径,默认为当前目录下的config.json')
|
||||
|
||||
# 保留一些关键参数作为命令行可覆盖选项
|
||||
parser.add_argument('--turbine_id', '--id', dest='turbine_id',
|
||||
help='覆盖配置文件中的风机ID')
|
||||
parser.add_argument('--output_dir', '--out', dest='output_dir',
|
||||
help='覆盖配置文件中的输出目录')
|
||||
parser.add_argument('--if_waibu', type=lambda x: x.lower() in ('true', '1', 'yes'),
|
||||
help='是否包含外部作业章节')
|
||||
parser.add_argument('--if_neibu', type=lambda x: x.lower() in ('true', '1', 'yes'),
|
||||
help='是否包含内部作业章节')
|
||||
parser.add_argument('--if_fanglei', type=lambda x: x.lower() in ('true', '1', 'yes'),
|
||||
help='是否包含防雷作业章节')
|
||||
parser.add_argument('--userName', dest='userName',
|
||||
help='报告编制人')
|
||||
parser.add_argument('--baogaoCheck', dest='baogaoCheck',
|
||||
help='报告审核状态')
|
||||
parser.add_argument('--key_words', dest='key_words',
|
||||
help='关键词,英文逗号分隔')
|
||||
parser.add_argument('--data_processor', dest='data_processor',
|
||||
help='数据处理人')
|
||||
parser.add_argument('--jiancha_renyuan', dest='jiancha_renyuan',
|
||||
help='检查人员')
|
||||
parser.add_argument('--check_date', dest='check_date',
|
||||
help='检查日期,格式 YYYY-MM-DD')
|
||||
parser.add_argument('--coverurl', dest='coverurl',
|
||||
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],
|
||||
file_cfg: Dict[str, Any],
|
||||
cli: argparse.Namespace) -> Dict[str, Dict[str, Any]]:
|
||||
"""合并默认配置、文件配置和命令行参数"""
|
||||
|
||||
merged = {
|
||||
'json1': {**default_cfg['json1'], **file_cfg.get('json1', {})},
|
||||
'json2': {**default_cfg['json2'], **file_cfg.get('json2', {})}
|
||||
}
|
||||
|
||||
# 高频覆盖项
|
||||
if cli.turbine_id:
|
||||
merged['json1']['turbine_id'] = cli.turbine_id
|
||||
if cli.output_dir:
|
||||
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'
|
||||
'choose_template'):
|
||||
val = getattr(cli, key, None)
|
||||
if val is not None:
|
||||
merged['json2'][key] = val
|
||||
|
||||
return merged
|
||||
def get_default_config() -> Dict[str, Dict[str, Any]]:
|
||||
"""获取默认配置"""
|
||||
return {
|
||||
'json1': {
|
||||
"turbine_id": "183463dbf40d9278549a76b82b175dd9",
|
||||
},
|
||||
'json2': {
|
||||
'shengcheng_dir': "",
|
||||
"dianxing_enum": "TYPICAL",
|
||||
"quexian_enum": "DEFECT",
|
||||
"other_enum": "OTHER",
|
||||
"waibu_enum": "out-work",
|
||||
"neibu_enum": "in-work",
|
||||
"fanglei_enum": "lightning-protection-work",
|
||||
"if_waibu": True,
|
||||
"if_neibu": True,
|
||||
"if_fanglei": True,
|
||||
"userName": "admin",
|
||||
"baogaoCheck": "未审核",
|
||||
'key_words': '缺,损,裂,脱,污',
|
||||
"data_processor": "未获取",
|
||||
"shigong_fangan": None,
|
||||
'jiancha_renyuan': '未获取',
|
||||
"check_date": None,
|
||||
"coverurl": None,
|
||||
"conclusion": "未填写总结",
|
||||
"if_docx_fengmian": True,
|
||||
"if_docx_project_overview": True,
|
||||
"if_docx_inspection_method": True,
|
||||
"if_docx_inspection_info": True,
|
||||
"if_docx_chengguo_sub": True,
|
||||
"if_docx_inspection_text": True,
|
||||
"if_docx_inspection_picture": True,
|
||||
"if_docx_defect_picture": True,
|
||||
"if_docx_conclusion": True,
|
||||
"choose_template" : "DT"
|
||||
}
|
||||
}
|
|
@ -138,7 +138,7 @@ def split_table_by_row_content(
|
|||
return f"处理表格时出错: {str(e)}"
|
||||
|
||||
|
||||
def add_heading(filename: str, text: str, level: int = 1) -> str:
|
||||
async def add_heading(filename: str, text: str, level: int = 1) -> str:
|
||||
"""对文档增加标题
|
||||
|
||||
Args:
|
||||
|
@ -179,10 +179,10 @@ def add_heading(filename: str, text: str, level: int = 1) -> str:
|
|||
doc.save(filename)
|
||||
return f"Heading '{text}' (level {level}) added to {filename}"
|
||||
except Exception as style_error:
|
||||
print("style-based approach fails, use direct formatting")
|
||||
# If style-based approach fails, use direct formatting
|
||||
paragraph = doc.add_paragraph()
|
||||
run = paragraph.add_run(text)
|
||||
paragraph = doc.add_paragraph(text)
|
||||
paragraph.style = doc.styles['Normal']
|
||||
run = paragraph.runs[0]
|
||||
run.bold = True
|
||||
rPr = run.element.get_or_add_rPr()
|
||||
rFonts = rPr.get_or_add_rFonts()
|
||||
|
@ -307,174 +307,149 @@ async def add_table(filename: str, rows: int, cols: int, data: Optional[List[Lis
|
|||
except Exception as e:
|
||||
return f"Failed to add table: {str(e)}"
|
||||
|
||||
def add_picture_to_table(
|
||||
target_doc: Document,
|
||||
target_filename: str,
|
||||
row: int,
|
||||
col: int,
|
||||
image_path: str,
|
||||
table_num: int = -1,
|
||||
width: Optional[float] = None,
|
||||
height: Optional[float] = None
|
||||
) -> str:
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
import requests
|
||||
|
||||
is_url = image_path.startswith(("http://", "https://"))
|
||||
image_bytes = None
|
||||
|
||||
try:
|
||||
# 1. 获取图片数据
|
||||
if is_url:
|
||||
response = requests.get(image_path, timeout=30)
|
||||
response.raise_for_status()
|
||||
image_bytes = BytesIO(response.content)
|
||||
else:
|
||||
if not os.path.exists(image_path):
|
||||
return f"Image not found: {image_path}"
|
||||
with open(image_path, 'rb') as f:
|
||||
image_bytes = BytesIO(f.read())
|
||||
|
||||
# 2. 准备图片数据(关键步骤)
|
||||
img = Image.open(image_bytes)
|
||||
final_bytes = BytesIO()
|
||||
|
||||
# 转换为Word兼容的最佳格式
|
||||
if img.mode == 'RGBA':
|
||||
img.save(final_bytes, format='PNG')
|
||||
else:
|
||||
img.save(final_bytes, format='JPEG', quality=85)
|
||||
|
||||
final_bytes.seek(0) # ⚠️ 必须重置指针!
|
||||
|
||||
# 3. 添加到文档
|
||||
table = target_doc.tables[table_num]
|
||||
cell = table.cell(row, col)
|
||||
|
||||
# 彻底清除单元格
|
||||
for paragraph in cell.paragraphs:
|
||||
paragraph.clear()
|
||||
paragraph = cell.add_paragraph()
|
||||
run = paragraph.add_run()
|
||||
|
||||
# 添加图片(带异常捕获)
|
||||
try:
|
||||
if width:
|
||||
try:
|
||||
run.add_picture(final_bytes, width=Inches(width), height=Inches(height))
|
||||
except:
|
||||
run.add_picture(final_bytes, width=Inches(width))
|
||||
else:
|
||||
run.add_picture(final_bytes)
|
||||
except Exception:
|
||||
final_bytes.seek(0) # 再次重置指针
|
||||
if width:
|
||||
run.add_picture(final_bytes, width=Inches(width), height=Inches(height))
|
||||
else:
|
||||
run.add_picture(final_bytes)
|
||||
|
||||
# 4. 设置对齐并保存
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
|
||||
paragraph.paragraph_format.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
|
||||
|
||||
# 确保保存到新文件(避免内存文档与文件不同步)
|
||||
temp_filename = target_filename.replace('.docx', '_temp.docx')
|
||||
target_doc.save(temp_filename)
|
||||
|
||||
# 验证文档有效性
|
||||
try:
|
||||
Document(temp_filename) # 尝试读取
|
||||
os.replace(temp_filename, target_filename)
|
||||
except Exception:
|
||||
os.remove(temp_filename)
|
||||
return "Failed to generate valid document"
|
||||
|
||||
return "Picture added successfully"
|
||||
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
import requests
|
||||
from io import BytesIO
|
||||
from PIL import Image
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
|
||||
|
||||
def add_picture(filename: str, image_path: str, width: Optional[float] = None, height: Optional[float] = None, is_center: Optional[bool] = False) -> str:
|
||||
"""添加一个图片到文档中(支持本地路径或 URL)
|
||||
async def add_picture_to_table(target_doc: Document, target_filename: str, row: int, col: int, image_path: str,table_num: int = -1, width: Optional[float] = None) -> str:
|
||||
"""向文档中对应表格添加图片
|
||||
|
||||
Args:
|
||||
filename: 文档路径
|
||||
image_path: 图片路径(本地路径或 URL)
|
||||
width: 图片大小(英寸)
|
||||
target_doc: 目标文档
|
||||
target_filename: 目标文档保存路径
|
||||
row: 表格行数
|
||||
col: 表格列数
|
||||
image_path: 图片路径
|
||||
table_num: 表格序号,默认为-1,即最后一个表格
|
||||
width: 图片宽度,默认为None,表示使用原始图片大小
|
||||
"""
|
||||
filename = ensure_docx_extension(filename)
|
||||
|
||||
# 检查文档是否存在
|
||||
if not os.path.exists(filename):
|
||||
return f"Document {filename} does not exist"
|
||||
|
||||
abs_filename = os.path.abspath(filename)
|
||||
is_url = image_path.startswith(("http://", "https://"))
|
||||
from PIL import Image
|
||||
if not os.path.exists(image_path):
|
||||
return f"Image file not found: {image_path}"
|
||||
|
||||
# Check image file size
|
||||
try:
|
||||
doc = Document(abs_filename)
|
||||
para = doc.add_paragraph()
|
||||
run = para.add_run()
|
||||
# 处理 URL 图片
|
||||
if is_url:
|
||||
try:
|
||||
response = requests.get(image_path, timeout=10)
|
||||
response.raise_for_status() # 检查请求是否成功
|
||||
image_bytes = BytesIO(response.content)
|
||||
|
||||
# 验证图片有效性(可选)
|
||||
Image.open(image_bytes).verify()
|
||||
image_bytes.seek(0) # 重置指针
|
||||
|
||||
# 添加到文档
|
||||
if width:
|
||||
run.add_picture(image_bytes, width=Inches(width), height=Inches(height))
|
||||
else:
|
||||
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:
|
||||
return f"Failed to download/add URL image: {str(url_error)}"
|
||||
|
||||
# 处理本地图片
|
||||
else:
|
||||
abs_image_path = os.path.abspath(image_path)
|
||||
if not os.path.exists(abs_image_path):
|
||||
return f"Image file not found: {abs_image_path}"
|
||||
|
||||
# 检查文件大小和可读性(原逻辑)
|
||||
try:
|
||||
image_size = os.path.getsize(abs_image_path) / 1024
|
||||
image_size = os.path.getsize(image_path) / 1024 # Size in KB
|
||||
if image_size <= 0:
|
||||
return f"Image file is empty: {abs_image_path}"
|
||||
return f"Image file appears to be empty: {image_path} (0 KB)"
|
||||
elif image_size > 9126:
|
||||
# Create the output directory if it doesn't exist
|
||||
output_dir = os.path.join(os.path.dirname(image_path), "压缩图片")
|
||||
if not os.path.exists(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# Define the output path for the compressed image
|
||||
image_name = os.path.basename(image_path)
|
||||
output_path = os.path.join(output_dir, image_name)
|
||||
|
||||
# Compress the image
|
||||
while image_size > 9126:
|
||||
print(f"压缩图片:{image_path} ({image_size:.2f} KB) -> {output_path} (9126 KB)")
|
||||
with Image.open(image_path) as img:
|
||||
img.save(output_path, optimize=True, quality=85)
|
||||
image_size = os.path.getsize(output_path) / 1024 # Size in KB
|
||||
|
||||
# Update the image path to the compressed image path
|
||||
image_path = output_path
|
||||
except Exception as size_error:
|
||||
return f"Error checking image file: {str(size_error)}"
|
||||
|
||||
# 添加到文档
|
||||
try:
|
||||
table = target_doc.tables[table_num]
|
||||
# Add the picture to the cell
|
||||
cell = table.cell(row, col)
|
||||
if len(cell.text) == 1: cell.text = ""
|
||||
paragraph = cell.paragraphs[-1]
|
||||
run = paragraph.add_run()
|
||||
try:
|
||||
if width:
|
||||
run.add_picture(abs_image_path, width=Inches(width), height=Inches(height))
|
||||
run.add_picture(image_path, width=Inches(width))
|
||||
else:
|
||||
run.add_picture(abs_image_path)
|
||||
if is_center:
|
||||
para.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
run.add_picture(image_path)
|
||||
except Exception as e:
|
||||
# 如果添加图片时出现问题,尝试将图片转换为PNG格式
|
||||
try:
|
||||
print(f"正常添加失败,尝试转换图片后添加:{image_path}")
|
||||
# 打开图片
|
||||
img = Image.open(image_path)
|
||||
# 转换为PNG格式
|
||||
temp_image_path = os.path.splitext(image_path)[0] + '.png'
|
||||
img.save(temp_image_path, 'PNG')
|
||||
|
||||
# 尝试添加转换后的图片
|
||||
if width:
|
||||
run.add_picture(temp_image_path, width=Inches(width))
|
||||
else:
|
||||
run.add_picture(temp_image_path)
|
||||
|
||||
# 添加完成后删除转换后的图片
|
||||
os.remove(temp_image_path)
|
||||
except Exception as e:
|
||||
# 如果转换或添加转换后的图片时出现问题,返回错误信息
|
||||
return f"调用add_picture函数出现问题: {str(e)}"
|
||||
from docx.enum.table import WD_TABLE_ALIGNMENT
|
||||
from docx.enum.table import WD_ALIGN_VERTICAL
|
||||
cell.paragraphs[0].paragraph_format.alignment = WD_TABLE_ALIGNMENT.CENTER
|
||||
cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
|
||||
|
||||
target_doc.save(target_filename)
|
||||
return f"Picture {image_path} added to table {table_num} cell ({row},{col})"
|
||||
except Exception as e:
|
||||
return f"Failed to add picture to table: {str(e)}"
|
||||
|
||||
async def add_picture(filename: str, image_path: str, width: Optional[float] = None) -> str:
|
||||
"""添加一个图片到文档中
|
||||
|
||||
Args:
|
||||
filename: 文档路径
|
||||
image_path: 图片路径
|
||||
width: 图片大小
|
||||
"""
|
||||
filename = ensure_docx_extension(filename)
|
||||
|
||||
# Validate document existence
|
||||
if not os.path.exists(filename):
|
||||
return f"Document {filename} does not exist"
|
||||
|
||||
# Get absolute paths for better diagnostics
|
||||
abs_filename = os.path.abspath(filename)
|
||||
abs_image_path = os.path.abspath(image_path)
|
||||
|
||||
# Validate image existence with improved error message
|
||||
if not os.path.exists(abs_image_path):
|
||||
return f"Image file not found: {abs_image_path}"
|
||||
|
||||
# Check image file size
|
||||
try:
|
||||
image_size = os.path.getsize(abs_image_path) / 1024 # Size in KB
|
||||
if image_size <= 0:
|
||||
return f"Image file appears to be empty: {abs_image_path} (0 KB)"
|
||||
except Exception as size_error:
|
||||
return f"Error checking image file: {str(size_error)}"
|
||||
|
||||
# Check if file is writeable
|
||||
is_writeable, error_message = check_file_writeable(abs_filename)
|
||||
if not is_writeable:
|
||||
return f"Cannot modify document: {error_message}. Consider creating a copy first or creating a new document."
|
||||
|
||||
try:
|
||||
doc = Document(abs_filename)
|
||||
# Additional diagnostic info
|
||||
diagnostic = f"Attempting to add image ({abs_image_path}, {image_size:.2f} KB) to document ({abs_filename})"
|
||||
|
||||
try:
|
||||
if width:
|
||||
doc.add_picture(abs_image_path, width=Inches(width))
|
||||
else:
|
||||
doc.add_picture(abs_image_path)
|
||||
doc.save(abs_filename)
|
||||
return f"Picture {image_path} added to {filename}"
|
||||
except Exception as inner_error:
|
||||
return f"Failed to add picture: {str(inner_error)}"
|
||||
|
||||
# More detailed error for the specific operation
|
||||
error_type = type(inner_error).__name__
|
||||
error_msg = str(inner_error)
|
||||
return f"Failed to add picture: {error_type} - {error_msg or 'No error details available'}\nDiagnostic info: {diagnostic}"
|
||||
except Exception as outer_error:
|
||||
return f"Document processing error: {str(outer_error)}"
|
||||
# Fallback error handling
|
||||
error_type = type(outer_error).__name__
|
||||
error_msg = str(outer_error)
|
||||
return f"Document processing error: {error_type} - {error_msg or 'No error details available'}"
|
||||
|
||||
|
||||
async def add_page_break(filename: str) -> str:
|
||||
|
@ -628,7 +603,7 @@ async def delete_paragraph(filename: str, paragraph_index: int) -> str:
|
|||
return f"Failed to delete paragraph: {str(e)}"
|
||||
|
||||
|
||||
def search_and_replace(filename: str, find_text: str, replace_text: str) -> str:
|
||||
async def search_and_replace(filename: str, find_text: str, replace_text: str) -> str:
|
||||
"""替换所有find_text为replace_text
|
||||
|
||||
Args:
|
||||
|
@ -659,3 +634,4 @@ def search_and_replace(filename: str, find_text: str, replace_text: str) -> str:
|
|||
return f"No occurrences of '{find_text}' found."
|
||||
except Exception as e:
|
||||
return f"Failed to search and replace: {str(e)}"
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import sys, os
|
||||
from tools.Get_Json import get_defect_detail
|
||||
from tools.defines import *
|
||||
from tools.content_tools import add_picture_to_table
|
||||
from tools.document_tools import add_table_to_document,search_and_replace
|
||||
from tools.get_pictures import resize_and_reduce_quality
|
||||
|
||||
def caculate_work_days(start_date : str, end_date : str) -> str:
|
||||
"""根据起止日期计算工期
|
||||
|
@ -21,7 +21,46 @@ def caculate_work_days(start_date : str, end_date : str) -> str:
|
|||
|
||||
return (end_date - start_date).days
|
||||
|
||||
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:
|
||||
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
|
||||
|
||||
def get_year_month(date):
|
||||
"""根据格式化date字符串获取年月 'date': '二〇二一年十二月十日 9:00'
|
||||
|
@ -41,7 +80,7 @@ def get_year_month(date):
|
|||
def merge_info(frontend_info, default_info):
|
||||
"""
|
||||
合并前端传入的 info 和默认 info
|
||||
新规则:以default_info为基础字典,用frontend_info完全覆写,取两者的并集
|
||||
规则:如果前端传入的值为空(None 或空字符串),则使用默认值
|
||||
|
||||
Args:
|
||||
frontend_info: 前端传入的字典
|
||||
|
@ -52,371 +91,16 @@ def merge_info(frontend_info, default_info):
|
|||
if not isinstance(frontend_info, dict) or frontend_info is None:
|
||||
return default_info.copy()
|
||||
|
||||
# 先复制默认字典
|
||||
merged_info = default_info.copy()
|
||||
# 用前端字典完全覆写
|
||||
merged_info.update(frontend_info)
|
||||
merged_info = {}
|
||||
|
||||
for key, default_value in default_info.items():
|
||||
# 获取前端传入的值
|
||||
frontend_value = frontend_info.get(key)
|
||||
|
||||
# 判断前端值是否为空(None 或空字符串)
|
||||
if frontend_value is None or frontend_value == "":
|
||||
merged_info[key] = default_value
|
||||
else:
|
||||
merged_info[key] = frontend_value
|
||||
|
||||
return merged_info
|
||||
|
||||
def merge_dicts(dict1, dict2):
|
||||
# 创建一个新的字典来存储合并结果
|
||||
merged_dict = {}
|
||||
|
||||
# 遍历第一个字典
|
||||
for key, value_list in dict1.items():
|
||||
if key in dict2:
|
||||
# 如果键在第二个字典中存在,合并两个列表
|
||||
merged_dict[key] = value_list + dict2[key]
|
||||
else:
|
||||
# 如果键在第二个字典中不存在,直接使用第一个字典的值列表
|
||||
merged_dict[key] = value_list
|
||||
|
||||
# 遍历第二个字典
|
||||
for key, value_list in dict2.items():
|
||||
if key not in dict1:
|
||||
# 如果键在第一个字典中不存在,直接使用第二个字典的值列表
|
||||
merged_dict[key] = value_list
|
||||
|
||||
return merged_dict
|
||||
|
||||
def get_defect_str(Y_defect_list : list[dict] ) -> list:
|
||||
"""将叶片缺陷信息转换为一条条描述信息
|
||||
|
||||
Args:
|
||||
Y_defect_list (list):[
|
||||
{
|
||||
'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' : '/image/path'
|
||||
},
|
||||
....
|
||||
]
|
||||
Returns:
|
||||
result (list):
|
||||
[
|
||||
"叶片1表面裂纹轻微缺陷,位于{defectPosition},建议进行进一步检查",
|
||||
],
|
||||
...
|
||||
"""
|
||||
result = []
|
||||
for item in Y_defect_list:
|
||||
record = item['record']
|
||||
defect_type_label = record.get('defectTypeLabel', '未知类型')
|
||||
defect_level_label = record.get('defectLevelLabel', '未知等级')
|
||||
defect_position = record.get('defectPosition', '未知位置')
|
||||
repair_idea = record.get('repairIdea', '无建议')
|
||||
|
||||
defect_description = f"{defect_type_label}{defect_level_label}缺陷,位于{defect_position},{repair_idea}"
|
||||
result.append(defect_description)
|
||||
|
||||
return result
|
||||
|
||||
def safe_get(data, *keys, default=None):
|
||||
"""
|
||||
递归安全访问嵌套字典的键,如果中间键不存在,则返回默认值
|
||||
|
||||
Args:
|
||||
data (dict): 要访问的字典
|
||||
*keys: 要访问的键(可以是多个,如 "叶片1", "裂纹")
|
||||
default: 如果键不存在,返回的默认值(默认None)
|
||||
|
||||
Returns:
|
||||
如果所有键都存在,返回对应的值;否则返回 default
|
||||
"""
|
||||
if not keys or data is None:
|
||||
return data if data is not None else default
|
||||
|
||||
current_key = keys[0]
|
||||
if isinstance(data, dict):
|
||||
return safe_get(data.get(current_key), *keys[1:], default=default)
|
||||
else:
|
||||
return default
|
||||
|
||||
def is_frozen():
|
||||
return hasattr(sys, 'frozen') or hasattr(sys, '_MEIPASS')
|
||||
|
||||
def get_resource_path(relative_path):
|
||||
""" 获取资源的路径 """
|
||||
if is_frozen():
|
||||
base_path = sys._MEIPASS
|
||||
else:
|
||||
base_path = os.path.abspath(".")
|
||||
|
||||
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:{...}
|
||||
}, #一张图片一个记录
|
||||
...
|
||||
]
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
result2 (dict):
|
||||
{
|
||||
'叶片1': { #按叶片分记录
|
||||
'表面裂纹': [ #按类型分记录
|
||||
|
||||
"""
|
||||
result = {} #缺陷等级数量列表
|
||||
result2 = {} #缺陷树状列表
|
||||
for part_name, part_info in defect_dict.items():
|
||||
if part_name not in result:
|
||||
result[part_name] = {}
|
||||
result2[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]:
|
||||
result2[part_name][type_label] = []
|
||||
|
||||
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)
|
||||
p = record['defectPosition']
|
||||
t = record['defectTypeLabel']
|
||||
l = record['defectLevelLabel']
|
||||
d = record['description']
|
||||
did = record['defectId']
|
||||
iid = record['imageId']
|
||||
detail = get_defect_detail(did)
|
||||
a = detail.get('axial', '')
|
||||
c = detail.get('chordwise', '')
|
||||
area = f"轴向:{a}mm,弦向:{c}mm"
|
||||
|
||||
record_info = [f"{p},{t},{l},{d}", area, iid]
|
||||
result2[part_name][type_label].append(record_info)
|
||||
return result, result2
|
||||
|
||||
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}
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
from typing import Dict, List, Any
|
||||
|
||||
def tree_dict_to_table_data(tree: Dict) -> List[List[Any]]:
|
||||
"""
|
||||
将树状字典转换为二维表格数据(保留原始类型)
|
||||
|
||||
参数:
|
||||
tree: 树状字典,结构为dict[dict[...[list]]]
|
||||
|
||||
返回:
|
||||
二维列表,每个子列表代表一条路径(元素类型与输入一致),确保最内层列表被展开
|
||||
"""
|
||||
result = []
|
||||
|
||||
def traverse(node: Dict, path: List[Any]) -> None:
|
||||
for key, value in node.items():
|
||||
current_path = path + [key] # 保留键的原始类型
|
||||
if isinstance(value, dict):
|
||||
traverse(value, current_path)
|
||||
elif isinstance(value, list):
|
||||
for item in value:
|
||||
if isinstance(item, list):
|
||||
# 如果列表项本身是列表,则展开它
|
||||
result.append(current_path + item)
|
||||
else:
|
||||
# 否则作为单个元素添加
|
||||
result.append(current_path + [item])
|
||||
else:
|
||||
result.append(current_path + [value]) # 保留值的原始类型
|
||||
|
||||
traverse(tree, [])
|
||||
return result
|
||||
|
||||
def defect_list_addtitle(list_data: List[List[str]], turbine_code: str):
|
||||
"""
|
||||
给缺陷列表添加标题行
|
||||
"""
|
||||
list_data.insert(0, DEFECT_TABLE)
|
||||
for i,l1 in enumerate(list_data):
|
||||
if i == 0:
|
||||
continue
|
||||
l1.insert(0, turbine_code)
|
||||
return list_data
|
302
tools/defines.py
|
@ -1,10 +1,22 @@
|
|||
DEFAULT_BASE_INFO = { #项目基本信息
|
||||
"turbine_id" : None,
|
||||
}
|
||||
"""
|
||||
缺陷图目录格式:
|
||||
|
||||
缺陷图期望格式 _隔开
|
||||
|
||||
外部内部命名格式都如下:
|
||||
图片名:xuhao_缺陷类型_缺陷位置_缺陷尺寸_可见程度_紧急程度_危重等级_维修建议
|
||||
例:涂层损伤_叶片ps面距叶根3m处_缺陷尺寸弦向100mm,轴向800mm_轻微_紧急_重要_建议打磨维修
|
||||
每个的选项:见我发的图
|
||||
|
||||
防雷:
|
||||
例:轮毂至塔基导通阻值_169mΩ
|
||||
缺陷例:轮毂至塔基未导通 #即标明未导通即可
|
||||
"""
|
||||
|
||||
DEFAULT_BAOGAO_INFO = {
|
||||
#目录
|
||||
'shengcheng_dir': "", #报告生成的路径
|
||||
'muban_dir': "", #文档模板存放路径
|
||||
|
||||
"dianxing_enum" : "TYPICAL",
|
||||
"quexian_enum" : "DEFECT",
|
||||
|
@ -19,11 +31,15 @@ DEFAULT_BAOGAO_INFO = {
|
|||
"baogaoCheck" : "未审核",
|
||||
'key_words': '缺,损,裂,脱,污', #关键字,用于汇总图的名字包含缺陷时标红,匹配逻辑为正则匹配单个字则为红 后续可优化
|
||||
|
||||
"shigong_fangan" : None,
|
||||
"shigong_fangan" : "None",
|
||||
'jiancha_renyuan': '张三',
|
||||
"check_date" : None,
|
||||
}
|
||||
|
||||
class JIANCHA_ENUM :
|
||||
class WAIBU:
|
||||
PART = "无人机外部高精度飞行"
|
||||
#NEIRONG =
|
||||
|
||||
class SHIGONG_FANGAN_ENUM :
|
||||
class WAIBU:
|
||||
GONGZUO_NEIRONG = "无人机叶片外观巡检"
|
||||
|
@ -54,192 +70,100 @@ class SHIGONG_FANGAN_ENUM :
|
|||
FEISHOURENYUAN_PEIZHI = "1人;主检飞手1人"
|
||||
LUNGUZUOYERENYUAN_PEIZHI = "2人;轮毂作业检查2人"
|
||||
|
||||
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 FANGLEI:
|
||||
TYPICAL_LIST = [
|
||||
"防雷导通测试\n叶尖至塔基测试阻值({Resistance} mΩ)",
|
||||
"防雷导通测试\n叶尖与无人吊篮平台接触良好",
|
||||
"防雷导通测试\n叶尖至塔基检测导线线组值"
|
||||
]
|
||||
class WAIBU:
|
||||
TYPICAL_LIST = [
|
||||
"外观检查(迎、背风面是否有漆面脱落、裂纹等)",
|
||||
"外观检查(前、后缘;如前缘漆面脱落、合模缝开裂等)",
|
||||
"叶片防雨环检查",
|
||||
"叶尖接闪器、排水孔检查,如接闪器损伤、雷击熔融;流水孔堵塞",
|
||||
]
|
||||
class NEIBU:
|
||||
TYPICAL_LIST = [
|
||||
"叶片铭牌",
|
||||
"根部检查(挡板、盖板检查是否损坏、金属件是否丢失等)",
|
||||
"避雷系统检查(避雷线是否断裂、雷电记录卡是否缺失等)",
|
||||
"前后缘检查(前缘粘接及补强是否有开裂、裂纹)",
|
||||
"上下蒙皮检查(蒙皮是否有褶皱、发白、折痕、分层、裂纹等)",
|
||||
"腹板检查(腹板是否变形、偏移,腹板粘接是否开裂、裂纹等)",
|
||||
]
|
||||
|
||||
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": 8,
|
||||
"bold": False,
|
||||
},
|
||||
"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"}
|
||||
},
|
||||
"shading": {
|
||||
"color": "FFFFFF"
|
||||
DEFAULT_BASE_INFO = { #项目基本信息
|
||||
#项目概况
|
||||
'jituan_jianxie': '甲方集团',
|
||||
'jia_Company': '甲方公司名',
|
||||
'jizu_num': '项目规格(台)',
|
||||
'fengchang_name': '风场名称',
|
||||
'fengchang_location': '风场位置',
|
||||
'jizu_xinghao': '机组型号', #机组的型号
|
||||
#乙方信息
|
||||
'yi_Company': '乙方公司名',
|
||||
'fuzeren': '甲方负责人(吴明洲)',
|
||||
'phone_fuzeren': '联系电话:18807109269 ',
|
||||
}
|
||||
}
|
||||
TITLE_STYLE_CONFIG = {
|
||||
"alignment": "center",
|
||||
"font": {
|
||||
"name": "宋体",
|
||||
"size": 9,
|
||||
"bold": False,
|
||||
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": "待施工"
|
||||
},
|
||||
"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"}
|
||||
"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"
|
||||
},
|
||||
"shading": {
|
||||
"color": "FFFFFF"
|
||||
{
|
||||
"partId": "12bc30fb209f3af3bf530541c5b062bd",
|
||||
"projectId": "96e0debf78187300f144d7f3450a2477",
|
||||
"projectName": "三峡能源阿城万兴风电场防雷通道检测项目",
|
||||
"turbineId": "183463dbf40d9278549a76b82b175dd9",
|
||||
"turbineName": "一期012号",
|
||||
"partName": "叶片1",
|
||||
"partCode": "0000",
|
||||
"partType": "VANE-1",
|
||||
"partTypeLabel": "叶片1"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DEFECT_TABLE_TITLE = '叶片故障信息表'
|
||||
DEFECT_TABLE = ['机组号', '叶片编号', '损伤名称', '损坏描述', '面积/S', '备注']
|
||||
|
||||
USE_TOOL_TABLE_TITLE = '使用工器具记录'
|
||||
USE_TOOL_TABLE = ['序号', '工器具名称', '型号规格', '数量', '用途', '备注']
|
||||
class USE_TOOL_ENUM:
|
||||
class OUT:
|
||||
LIST = [
|
||||
["无人机", "大疆 M30", "1", "叶片外观检查观测"]
|
||||
]
|
||||
|
||||
class LIGHT:
|
||||
LIST = [
|
||||
["智能无人悬吊系统", "DT01", "1", "叶片导通测试"],
|
||||
["检测导线", "/", "3", "叶片导通测试"],
|
||||
["电阻仪", "S1950", "1", "叶片导通测试"]
|
||||
]
|
||||
|
||||
class IN:
|
||||
LIST = [
|
||||
["头灯", "/", "2", "轮毂叶片内照明"],
|
||||
["含氧量检测仪", "/", "1", "叶片内部环境检测"],
|
||||
["活动扳手", "17-19", "2", "拆卸·禁锢盖板螺栓"],
|
||||
["手机", "/", "2", "拍照记录"],
|
||||
["叶片内部爬壁机器人", "DT02", "1", "拍照记录"]
|
||||
]
|
||||
|
||||
from enum import Enum
|
||||
from docx.shared import RGBColor
|
||||
class DocxColors(Enum):
|
||||
"""常用颜色枚举"""
|
||||
BLACK = RGBColor(0, 0, 0)
|
||||
WHITE = RGBColor(255, 255, 255)
|
||||
RED = RGBColor(255, 0, 0)
|
||||
GREEN = RGBColor(0, 255, 0)
|
||||
BLUE = RGBColor(0, 0, 255)
|
||||
YELLOW = RGBColor(255, 255, 0)
|
||||
PURPLE = RGBColor(128, 0, 128)
|
||||
ORANGE = RGBColor(255, 165, 0)
|
||||
GRAY = RGBColor(128, 128, 128)
|
||||
DARK_RED = RGBColor(139, 0, 0)
|
||||
DARK_GREEN = RGBColor(0, 100, 0)
|
||||
DARK_BLUE = RGBColor(0, 0, 139)
|
||||
LIGHT_BLUE = RGBColor(173, 216, 230)
|
||||
PINK = RGBColor(255, 192, 203)
|
||||
BROWN = RGBColor(165, 42, 42)
|
||||
|
||||
# 微软Office常用颜色
|
||||
MS_BLUE = RGBColor(46, 116, 181)
|
||||
MS_ORANGE = RGBColor(247, 150, 70)
|
||||
MS_GREEN = RGBColor(80, 175, 74)
|
||||
MS_RED = RGBColor(217, 83, 79)
|
||||
MS_GRAY = RGBColor(166, 166, 166)
|
||||
|
||||
# 获取颜色值
|
||||
@property
|
||||
def rgb(self):
|
||||
return self.value
|
||||
|
||||
DT_EADING_1_CONFIG = {
|
||||
"alignment": "left",
|
||||
"font" : {
|
||||
"name" : "宋体",
|
||||
"size" : Pt(14),
|
||||
"bold" : True,
|
||||
"color" : DocxColors.BLACK.rgb
|
||||
},
|
||||
}
|
||||
|
||||
HEADING_1_CONFIG = {
|
||||
"alignment": "left",
|
||||
"font" : {
|
||||
"name" : "宋体",
|
||||
"size" : Pt(11),
|
||||
"bold" : True,
|
||||
"color" : DocxColors.BLACK.rgb
|
||||
},
|
||||
}
|
||||
|
||||
HEADING_2_CONFIG = {
|
||||
"alignment": "left",
|
||||
"font" : {
|
||||
"name" : "宋体",
|
||||
"size" : Pt(10),
|
||||
"bold" : True,
|
||||
"color" : DocxColors.BLACK.rgb
|
||||
},
|
||||
}
|
||||
HEADING_3_CONFIG = {
|
||||
"alignment": "left",
|
||||
"font" : {
|
||||
"name" : "宋体",
|
||||
"size" : Pt(9),
|
||||
"bold" : True,
|
||||
"color" : DocxColors.BLACK.rgb
|
||||
},
|
||||
],
|
||||
"msg": "",
|
||||
"code": 200,
|
||||
"success": True
|
||||
}
|
||||
|
|
|
@ -6,21 +6,14 @@ import json, re
|
|||
from typing import Dict, List, Optional, Any
|
||||
from docx import Document
|
||||
|
||||
from core.tables import copy_table
|
||||
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, clear_header
|
||||
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, parse_xml
|
||||
from tools.content_tools import search_and_replace,add_picture_to_table,add_picture
|
||||
from tools.Get_Json import get_full_picture_url
|
||||
from tools.get_pictures import resize_and_reduce_quality, get_template_pic
|
||||
from tools.defines import *
|
||||
from docx.enum.section import WD_SECTION
|
||||
from tools.json_to_docx import list_to_json_with_merges, json_to_docx
|
||||
from docx.oxml import OxmlElement
|
||||
from tools.content_tools import search_and_replace,add_picture_to_table
|
||||
|
||||
|
||||
async def create_document(filename: str, title: Optional[str] = None, author: Optional[str] = None, section_args: Optional[Dict[str, Any]] = None) -> str:
|
||||
async def create_document(filename: str, title: Optional[str] = None, author: Optional[str] = None) -> str:
|
||||
"""创建一个包含可选元数据的新Word文档。
|
||||
|
||||
参数:
|
||||
|
@ -50,15 +43,6 @@ async def create_document(filename: str, title: Optional[str] = None, author: Op
|
|||
# 更改纸张大小为A4
|
||||
from docx.shared import Mm, Inches
|
||||
sections = doc.sections
|
||||
if section_args:
|
||||
for section in sections:
|
||||
section.page_height = Mm(section_args.get('page_height', 297))
|
||||
section.page_width = Mm(section_args.get('page_width', 210))
|
||||
section.left_margin = Mm(section_args.get('left_margin', 20))
|
||||
section.right_margin = Mm(section_args.get('right_margin', 20))
|
||||
section.top_margin = Mm(section_args.get('top_margin', 10))
|
||||
section.bottom_margin = Mm(section_args.get('bottom_margin', 10))
|
||||
else:
|
||||
for section in sections:
|
||||
section.page_height = Mm(297)
|
||||
section.page_width = Mm(210)
|
||||
|
@ -166,13 +150,13 @@ def add_documents(target_filename: str, source_filename: str) -> str:
|
|||
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:
|
||||
|
@ -208,7 +192,7 @@ def add_documents(target_filename: str, source_filename: str) -> str:
|
|||
|
||||
|
||||
|
||||
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] = None, key_words: re.Pattern[str] = None, ALIGMENT: Optional[str] = 'CENTER', width: Optional[float] = None) -> Document:
|
||||
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:
|
||||
|
@ -219,7 +203,6 @@ def write_table(target_filename: str, rows: int, cols: int, table_num: int, data
|
|||
data: 表格数据,二维列表,每个单元格为字符串
|
||||
ifadjustheight: bool,为真则表格行高自动调整
|
||||
"""
|
||||
table_num = -1
|
||||
target_filename = ensure_docx_extension(target_filename)
|
||||
# Check if target file is writeable
|
||||
is_writeable, error_message = check_file_writeable(target_filename)
|
||||
|
@ -252,53 +235,46 @@ def write_table(target_filename: str, rows: int, cols: int, table_num: int, data
|
|||
for j, cell_text in enumerate(row_data):
|
||||
if j >= cols + 1:
|
||||
break
|
||||
|
||||
if cell_text is not None and cell_text.endswith((".png", ".jpg", ".jpeg")):
|
||||
target_doc.save(target_filename)
|
||||
target_doc.tables[table_num].cell(i,j).text = ""
|
||||
print(f"在[{i},{j}]处添加图片{cell_text}")
|
||||
if os.path.exists(cell_text):
|
||||
if height:
|
||||
target_doc.tables[table_num].cell(i,j).add_paragraph().add_run().add_picture(cell_text, height=Inches(height), width=Inches(width))
|
||||
else:
|
||||
target_doc.tables[table_num].cell(i,j).add_paragraph().add_run().add_picture(cell_text,)
|
||||
continue
|
||||
else:
|
||||
print(f"本地没有图片{cell_text},从服务器下载图片")
|
||||
if height:
|
||||
add_picture_to_table(target_doc, target_filename, i, j, cell_text, height=Inches(height), width=Inches(width))
|
||||
else:
|
||||
add_picture_to_table(target_doc, target_filename, i, j, get_full_picture_url(cell_text))
|
||||
continue
|
||||
|
||||
if str(cell_text) == "": continue
|
||||
|
||||
print(f"在的[{i},{j}]处写入{str(cell_text)}")
|
||||
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}包含关键字,已置红')
|
||||
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(9) # 字体大小
|
||||
target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体') #设置中文字体
|
||||
|
||||
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 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] = None, key_words: re.Pattern[str] = None, ALIGMENT: Optional[str] = 'CENTER', if_merge : Optional[bool] = True, Report_Enum = 'DT', if_para : Optional[bool] = True, width : Optional[float] = None) -> str:
|
||||
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: 目标文档路径
|
||||
|
@ -313,14 +289,13 @@ def add_table_to_document(target_filename: str, source_filename: str, rows: int,
|
|||
target_filename = ensure_docx_extension(target_filename)
|
||||
source_filename = ensure_docx_extension(source_filename)
|
||||
source_doc = Document(source_filename)
|
||||
|
||||
target_doc = Document(target_filename)
|
||||
if if_para:
|
||||
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
|
||||
|
@ -350,66 +325,56 @@ def add_table_to_document(target_filename: str, source_filename: str, rows: int,
|
|||
# 检查 run.font.name 是否为 None
|
||||
if run.font.name is None:
|
||||
# 设置默认的中文字体名称
|
||||
run.font.name = '宋体(中文正文)'
|
||||
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
|
||||
copy_table(source_doc.tables[0], target_doc, ifadjustheight, height, if_merge, REPORT_ENUM=Report_Enum)
|
||||
from core.tables import copy_table
|
||||
copy_table(source_doc.tables[0], target_doc, ifadjustheight, height)
|
||||
except Exception as e:
|
||||
print(f"添加表格失败:{str(e)}")
|
||||
target_doc.save(target_filename)
|
||||
print(f"{target_doc}写入表格{source_doc.tables[0]}成功")
|
||||
if data:
|
||||
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, width)
|
||||
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}成功"
|
||||
|
||||
|
||||
from docx.document import Document as Document_
|
||||
def add_table(target_filename: str, source_filename : str | Document_, if_merge = True, REPORT_ENUM = 'DT'):
|
||||
if isinstance(source_filename, str):
|
||||
output_doc = copy_table(Document(source_filename).tables[0], Document(target_filename), True, if_merge=if_merge, REPORT_ENUM = REPORT_ENUM)
|
||||
output_doc.save(target_filename)
|
||||
else:
|
||||
try:
|
||||
output_doc = copy_table(source_filename.tables[0], Document(target_filename), True, if_merge=if_merge, REPORT_ENUM = REPORT_ENUM)
|
||||
output_doc.save(target_filename)
|
||||
except Exception as e:
|
||||
print(f"添加表格失败:{str(e)}")
|
||||
return f"{target_filename}添加表格{source_filename}成功"
|
||||
|
||||
def add_table_and_replace(
|
||||
target_filename: str,
|
||||
source_filename: str,
|
||||
ifadjustheight: Optional[bool] = True,
|
||||
list_to_replace: dict = {},
|
||||
height: Optional[float] = 1,
|
||||
no_para : Optional[bool] = False,
|
||||
REPORT_ENUM = 'DT'):
|
||||
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, 待替换内容和替换内容
|
||||
height: float, 表格行高
|
||||
no_para: bool, 无段落
|
||||
"""
|
||||
target_filename = ensure_docx_extension(target_filename)
|
||||
source_filename = ensure_docx_extension(source_filename)
|
||||
source_doc = Document(source_filename)
|
||||
|
||||
target_doc = Document(target_filename)
|
||||
if not no_para:
|
||||
try:
|
||||
# Copy all paragraphs
|
||||
for paragraph in source_doc.paragraphs:
|
||||
|
@ -451,16 +416,24 @@ def add_table_and_replace(
|
|||
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, REPORT_ENUM=REPORT_ENUM)
|
||||
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(search_and_replace(target_filename, find_text, replace_text))
|
||||
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:
|
||||
"""合并文档(文本) 表格会添加到最后
|
||||
|
@ -584,51 +557,6 @@ async def right_align_last_three_para(target_filename: str) -> str:
|
|||
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 = add_table_to_document(output_dir, JIANCHA_XIANGQING_DIR, row, col, i, Table, FLAG, if_para=False)
|
||||
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)
|
||||
add_picture_to_table(output_doc, output_dir, 4, 0, picturedir, i, 4.7232)
|
||||
except Exception as e:
|
||||
print(f"添加图片失败:{e}")
|
||||
try:
|
||||
print(search_and_replace(output_dir, 'tupian_xuhao', f'{xuhao}'))
|
||||
except Exception as e:
|
||||
print(f"替换图片序号失败:{e}")
|
||||
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):
|
||||
"""添加对应表格且填写图片名与插入图片
|
||||
|
||||
|
@ -660,456 +588,22 @@ async def process_images_table(data_dict, output_dir, start_i, JIANCHA_NEIRONG_P
|
|||
line_index += 1
|
||||
print(f"当前待插入表格: {JIANCHA_NEIRONG_TEXT}")
|
||||
print(f"当前表格序号为 {i}")
|
||||
output_doc, message = add_table_to_document(
|
||||
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 = add_table_to_document(
|
||||
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(add_picture_to_table(output_doc, output_dir, 0, k, pic_path, i, 1.8898))
|
||||
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 = 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 = 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(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 # 返回最后使用的表格序号
|
||||
|
||||
def add_header(target_dir : str, report_enum : str, if_clear_header : Optional[bool] = True, if_section : Optional[bool] = True, text : str = None):
|
||||
"""添加页眉,添加封面后调用此函数,会分离页面和后续页面的节。
|
||||
|
||||
Args:
|
||||
target_dir (str): 目标目录
|
||||
start_section (int): 开始节
|
||||
end_section (int): 结束节
|
||||
report_enum (str): 报告类型(DT或JF)
|
||||
"""
|
||||
document = Document(target_dir) # 打开文档
|
||||
|
||||
if if_clear_header:
|
||||
for section in document.sections: # 遍历所有节的页眉
|
||||
clear_header(section) # 清除页眉的段落
|
||||
|
||||
if if_section:
|
||||
print(f"文档节数:{len(document.sections)},开始往当前节后添加页眉")
|
||||
document.sections[0].header.is_linked_to_previous = False # 取消页眉与上一页关联
|
||||
|
||||
document.add_section(WD_SECTION.NEW_PAGE)
|
||||
header = document.sections[1].header
|
||||
|
||||
header.is_linked_to_previous = False # 取消页眉与上一页关联
|
||||
else:
|
||||
header = document.sections[-1].header
|
||||
header.is_linked_to_previous = False # 取消页眉与上一页关联
|
||||
paragraph = header.paragraphs[0] # 获取页眉的第一个段落
|
||||
run = paragraph.add_run()
|
||||
|
||||
if report_enum == 'JF':
|
||||
print("添加金风模板的页眉")
|
||||
pic = run.add_picture(get_template_pic(TEMPLATE_HEADER.JINFENG_HEADER.PIC_DIR))
|
||||
if text:
|
||||
run = paragraph.add_run(text)
|
||||
else:
|
||||
run = paragraph.add_run(TEMPLATE_HEADER.JINFENG_HEADER.PARA)
|
||||
run.font.name = TEMPLATE_HEADER.JINFENG_HEADER.FONT
|
||||
run.font.size = TEMPLATE_HEADER.JINFENG_HEADER.PT
|
||||
|
||||
elif report_enum == 'DT':
|
||||
print("添加迪特模板的页眉")
|
||||
pic = run.add_picture(get_template_pic(TEMPLATE_HEADER.DT_HEADER.PIC_DIR))
|
||||
if text:
|
||||
run = paragraph.add_run(text)
|
||||
else:
|
||||
run = paragraph.add_run(TEMPLATE_HEADER.DT_HEADER.PARA)
|
||||
run.font.name = TEMPLATE_HEADER.DT_HEADER.FONT
|
||||
run.font.size = TEMPLATE_HEADER.DT_HEADER.PT
|
||||
|
||||
else:
|
||||
print("未知模板,不添加页眉")
|
||||
# 定义边框的 XML 字符串
|
||||
border_xml = """
|
||||
<w:pBdr xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
||||
<w:bottom w:val="single" w:sz="8" w:space="1" w:color="000000"/>
|
||||
</w:pBdr>
|
||||
"""
|
||||
|
||||
# 解析 XML 并添加到段落属性
|
||||
pPr = paragraph._element.get_or_add_pPr()
|
||||
pBdr = parse_xml(border_xml)
|
||||
pPr.append(pBdr)
|
||||
|
||||
print(f"文档节数:{len(document.sections)}")
|
||||
document.save(target_dir) # 保存文档
|
||||
|
||||
from docx.enum.section import WD_ORIENT
|
||||
from docx.enum.text import WD_BREAK
|
||||
from docx.document import Document as Document_
|
||||
def add_landscape_section(target_dir : str):
|
||||
# 添加横向节
|
||||
doc = Document(target_dir)
|
||||
section = doc.add_section()
|
||||
section.orientation = WD_ORIENT.LANDSCAPE
|
||||
section.page_width, section.page_height = section.page_height, section.page_width
|
||||
print(f"文档:{target_dir},添加了新横向节,页宽:{section.page_width}, 页高:{section.page_height}")
|
||||
return doc
|
||||
|
||||
def add_section(target_dir : str) -> Document_:
|
||||
doc = Document(target_dir)
|
||||
section = doc.add_section()
|
||||
return doc
|
||||
|
||||
def add_title(target_dir : str, title : str, style_config : dict = {}):
|
||||
doc = Document(target_dir)
|
||||
para = doc.add_paragraph()
|
||||
run = para.add_run(title)
|
||||
font_config = style_config.get('font', {})
|
||||
run.font.name = font_config.get('name', "宋体")
|
||||
run.element.get_or_add_rPr().get_or_add_rFonts().set(qn('w:eastAsia'), font_config.get('name', "宋体"))
|
||||
run.font.size = font_config.get('size', Pt(14))
|
||||
run.bold = font_config.get('bold', False)
|
||||
para.alignment = style_config.get('alignment', WD_ALIGN_PARAGRAPH.CENTER)
|
||||
print(f"添加新标题:{title},字体:{run.font.name},大小:{run.font.size},是否加粗:{run.bold}")
|
||||
return doc
|
||||
|
||||
def merge_documents(target_dir : str, source_dirs : List[str]):
|
||||
"""合并多个文档、图片
|
||||
|
||||
Args:
|
||||
target_dir (str): 目标目录
|
||||
source_dirs (List[str]): 源目录列表
|
||||
"""
|
||||
# 打开目标文档
|
||||
document = Document(target_dir)
|
||||
|
||||
for dir in source_dirs:
|
||||
if dir.endswith('.docx'):
|
||||
print(add_documents(target_dir, dir))
|
||||
if dir.endswith('.jpg') or dir.endswith('.png'):
|
||||
print(add_picture(target_dir, dir, is_center=True))
|
||||
|
||||
def add_defect_info_table(output_dir, defect_info, MUBAN_DIR, total_table_num, REPORT_ENUM):
|
||||
"""将defectinfo写入word文档
|
||||
|
||||
Args:
|
||||
output_dir: 输出目录
|
||||
defect_info: 缺陷信息
|
||||
{
|
||||
'叶片1': { #按叶片分记录
|
||||
'表面裂纹': { 'SLIGHT': #按类型分等级,等级从'defectLevel'获取
|
||||
[ #按等级分记录
|
||||
{
|
||||
record:{...}
|
||||
}, #一张图片一个记录
|
||||
...
|
||||
]
|
||||
...
|
||||
}
|
||||
}
|
||||
total_table_num: 总表格数量
|
||||
Returns:
|
||||
table_num: 表格数量
|
||||
功能:表头分别为:序号,缺陷类型,等级,损伤数量,处理建议,备注(是否完成处理) (默认否)。
|
||||
总体就按等级分类,但损伤数量需要说明为:n支m处。n为有这个缺陷和等级的叶片数,m为有这个缺陷和等级的所有叶片加起来的缺陷数。
|
||||
处理建议从record[0]的值中获取
|
||||
"""
|
||||
table_data = [] # 存储所有表格数据的列表,每个元素是一个字典 # 添加新记录(使用字典格式)
|
||||
table_data.append({
|
||||
"xuhao" : "序号", # 序号
|
||||
"defecttype" : "损伤名称", # 缺陷类型
|
||||
"level" : "损伤等级", # 等级
|
||||
"defectnuminfo" : "损伤数量", # 损伤数量
|
||||
"suggestion" : "处理建议", # 处理建议
|
||||
"remark" : "备注(是否处理完成)" # 备注(是否完成处理)
|
||||
})
|
||||
# 遍历缺陷信息,整理数据
|
||||
for blade_name, defects in defect_info.items():
|
||||
for defect_type, levels in defects.items():
|
||||
for level, records in levels.items():
|
||||
if records: # 如果有记录
|
||||
# 计算损伤数量:n支m处
|
||||
blade_count = 1 # 当前叶片就是1支
|
||||
defect_count = len(records) # 当前叶片的缺陷数量
|
||||
|
||||
# 查找是否已有同类型同等级的数据
|
||||
found = False
|
||||
for item in table_data:
|
||||
if item["defecttype"] == defect_type and item["level"] == level:
|
||||
# 更新已有记录
|
||||
current_n = item["defectnuminfo"]
|
||||
parts = current_n.split("支")
|
||||
existing_blades = int(parts[0])
|
||||
existing_defects = int(parts[1].split("处")[0])
|
||||
item["defectnuminfo"] = f"{existing_blades + blade_count}支{existing_defects + defect_count}处"
|
||||
found = True
|
||||
break
|
||||
|
||||
if not found:
|
||||
# 获取处理建议(假设第一条记录中有处理建议)
|
||||
suggestion = records[0].get('record', {}).get('处理建议', '')
|
||||
|
||||
# 添加新记录(使用字典格式)
|
||||
table_data.append({
|
||||
"xuhao" : str(len(table_data)), # 序号
|
||||
"defecttype" : defect_type, # 缺陷类型
|
||||
"level" : level, # 等级
|
||||
"defectnuminfo" : f"{blade_count}支{defect_count}处", # 损伤数量
|
||||
"suggestion" : suggestion, # 处理建议
|
||||
"remark" : "否" # 备注(是否完成处理)
|
||||
})
|
||||
# 添加空行
|
||||
for _ in range(2):
|
||||
table_data.append({
|
||||
"xuhao" : "", # 序号
|
||||
"defecttype" : "", # 缺陷类型
|
||||
"level" : "", # 等级
|
||||
"defectnuminfo" : "", # 损伤数量
|
||||
"suggestion" : "", # 处理建议
|
||||
"remark" : "" # 备注(是否完成处理)
|
||||
})
|
||||
for row in table_data:
|
||||
add_table_and_replace(output_dir, MUBAN_DIR, list_to_replace=row, no_para = True, ifadjustheight=True, REPORT_ENUM=REPORT_ENUM)
|
||||
total_table_num += 1
|
||||
return total_table_num
|
||||
|
||||
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
||||
from docx.shared import RGBColor
|
||||
def add_table_title(output_dir, TITLE):
|
||||
doc = Document(output_dir)
|
||||
table = doc.add_table(rows=1, cols=6, style='Table Grid')
|
||||
table.cell(0,0).merge(table.cell(0,5))
|
||||
para = table.cell(0,0).paragraphs[0]
|
||||
run = para.add_run(TITLE)
|
||||
run.font.name = "宋体"
|
||||
run.font.size = Pt(9)
|
||||
para.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
||||
print(f"添加了标题表格:{TITLE}")
|
||||
doc.save(output_dir)
|
||||
|
||||
def change_heading(output_dir, style_name, style_config = {}):
|
||||
doc = Document(output_dir)
|
||||
heading_style = doc.styles[style_name]
|
||||
font_config = style_config.get('font', {})
|
||||
heading_style.font.name = font_config.get('name', "宋体")
|
||||
heading_style.element.get_or_add_rPr().get_or_add_rFonts().set(qn('w:eastAsia'), font_config.get('name', "宋体"))
|
||||
heading_style.font.size = font_config.get('size', Pt(11))
|
||||
heading_style.bold = font_config.get('bold', True)
|
||||
heading_style.font.color.rgb = font_config.get('color', DocxColors.BLACK.rgb)
|
||||
print(f"修改了标题样式:{style_name},字体:{heading_style.font.name},大小:{heading_style.font.size},是否加粗:{heading_style.bold}")
|
||||
doc.save(output_dir)
|
||||
|
||||
from docxtpl import DocxTemplate
|
||||
|
||||
def add_auto_toc_at_end(doc_path):
|
||||
"""使用 python-docx-template 在末尾自动生成目录"""
|
||||
doc = DocxTemplate(doc_path)
|
||||
|
||||
# 渲染模板(r.toc=True 会触发目录生成)
|
||||
context = {
|
||||
'r': {
|
||||
'toc': True # 自动生成目录
|
||||
}
|
||||
}
|
||||
doc.render(context)
|
||||
doc.save(doc_path)
|
||||
|
||||
|
||||
def add_jf_picture_table(
|
||||
typical_picture_list: list,
|
||||
defect_picture_list: list,
|
||||
TYPICAL_MUBAN_DIR: str,
|
||||
DEFECT_MUBAN_DIR: str,
|
||||
output_dir: str,
|
||||
total_table_num: int,
|
||||
):
|
||||
"""添加金风版本的图片展示表格
|
||||
|
||||
Args:
|
||||
typical_picture_list: 每列有两种情况
|
||||
[
|
||||
[str(图片描述), str(图片描述), ...],
|
||||
[str(图片地址), int(n), ...],
|
||||
[str(""), str(缺陷类型), ...]
|
||||
]
|
||||
defect_picture_list: 只有一种情况
|
||||
[
|
||||
[str(图片描述), str(图片描述), ...],
|
||||
[str(图片地址), str(图片地址), ...]
|
||||
]
|
||||
TYPICAL_MUBAN_DIR: 典型图模板路径
|
||||
DEFECT_MUBAN_DIR: 缺陷图模板路径
|
||||
"""
|
||||
def add_table(table_type, data, rows, cols):
|
||||
nonlocal total_table_num
|
||||
template = TYPICAL_MUBAN_DIR if table_type == "typical" else DEFECT_MUBAN_DIR
|
||||
key_words = re.compile(r'损伤|处') if table_type == "typical" else re.compile(r'损伤')
|
||||
|
||||
print(f"添加{table_type}表格:{data}")
|
||||
add_table_to_document(
|
||||
output_dir,
|
||||
template,
|
||||
rows, cols, total_table_num,
|
||||
data,
|
||||
Report_Enum='JF',
|
||||
if_merge=False,
|
||||
height=1.4173,
|
||||
width=1.8898,
|
||||
if_para=False,
|
||||
key_words=key_words
|
||||
)
|
||||
total_table_num += 1
|
||||
return list(list("" for _ in range(5)) for _ in range(rows)) # 返回新表格
|
||||
|
||||
print(f"获得典型图列表:{typical_picture_list}")
|
||||
print(f"获得缺陷图列表:{defect_picture_list}")
|
||||
|
||||
# 初始化表格
|
||||
typical_table = [[""] * 5 for _ in range(3)] # 三行五列
|
||||
defect_table = [[""] * 5 for _ in range(2)] # 二行五列
|
||||
|
||||
typical_col_idx = 0
|
||||
defect_count = 0 # 需要添加的缺陷图数量
|
||||
pending_defects = False
|
||||
|
||||
# 处理典型图
|
||||
while all(typical_picture_list) and all(len(lst) > 0 for lst in typical_picture_list):
|
||||
desc = typical_picture_list[0].pop(0)
|
||||
content = typical_picture_list[1].pop(0)
|
||||
defect_type = typical_picture_list[2].pop(0)
|
||||
|
||||
# 检查是否需要新建表格
|
||||
if typical_col_idx >= 5:
|
||||
typical_table = add_table("typical", typical_table, 3, 5)
|
||||
typical_col_idx = 0
|
||||
|
||||
# 如果有待处理的缺陷图,先处理
|
||||
if pending_defects:
|
||||
defect_col_idx = 0
|
||||
while defect_picture_list[0] and defect_picture_list[1] and defect_count > 0:
|
||||
if defect_col_idx >= 5:
|
||||
defect_table = add_table("defect", defect_table, 2, 5)
|
||||
defect_col_idx = 0
|
||||
|
||||
defect_table[0][defect_col_idx] = defect_picture_list[0].pop(0)
|
||||
defect_table[1][defect_col_idx] = defect_picture_list[1].pop(0)
|
||||
defect_col_idx += 1
|
||||
defect_count -= 1
|
||||
|
||||
if defect_col_idx > 0:
|
||||
defect_table = add_table("defect", defect_table, 2, 5)
|
||||
|
||||
pending_defects = False
|
||||
|
||||
# 填充典型图表
|
||||
if isinstance(content, int):
|
||||
typical_table[0][typical_col_idx] = desc
|
||||
typical_table[1][typical_col_idx] = f"损伤有{content}处,详见下表"
|
||||
typical_table[2][typical_col_idx] = f"{defect_type}{content}处"
|
||||
defect_count += content
|
||||
pending_defects = True
|
||||
else:
|
||||
typical_table[0][typical_col_idx] = desc
|
||||
typical_table[1][typical_col_idx] = content
|
||||
|
||||
typical_col_idx += 1
|
||||
|
||||
# 添加剩余的典型图表
|
||||
if typical_col_idx > 0:
|
||||
add_table("typical", typical_table, 3, 5)
|
||||
|
||||
# 处理剩余的缺陷图
|
||||
if pending_defects and defect_picture_list[0] and defect_picture_list[1]:
|
||||
defect_col_idx = 0
|
||||
while defect_picture_list[0] and defect_picture_list[1] and defect_count > 0:
|
||||
if defect_col_idx >= 5:
|
||||
defect_table = add_table("defect", defect_table, 2, 5)
|
||||
defect_col_idx = 0
|
||||
|
||||
defect_table[0][defect_col_idx] = defect_picture_list[0].pop(0)
|
||||
defect_table[1][defect_col_idx] = defect_picture_list[1].pop(0)
|
||||
defect_col_idx += 1
|
||||
defect_count -= 1
|
||||
|
||||
if defect_col_idx > 0:
|
||||
add_table("defect", defect_table, 2, 5)
|
||||
|
||||
return total_table_num
|
|
@ -1,8 +1,7 @@
|
|||
from content_tools import add_picture_to_table, search_and_replace,add_picture
|
||||
from content_tools import add_picture_to_table, search_and_replace
|
||||
from get_pictures import resize_and_reduce_quality
|
||||
from document_tools import add_table_to_document,add_documents
|
||||
from docx import Document
|
||||
from typing import List
|
||||
from document_tools import add_table_to_document
|
||||
|
||||
def fill_tables(Y_table_list, row, col, Y_Table_num, Y):
|
||||
"""根据前端返回json块填写表格list,并实时跟进已填写表格数量
|
||||
目前只支持固定的缺陷图的填写
|
||||
|
@ -91,7 +90,7 @@ async def process_images_table(data_dict, output_dir, start_i, JIANCHA_NEIRONG_P
|
|||
if picture_index < picture_num:
|
||||
pic_path = items[picture_index][1] # 图片路径
|
||||
print(f"当前为图片表格,在(0,{k})位置插入图片: {pic_path}")
|
||||
print(add_picture_to_table(output_doc, output_dir, 0, k, pic_path, i, 1.8898))
|
||||
print(await add_picture_to_table(output_doc, output_dir, 0, k, pic_path, i, 1.8898))
|
||||
picture_index += 1
|
||||
i += 1
|
||||
print(message)
|
||||
|
@ -128,7 +127,7 @@ async def add_dynamic_table(output_doc, output_dir, table_num, TABLES, JIANCHA_X
|
|||
try:
|
||||
print(f"添加 {picturedir} {type(picturedir)}到表格{table_idx}")
|
||||
resize_and_reduce_quality(picturedir, picturedir)
|
||||
add_picture_to_table(output_doc, output_dir, 4, 0, picturedir, i, 4.7232)
|
||||
await add_picture_to_table(output_doc, output_dir, 4, 0, picturedir, i, 4.7232)
|
||||
except Exception as e:
|
||||
print(f"添加图片失败:{e}")
|
||||
|
||||
|
@ -180,3 +179,4 @@ def merge_info(frontend_info, default_info):
|
|||
merged_info[key] = frontend_value
|
||||
|
||||
return merged_info
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ 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:
|
||||
|
@ -234,108 +233,33 @@ def create_thumbnail(file_path: str, size: tuple) -> Image:
|
|||
print(f"图片处理有问题:{e}")
|
||||
return None
|
||||
|
||||
def process_picture_data(picture_data : dict[list[dict]],
|
||||
image_source_to_find : list[str]
|
||||
) -> tuple[dict[dict[list[dict]]],int]:
|
||||
def process_picture_data(picture_data : list[dict],
|
||||
image_source_to_find : list[str],
|
||||
quexian_type : str,
|
||||
dianxing_type : str,
|
||||
other_type : str
|
||||
) -> tuple[list[str], list[str]]:
|
||||
"""处理从数据库获取的图片数据
|
||||
|
||||
Args:
|
||||
picture_data (dict[list[dict]]: 图片数据(按部件分类,返回的列表也要按部件分类)
|
||||
picture_data (list[dict]): 图片数据
|
||||
image_source_to_find (list[str]): 要查找的图片来源枚举(外内防雷)
|
||||
quexian_type (str): 缺陷类型枚举值
|
||||
dianxing_type (str): 典型类型枚举值
|
||||
other_type (str): 其他类型枚举值
|
||||
Returns:
|
||||
tuple(
|
||||
filtered_picture_data, 按部件、缺陷类型、典型类型、其他类型层层分类的图片数据
|
||||
defct_pictures, 缺陷图列表
|
||||
dianxing_pictures, 典型图列表
|
||||
other_pictures, 其他图列表
|
||||
total_num 总图片数量
|
||||
)
|
||||
"""
|
||||
#过滤目标来源的图片数据
|
||||
filtered_picture_data = {}
|
||||
total_num = 0
|
||||
for partName, values in picture_data.items():
|
||||
total_num = total_num + len(values)
|
||||
for pic in values:
|
||||
if pic['imageSource'] in image_source_to_find:
|
||||
if partName not in filtered_picture_data:
|
||||
filtered_picture_data[partName] = {}
|
||||
if pic['imageType'] not in filtered_picture_data[partName]:
|
||||
filtered_picture_data[partName][pic['imageType']] = []
|
||||
filtered_picture_data[partName][pic['imageType']].append(pic)
|
||||
|
||||
return filtered_picture_data, total_num
|
||||
|
||||
def print_data(filtered_picture_data : dict[dict[list[dict]]]) -> None:
|
||||
"""打印图片数据"""
|
||||
for partName, types in filtered_picture_data.items():
|
||||
print(f"Part Name: {partName}")
|
||||
for imageType, pictures in types.items():
|
||||
print(f" Image Type: {imageType}, Number of Pictures: {len(pictures)}")
|
||||
|
||||
|
||||
def get_records_with_pic(records: list[dict], filtered_picture_data: dict[str, dict[str, list[dict]]], defect_enum: str) -> tuple[dict[str, dict[str, list[dict]]], dict[str, list[dict]]]:
|
||||
"""获取指定图片ID的记录
|
||||
|
||||
Args:
|
||||
records (list[dict]): 记录列表,每条记录包含部件名、缺陷类型等信息
|
||||
filtered_picture_data (dict[str, dict[str, list[dict]]]): 图片数据,最外层的字典的键是部件名,内层的字典的键是缺陷类型,值是图片信息列表
|
||||
defect_enum (str): 指定的缺陷类型
|
||||
|
||||
Returns:
|
||||
tuple(
|
||||
records_with_defect_pic: 按部件名分类的缺陷记录,映射到图片的imagePath
|
||||
defect_pic_with_no_records: 对应缺陷类型的图片没有记录的部件名映射
|
||||
)
|
||||
"""
|
||||
records_with_defect_pic = {}
|
||||
defect_pic_with_no_records = {}
|
||||
|
||||
# 收集所有filtered_picture_data中的imageId
|
||||
all_image_ids = set()
|
||||
for partName, types in filtered_picture_data.items():
|
||||
defect_pictures = types.get(defect_enum, [])
|
||||
for pic in defect_pictures:
|
||||
all_image_ids.add(pic.get('imageId'))
|
||||
|
||||
# 过滤掉records中imageId不在all_image_ids中的记录
|
||||
filtered_records = [record for record in records if record.get('imageId') in all_image_ids]
|
||||
|
||||
# 遍历每个部件
|
||||
for partName, types in filtered_picture_data.items():
|
||||
# 获取指定缺陷类型的图片列表
|
||||
defect_pictures = types.get(defect_enum, [])
|
||||
if defect_pictures:
|
||||
# 初始化部件名的字典
|
||||
if partName not in records_with_defect_pic:
|
||||
records_with_defect_pic[partName] = {}
|
||||
|
||||
# 初始化缺陷类型的字典
|
||||
records_with_defect_pic[partName][defect_enum] = []
|
||||
|
||||
# 遍历缺陷图片列表,找到对应的记录并添加到字典中
|
||||
for pic in defect_pictures:
|
||||
pic_id = pic.get('imageId')
|
||||
matched_records = [record for record in filtered_records if record.get('imageId') == pic_id]
|
||||
|
||||
# 如果有匹配的记录,则添加到字典中
|
||||
if matched_records:
|
||||
for record in matched_records:
|
||||
records_with_defect_pic[partName][defect_enum].append({
|
||||
'record': record,
|
||||
'imagePath': pic.get('imagePath')
|
||||
})
|
||||
else:
|
||||
# 如果没有匹配的记录,则将图片信息添加到没有记录的字典中
|
||||
if partName not in defect_pic_with_no_records:
|
||||
defect_pic_with_no_records[partName] = []
|
||||
defect_pic_with_no_records[partName].append({
|
||||
'defect_enum': defect_enum,
|
||||
'imagePath': pic.get('imagePath')
|
||||
})
|
||||
|
||||
return records_with_defect_pic, defect_pic_with_no_records
|
||||
|
||||
|
||||
|
||||
def get_template_pic(pic_name : str):
|
||||
"""获取模板图片路径"""
|
||||
return get_resource_path(pic_name)
|
||||
picture_data = [pic for pic in picture_data if pic['imageSource'] in image_source_to_find]
|
||||
#分别择出缺陷图和典型图
|
||||
return ([pic for pic in picture_data if pic['imageType'] == quexian_type],
|
||||
[pic for pic in picture_data if pic['imageType'] == dianxing_type],
|
||||
[pic for pic in picture_data if pic['imageType'] == other_type],
|
||||
len(picture_data))
|
||||
|
|
@ -1,395 +0,0 @@
|
|||
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, output_dir = None):
|
||||
print(f"\n开始转换JSON到DOCX文档")
|
||||
if output_dir:
|
||||
doc = Document(output_dir)
|
||||
else:
|
||||
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.autofit = True # 自动调整列宽和行高
|
||||
# 设置表格样式为无网格线(我们将自定义边框)
|
||||
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', 'center')
|
||||
print(f" 设置垂直对齐: {align_value}")
|
||||
|
||||
# 确保使用有效的对齐值
|
||||
valid_alignments = ['top', 'center', 'bottom']
|
||||
if align_value not in valid_alignments:
|
||||
align_value = 'center' # 默认值
|
||||
|
||||
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, Union
|
||||
|
||||
def list_to_json_with_merges(
|
||||
table_data: List[List[str]],
|
||||
style_config: Dict[str, Any] = None,
|
||||
detect_merges: bool = True,
|
||||
merge_columns: Union[int, List[int]] = None # 新增参数,控制合并哪些列
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
将二维列表转换为表格JSON,可选是否合并相邻相同单元格
|
||||
|
||||
参数:
|
||||
table_data: 二维字符串列表表示的表格数据
|
||||
style_config: 包含样式配置的字典(可选)
|
||||
detect_merges: 是否检测并合并相邻相同单元格(默认为True)
|
||||
merge_columns: 控制合并哪些列,可以是:
|
||||
- None:合并所有列(默认)
|
||||
- int n:只合并前n列
|
||||
- List[int]:只合并指定的列
|
||||
|
||||
返回:
|
||||
符合表格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)]
|
||||
}
|
||||
}
|
||||
|
||||
# 处理merge_columns参数
|
||||
columns_to_merge = set()
|
||||
if merge_columns is not None:
|
||||
if isinstance(merge_columns, int):
|
||||
columns_to_merge = set(range(merge_columns)) # 前n列
|
||||
elif isinstance(merge_columns, list):
|
||||
columns_to_merge = set(merge_columns) # 指定列
|
||||
|
||||
for col in range(cols):
|
||||
# 检查当前列是否需要合并
|
||||
should_merge = detect_merges
|
||||
if merge_columns is not None:
|
||||
should_merge = should_merge and (col in columns_to_merge)
|
||||
|
||||
start_row = 0
|
||||
while start_row < rows:
|
||||
current_value = table_data[start_row][col]
|
||||
end_row = start_row
|
||||
|
||||
if should_merge:
|
||||
while end_row + 1 < rows and table_data[end_row + 1][col] == current_value:
|
||||
end_row += 1
|
||||
|
||||
if should_merge 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", "center") if style_config else "center",
|
||||
"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
|
||||
}]
|
||||
}]
|
|
@ -165,10 +165,3 @@ 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()
|
||||
|
||||
|