- 删除破损文件: main.py/main.ui/main_ui.py - 删除 runtime_hook.py(归宿主维护),清除 tools/ 目录 - 合并 export_runtime/pack_zip/verify_companions 到 build_pyd.py - 修复注释: docstring 参数与实际签名不一致、过时引用、拼写错误 - 修复日志: 消除静默吞异常、删冗余 log+raise、补缺失日志 - 精简 .gitignore
141 lines
5.0 KiB
Python
141 lines
5.0 KiB
Python
"""MIL 测试用例 Excel 更新模块
|
||
|
||
主要功能:
|
||
- 比较新旧数据差异
|
||
- 添加新信号列或删除旧信号列
|
||
"""
|
||
import logging
|
||
from typing import Any
|
||
|
||
from .base import SignalData
|
||
from .exceptions import CaseDataError
|
||
from openpyxl import Workbook
|
||
from openpyxl.worksheet.worksheet import Worksheet
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def update_case_excel(filename: str, old_data: dict, new_data: dict) -> None:
|
||
"""将旧 Excel 同步到新 Excel 的信号列集合(增 / 删)。
|
||
|
||
流程:
|
||
1. 拆出 wb / sheet / source_row 等元数据;
|
||
2. 算 diff:要新增的信号、要删除的信号;
|
||
3. 先删后插(保持列号稳定);
|
||
4. 回写文件。
|
||
|
||
Args:
|
||
filename: 目标 Excel 文件路径。
|
||
old_data: 旧数据字典(含 wb / sheet / source_row 及各信号)。
|
||
new_data: 新数据字典(结构同上,用于 diff 比较)。
|
||
|
||
Raises:
|
||
CaseDataError: 旧数据字典中 Workbook/Worksheet 类型不匹配。
|
||
"""
|
||
logger.info(f"开始更新 Excel 文件 {filename}")
|
||
|
||
# 注意:pop 会从字典中移除键,调用前请确认数据不再被复用
|
||
old_wb = old_data.pop('wb')
|
||
old_sheet = old_data.pop('sheet')
|
||
old_source_row = old_data.pop('source_row')
|
||
|
||
new_data.pop('wb')
|
||
new_data.pop('sheet')
|
||
new_source_row = new_data.pop('source_row')
|
||
|
||
if not isinstance(old_wb, Workbook) or not isinstance(old_sheet, Worksheet):
|
||
raise CaseDataError("数据异常,Workbook 或 Worksheet 类型错误")
|
||
|
||
# 计算"新增"与"删除"两组信号
|
||
add_input = _get_add_input(old_data, new_data)
|
||
del_input = _get_del_input(old_data, new_data)
|
||
datalog_len = _get_datalog_len(old_data)
|
||
|
||
# 先删除旧列(逆序删除是为了让被删列的索引不会影响后续操作)
|
||
for name, info in reversed(del_input.items()):
|
||
old_sheet.delete_cols(info['column'])
|
||
logger.info(f"删除第{info['column']}列\t信号名: {name}")
|
||
|
||
# 再插入新增列:先 insert_cols 占位,再填充属性行与数据点
|
||
for name, info in add_input.items():
|
||
old_sheet.insert_cols(info['column'])
|
||
|
||
# 1) 属性行(header 行以下至 source_row - 1)
|
||
for index in range(1, old_source_row):
|
||
if index == old_source_row - 1:
|
||
# source_header 行:从 attributes 字典里取出 source_header 文本
|
||
# 注:attributes 的行号空间与 old_data 保持一致,应使用 old_source_row
|
||
old_sheet.cell(index, info['column']).value = info['attributes'][old_source_row - 1]
|
||
old_sheet.cell(index, info['column']).data_type = "str"
|
||
else:
|
||
old_sheet.cell(index, info['column']).value = info['attributes'][index]
|
||
old_sheet.cell(index, info['column']).data_type = "str"
|
||
|
||
# 2) 数据行:根据值类型设置 data_type
|
||
for index in range(old_source_row + 1, datalog_len + old_source_row + 1):
|
||
try:
|
||
value = int(info['datalog'][0].value)
|
||
old_sheet.cell(index, info['column']).value = value
|
||
old_sheet.cell(index, info['column']).data_type = "int"
|
||
except (ValueError, TypeError):
|
||
value = str(info['datalog'][0].value)
|
||
old_sheet.cell(index, info['column']).value = value
|
||
old_sheet.cell(index, info['column']).data_type = "str"
|
||
logger.info(f"新增第{info['column']}列\t信号名: {name}")
|
||
|
||
old_wb.save(filename)
|
||
logger.info(f"更新 Excel 文件 {filename} 完成")
|
||
|
||
|
||
def _get_add_input(old_data: dict, new_data: dict) -> dict[str, SignalData]:
|
||
"""计算"新增"信号集合:存在于 new_data 但不存在于 old_data 的信号。
|
||
|
||
Args:
|
||
old_data: 旧数据字典。
|
||
new_data: 新数据字典。
|
||
|
||
Returns:
|
||
新增信号字典。
|
||
"""
|
||
add_input: dict[str, SignalData] = {}
|
||
|
||
for name in new_data.keys():
|
||
if name not in old_data.keys():
|
||
add_input[name] = new_data[name]
|
||
return add_input
|
||
|
||
|
||
def _get_del_input(old_data: dict, new_data: dict) -> dict[str, SignalData]:
|
||
"""计算"删除"信号集合:存在于 old_data 但不存在于 new_data 的信号。
|
||
|
||
Args:
|
||
old_data: 旧数据字典。
|
||
new_data: 新数据字典。
|
||
|
||
Returns:
|
||
删除信号字典。
|
||
"""
|
||
del_input: dict[str, SignalData] = {}
|
||
|
||
for name in old_data.keys():
|
||
if name not in new_data.keys():
|
||
del_input[name] = old_data[name]
|
||
return del_input
|
||
|
||
|
||
def _get_datalog_len(old_data: dict) -> int:
|
||
"""获取旧数据中第一个信号的 datalog 长度,作为新增列填充行数的基准。
|
||
|
||
假设 old_data 中各信号 datalog 长度一致(来自同一时间轴)。
|
||
|
||
Args:
|
||
old_data: 旧数据字典。
|
||
|
||
Returns:
|
||
数据日志长度(行数);空字典返回 0。
|
||
"""
|
||
if not old_data:
|
||
return 0
|
||
# 取首项的 datalog 长度即可,无需遍历整张字典
|
||
first_name = next(iter(old_data))
|
||
return len(old_data[first_name]['datalog']) |