Report_Generate_Server/tools/document_tools.py

934 lines
41 KiB
Python
Raw Normal View History

"""
Document creation and manipulation tools for Word Document Server.
"""
import os
import json, re
from typing import Dict, List, Optional, Any
from docx import Document
from 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 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
async def create_document(filename: str, title: Optional[str] = None, author: Optional[str] = None, section_args: Optional[Dict[str, Any]] = None) -> str:
"""创建一个包含可选元数据的新Word文档。
参数:
filename: 要创建的文档名称带或不带.docx扩展名
title: 可选标题
author: 可选作者
"""
filename = ensure_docx_extension(filename)
# Check if file is writeable
is_writeable, error_message = check_file_writeable(filename)
if not is_writeable:
return f"Cannot create document: {error_message}"
try:
doc = Document()
# Set properties if provided
if title:
doc.core_properties.title = title
if author:
doc.core_properties.author = author
# Ensure necessary styles exist
ensure_heading_style(doc)
ensure_table_style(doc)
# 更改纸张大小为A4
from docx.shared import Mm, Inches
sections = doc.sections
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)
section.left_margin = Inches(0.94)
section.right_margin = Inches(0.94)
# Save the document
doc.save(filename)
return f"Document {filename} created successfully"
except Exception as e:
return f"Failed to create document: {str(e)}"
async def get_document_info(filename: str) -> str:
"""获得文档信息
Args:
filename: 目标文档
"""
filename = ensure_docx_extension(filename)
if not os.path.exists(filename):
return f"Document {filename} does not exist"
try:
properties = get_document_properties(filename)
return json.dumps(properties, indent=2)
except Exception as e:
return f"Failed to get document info: {str(e)}"
async def get_document_text(filename: str) -> str:
"""获得文档的所有文本
Args:
filename: 目标文档
"""
filename = ensure_docx_extension(filename)
return extract_document_text(filename)
async def get_document_outline(filename: str) -> str:
"""获得文档的所有结构信息
Args:
filename: 目标文档
"""
filename = ensure_docx_extension(filename)
structure = get_document_structure(filename)
return json.dumps(structure, indent=2)
async def list_available_documents(directory: str = ".") -> str:
"""列出目录下所有Word文档
Args:
directory: 目录
"""
try:
if not os.path.exists(directory):
return f"Directory {directory} does not exist"
docx_files = [f for f in os.listdir(directory) if f.endswith('.docx')]
if not docx_files:
return f"No Word documents found in {directory}"
result = f"Found {len(docx_files)} Word documents in {directory}:\n"
for file in docx_files:
file_path = os.path.join(directory, file)
size = os.path.getsize(file_path) / 1024 # KB
result += f"- {file} ({size:.2f} KB)\n"
return result
except Exception as e:
return f"Failed to list documents: {str(e)}"
async def copy_document(source_filename: str, destination_filename: Optional[str] = None) -> str:
"""创建文档的副本
Args:
source_filename: 源文档路径
destination_filename: 目标文档路径为空则为当前目录
"""
source_filename = ensure_docx_extension(source_filename)
if destination_filename:
destination_filename = ensure_docx_extension(destination_filename)
success, message, new_path = create_document_copy(source_filename, destination_filename)
if success:
return message
else:
return f"Failed to copy document: {message}"
def add_documents(target_filename: str, source_filename: str) -> str:
"""将源文档(文本)添加到目标文档尾部
Args:
target_doc: 目标文档
source_filename: 源文档路径
"""
target_doc = Document(target_filename)
source_filename = ensure_docx_extension(source_filename)
source_doc = Document(source_filename)
for source_paragraph in source_doc.paragraphs:
new_paragraph = target_doc.add_paragraph(source_paragraph.text)
new_paragraph.style = target_doc.styles['Normal'] # Default style
#获取合并等样式2025427
new_paragraph.alignment = source_paragraph.alignment
# Try to match the style if possible
try:
if source_paragraph.style and source_paragraph.style.name in target_doc.styles:
new_paragraph.style = target_doc.styles[source_paragraph.style.name]
except Exception as e:
print(f"Failed to apply style: {e}")
# Copy run formatting
for i, run in enumerate(source_paragraph.runs):
if i < len(new_paragraph.runs):
new_run = new_paragraph.runs[i]
# Copy basic formatting
new_run.bold = run.bold
new_run.italic = run.italic
new_run.underline = run.underline
#添加同时合并字体2025427
new_run.font.name = run.font.name
rPr = new_run.element.get_or_add_rPr()
rFonts = rPr.get_or_add_rFonts()
# 检查 run.font.name 是否为 None
if run.font.name is None:
# 设置默认的中文字体名称
run.font.name = '宋体 (中文正文)' # 或者使用其他你喜欢的中文字体
rFonts.set(qn('w:eastAsia'), run.font.name)
new_run.font.color.rgb = run.font.color.rgb
# Font size if specified
if run.font.size:
new_run.font.size = run.font.size
target_doc.save(target_filename)
return f"{target_filename}添加{source_filename}成功"
def write_table(target_filename: str, rows: int, cols: int, table_num: int, data: Optional[List[List[str]]] = None, ifadjustheight: Optional[bool] = True, height: Optional[float] = 1, key_words: re.Pattern[str] = None, ALIGMENT: Optional[str] = 'CENTER') -> Document:
"""填写word文档里的表格返回填写后的文档
Args:
target_filename: 目标文档路径
rows: 表格行数
cols: 表格列数
table_num: 表格序号
data: 表格数据二维列表每个单元格为字符串
ifadjustheight: bool为真则表格行高自动调整
"""
target_filename = ensure_docx_extension(target_filename)
# Check if target file is writeable
is_writeable, error_message = check_file_writeable(target_filename)
if not is_writeable:
return f"Cannot create target document: {error_message}"
try:
target_filename = ensure_docx_extension(target_filename)
target_doc = Document(target_filename)
except Exception as e:
print(f"获取{target_filename}失败:{str(e)}")
# Try to set the table style
try:
target_doc.tables[table_num].style = 'Table Grid'
except KeyError as k:
pass
except Exception as e:
print(f"{target_doc}最后一个表格更改样式失败: {str(e)}")
print("开始写入表格")
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.enum.table import WD_ALIGN_VERTICAL
from docx.shared import Pt, Inches, Cm, RGBColor
try:
if data:
for i, row_data in enumerate(data):
if i >= rows + 1:
break
for j, cell_text in enumerate(row_data):
if j >= cols + 1:
break
if str(cell_text) == "": continue
print(f"在[{i},{j}]处写入{str(cell_text)}")
target_doc.tables[table_num].cell(i,j).text = str(cell_text)
print(key_words, cell_text)
if key_words and key_words.search(str(cell_text)):
print(f'{cell_text}包含关键之,已置红')
target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 0, 0)
target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0].font.name = "Times New Roman" #设置英文字体
target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0].font.size = Pt(10.5) # 字体大小
target_doc.tables[table_num].cell(i,j).paragraphs[0].runs[0]._element.rPr.rFonts.set(qn('w:eastAsia'), '仿宋') #设置中文字体
if ALIGMENT == 'CENTER':
target_doc.tables[table_num].cell(i,j).paragraphs[0].paragraph_format.alignment = WD_TABLE_ALIGNMENT.CENTER
elif ALIGMENT == 'LEFT':
target_doc.tables[table_num].cell(i,j).paragraphs[0].paragraph_format.alignment = WD_TABLE_ALIGNMENT.LEFT
target_doc.tables[table_num].cell(i,j).vertical_alignment = WD_ALIGN_VERTICAL.CENTER
if ifadjustheight:
target_doc.tables[table_num].rows[i].height = Cm(height)
except Exception as e:
print(f"写入{target_filename}tables.cell({i},{j})失败:{str(e)}")
print("表格写入完成")
return target_doc
def set_document_para(target_doc: Document) -> Document:
"""设置文档的段落格式
"""
paragraphs_to_remove = []
for i, paragraph in enumerate(target_doc.paragraphs):
if i <= 11:
continue
if not paragraph.text.strip():
paragraphs_to_remove.append(paragraph)
for paragraph in paragraphs_to_remove:
p = paragraph._element
p.getparent().remove(p)
return target_doc
def add_table_to_document(target_filename: str, source_filename: str, rows: int, cols: int, table_num: int, data: Optional[List[List[str]]] = None, ifadjustheight: Optional[bool] = True, height: Optional[float] = 1, key_words: re.Pattern[str] = None, ALIGMENT: Optional[str] = 'CENTER') -> str:
"""复制源文件中的文字与表格(先文字后表格格式)到目标文档
Args:
target_filename: 目标文档路径
source_doc: 源文档路径
rows: 表格行数
cols: 表格列数
table_num: 表格序号
data: 表格数据二维列表每个单元格为字符串
ifadjustheight: bool为真则表格行高自动调整
key_words: list, 关键字
"""
target_filename = ensure_docx_extension(target_filename)
source_filename = ensure_docx_extension(source_filename)
source_doc = Document(source_filename)
target_doc = Document(target_filename)
target_doc.add_paragraph()
try:
# Copy all paragraphs
for paragraph in source_doc.paragraphs:
# Create a new paragraph with the same text and style
new_paragraph = target_doc.add_paragraph(paragraph.text)
new_paragraph.style = target_doc.styles['Normal'] # Default style
#获取合并等样式2025427
new_paragraph.alignment = paragraph.alignment
# 复制段落分页属性
new_paragraph.paragraph_format.page_break_before = paragraph.paragraph_format.page_break_before
# Try to match the style if possible
try:
if paragraph.style and paragraph.style.name in target_doc.styles:
new_paragraph.style = target_doc.styles[paragraph.style.name]
except:
pass
# Copy run formatting
for i, run in enumerate(paragraph.runs):
if i < len(new_paragraph.runs):
new_run = new_paragraph.runs[i]
# Copy basic formatting
new_run.bold = run.bold
new_run.italic = run.italic
new_run.underline = run.underline
#添加同时合并字体2025427
new_run.font.name = run.font.name
rPr = new_run.element.get_or_add_rPr()
rFonts = rPr.get_or_add_rFonts()
# 检查 run.font.name 是否为 None
if run.font.name is None:
# 设置默认的中文字体名称
run.font.name = '宋体(中文正文)' # 或者使用其他你喜欢的中文字体
rFonts.set(qn('w:eastAsia'), run.font.name)
new_run.font.color.rgb = run.font.color.rgb
# Font size if specified
if run.font.size:
new_run.font.size = run.font.size
except Exception as e:
print(f"添加表格前文章失败:{str(e)}")
try:# Copy all tables
copy_table(source_doc.tables[0], target_doc, ifadjustheight, height)
except Exception as e:
print(f"添加表格失败:{str(e)}")
print(f"{target_doc}写入表格{source_doc.tables[0]}成功")
target_doc = set_document_para(target_doc)
target_doc.save(target_filename)
target_doc = Document(target_filename)
if data:
try:
target_doc = write_table(target_filename, rows, cols, table_num, data, ifadjustheight, height, key_words, ALIGMENT)
except Exception as e:
print(f"{target_filename}写入{data}失败:{str(e)}")
target_doc.save(target_filename)
return target_doc,f"{target_filename}添加表格{source_doc}成功"
from docx.document import Document as Document_
from docx.table import Table
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'):
"""复制源文件中的文字与表格(先文字后表格格式)到目标文档
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:
# Create a new paragraph with the same text and style
new_paragraph = target_doc.add_paragraph(paragraph.text)
new_paragraph.style = target_doc.styles['Normal'] # Default style
#获取合并等样式2025427
new_paragraph.alignment = paragraph.alignment
# 复制段落分页属性
new_paragraph.paragraph_format.page_break_before = paragraph.paragraph_format.page_break_before
# Try to match the style if possible
try:
if paragraph.style and paragraph.style.name in target_doc.styles:
new_paragraph.style = target_doc.styles[paragraph.style.name]
except:
pass
# Copy run formatting
for i, run in enumerate(paragraph.runs):
if i < len(new_paragraph.runs):
new_run = new_paragraph.runs[i]
# Copy basic formatting
new_run.bold = run.bold
new_run.italic = run.italic
new_run.underline = run.underline
#添加同时合并字体2025427
new_run.font.name = run.font.name
rPr = new_run.element.get_or_add_rPr()
rFonts = rPr.get_or_add_rFonts()
# 检查 run.font.name 是否为 None
if run.font.name is None:
# 设置默认的中文字体名称
run.font.name = '宋体(中文正文)' # 或者使用其他你喜欢的中文字体
rFonts.set(qn('w:eastAsia'), run.font.name)
new_run.font.color.rgb = run.font.color.rgb
# Font size if specified
if run.font.size:
new_run.font.size = run.font.size
# 复制分页符处理w:br标签
for element in run._element:
if element.tag.endswith('br'):
br_type = element.get(qn('type'), '')
if br_type == 'page':
new_br = OxmlElement('w:br')
new_br.set(qn('type'), 'page')
new_run._element.append(new_br)
except Exception as e:
print(f"添加表格前文章失败:{str(e)}")
try:# Copy all tables
from core.tables import copy_table
copy_table(source_doc.tables[0], target_doc, ifadjustheight, height, REPORT_ENUM=REPORT_ENUM)
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))
async def merge_documents(target_filename: str, source_filenames: List[str], add_page_breaks: bool = True) -> str:
"""合并文档(文本) 表格会添加到最后
Args:
target_filename: 合并后文档路径
source_filenames: 源文档路径列表
add_page_breaks: bool为真则每个源文档中间加入分页符
"""
from core.tables import copy_table
target_filename = ensure_docx_extension(target_filename)
# Check if target file is writeable
is_writeable, error_message = check_file_writeable(target_filename)
if not is_writeable:
return f"Cannot create target document: {error_message}"
# Validate all source documents exist
missing_files = []
for filename in source_filenames:
doc_filename = ensure_docx_extension(filename)
if not os.path.exists(doc_filename):
missing_files.append(doc_filename)
if missing_files:
return f"Cannot merge documents. The following source files do not exist: {', '.join(missing_files)}"
try:
# Create a new document for the merged result
target_doc = Document()
# Process each source document
for i, filename in enumerate(source_filenames):
doc_filename = ensure_docx_extension(filename)
source_doc = Document(doc_filename)
# Add page break between documents (except before the first one)
if add_page_breaks and i > 0:
target_doc.add_page_break()
# Copy all paragraphs
for paragraph in source_doc.paragraphs:
# Create a new paragraph with the same text and style
new_paragraph = target_doc.add_paragraph(paragraph.text)
new_paragraph.style = target_doc.styles['Normal'] # Default style
#获取合并等样式2025427
new_paragraph.alignment = paragraph.alignment
# Try to match the style if possible
try:
if paragraph.style and paragraph.style.name in target_doc.styles:
new_paragraph.style = target_doc.styles[paragraph.style.name]
except:
pass
# Copy run formatting
for i, run in enumerate(paragraph.runs):
if i < len(new_paragraph.runs):
new_run = new_paragraph.runs[i]
# Copy basic formatting
new_run.bold = run.bold
new_run.italic = run.italic
new_run.underline = run.underline
#添加同时合并字体2025427
new_run.font.name = run.font.name
rPr = new_run.element.get_or_add_rPr()
rFonts = rPr.get_or_add_rFonts()
# 检查 run.font.name 是否为 None
if run.font.name is None:
# 设置默认的中文字体名称
run.font.name = '宋体(中文正文)' # 或者使用其他你喜欢的中文字体
rFonts.set(qn('w:eastAsia'), run.font.name)
new_run.font.color.rgb = run.font.color.rgb
# Font size if specified
if run.font.size:
new_run.font.size = run.font.size
# Copy all tables
for table in source_doc.tables:
copy_table(table, target_doc)
# Save the merged document
target_doc.save(target_filename)
return f"Successfully merged {len(source_filenames)} documents into {target_filename}"
except Exception as e:
return f"Failed to merge documents: {str(e)}"
async def right_align_last_three_para(target_filename: str) -> str:
"""右对齐最后三个段落
Args:
target_filename: 目标文档路径
"""
target_filename = ensure_docx_extension(target_filename)
# Check if target file is writeable
is_writeable, error_message = check_file_writeable(target_filename)
if not is_writeable:
return f"Cannot right align paragraphs: {error_message}"
try:
# Open the target document
target_doc = Document(target_filename)
# Get the last three paragraphs
paragraphs = target_doc.paragraphs[-3:]
# Set the alignment of each paragraph to right
from docx.enum.text import WD_ALIGN_PARAGRAPH
for paragraph in paragraphs:
paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT
# Save the modified document
target_doc.save(target_filename)
return f"Successfully right aligned the last three paragraphs in {target_filename}"
except Exception as e:
return f"Failed to right align paragraphs: {str(e)}"
async def add_dynamic_table(output_doc, output_dir, table_num, TABLES, JIANCHA_XIANGQING_DIR, PICTURES, row, col, i, FLAG, xuhao):
"""创建动态表
Args:
output_doc (Document): 文档对象
output_dir (str): 输出目录
table_num (int): 表格序号
TABLES (list): 表格数据
JIANCHA_XIANGQING_DIR (str): 检查详情表目录
PICTURES (dict): 图片数据字典键为表索引值为图片路径列表
row (int): 行数
col (int): 列数
i (int): 表格序号
FLAG: 其他标志
Returns:
tuple: (i, table_num) 更新后的表格序号和表格数量
"""
for table_idx, Table in enumerate(TABLES):
print(Table)
output_doc, message = await add_table_to_document(output_dir, JIANCHA_XIANGQING_DIR, row, col, i, Table, FLAG)
print(message)
# 获取当前表格对应的图片
current_table_pictures = PICTURES.get(table_idx, [])
print(f"开始处理图片列表: {current_table_pictures}")
for picturedir in current_table_pictures:
if picturedir is None:
print(f"图片路径为空,跳过")
continue
try:
print(f"添加 {picturedir} {type(picturedir)}到表格{table_idx}")
resize_and_reduce_quality(picturedir, picturedir)
await add_picture_to_table(output_doc, output_dir, 4, 0, picturedir, i, 4.7232)
except Exception as e:
print(f"添加图片失败:{e}")
print(await search_and_replace(output_dir, 'tupian_xuhao', f'{xuhao}'))
table_num += 1
i += 1
xuhao += 1
return i, table_num, xuhao
async def process_images_table(data_dict, output_dir, start_i, JIANCHA_NEIRONG_PICTURES_TABLE, key_words = None):
"""添加对应表格且填写图片名与插入图片
Args:
data_dict (dict): dict内容图片:图片路径
output_dir (str): 输出路径
start_i (int): 总表格数量
JIANCHA_NEIRONG_PICTURES_TABLE (str): 二维表模板路径
Returns:
int: 最后使用的表格序号
"""
items = list(data_dict.items())
picture_num = len(items)
line_index = 0
picture_index = 0
i = start_i
for content_row in range(((picture_num + 2) // 3) * 2):
if content_row % 2 == 1:
# 文字行(从 items 取图片名)
JIANCHA_NEIRONG_TEXT = [["" for _ in range(3)] for _ in range(1)] # 1行3列
for k in range(1): # 只有1行
for l in range(3):
if line_index >= picture_num:
break
JIANCHA_NEIRONG_TEXT[k][l] = items[line_index][0] # 图片名
print(f'当前为文字表格,在({k},{l})位置插入文字: {items[line_index][0]}')
line_index += 1
print(f"当前待插入表格: {JIANCHA_NEIRONG_TEXT}")
print(f"当前表格序号为 {i}")
output_doc, message = await add_table_to_document(
output_dir, JIANCHA_NEIRONG_PICTURES_TABLE, 1, 3, i, JIANCHA_NEIRONG_TEXT, False, None, key_words
)
i += 1
else:
# 图片行(从 items 取图片路径)
print(f"当前表格序号为 {i}")
output_doc, message = await add_table_to_document(
output_dir, JIANCHA_NEIRONG_PICTURES_TABLE, 1, 3, i, None, False
)
for k in range(3):
if picture_index < picture_num:
pic_path = items[picture_index][1] # 图片路径
print(f"当前为图片表格,在(0,{k})位置插入图片: {pic_path}")
print(await add_picture_to_table(output_doc, output_dir, 0, k, pic_path, i, 1.8898))
picture_index += 1
i += 1
print(message)
return i # 返回最后使用的表格序号
async def process_server_images_table(data_list, image_source_list, output_dir, start_i, JIANCHA_NEIRONG_PICTURES_TABLE, key_words=None):
"""从服务器获取的图片数据添加对应表格且填写图片名与插入图片
逻辑按来源排序添加
Args:
data_list (list): 图片数据列表每个元素为图片路径
[
{'imageId': '41543f531be24522b7bec741b9a483a2', 'imageName': 'F02_机组10060_叶片叶片PS面距叶根15.5米处轴向裂纹轴向260mm弦向20mm。DJI_20250526090227_0052_Z.png', 'imagePath': '/static/image/temp/out-work/12bc30fb209f3af3bf530541c5b062bd/F02_机组10060_叶片叶片PS面距叶根15.5米处轴向裂纹轴向260mm弦向20mm。DJI_20250526090227_0052_Z.png', 'partId': '12bc30fb209f3af3bf530541c5b062bd', 'partName': '叶片1', 'imageResolution': '8000x6000', 'focalDistance': None, 'shootingTime': None, 'cameraManufacturer': None, 'cameraModel': None, 'weather': None, 'weatherLabel': None, 'humidness': None, 'temperature': None, 'windLevel': None, 'shootingMethod': 'UAV', 'shootingMethodLabel': '无人机航拍', 'shootingDistance': None, 'collectorName': '程启谦', 'imageSource': 'out-work', 'imageSourceLabel': '外部工作', 'imageType': 'DEFECT', 'imageTypeLabel': '缺陷影像', 'audioList': None, 'preImagePath': None, 'preTreatment': False, 'projectId': None, 'gps': None},
...
]
image_source_list (list): 来源列表每个元素为来源名称
['out-work', 'in-work', 'in-field'] (来源可能会少于3个pop出没有给出对应来源的图片)
output_dir (str): 输出路径
start_i (int): 总表格数量
JIANCHA_NEIRONG_PICTURES_TABLE (str): 二维表模板路径
key_words (re)
Returns:
int: 最后使用的表格序号
"""
# 按来源对图片数据进行分组和排序
sorted_data = {source: [] for source in image_source_list}
# 将图片数据按来源分组
for item in data_list:
source = item['imageSource']
if source in sorted_data:
# 存储图片名和图片路径的元组与非server版本保持一致
sorted_data[source].append((item['imageTypeLabel'], item['imagePath']))
i = start_i
# 按照image_source_list的顺序处理每个来源的图片
for source in image_source_list:
items = sorted_data[source]
if not items:
continue # 如果该来源没有图片则跳过
print(f"处理来源: {source}")
picture_num = len(items)
line_index = 0
picture_index = 0
for content_row in range(((picture_num + 2) // 3) * 2):
if content_row % 2 == 1:
# 文字行(从 items 取图片名)
JIANCHA_NEIRONG_TEXT = [["" for _ in range(3)] for _ in range(1)] # 1行3列
for k in range(1): # 只有1行
for l in range(3):
if line_index >= picture_num:
break
JIANCHA_NEIRONG_TEXT[k][l] = items[line_index][0] # 图片名
print(f'当前为文字表格,在({k},{l})位置插入文字: {items[line_index][0]}')
line_index += 1
print(f"当前待插入表格: {JIANCHA_NEIRONG_TEXT}")
print(f"当前表格序号为 {i}")
output_doc, message = await add_table_to_document(
output_dir, JIANCHA_NEIRONG_PICTURES_TABLE, 1, 3, i, JIANCHA_NEIRONG_TEXT, False, None, key_words
)
i += 1
else:
# 图片行(从 items 取图片路径)
print(f"当前表格序号为 {i}")
output_doc, message = await add_table_to_document(
output_dir, JIANCHA_NEIRONG_PICTURES_TABLE, 1, 3, i, None, False
)
for k in range(3):
if picture_index < picture_num:
pic_path = items[picture_index][1] # 图片路径
print(f"当前为图片表格,在(0,{k})位置插入图片: {pic_path}")
print(await add_picture_to_table(output_doc, output_dir, 0, k, get_full_picture_url(pic_path), i, 1.8898))
picture_index += 1
i += 1
print(message)
return i # 返回最后使用的表格序号
def add_header(target_dir : str, report_enum : str, if_clear_header : Optional[bool] = True):
"""添加页眉,添加封面后调用此函数,会分离页面和后续页面的节。
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) # 清除页眉的段落
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 # 取消页眉与上一页关联
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))
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
document.save(target_dir) # 保存文档
elif report_enum == 'DT':
print("添加迪特模板的页眉")
pic = run.add_picture(get_template_pic(TEMPLATE_HEADER.DT_HEADER.PIC_DIR))
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
document.save(target_dir) # 保存文档
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)}")
from docx.enum.section import WD_ORIENT
from docx.enum.text import WD_BREAK
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
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
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]
para.text = TITLE
para.alignment = WD_ALIGN_PARAGRAPH.CENTER
doc.save(output_dir)