zbparse/flask_app/test_case/test_extract_matching_keys.py
2024-12-04 11:48:33 +08:00

362 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import json
from flask_app.货物标.技术参数要求提取后处理函数 import extract_matching_keys
def extract_matching_keys(data, good_list, special_keys=None, parent_key=''):
import re
from collections import defaultdict
def get_suffix(n):
"""
根据数字n返回对应的字母后缀。
1 -> 'a', 2 -> 'b', ..., 26 -> 'z', 27 -> 'aa', 28 -> 'ab', ...
"""
suffix = ''
while n > 0:
n, r = divmod(n - 1, 26)
suffix = chr(97 + r) + suffix
return suffix
def count_matching_keys(data, patterns, special_keys, counter=None):
"""递归统计匹配键的出现次数,仅统计值为列表的键"""
if counter is None:
counter = defaultdict(int)
if isinstance(data, dict):
for key, value in data.items():
clean_key = key.replace(" ", "") # 去除键中的空格
if isinstance(value, list):
if clean_key not in special_keys and any(pattern.match(clean_key) for pattern in patterns):
counter[clean_key] += 1
elif isinstance(value, dict):
count_matching_keys(value, patterns, special_keys, counter)
elif isinstance(data, list):
for item in data:
if isinstance(item, (dict, list)):
count_matching_keys(item, patterns, special_keys, counter)
return counter
def process_data(data, patterns, special_keys, key_counter, suffix_map, filtered_data, parent_key):
"""递归处理数据并构建结果"""
def get_suffix_label(key):
suffix_map[key] += 1
return get_suffix(suffix_map[key])
if isinstance(data, dict):
for key, value in data.items():
clean_key = key.replace(" ", "") # 去除键中的空格
if isinstance(value, list):
# 处理值为列表的键
if any(pattern.match(clean_key) for pattern in patterns):
# 检查是否以特殊符号开头
if clean_key.startswith(('', '','','','','','','','','')):
symbol = clean_key[0]
stripped_key = clean_key[1:]
new_key = generate_key(stripped_key, parent_key, key_counter, suffix_map, special_keys)
# 将符号添加到每个字符串的开头
new_value = [symbol + item for item in value]
filtered_data[new_key] = new_value
else:
new_key = generate_key(clean_key, parent_key, key_counter, suffix_map, special_keys)
filtered_data[new_key] = value
elif isinstance(value, dict):
# 继续递归处理嵌套字典
new_parent_key = clean_key if parent_key == '' else f"{parent_key}{clean_key}"
process_data(value, patterns, special_keys, key_counter, suffix_map,
filtered_data, new_parent_key)
elif isinstance(data, list):
for item in data:
if isinstance(item, (dict, list)):
process_data(item, patterns, special_keys, key_counter, suffix_map,
filtered_data, parent_key)
def generate_key(key, parent_key, key_counter, suffix_map, special_keys):
"""生成新的键名"""
if key in special_keys and parent_key:
return f"{parent_key}{key}"
elif key_counter[key] > 1:
suffix = get_suffix(suffix_map[key] + 1)
suffix_map[key] += 1
return f"{key}-{suffix}"
return key
if special_keys is None:
special_keys = ["系统功能"] # 默认值为 ["系统功能"]
# 去除 good_list 中的空格
clean_good_list = [g.replace(" ", "") for g in good_list]
# 构建匹配的正则表达式
patterns = [re.compile(r'^' + re.escape(g) + r'(?:-\d+)?$') for g in clean_good_list]
# 先统计所有匹配键的出现次数,仅统计值为列表的键
key_counter = count_matching_keys(data, patterns, special_keys)
# 初始化后缀映射
suffix_map = {key: 0 for key, count in key_counter.items() if count > 1}
# 用于存储最终结果
filtered_data = {}
# 递归处理数据
process_data(data, patterns, special_keys, key_counter, suffix_map, filtered_data, parent_key)
return filtered_data
def test_extract_matching_keys():
# 定义测试数据
data = {
"采购需求": {
"显示系统": {
"系统功能":["1","2"],
"★LED全彩显示屏": [
"1、显示尺寸6m±0.3W× 1.5m±0.2(H),单屏分辨率≥3744 × 1040",
"2 、像素间距≤1.53mm",
"3 、亮度≥450nits 色温: 3000-15000K 可调, 对比度: 5000:1",
"4 、峰值功耗≤440W 平均功耗≤146W 带有智能(黑屏) 节电功 能, 开启智能节电功能比没开启节能 40%以上;",
"5 、水平可视角度≥160 ° , 垂直可视角度≥140 ° ;",
"6 、亮度均匀性≥97% 刷新率≥3840 Hz 发光点中心距偏差3%",
"7 、色域覆盖率≥100% 低亮高灰: 100%亮度时, 16 bits 灰度20% 亮度时, 12bits 灰度;",
"8 、铝底壳材质, 无风扇散热结构;",
"9 、模组电源接口采用4P 接插头, 免工具维护, 同时有防呆设计, 预防接错电源线短路而导致的烧毁模组行为,采用集成 HUB 接收卡 控制, 支持通讯状态监控;",
"10 、冗余备份, 支持双电网供电, 当其中一路交流电网跳闸后, 另 外一路电网继续供电, 实现不间断供电, 支持热备份, 当其中一块 电源失效后, 另一块电源继续工作, 从而实现不间断供电;",
"11 、屏体发光模组采用 4.5 VDC 的安全电压供电;",
"12 、彩色信号处理位数≥16bit",
"13 、具备故障自诊及排查功能;",
"14 、 图像有降噪 、增强 、运动补偿 、色坐标变换处理 、钝 化处理无 几何失真和线性失真现象 、消除鬼影拖尾, 无“毛毛虫 ”“鬼影 ” 跟随现象;",
"15 、防护等级符合 IP6X 显示屏具有防潮 、防尘 、防腐蚀 、防电磁 干扰 、防静电等功能, 并具有过流、短路 、过压 、欠压保护等功能;",
"16 、工作噪音声压等级一米处≤7.8 dB (A),盐雾符合 10 级要求, PCB 阻燃等级达到 UL 94 V-0 级要求, 通过 9 级烈度地震模拟实验。"
],
"☆钢结构底座及铝型材支架": [
"1 、主体钢架结构及定制型材;",
"2 、确保楼层承受力许可,按需加固楼层地面;",
"3 、钢结构。"
],
"电缆及信号线缆": [
"1 、配套所需控制网线 、高清视频线缆 、 电源线缆等适配。"
],
"控制终端": [
"1 、处理器: 八核心 16 线程;",
"2 、显卡: 8G/DDR6/PCI Express 4.0 16X",
"3 、 内存: ≥8G DDR4 内存;",
"4 、硬盘: SSD 固态硬盘(容量≥480G)",
"5 、接口: 音频/网络/HDMI 接口/9 针串口;",
"6 、显示器: 21.5 英寸。"
]
},
"摄像系统": {
"系统功能": ["a", "b"],
"★高清摄像机": [
"1 、成像器件: 1/2.8 Exmor CMOS",
"2 、镜头: 30 倍光学f=4.3mm to 129mm ",
"3 、水平视角: 63.7 ° ;",
"4、视频输出格式1080P/601 080P/501080P/301080P/251080i/60 720p/60",
"5 、视频输出: 3G-SDI HDMI CVBS,IP (可同步输出 ",
"6 、真双输出: IP 和 SDI 视频格式可以独立设置;",
"7 、控制方式: RS232 / RS422 / RS485 IP/Onvif/Visca-over-IP IP 控制软件, 红外遥控器;",
"8 、IP 最高 1080p60 支持 H.264/H.265/MJPEG",
"9 、支持 Tally 灯;",
"10 、支持独立 PoE+IEEE 802.3 at 和 DC 12V 电源;",
"11 、扩展存储: Micro SD,最高支持 128GB。"
],
"摄像机三脚架": [
"1 、铝合金材质, 承重 2-10Kg",
"2 、满足高清摄像机承重 、尺寸要求。"
]
},
"视频处理系统": {
"★高清视频拼控矩阵(16*16)": [
"1 、8U 切换主箱体, 支持输入 13 槽, 输出 4.5 槽, 支持 8 路高分采集, 支持冗余电源, 标配 1 个电源模块; 本项目配置输入接 口 16 路和 1 张字幕卡, 输出接口 16 路;",
"2 、设备应为纯硬件 FPGA 架构, CrossPoint 全总线交换技术, 背板 等效带宽;",
"3 、单张板卡支持 4 通道输入或输出, 紧凑型机箱,模拟视频单板卡 支持 16 路同时输入, 单卡支持 2 种信号源任意组合;",
"4、输入输出板卡可热插拔输入板卡热插拔恢复时间 2s输出板 卡热插拔恢复时间8s",
"5 、开机时间≤10s 启动电源至输出最总画面的时间间隔;",
"6 、平均故障时间间隔 MTBF 不小于 96000 小时, 保证设备能够 稳定运行;",
"7 、最大单机背板信号处理带宽不小于 720Gbps",
"8、对各个输入通道采用纯硬件处理技术采用独享带宽方式为每个 输入通道分配带宽, 切换过程中对其他信号无影响, 实现了对输入 通道的实时处理;",
"9、支持集中采集 DVI、VGA、CVBS、HDBaseT、HDMI、SDI、YPbPr 、 光纤等 2K 信号Dual-link DVI、HDMI 1.4、DisplayPort 等 4K 信号;",
"10 、支持 DVI 、HDBaseT 、HDMI 、SDI 、光纤 、CVBS 、Ypbp r 等常见的 2K 信号输出, Dual-link DVI 、HDMI 1.4 等 4K 信号输出;",
"11、设备可实现任意一路画面的任意比例缩放、漫游、 跨屏 、叠加、 开窗;",
"12 、设备支持图像无缝实时切换功能, 无缝切换时间20 ms ",
"13 、支持场景保存及快速调用, 支持场景轮巡, 适应于不同的应用 场景;",
"14 、支持信号源预监功能, 支持浏览所有输入信号源的实时预览画 面;",
"15 、支持大屏图像回显, 可显示整面拼接墙的显示图像;",
"16 、支持设置拼接屏的拼缝补偿, 可精确到 1 个像素;",
"17 、支持 RRTA 分辨率实时全兼容技术, 单台 设备应支持同时控制 4 组不同分辨率的大屏幕显示;",
"18 、设备具备静态底图功能, 设备支持超大分辨率底图显示, 横纵 分辨率最大 65535 像素。"
],
"分量信号接口器": [
"用于现有视频会议专业对接高清矩阵接口器"
],
"高清四画面分割器": [
"画面预览使用, 具有画中画 、独立单画面放大功能。"
]
},
"发言系统": {
"数字会议发言主机": [
"1 、标准挂载单元数量: 4 路总线接口, 单路可连接 32 个 最多系统可挂载 128 个会议单元, 且最远线路长度可高达 100 米;",
"2、主机面板彩屏显示系统菜单通过设置可设 定 1/2/4/6 发言数量;",
"3 、支持先入先出模式, 后入后出模式, 限制模式, 电脑/主席允许 模式, 自由讨论模式;",
"4 、可直接控制最多三个摄像球, 完成视频会议功能;",
"5、多种输入输出接口主输入、卡座输入和前置输出、辅助输出及录音输出接口",
"6 、带有 RS 232 视频控制输出 口, 可以直接输出派尔高-P 派尔高 -D VISCA 控制协议, 控制最大 3 个摄像机, 完成摄像自动跟踪;",
"7 、 内置 4 切 1 视频切换器, 用于摄像机的视频 接连;",
"8 、可以响应处理话筒的会议中服务的请求;",
"9 、 内置签到表决功能, 可以配合话筒进行签到表决;",
"10 、 内置 DSP 自适应音频处理器,可以最大可能的抑制声回输。"
],
"方形短杆代表话筒": [
"1 、超大静音开关设计;",
"2 、会议操作系统,全新的触摸操控技术, 2.8 英寸的彩色触摸屏幕;",
"3 、超短全金属短咪杆设计;",
"4 、高灵敏度咪芯设计,拾音距离≥80 cm ",
"5 、红色雾面指示灯设计, 指示发言状态;",
"6 、支持视像跟踪;",
"7、配合主机 可以实现先入先出,后入后出, 限制模式,主席允许模式, 自由讨论模式;",
"8、话筒的身份可以自行设定可以通过主机设置改变话筒身份在 代表, 主席, VIP 自由切换, 让使用更灵活多样, 满足 高端需求;",
"9 、长距离传输对音质不会有影响; 具备超强的抗手机 RF 干扰性。"
],
"专用连接线缆": [
"主机与话筒专用连接线缆, 长度≥30m。"
],
"手持无线话筒": [
"1 、含一台接收机, 两个无线手持话筒发射器;",
"2 、频率响应: 50Hz-18KHz",
"3 、有效使用距离≥100 米;",
"4 、信噪比≥105dB(1KHz-A)",
"5 、灵敏度: -105dBm(12dB S/N AD)。"
]
},
"视频会议系统": {
"▲多点控制器": [
"1 、遵循 H.323 、H.320 、SIP 标准协议;",
"2 、支持 H.265 H.264 HP H.264 编解码标准。",
"3 、支持不低于 25 分屏高清多画面;",
"4 、最大线路速率: 8M",
"5、视频抗丢包能力支持高至 60%丢包率情况下 图像流畅无马赛克;音频抗 IP 网络丢包能力:支持高至 75%丢包 率情况下,声音清晰流畅; 会议抗 IP 网络丢包能力:支持高至 70%丢包率情况下,会 议仍可正常召开。 以上 5 项参数需提供第三方检测机构检验 报告。"
],
"★多串口控制服务器": [
"1 、具有高速数据处理能力, 内嵌高速嵌入式 CPU ",
"2 、提供 16 路一控多 、多控一;",
"3 、具有多种转发机制, 支持 IP 、串口间双向转发机制;",
"4、控制会议矩阵、会议摄像机外围设备串口设备实现对会议系统设备的控制"
],
"★综合会议管理调度平台": [
"1、含硬件终端和视频会议专用软件用于控制会议、矩阵、会议摄像机实现与省厅 、 随州市综合会议管理调度平台对接 、融合, 互联互通;",
"2 、统一调度管理平台, 根据业务需要, 可互为控制 、互为 备份;",
"3、可以与原有的主控平台互为操作、实现控制备份保证会议正常召开 需在设计方案中详细阐明如何实现;",
"4、实现对会议设备的整合控制采用一键拖拉式操作软件界面友好 、操作管理简易 、直观;",
"5 、可在综合会议管理平台实现四画面预览各分会场及中心视频信 号;",
"6 、提供软件著作权证书。"
],
"65寸电视机移动推车(9楼)": [
"1 、全钢结构, 满足 70 寸电视承重安装要求;",
"2 、承载: 200Kg",
"3 、轮子带自锁刹车功能。"
],
"65寸液晶电视机(分会场)": [
"1 、屏幕尺寸: 65 英寸; 含挂架及安装;",
"2 、背光类型: LED",
"3 、屏幕分辨率: 超高清 4K 3840 ×2 160",
"4 、支持 HDR 显示;",
"5 、CPU Cortex A55 四核;",
"6 、接口: USB2.0 ×2 、HDMI2.0 ×2",
"7 、 网络连接方式: 无线/网线。"
],
"控制平板及软件": [
"10.2 寸无线触摸屏, 含控制软件, 实现远程一键式控制 、视频会议调度。"
],
"鹅颈话筒": [
"1 、采样率: 48kHz",
"2 、频响: 20Hz 20kHz",
"3 、灵敏度: 38 ±2dB",
"4 、拾音距离: 20-50CM;含接头 、线缆, 线缆 长度≥3.5m",
"5 、支持终端远程供电, 无需外接电源。"
]
},
"辅助系统": {
"时序电源": [
"1、具有 12 路 1KW 电源;",
"2、具有电压表指示 支持串口控制;",
"3、采用触点闭合控制功能",
"4、具有过压 、过流保护。"
],
"多媒体地插盒": [
"1 、具有至少 1 路 HDMI 、 1 路电源 、2 路网络接口模块;",
"2 、采用优质接插件。"
],
"线材辅料": [
"采用专用线材 、材料 、接口 、各种辅料等。"
],
"墙体拆除及修复": [
"对大屏安装区域墙体 、天花进行拆除及修复。"
]
}
}
}
good_list = [
"★LED全彩显示屏",
"控制盒及电源",
"大屏播控系统",
"配电柜(含PLC)",
"☆钢结构底座及铝型材支架",
"电缆及信号线缆",
"控制终端",
"50寸液晶电视机",
"50寸电视机地面推车",
"高清监视器",
"★高清摄像机",
"摄像机三脚架",
"摄像机壁装架",
"摄像机控制键盘",
"★高清视频拼控矩阵(16*16)",
"分量信号接口器",
"高清四画面分割器",
"数字会议发言主机",
"方形短杆代表话筒",
"专用连接线缆",
"手持无线话筒",
"▲多点控制器",
"★多串口控制服务器",
"★综合会议管理调度平台",
"★高清会议终端(主会场)",
"★高清会议终端(分会场)",
"65寸电视机移动推车(9楼)",
"65寸液晶电视机(分会场)",
"控制平板及软件",
"鹅颈话筒",
"时序电源",
"多媒体地插盒",
"线材辅料",
"墙体拆除及修复",
"系统功能"
]
special_keys = ["系统功能", "dairy"] # 假设 'dairy' 也是特殊键
# 注意:根据您的函数逻辑,特殊键会被排除,且嵌套的 'fruits' 会被处理
# 这里我们需要根据函数实际行为调整预期输出
# 让我们根据函数逻辑重新定义预期输出
# 函数会生成新的键名,对于重复的 'fruits' 会添加后缀
# 'grains' 内的 'whole' 和 'whole-1' 也会被处理为 'whole-a', 'whole-b'
# 'chips' 和 'chips-1' 会被处理为 'chips-a', 'chips-b'
# 'dairy' 是特殊键,应被排除
# '系统功能' 和 '系统 功能' 是特殊键,应被排除
# 运行函数
result = extract_matching_keys(data, good_list, special_keys)
# 打印结果
print("测试用例: 提取匹配键并处理各种情况")
print("good_list:", good_list)
print("special_keys:", special_keys)
print("实际输出:", json.dumps(result,ensure_ascii=False,indent=4))
# 运行测试
if __name__ == "__main__":
test_extract_matching_keys()