258 lines
8.7 KiB
Python
258 lines
8.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
带调试功能的报告生成器 - 用于定位卡住问题
|
|
"""
|
|
|
|
import logging
|
|
import time
|
|
import threading
|
|
from pathlib import Path
|
|
|
|
# 设置日志
|
|
logging.basicConfig(
|
|
level=logging.DEBUG,
|
|
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
|
handlers=[
|
|
logging.FileHandler('report_debug.log', encoding='utf-8'),
|
|
logging.StreamHandler()
|
|
]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class TimeoutError(Exception):
|
|
"""超时异常"""
|
|
pass
|
|
|
|
def timeout_handler(func, timeout_seconds=30):
|
|
"""为函数添加超时保护"""
|
|
result = [None]
|
|
exception = [None]
|
|
|
|
def target():
|
|
try:
|
|
result[0] = func()
|
|
except Exception as e:
|
|
exception[0] = e
|
|
|
|
thread = threading.Thread(target=target)
|
|
thread.daemon = True
|
|
thread.start()
|
|
thread.join(timeout_seconds)
|
|
|
|
if thread.is_alive():
|
|
logger.error(f"函数执行超时 ({timeout_seconds}秒)")
|
|
raise TimeoutError(f"函数执行超时 ({timeout_seconds}秒)")
|
|
|
|
if exception[0]:
|
|
raise exception[0]
|
|
|
|
return result[0]
|
|
|
|
def debug_fill_script_table_at_token_word(doc, constants, token: str, table_spec: dict) -> None:
|
|
"""带调试的表格填充函数"""
|
|
logger.info(f"开始处理表格 {token}")
|
|
|
|
cells = table_spec.get("cells") or table_spec.get("values") or []
|
|
if not isinstance(cells, list):
|
|
logger.warning(f"表格 {token} 没有有效的单元格数据")
|
|
return
|
|
|
|
logger.info(f"表格 {token} 包含 {len(cells)} 个单元格")
|
|
|
|
start_row_offset = int(table_spec.get("startRow", 0) or 0)
|
|
start_col_offset = int(table_spec.get("startCol", 0) or 0)
|
|
|
|
# 查找表格中的token
|
|
logger.debug(f"查找token: {token}")
|
|
|
|
try:
|
|
# 简化的token查找逻辑
|
|
found_table = None
|
|
if doc.Tables.Count > 0:
|
|
found_table = doc.Tables(1) # 使用第一个表格
|
|
logger.info(f"使用第一个表格,尺寸: {found_table.Rows.Count} x {found_table.Columns.Count}")
|
|
else:
|
|
logger.error("文档中没有找到表格")
|
|
return
|
|
|
|
if not found_table:
|
|
logger.error(f"未找到包含token {token} 的表格")
|
|
return
|
|
|
|
# 确保表格有足够的行
|
|
max_row_needed = 1
|
|
for cell_info in cells:
|
|
if isinstance(cell_info, dict):
|
|
row_off = int(cell_info.get("row", 0) or 0)
|
|
max_row_needed = max(max_row_needed, start_row_offset + row_off + 1)
|
|
|
|
logger.debug(f"需要的最大行数: {max_row_needed}")
|
|
|
|
current_rows = found_table.Rows.Count
|
|
if current_rows < max_row_needed:
|
|
logger.info(f"添加行: 从 {current_rows} 到 {max_row_needed}")
|
|
for _ in range(max_row_needed - current_rows):
|
|
try:
|
|
def add_row():
|
|
found_table.Rows.Add()
|
|
timeout_handler(add_row, 10) # 10秒超时
|
|
except TimeoutError:
|
|
logger.error("添加行操作超时")
|
|
return
|
|
except Exception as e:
|
|
logger.error(f"添加行失败: {e}")
|
|
break
|
|
|
|
# 处理单元格数据
|
|
logger.info("开始填充单元格数据")
|
|
processed_count = 0
|
|
error_count = 0
|
|
|
|
for i, cell_info in enumerate(cells):
|
|
if not isinstance(cell_info, dict):
|
|
continue
|
|
|
|
# 进度报告
|
|
if i % 20 == 0:
|
|
logger.info(f"处理进度: {i}/{len(cells)} ({i/len(cells)*100:.1f}%)")
|
|
|
|
try:
|
|
row_off = int(cell_info.get("row", 0) or 0)
|
|
col_off = int(cell_info.get("col", cell_info.get("column", 0)) or 0)
|
|
|
|
abs_row = start_row_offset + row_off + 1 # Word索引从1开始
|
|
abs_col = start_col_offset + col_off + 1
|
|
|
|
value = cell_info.get("value", "")
|
|
if value is None:
|
|
continue
|
|
|
|
text = str(value)
|
|
if text.strip() == "":
|
|
continue
|
|
|
|
# 检查行列范围
|
|
if abs_row > found_table.Rows.Count:
|
|
logger.warning(f"行索引超出范围: {abs_row} > {found_table.Rows.Count}")
|
|
continue
|
|
|
|
if abs_col > found_table.Columns.Count:
|
|
logger.warning(f"列索引超出范围: {abs_col} > {found_table.Columns.Count}")
|
|
continue
|
|
|
|
# 写入单元格(带超时保护)
|
|
def write_cell():
|
|
cell_obj = found_table.Cell(abs_row, abs_col)
|
|
cell_obj.Range.Text = text
|
|
|
|
try:
|
|
timeout_handler(write_cell, 5) # 5秒超时
|
|
processed_count += 1
|
|
|
|
if processed_count % 10 == 0:
|
|
logger.debug(f"已处理 {processed_count} 个单元格")
|
|
|
|
except TimeoutError:
|
|
logger.error(f"写入单元格 ({abs_row}, {abs_col}) 超时")
|
|
error_count += 1
|
|
if error_count > 5: # 连续5个错误就退出
|
|
logger.error("错误过多,停止处理")
|
|
break
|
|
except Exception as e:
|
|
logger.error(f"写入单元格 ({abs_row}, {abs_col}) 失败: {e}")
|
|
error_count += 1
|
|
if error_count > 5:
|
|
logger.error("错误过多,停止处理")
|
|
break
|
|
|
|
except Exception as e:
|
|
logger.error(f"处理单元格 {i} 时出错: {e}")
|
|
error_count += 1
|
|
if error_count > 5:
|
|
logger.error("错误过多,停止处理")
|
|
break
|
|
|
|
logger.info(f"表格 {token} 处理完成: 成功 {processed_count}, 错误 {error_count}")
|
|
|
|
except Exception as e:
|
|
logger.error(f"处理表格 {token} 时发生严重错误: {e}")
|
|
raise
|
|
|
|
def test_debug_report_generation():
|
|
"""测试调试版本的报告生成"""
|
|
logger.info("开始调试报告生成测试")
|
|
|
|
# 模拟你的JSON数据
|
|
test_data = {
|
|
"tables": [{
|
|
"token": "scriptTable1",
|
|
"startRow": 0,
|
|
"startCol": 0,
|
|
"cells": [
|
|
{"row": 1, "col": 1, "value": "2025-11-27 11:30:00"},
|
|
{"row": 1, "col": 3, "value": "2025-11-27 15:00:00"},
|
|
{"row": 0, "col": 1, "value": "16.5"},
|
|
{"row": 4, "col": 0, "value": "23.2"},
|
|
{"row": 4, "col": 1, "value": "27.5"},
|
|
{"row": 5, "col": 0, "value": "24.2"},
|
|
{"row": 20, "col": 6, "value": "28.5"}
|
|
]
|
|
}]
|
|
}
|
|
|
|
try:
|
|
import win32com.client
|
|
from win32com.client import constants
|
|
|
|
logger.info("创建Word应用程序")
|
|
word_app = win32com.client.Dispatch("Word.Application")
|
|
word_app.Visible = True # 显示Word以便观察
|
|
|
|
logger.info("创建新文档")
|
|
doc = word_app.Documents.Add()
|
|
|
|
logger.info("插入测试表格")
|
|
table = doc.Tables.Add(doc.Range(0, 0), 25, 10)
|
|
|
|
# 在第一个单元格插入token标记
|
|
table.Cell(1, 1).Range.Text = "scriptTable1"
|
|
|
|
logger.info("开始处理脚本表格")
|
|
table_spec = test_data["tables"][0]
|
|
|
|
debug_fill_script_table_at_token_word(doc, constants, "scriptTable1", table_spec)
|
|
|
|
logger.info("报告生成测试完成")
|
|
|
|
# 保存文档用于检查
|
|
output_path = Path("debug_report_test.docx")
|
|
doc.SaveAs(str(output_path.absolute()))
|
|
logger.info(f"测试文档已保存: {output_path}")
|
|
|
|
# 不自动关闭,让用户检查结果
|
|
# doc.Close()
|
|
# word_app.Quit()
|
|
|
|
return True
|
|
|
|
except Exception as e:
|
|
logger.error(f"调试报告生成失败: {e}")
|
|
try:
|
|
word_app.Quit()
|
|
except:
|
|
pass
|
|
return False
|
|
|
|
if __name__ == "__main__":
|
|
print("调试版报告生成器测试")
|
|
print("=" * 50)
|
|
|
|
success = test_debug_report_generation()
|
|
|
|
if success:
|
|
print("✓ 测试完成,请检查生成的文档和日志")
|
|
else:
|
|
print("✗ 测试失败,请查看日志了解详情")
|
|
|
|
print("日志文件: report_debug.log")
|