This commit is contained in:
zy123 2024-12-03 17:37:02 +08:00
parent 643fc904a3
commit 2119f9879d
12 changed files with 1470 additions and 42 deletions

View File

@ -576,7 +576,6 @@ def combine_find_invalid(file_path, output_dir):
print("无效标与废标done...")
return {"无效标与废标项": combined_dict}
# TODO:无效标目前以整个docx文档作为输入可能导致后面两章不必要的信息也导入。 无效投标至少>8个字
if __name__ == '__main__':
start_time = time.time()
# truncate_json_path = "C:\\Users\\Administrator\\Desktop\\货物标\\output4\\tmp2\\竞争性谈判文件(3)_tobidders_notice_part1\\truncate_output.json"

View File

@ -514,8 +514,6 @@ def truncate_pdf_specific_engineering(pdf_path, output_folder, selections, uniqu
logger.error(f"Error in truncate_pdf_specific_engineering: {e}")
return [""] * len(selections) # 返回与 selections 数量相同的空字符串列表
#TODO:zbtest8 zbtest18有问题 后期需要完善,截取需要截两次,第一次严格第二次宽松
if __name__ == "__main__":
input_path = "C:\\Users\\Administrator\\Desktop\\招标文件\\new_test\\zbtest8.pdf"
# input_path="C:\\Users\\Administrator\\Desktop\\fsdownload\\68549b0b-e892-41a9-897c-c3694535ee61\\ztbfile.pdf"

View File

@ -278,6 +278,7 @@ def goods_bid_main(output_folder, file_path, file_type, unique_id):
#TODO:同系统下的多个货物,记录一下数量
#TODO:设备前面带星,而不是要求前面带星。
#TODO:重置一下投标文件格式提取那部分的代码
#商务标这里改为列表最里层
#good_list 金额 截取上下文
if __name__ == "__main__":

View File

View File

@ -0,0 +1,416 @@
def combine_and_update_results(original_data, updates):
def normalize_key(key):
"""
规范化键名
- 替换全角点号为半角点号
- 删除所有空格包括半角和全角
"""
# 替换全角点号(.、。)为半角点号(.
key = key.replace('', '.').replace('', '.')
# 删除所有空格(半角空格和全角空格)
key = key.replace(' ', '').replace('\u3000', '')
return key
def normalize_original_data(d):
"""
递归规范化原始数据字典的键
"""
if not isinstance(d, dict):
return d
normalized = {}
for k, v in d.items():
nk = normalize_key(k)
normalized[nk] = normalize_original_data(v)
return normalized
def normalize_update_value(value):
"""
递归规范化更新字典中嵌套的字典的键
"""
if isinstance(value, dict):
return {normalize_key(k): normalize_update_value(v) for k, v in value.items()}
else:
return value
def recursive_update(data, key, value):
"""
递归更新嵌套字典
"""
keys = key.split('.')
for k in keys[:-1]:
data = data.setdefault(k, {})
if isinstance(value, dict) and isinstance(data.get(keys[-1], None), dict):
data[keys[-1]] = {**data.get(keys[-1], {}), **value}
else:
data[keys[-1]] = value
# 1. 规范化原始数据字典的键
original_data = normalize_original_data(original_data)
# 2. 规范化更新字典的键
normalized_updates = {}
for key, value in updates.items():
nk = normalize_key(key)
nv = normalize_update_value(value)
normalized_updates[nk] = nv
# 3. 执行递归更新
for key, value in normalized_updates.items():
recursive_update(original_data, key, value)
return original_data
# 测试用例1复杂情况
def test_complex_case():
original_data = {
'username': 'Alice',
'user。details': {
'age': 30,
'address line': '123 Main St', # 全角空格
'preferences': {
'color': 'blue',
'food': 'sushi'
}
},
'status': 'active',
'metrics': {
'score': 85,
'rank': 5
},
'list_field': [1, 2, 3]
}
updates = {
'user. name': 'Bob', # 更新user.name
'user.details.age': 31, # 更新嵌套字段
'user.details.addressline': '456 Elm St', # 更新地址,键中有空格被移除
'user.details.preferences.hobby': 'cycling', # 添加新的嵌套字段
'status': 'inactive', # 更新顶级字段
'metrics.score': {'current': 90, 'max': 100}, # 更新为字典
'metrics.new_metric': 50, # 添加新的顶级嵌套字段
'new_field': 'new_value', # 添加新的顶级字段
'list_field': [4, 5] # 更新列表字段
}
expected_result = {
'user': {
'name': 'Bob',
'details': {
'age': 31,
'addressline': '456 Elm St',
'preferences': {
'color': 'blue',
'food': 'sushi',
'hobby': 'cycling'
}
}
},
'status': 'inactive',
'metrics': {
'score': {'current': 90, 'max': 100},
'rank': 5,
'new_metric': 50
},
'list_field': [4, 5],
'new_field': 'new_value'
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试复杂情况失败。\n预期: {expected_result}\n实际: {result}"
print("测试复杂情况通过。")
# 测试用例2更新为空
def test_empty_updates():
original_data = {
'key1': 'value1',
'key2': {
'subkey': 'subvalue'
}
}
updates = {}
expected_result = {
'key1': 'value1',
'key2': {
'subkey': 'subvalue'
}
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试更新为空失败。\n预期: {expected_result}\n实际: {result}"
print("测试更新为空通过。")
# 测试用例3原始数据为空
def test_empty_original():
original_data = {}
updates = {
'newkey': 'new value',
'nested。key.sub key': 123
}
expected_result = {
'new.key': 'newvalue',
'nested.key': {
'subkey': 123
}
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试原始数据为空失败。\n预期: {expected_result}\n实际: {result}"
print("测试原始数据为空通过。")
# 测试用例4更新中包含非字典类型的值
def test_non_dict_values():
original_data = {
'ab': {
'cd': 1
},
'listfield': [1, 2, 3]
}
updates = {
'a.b.c.d': {'new': 2},
'list.field': 'not a list anymore'
}
expected_result = {
'a': {
'b': {
'c': {
'd': {'new': 2}
}
}
},
'list.field': 'not a list anymore'
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试更新中包含非字典类型的值失败。\n预期: {expected_result}\n实际: {result}"
print("测试更新中包含非字典类型的值通过。")
# 测试用例5键中包含全角空格
def test_full_width_spaces():
original_data = {
'key with fullwidth spaces': 'value1',
'nestedkey withspaces': {
'sub key': 'subvalue'
}
}
updates = {
'key with fullwidth spaces': 'updated_value',
'nested.key with spaces.new subkey': 'new_subvalue'
}
expected_result = {
'keywithfullwidthspaces': 'updated_value',
'nested': {
'keywithspaces': {
'subkey': 'new_subvalue'
}
}
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试键中包含全角空格失败。\n预期: {expected_result}\n实际: {result}"
print("测试键中包含全角空格通过。")
# 测试用例6复杂嵌套和不同数据类型
def test_nested_and_various_types():
original_data = {
'configsettings': {
'resolution': '1080p',
'volume': 75,
'controls': {
'jump': 'space',
'crouch': 'ctrl'
}
},
'userpreferences': {
'theme': 'dark',
'notifications': True
}
}
updates = {
'config.settings.resolution': '4K',
'config.settings.controls.run': 'shift', # 添加新控制
'config.settings.volume': 80, # 更新音量
'user.preferences.theme': 'light', # 更新主题
'user.preferences.language': 'en-US', # 添加新偏好
'newsection.setting': 'enabled' # 添加新部分
}
expected_result = {
'config': {
'settings': {
'resolution': '4K',
'volume': 80,
'controls': {
'jump': 'space',
'crouch': 'ctrl',
'run': 'shift'
}
}
},
'user': {
'preferences': {
'theme': 'light',
'notifications': True,
'language': 'en-US'
}
},
'new.section': {
'setting': 'enabled'
}
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试复杂嵌套和不同数据类型失败。\n预期: {expected_result}\n实际: {result}"
print("测试复杂嵌套和不同数据类型通过。")
# 测试用例7键仅包含空格和点号
def test_keys_only_spaces_and_dots():
original_data = {
' .': 'value1',
'  ': 'value2', # 仅全角空格
'nestedkey': {
'subkey': 'subvalue'
}
}
updates = {
'.': 'updated_value1',
'nested.key.subkey': 'updated_subvalue'
}
expected_result = {
'..': 'updated_value1',
'': 'value2', # 全角空格被删除后键为空字符串
'nested': {
'key': {
'subkey': 'updated_subvalue'
}
}
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试键仅包含空格和点号失败。\n预期: {expected_result}\n实际: {result}"
print("测试键仅包含空格和点号通过。")
# 测试用例8更新覆盖整个嵌套结构
def test_overwrite_nested_structure():
original_data = {
'settings': {
'display': {
'brightness': 70,
'contrast': 50
},
'sound': {
'volume': 80
}
}
}
updates = {
'settings.display': 'default', # 覆盖整个display字典
'settings.sound.volume': 90 # 更新音量
}
expected_result = {
'settings': {
'display': 'default',
'sound': {
'volume': 90
}
}
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试覆盖整个嵌套结构失败。\n预期: {expected_result}\n实际: {result}"
print("测试覆盖整个嵌套结构通过。")
# 测试用例9更新包含列表中的字典
def test_update_with_list_of_dicts():
original_data = {
'users': [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25}
],
'settings': {
'theme': 'dark'
}
}
updates = {
'users': [
{'name': 'Alice', 'age': 31}, # 更新第一个用户的年龄
{'name': 'Bob', 'age': 26}, # 更新第二个用户的年龄
{'name': 'Charlie', 'age': 22} # 添加新用户
],
'settings.theme': 'light' # 更新主题
}
expected_result = {
'users': [
{'name': 'Alice', 'age': 31},
{'name': 'Bob', 'age': 26},
{'name': 'Charlie', 'age': 22}
],
'settings': {
'theme': 'light'
}
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试更新包含列表中的字典失败。\n预期: {expected_result}\n实际: {result}"
print("测试更新包含列表中的字典通过。")
# 测试用例10键重复但规范化后不同
def test_duplicate_keys_after_normalization():
original_data = {
'key': {
'one':'value1',
'two':'value2'
}
}
updates = {
'key.one': 'updated_value',
'keytwo': 'updated_value3'
}
expected_result = {
'keyone': 'updated_value', # 'keyone' 和 'key.one ' 规范化后合并为 'keyone'
'keytwo': 'updated_value3' # 'key two' 规范化后为 'keytwo'
}
result = combine_and_update_results(original_data, updates)
assert result == expected_result, f"测试键重复但规范化后不同失败。\n预期: {expected_result}\n实际: {result}"
print("测试键重复但规范化后不同通过。")
# 主函数,运行所有测试用例
def run_all_tests():
test_functions = [
# test_complex_case,
# test_empty_updates,
# test_empty_original,
# test_non_dict_values,
# test_full_width_spaces,
# test_nested_and_various_types,
# test_keys_only_spaces_and_dots,
# test_overwrite_nested_structure,
# test_update_with_list_of_dicts,
test_duplicate_keys_after_normalization
]
for test_func in test_functions:
try:
test_func()
except AssertionError as e:
print(e)
except Exception as ex:
print(f"{test_func.__name__} 运行时发生错误: {ex}")
print("所有测试用例已执行完毕。")
if __name__ == '__main__':
run_all_tests()

View File

@ -0,0 +1,92 @@
from flask_app.货物标.技术参数要求提取后处理函数 import extract_matching_keys
def test_extract_matching_keys():
# 定义测试数据
data = {
"fruits": ["apple", "banana"],
"fruits-1": ["orange"],
"vegetables": ["carrot"],
"系统功能": ["feature1"], # 特殊键,应被特殊处理或排除
"grains": {
"whole": ["rice", "wheat"],
"whole-1": ["barley"],
"refined": ["white rice"]
},
"misc": {
"fruits": ["strawberry"],
"fruits-1": ["blueberry"],
"系统 功能": ["feature2"], # 特殊键,带空格
"dairy": ["milk", "cheese"]
},
"beverages": {
"alcoholic": {
"beer": ["lager", "ale"],
"wine": ["red", "white"]
},
"non-alcoholic": ["juice", "soda"]
},
"snacks": [
{
"chips": ["potato", "tortilla"],
"nuts": ["almonds", "cashews"]
},
{
"chips-1": ["kettle", "baked"],
"candies": ["chocolate", "gummy"]
}
]
}
good_list = ["fruits", "vegetables", "grains", "chips"]
special_keys = ["系统功能", "dairy"] # 假设 'dairy' 也是特殊键
# 预期输出
expected_output = {
"fruits-a": ["apple", "banana"],
"fruits-b": ["orange"],
"vegetables": ["carrot"],
"grains-a": ["rice", "wheat"],
"grains-b": ["barley"],
"grains": {"refined": ["white rice"]},
"misc": {}, # 'fruits' and 'fruits-1' inside 'misc' should be processed
"chips-a": ["potato", "tortilla"],
"chips-b": ["kettle", "baked"]
}
# 注意:根据您的函数逻辑,特殊键会被排除,且嵌套的 'fruits' 会被处理
# 这里我们需要根据函数实际行为调整预期输出
# 让我们根据函数逻辑重新定义预期输出
# 函数会生成新的键名,对于重复的 'fruits' 会添加后缀
# 'grains' 内的 'whole' 和 'whole-1' 也会被处理为 'whole-a', 'whole-b'
# 'chips' 和 'chips-1' 会被处理为 'chips-a', 'chips-b'
# 'dairy' 是特殊键,应被排除
# '系统功能' 和 '系统 功能' 是特殊键,应被排除
expected_output_correct = {
"fruits-a": ["apple", "banana"],
"fruits-b": ["orange"],
"vegetables": ["carrot"],
"grains-a": ["rice", "wheat"],
"grains-b": ["barley"],
"chips-a": ["potato", "tortilla"],
"chips-b": ["kettle", "baked"]
}
# 运行函数
result = extract_matching_keys(data, good_list, special_keys)
# 打印结果
print("测试用例: 提取匹配键并处理各种情况")
print("输入数据:", data)
print("good_list:", good_list)
print("special_keys:", special_keys)
print("\n预期输出:", expected_output_correct)
print("实际输出:", result)
print("\n测试通过:", result == expected_output_correct)
# 运行测试
if __name__ == "__main__":
test_extract_matching_keys()

View File

@ -0,0 +1,352 @@
import json
import re
from collections import defaultdict
from copy import deepcopy
def generate_key_paths(data):
"""
处理输入的字典生成 key_paths, grouped_paths good_list并根据条件修改原始字典
参数:
data (dict): 输入的嵌套字典
返回:
tuple: 包含 key_paths, grouped_paths good_list 的元组
"""
# 编译用于匹配后缀的正则表达式模式
pattern = re.compile(r'(.+)-\d+$')
# 初始化结果列表和字典
key_paths = []
grouped_counts = defaultdict(int) # 用于记录每个 grouped_path 的数量
good_list = []
def recurse(current_dict, path):
"""
递归遍历字典处理 key_paths grouped_paths并收集 good_list grouped_counts
参数:
current_dict (dict): 当前遍历的字典
path (list): 当前路径的键列表
"""
# 第一遍遍历,统计每个基名的出现次数
base_name_count = {}
base_names = {}
for key in current_dict.keys():
match = pattern.match(key)
if match:
base = match.group(1)
else:
base = key
base_names[key] = base
base_name_count[base] = base_name_count.get(base, 0) + 1
# 第二遍遍历,根据基名的出现次数分类
keys_to_rename = {}
for key, base in base_names.items():
if base_name_count[base] == 1:
# 检查是否是最内层(值为列表)
value = current_dict[key]
if isinstance(value, list):
current_path = '.'.join(path + [base])
key_paths.append(current_path)
# 收集 good_list保持顺序且不重复
if base not in good_list:
good_list.append(base)
# 如果原键名有后缀,需要记录以便后续重命名
if key != base:
keys_to_rename[key] = base
elif isinstance(value, dict):
# 继续递归处理
recurse(value, path + [base])
else:
# 记录分组路径,并统计数量
grouped_path = '.'.join(path + [base])
grouped_counts[grouped_path] += 1
# 执行键名的重命名,同时保持原有顺序
if keys_to_rename:
new_ordered_dict = {}
for key in current_dict.keys():
if key in keys_to_rename:
new_key = keys_to_rename[key]
new_ordered_dict[new_key] = current_dict[key]
else:
new_ordered_dict[key] = current_dict[key]
current_dict.clear()
current_dict.update(new_ordered_dict)
# 对于基名重复的键,继续递归(如果值是字典)
for key, base in base_names.items():
if base_name_count[base] > 1:
value = current_dict[key]
if isinstance(value, dict):
recurse(value, path + [base])
elif isinstance(value, list):
# 如果值是列表,仍需收集基名到 good_list
if base not in good_list:
good_list.append(base)
# 深拷贝数据以避免修改原始输入
data_copy = deepcopy(data)
# 开始递归遍历
recurse(data_copy, [])
def collect_grouped_paths(current_dict, path, collected):
for key in current_dict.keys():
match = pattern.match(key)
if match:
base = match.group(1)
else:
base = key
current_path = '.'.join(path + [base])
if current_path in grouped_counts and current_path not in collected:
collected.append(current_path)
value = current_dict[key]
if isinstance(value, dict):
collect_grouped_paths(value, path + [base], collected)
collected_grouped_paths = []
collect_grouped_paths(data_copy, [], collected_grouped_paths)
# 将 grouped_paths 转换为包含数量的字典列表
grouped_paths = [{path: grouped_counts[path]} for path in collected_grouped_paths]
return key_paths, grouped_paths, good_list, data_copy
def rename_keys(data):
"""
对整个数据结构进行重命名处理
"""
def rename_keys_recursive(current_dict):
"""
递归地重名字典中的键确保同一层级下具有相同基名的键被正确编号
"""
if not isinstance(current_dict, dict):
return current_dict
key_order = list(current_dict.keys())
base_name_dict = defaultdict(list)
# 辅助函数:提取基名(去除可能的 -数字 后缀)
def get_base_name(key):
if '-' in key:
parts = key.rsplit('-', 1)
if parts[1].isdigit():
return parts[0]
return key
# 将键按基名分组
for key in key_order:
base = get_base_name(key)
base_name_dict[base].append(key)
new_dict = {}
for key in key_order:
base = get_base_name(key)
keys = base_name_dict[base]
if len(keys) > 1:
# 如果存在同基名的多个键,则进行重命名
if base not in new_dict:
# 按原始顺序对需要重命名的键进行排序
sorted_keys = sorted(keys, key=lambda x: key_order.index(x))
for idx, original_key in enumerate(sorted_keys, start=1):
new_key = f"{base}-{idx}"
# 如果值是字典,递归处理
if isinstance(current_dict[original_key], dict):
new_dict[new_key] = rename_keys_recursive(current_dict[original_key])
else:
new_dict[new_key] = current_dict[original_key]
else:
# 如果没有重复的基名,保持原名
if isinstance(current_dict[key], dict):
new_dict[key] = rename_keys_recursive(current_dict[key])
else:
new_dict[key] = current_dict[key]
return new_dict
# 对整个数据结构进行递归重命名
return rename_keys_recursive(data)
def test_generate_key_paths():
data0={
"显示系统": {
"LED全彩显示屏": [],
"控制盒及电源": [],
"大屏播控系统": [],
"配电柜(含PLC)": [],
"钢结构底座及铝型材支架": [],
"电缆及信号线缆": [],
"控制终端": [],
"50寸液晶电视机": [],
"50寸电视机地面推车": [],
"高清监视器": []
},
"摄像系统": {
"高清摄像机": [],
"摄像机三脚架": [],
"摄像机壁装架": [],
"摄像机控制键盘": []
},
"视频处理系统": {
"高清视频拼控矩阵(16*16)": [],
"分量信号接口器": [],
"高清四画面分割器": []
},
"发言系统": {
"数字会议发言主机": [],
"方形短杆代表话筒": [],
"专用连接线缆": [],
"手持无线话筒": []
},
"视频会议系统": {
"多点控制器": [],
"多串口控制服务器": [],
"综合会议管理调度平台": [],
"高清会议终端(主会场)": [],
"高清会议终端(分会场)": [],
"65寸电视机移动推车(9楼)": [],
"65寸液晶电视机(分会场)": [],
"控制平板及软件": [],
"鹅颈话筒": []
},
"辅助系统": {
"时序电源": [],
"多媒体地插盒": [],
"线材辅料": [],
"墙体拆除及修复": []
}
}
# 定义测试数据
data = {
"fruits": [],
"fruits-1": [],
"vegetables": {
"root": [],
"root-1": [],
"leafy": []
},
"grains": {
"交换机":[],
"whole grains": [], # 键名包含空格
"whole-grains-1": [] # 同一基名,带后缀
},
"misc": {
"system functions": [], # 特殊键,包含空格
"system functions-1": [], # 特殊键,带后缀
"fruits-1": [],
"fruits-2": [],
"fruits-3": []
},
"beverages": []
}
# 运行函数
key_paths, grouped_paths, good_list, data_copy = generate_key_paths(data0)
# 打印结果
print("实际 data_copy:")
print(json.dumps(data_copy,ensure_ascii=False,indent=4))
modi=rename_keys(data_copy)
print(json.dumps(modi, ensure_ascii=False, indent=4))
print(key_paths)
print(grouped_paths)
print(good_list)
user_query_template = """请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。请以 JSON 格式返回结果,键名为\"{}\",键值为一个列表,列表中包含若干描述\"{}\"的技术参数或采购要求的字符串,请按原文内容回答,保留三角▲、五角★和序号,不可擅自增删内容,尤其是不可擅自添加序号。
要求与指南
1. 如果该货物没有相关采购要求或技术参数要求键值应为空列表[]
2. 如果存在嵌套结构且原文为Markdown 的表格语法'摄像机|有效像素|≥900W像素' 请不要返回该Markdown语法而是使用冒号':'将相关信息拼接在一起生成一条完整且清晰的技术参数或采购要求描述作为列表中的一个字符串"摄像机有效像素≥900W像素"
3. 字符串中的内容为具体的技术参数要求或采购要求请不要返回诸如'1高清录像功能'这种标题性质且不能体现要求的内容
4. 你的键值应该全面对于同一个单元格内的数据尽量全面不要遗漏对于单元格内以序号分隔的各条参数要求请分别添加进键值即字符串列表
### 示例输出1如下
{{
"摄像机控制键盘": [
"1、▲支持串行 RS232/RS422 和 IP 混合控制,允许在一个控制器上使用 RS232/RS422/IP 控制单个系统中的摄像机;",
"2、支持 2 组 RS422 串口 VISCA 协议菊花链控制 2x7 台摄像机。",
"★能够自动对焦,提供检测报告"
]
}}
### 示例输出2如下包含嵌套结构
{{
"摄像机": [
"摄像机有效像素≥900W像素",
"摄像机最低照度彩色≤0.001lx",
"协议routes 接口开放:具备;▲支持标准 ONVIF 协议与第三方厂家设备进行互联;支持 GB/T28181应提供 SDK"
]
}}
"""
user_query_template_two="""请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。由于该货物存在 {} 种不同的采购要求或技术参数,请逐一列出,并以 JSON 格式返回结果。请以'货物名-编号'区分多种型号,编号为从 1 开始的自然数,依次递增,即第一个键名为\"{}-1\", 键值为一个列表,列表中包含若干描述\"{}\"的技术参数(或采购要求)的字符串,请按原文内容回答,保留三角▲、五角★和序号(若有),不可擅自增添内容。
请注意以下特殊情况
要求与指南
1. 如果该货物没有相关采购要求或技术参数要求键值应为空列表
2. 如果存在嵌套结构且原文为Markdown 的表格语法'摄像机|有效像素|≥900W像素' 请不要返回该Markdown语法而是使用冒号':'将相关信息拼接在一起生成一条完整且清晰的技术参数或采购要求描述作为列表中的一个字符串"摄像机有效像素≥900W像素"
3. 字符串中的内容为具体的技术参数要求或采购要求请不要返回诸如'1高清录像功能'这种标题性质且不能体现要求的内容
4. 你的键值应该全面对于同一个单元格内的数据尽量全面不要遗漏对于单元格内以序号分隔的各条参数要求请分别添加进键值即字符串列表
### 示例输出1如下
{{
"交换机-1": [
"★1、支持固化千兆电口≥8 个固化千兆光口≥2 个,桌面型设备;",
"2、支持静态链路聚合"
],
"交换机-2": [
"1、交换容量≥52Gbps包转发率≥38.69Mpps",
"2、提供国家强制性产品认证证书及测试报告3C",
"★能实现信号控制独立传输"
]
}}
### 示例输出2如下包含嵌套结构
{{
"摄像机-1": [
"摄像机有效像素≥900W像素",
"摄像机最低照度彩色≤0.001lx",
"协议routes 接口开放:具备;▲支持标准 ONVIF 协议与第三方厂家设备进行互联;支持 GB/T28181应提供 SDK"
],
"摄像机-2": [
"支持夜视", "支持云存储"
]
}}
"""
def test_generate_ques_two(grouped_paths):
queries=[]
for grouped_dict in grouped_paths:
for grouped_key, grouped_key_cnt in grouped_dict.items():
# 将键中的 '.' 替换为 '下的'
modified_grouped_key = grouped_key.replace('.', '下的')
# 使用修改后的键填充第一个占位符,原始键填充第二个占位符
# 如果需要使用 full_text可以取消注释并提供相应的实现
# full_text = read_txt_to_string(processed_filepath)
# new_query = user_query_template_two.format(modified_grouped_key, grouped_key, modified_grouped_key, full_text)
# 根据您的需求,生成新的查询字符串
new_query = user_query_template_two.format(modified_grouped_key, grouped_key_cnt,grouped_key, modified_grouped_key)
queries.append(new_query)
print(new_query)
def test_generate_ques_one(key_paths):
queries=[]
for key in key_paths:
# 将键中的 '.' 替换为 '下的'
modified_key = key.replace('.', '下的')
# 使用修改后的键填充第一个占位符,原始键填充第二个占位符
# full_text = read_txt_to_string(processed_filepath)
# new_query = user_query_template.format(modified_key, key, modified_key,full_text) #转豆包后取消注释
new_query = user_query_template.format(modified_key, key, modified_key)
queries.append(new_query)
print(new_query)
if __name__ == "__main__":
# test_generate_key_paths()
# grouped_paths=[{'fruits': 2}, {'vegetables.root': 2}, {'misc.system functions': 2}, {'misc.fruits': 3}]
# key_paths=['显示系统.LED全彩显示屏', '显示系统.控制盒及电源', '显示系统.大屏播控系统', '显示系统.配电柜(含PLC)', '显示系统.钢结构底座及铝型材支架', '显示系统.电缆及信号线缆', '显示系统.控制终端', '显示系统.50寸液晶电视机', '显示系统.50寸电视机地面推车', '显示系统.高清监视器', '摄像系统.高清摄像机', '摄像系统.摄像机三脚架', '摄像系统.摄像机壁装架', '摄像系统.摄像机控制键盘', '视频处理系统.高清视频拼控矩阵(16*16)', '视频处理系统.分量信号接口器', '视频处理系统.高清四画面分割器', '发言系统.数字会议发言主机', '发言系统.方形短杆代表话筒', '发言系统.专用连接线缆', '发言系统.手持无线话筒', '视频会议系统.多点控制器', '视频会议系统.多串口控制服务器', '视频会议系统.综合会议管理调度平台', '视频会议系统.高清会议终端(主会场)', '视频会议系统.高清会议终端(分会场)', '视频会议系统.65寸电视机移动推车(9楼)', '视频会议系统.65寸液晶电视机(分会场)', '视频会议系统.控制平板及软件', '视频会议系统.鹅颈话筒', '辅助系统.时序电源', '辅助系统.多媒体地插盒', '辅助系统.线材辅料', '辅助系统.墙体拆除及修复']
key_paths=['显示系统.LED全彩显示屏', '显示系统.控制盒及电源', '显示系统.大屏播控系统']
# test_generate_ques(grouped_paths)
test_generate_ques_one(key_paths)

View File

@ -0,0 +1,213 @@
from flask_app.货物标.技术参数要求提取后处理函数 import restructure_data
#注意!这里的期待输出有误
def test_restructure_data():
print("开始测试 restructure_data 函数...\n")
# 测试用例 1: 所有顶层键的值都是列表(二层结构)
data1 = {
"fruits": ["apple", "banana", "cherry"],
"vegetables": ["carrot", "lettuce", "spinach"],
"grains": ["rice", "wheat", "barley"]
}
expected1 = data1.copy()
result1 = restructure_data(data1)
print("测试用例 1: 所有顶层键的值都是列表(二层结构)")
print("输入:", data1)
print("预期输出:", expected1)
print("实际输出:", result1)
print("测试通过:", result1 == expected1, "\n")
# 测试用例 2: 所有顶层键的值都是字典,且已经是三层结构
data2 = {
"fruits": {
"citrus": ["orange", "lemon"],
"berries": ["strawberry", "blueberry"]
},
"vegetables": {
"root": ["carrot", "beet"],
"leafy": ["lettuce", "spinach"]
}
}
expected2 = data2.copy()
result2 = restructure_data(data2)
print("测试用例 2: 所有顶层键的值都是字典,且已经是三层结构")
print("输入:", data2)
print("预期输出:", expected2)
print("实际输出:", result2)
print("测试通过:", result2 == expected2, "\n")
# 测试用例 3: 混合的两层和三层结构
data3 = {
"fruits": ["apple", "banana"],
"vegetables": {
"root": ["carrot", "beet"],
"leafy": ["lettuce", "spinach"]
},
"grains": ["rice", "wheat"]
}
expected3 = {
"fruits": {"fruits": ["apple", "banana"]},
"vegetables": {
"root": ["carrot", "beet"],
"leafy": ["lettuce", "spinach"]
},
"grains": {"grains": ["rice", "wheat"]}
}
result3 = restructure_data(data3)
print("测试用例 3: 混合的两层和三层结构")
print("输入:", data3)
print("预期输出:", expected3)
print("实际输出:", result3)
print("测试通过:", result3 == expected3, "\n")
# 测试用例 4: 超过三层嵌套
data4 = {
"fruits": {
"citrus": {
"tropical": ["orange", "lemon"]
},
"berries": ["strawberry", "blueberry"]
},
"vegetables": ["carrot", "lettuce"]
}
expected4 = {
"fruits.citrus": {
"tropical": ["orange", "lemon"]
},
"fruits.berries": {
"berries": ["strawberry", "blueberry"]
},
"vegetables": {
"vegetables": ["carrot", "lettuce"]
}
}
result4 = restructure_data(data4)
print("测试用例 4: 超过三层嵌套")
print("输入:", data4)
print("预期输出:", expected4)
print("实际输出:", result4)
print("测试通过:", result4 == expected4, "\n")
# 测试用例 5: 超过四层嵌套
data5 = {
"animals": {
"mammals": {
"primates": {
"humans": ["Alice", "Bob"],
"monkeys": ["Charlie", "Dave"]
},
"carnivores": ["Lion", "Tiger"]
},
"birds": ["Eagle", "Parrot"]
},
"plants": ["Oak", "Pine"]
}
expected5 = {
"animals.mammals.primates": {
"humans": ["Alice", "Bob"],
"monkeys": ["Charlie", "Dave"]
},
"animals.mammals.carnivores": ["Lion", "Tiger"],
"animals.birds": ["Eagle", "Parrot"],
"plants": {"plants": ["Oak", "Pine"]}
}
result5 = restructure_data(data5)
print("测试用例 5: 超过四层嵌套")
print("输入:", data5)
print("预期输出:", expected5)
print("实际输出:", result5)
print("测试通过:", result5 == expected5, "\n")
# 测试用例 6: 空字典
data6 = {}
expected6 = {}
result6 = restructure_data(data6)
print("测试用例 6: 空字典")
print("输入:", data6)
print("预期输出:", expected6)
print("实际输出:", result6)
print("测试通过:", result6 == expected6, "\n")
# 测试用例 7: 顶层键的值类型无效
data7 = {
"fruits": "apple",
"vegetables": ["carrot", "lettuce"]
}
print("测试用例 7: 顶层键的值类型无效")
print("输入:", data7)
try:
restructure_data(data7)
print("预期结果: 引发 ValueError")
print("实际输出: 无异常")
print("测试通过: False\n")
except ValueError as ve:
print("预期结果: 引发 ValueError")
print("实际输出: 引发 ValueError:", ve)
print("测试通过:", "数据格式异常" in str(ve), "\n")
# 测试用例 8: 嵌套字典中存在无效的值类型
data8 = {
"fruits": {
"citrus": "orange",
"berries": ["strawberry", "blueberry"]
},
"vegetables": ["carrot", "lettuce"]
}
print("测试用例 8: 嵌套字典中存在无效的值类型")
print("输入:", data8)
try:
restructure_data(data8)
print("预期结果: 引发 ValueError")
print("实际输出: 无异常")
print("测试通过: False\n")
except ValueError as ve:
print("预期结果: 引发 ValueError")
print("实际输出: 引发 ValueError:", ve)
print("测试通过:", "数据格式异常" in str(ve), "\n")
# 测试用例 9: 复杂的嵌套结构
data9 = {
"fruits": {
"citrus": ["orange", "lemon"],
"berries": {
"summer": ["strawberry", "blueberry"],
"winter": ["cranberry"]
}
},
"vegetables": ["carrot", "lettuce"],
"grains": {
"whole": ["rice", "wheat"],
"refined": ["white rice"]
}
}
expected9 = {
"fruits.berries": {
"summer": ["strawberry", "blueberry"],
"winter": ["cranberry"]
},
"fruits.citrus": {
"citrus": ["orange", "lemon"]
},
"vegetables": {
"vegetables": ["carrot", "lettuce"]
},
"grains.whole": {
"whole": ["rice", "wheat"]
},
"grains.refined": {
"refined": ["white rice"]
}
}
result9 = restructure_data(data9)
print("测试用例 9: 复杂的嵌套结构")
print("输入:", data9)
print("预期输出:", expected9)
print("实际输出:", result9)
print("测试通过:", result9 == expected9, "\n")
print("所有测试用例已完成。")
# 运行测试
if __name__ == "__main__":
test_restructure_data()

View File

@ -0,0 +1,342 @@
data1={
"采购需求": {
"显示系统": {
"★LED全彩显示屏": [],
"控制盒及电源": [],
"大屏播控系统": [],
"配电柜(含PLC)": [],
"钢结构底座及铝型材支架": [],
"电缆及信号线缆": [],
"控制终端": [],
"50寸液晶电视机": [],
"50寸电视机地面推车": [],
"高清监视器": []
},
"摄像系统": {
"★高清摄像机": [],
"摄像机三角架": [],
"摄像机壁装架": [],
"摄像机控制键盘": []
},
"视频处理系统": {
"★高清视频拼控矩阵(16*16)": [],
"分量信号接口器": [],
"高清四画面分割器": []
},
"发言系统": {
"数字会议发言主机": [],
"方形短杆代表话筒": [],
"专用连接线缆": [],
"手持无线话筒": []
},
"视频会议系统": {
"★多点控制器": [],
"★多串口控制服务器": [],
"★综合会议管理调度平台": [],
"★高清会议终端(主会场)": [],
"★高清会议终端(分会场)": [],
"65寸电视机移动推车(9楼)": [],
"65寸液晶电视机(分会场)": [],
"控制平板及软件": [],
"鹅颈话筒": []
},
"辅助系统": {
"时序电源": [],
"多媒体地插盒": [],
"线材辅料": [],
"墙体拆除及修复": []
}
}
}
data2={
"采购需求": {
"显示系统": {
"★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 级烈度地震模拟实验。"
],
"控制盒及电源": [
"高清发送盒 2 台 、接收卡 27 张 、 电源 26 台。"
],
"大屏播控系统": [
"1、具有多用户多权限管理功能支持多用户同时登录客户端每个用户根据自身不同权限管理显示屏",
"2、系统对输入信号源进行预监视实现在播控前预先查看的功能。"
],
"配电柜(含PLC)": [
"1 、10 KW 容量,施耐德元器件,含 PLC 控制系统,可实时获取屏幕背部烟雾及温度数据, 可执行远程开关机操作; 数字量 输入: 12 路 24 V DC 输入;数字量输出: 10 路继电输出;模拟量输 入4 个电压 输入4 个电流输入;通讯口: 1 个 RS232,2 个 RS485 1 个以太网; 程序容量: 256K 片内 Flash 内存; 功能: 远程监控 、 电话监视 、温 度监控 、消防监控;",
"2、本配电柜具备过压、过流、欠压、短路、断路以及漏电保护措施。"
],
"钢结构底座及铝型材支架": [
"1 、主体钢架结构及定制型材;",
"2 、确保楼层承受力许可,按需加固楼层地面;",
"3 、钢结构。"
],
"电缆及信号线缆": [
"配套所需控制网线 、高清视频线缆 、 电源线缆等适配。"
],
"控制终端": [
"1 、处理器: 八核心 16 线程;",
"2 、显卡: 8G/DDR6/PCI Express 4.0 16X",
"3 、 内存: ≥8G DDR4 内存;",
"4 、硬盘: SSD 固态硬盘(容量≥480G)",
"5 、接口: 音频/网络/HDMI 接口/9 针串口;",
"6 、显示器: 21.5 英寸。"
],
"50寸液晶电视机": [
"1 、屏幕尺寸: 50 英寸;",
"2 、背光类型: LED",
"3 、屏幕分辨率: 超高清 4K 3840 ×2 160",
"4 、支持 HDR 显示;",
"5 、接口: USB2.0 ×2 、HDMI2.0 ×3",
"6 、 网络连接方式: 无线/网线。"
],
"50寸电视机地面推车": [
"1 、全钢结构, 满足 50 寸显示屏承重安装要求;",
"2 、承载: 50Kg",
"3 、高度: 650mm",
"4 、可以调节观看角度。"
],
"高清监视器": [
"1 、27 寸全屏高清监视器;",
"2 、 IPS 技术炫彩硬屏, 4K 高清分辨率;",
"3 、 内置音箱;",
"4 、亮度: 350cd/m2",
"5 、接口: DP × 1 HDMI ×2 USB ×2 音频/耳机 × 1。"
]
},
"摄像系统": {
"★高清摄像机": [
"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 、满足高清摄像机承重 、尺寸要求。"
],
"摄像机壁装架": [
"1 、钣金 2.0mm 优质冷轧钢板;",
"2 、满足高清摄像机壁挂安装承重 、尺寸要求;",
"3 、具有放置摄像机电源空间。"
],
"摄像机控制键盘": [
"1 、支持串行 RS232/RS422 和 IP 混合控制, 允许在一个控制器上使用 RS232/RS422/IP 控制单个系统中的摄像机;",
"2 、支持 2 组 RS422 串口 VISCA 协议菊花链控制 2x7 台摄像机;",
"3 、通过手柄或单独的 Seesaw 控制杆控制变焦,通过专用旋钮及按钮, 可直接设置摄像机的光圈 、快门 、增益 、 白平 衡 、 自动曝光等 级等参数, 无需通过菜单设置;",
"4 、使用 IP 控制,控制器可自动搜索系统中的 IP 摄像机, 快速分配 摄像机 IP 地址 。支持 Onvif 、CGI 、VISCA over IP",
"5 、单个网络上不限制控制器, 控制 255 台 IP 摄像机;",
"6 、多达 256 个预置位,带有图像参数记忆功能及 8 条 轨迹记忆存储, 方便快速调用摄像机的运动 (需摄像机支持 ",
"7 、6 个可选 ASSIGN 功能键, 可以为 ASSIGN 按钮分配附加功能;",
"8 、多色按键/旋钮照明指示摄像机当前状态;",
"9 、独立双电源供电: DC 12V ,POE。"
]
},
"视频处理系统": {
"★高清视频拼控矩阵(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、提供软件著作权证书。"
],
"★高清会议终端(主会场)": [
"1 、体系标准: ITU-T H.323 、H.320 、IETF SIP/专有平台协议;",
"2 、 网 络 传 输 协 议: TCP/IP UDP RTP/RTC P/ RTSP HTTP DHCP/PPPOE/ NAT IGMP",
"3 、视频编解码协议: H.265 H.264 HP H.264 、H.2 63 、MPEG-4",
"4 、音频编解码协议: G.711a/G.711u 、G.722/G.723/G.7 19/AAC-LC",
"5 、视频码率: 128Kbps 16Mbps 支持 16M E1*8 ",
"6 、音频码率: 24Kbps384Kbps",
"7 、 内 容 分 辨 率 典 型 : 动 态 (HDMI/VGA) 1 080P60 静 态 (VGA)1600*1200@60 Up to 60fps",
"8 、音频指标: AEC/ANS/AGC 等;",
"9 、音频特性: 唇音同步, 静音与哑音控制;",
"10 、接口指标: 遥控接口 NEC 码制标准红外接口,支持遥控器直接 控制终端;",
"11 、通信控制接 口: RJ45 × 2(RS232 x 1 RS48 5 x1) 摄像机控制 RJ45-232 接口通用路由器己置线;另预留 RS232 x 2",
"12 、 网 络 接 口 LAN RJ45 x 4 E1 (4x E1) CC4/miniBNC x 810/100/1000Mbps2/4/6/8Mbps",
"13、视频输出接口3G-SDIx1、HDMIx1DVI-Ix1CVBSx1VGAx1 DVI -I 可以配置选择 DVI / HDMI / VGA / YPrPb 任何一种接口模式输入 图像, 高标 、清接口可同时输出;",
"14、音频接口音频输出输出采用“凤凰接口 ”, LINE IN, MAIN (3pin) × 1XLR IN (3pin) × 1XLR 用于 MIC 输入时支持,+48V 幻象供 电, LINE OUT,MAIN (3pin) × 1",
"15 、遵循 H.323 、H.320 、SIP 协议标准;",
"16 、视频抗丢包能力: 支持高至 60%丢包率情况下, 图像流畅 、无 卡顿、无马赛克;音频抗 IP 网络丢包能力:支 持高至 75%丢包率情 况下,声音清晰流畅; 会议抗 IP 网络丢包能力:支持高至 70%丢包 率情况下, 会议可正常召开。"
],
"★高清会议终端(分会场)": [
"内置 1080P 高清摄像机, 12 倍光学变焦72 度广角镜头DVI-I 接口辅流输入2 路 1080P@30fps 高清视频, 6M 速率,双 HDMI 接口输出,双屏双显、单屏双显、单屏三显、单屏四显 ,含终端控制软件。"
],
"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 、采用优质接插件。"
],
"线材辅料": [
"采用专用线材、材料、接口、各种辅料等。"
],
"墙体拆除及修复": [
"对大屏安装区域墙体 、天花进行拆除及修复。"
]
},
"货物列表": [
"★LED全彩显示屏",
"控制盒及电源",
"大屏播控系统",
"配电柜(含PLC)",
"钢结构底座及铝型材支架",
"电缆及信号线缆",
"控制终端",
"50寸液晶电视机",
"50寸电视机地面推车",
"高清监视器",
"★高清摄像机",
"摄像机三角架",
"摄像机壁装架",
"摄像机控制键盘",
"★高清视频拼控矩阵(16*16)",
"分量信号接口器",
"高清四画面分割器",
"数字会议发言主机",
"方形短杆代表话筒",
"专用连接线缆",
"手持无线话筒",
"★多点控制器",
"★多串口控制服务器",
"★综合会议管理调度平台",
"★高清会议终端(主会场)",
"★高清会议终端(分会场)",
"65寸电视机移动推车(9楼)",
"65寸液晶电视机(分会场)",
"控制平板及软件",
"鹅颈话筒",
"时序电源",
"多媒体地插盒",
"线材辅料",
"墙体拆除及修复"
]
}
}

View File

@ -65,14 +65,14 @@ def generate_key_paths(data):
# 编译用于匹配后缀的正则表达式模式
pattern = re.compile(r'(.+)-\d+$')
# 初始化结果列表
# 初始化结果列表和字典
key_paths = []
grouped_set = set() # 使用集合来避免重复
grouped_counts = defaultdict(int) # 用于记录每个 grouped_path 的数量
good_list = []
def recurse(current_dict, path):
"""
递归遍历字典处理 key_paths grouped_paths并收集 good_list
递归遍历字典处理 key_paths grouped_paths并收集 good_list grouped_counts
参数:
current_dict (dict): 当前遍历的字典
@ -109,8 +109,9 @@ def generate_key_paths(data):
# 继续递归处理
recurse(value, path + [base])
else:
# 记录分组路径,确保唯一性
grouped_set.add('.'.join(path + [base]))
# 记录分组路径,并统计数量
grouped_path = '.'.join(path + [base])
grouped_counts[grouped_path] += 1
# 执行键名的重命名,同时保持原有顺序
if keys_to_rename:
@ -141,16 +142,6 @@ def generate_key_paths(data):
# 开始递归遍历
recurse(data_copy, [])
# 转换 grouped_set 为列表,并排序以保持一致性
# 为了保持 grouped_paths 的顺序与它们首次出现的顺序一致,
# 我们需要在递归过程中记录它们的顺序,而不是简单地排序。
# 因此,修改 grouped_set 为列表并在添加时检查重复。
grouped_paths = []
for path in data.keys():
pass # This is a placeholder if needed for order
# 实际上,由于 grouped_set 已经去重且不保顺序,
# 我们需要重新遍历数据_copy 来收集 grouped_paths 按顺序
def collect_grouped_paths(current_dict, path, collected):
for key in current_dict.keys():
match = pattern.match(key)
@ -159,7 +150,7 @@ def generate_key_paths(data):
else:
base = key
current_path = '.'.join(path + [base])
if current_path in grouped_set and current_path not in collected:
if current_path in grouped_counts and current_path not in collected:
collected.append(current_path)
value = current_dict[key]
if isinstance(value, dict):
@ -167,7 +158,9 @@ def generate_key_paths(data):
collected_grouped_paths = []
collect_grouped_paths(data_copy, [], collected_grouped_paths)
grouped_paths = collected_grouped_paths
# 将 grouped_paths 转换为包含数量的字典列表
grouped_paths = [{path: grouped_counts[path]} for path in collected_grouped_paths]
return key_paths, grouped_paths, good_list, data_copy
def rename_keys(data):
@ -177,7 +170,7 @@ def rename_keys(data):
def rename_keys_recursive(current_dict):
"""
递归地重名字典中的键确保同一层级下具有相同基名的键被正确编号
递归地重名字典中的键确保同一层级下具有相同基名的键被正确编号
"""
if not isinstance(current_dict, dict):
return current_dict
@ -222,13 +215,15 @@ def rename_keys(data):
new_dict[key] = current_dict[key]
return new_dict
for top_key, top_value in data.items():
if isinstance(top_value, dict):
data[top_key] = rename_keys_recursive(top_value)
return data
# 对整个数据结构进行递归重命名
return rename_keys_recursive(data)
def combine_and_update_results(original_data, updates):
"""
先规范化original和updates中的字典防止空格的情况导致匹配不上无法更新
"""
def normalize_key(key):
"""
规范化键名
@ -364,6 +359,9 @@ def get_technical_requirements(file_path,invalid_path,processed_filepath):
5. 系统功能若采购的某系统提及总体系统功能则在系统值中添加'系统功能'二级键不展开具体内容
6. 完整性确保不遗漏系统内的货物也不添加未提及的内容'采购清单'中未提取的货物或系统名称在形如'主要设备功能指标'的标题下有详细参数指标要求请将该货物名也添加至返回中
特殊情况
若采购的货物或系统或模块名称前存在三角五角注意是名称前而非具体的技术参数或采购要求前在返回名称时请保留前面的符号'★高清摄像机'
输出格式
1.JSON格式最外层键名为'采购需求'
2.层次关系用嵌套键值对表示
@ -428,8 +426,8 @@ def get_technical_requirements(file_path,invalid_path,processed_filepath):
processed_data=truncate_system_keys(cleaned_res['采购需求'])
key_paths, grouped_paths, good_list, data_copy= generate_key_paths(processed_data) # 提取需要采购的货物清单 key_list交通监控视频子系统.高清视频抓拍像机 ... grouped_paths是同一系统下同时有'交换机-1'和'交换机-2',提取'交换机' 输出eg:{'交通标志.标志牌铝板', '交通信号灯.交换机'}
modified_data=rename_keys(data_copy)
# user_query_template = "请你根据该货物标中采购要求部分的内容,请你给出\"{}\"的技术参数或采购要求请以json格式返回结果外层键名为\"{}\", 键值对中的键是你对该要求的总结,而值需要完全与原文保持一致,不可擅自总结删减。"
user_query_template = """请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。请以 JSON 格式返回结果,键名为\"{}\",键值为一个列表,列表中包含若干描述\"{}\"的技术参数或采购要求的字符串,请按原文内容回答,保留三角▲、五角★和序号,不可擅自增删内容,尤其是不可擅自添加序号。
要求与指南
1. 如果该货物没有相关采购要求或技术参数要求键值应为空列表[]
2. 如果存在嵌套结构且原文为Markdown 的表格语法'摄像机|有效像素|≥900W像素' 请不要返回该Markdown语法而是使用冒号':'将相关信息拼接在一起生成一条完整且清晰的技术参数或采购要求描述作为列表中的一个字符串"摄像机有效像素≥900W像素"
@ -454,8 +452,8 @@ def get_technical_requirements(file_path,invalid_path,processed_filepath):
]
}}
"""
user_query_template_two="""请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。由于该货物存在种不同的采购要求或技术参数,请逐一列出,并以 JSON 格式返回结果。请以'货物名-编号'区分多种型号,编号为从 1 开始的自然数,依次递增,即第一个键名为\"{}-1\", 键值为一个列表,列表中包含若干描述\"{}\"的技术参数(或采购要求)的字符串,请按原文内容回答,保留三角▲、五角★和序号(若有),不可擅自增添内容
请注意以下特殊情况
user_query_template_two="""请根据货物标中采购要求部分的内容,告诉我\"{}\"的技术参数或采购要求是什么。由于该货物存在 {} 种不同的采购要求或技术参数,请逐一列出,并以 JSON 格式返回结果。请以'货物名-编号'区分多种型号,编号为从 1 开始的自然数,依次递增,即第一个键名为\"{}-1\"键值为一个列表,列表中包含若干描述\"{}\"的技术参数(或采购要求)的字符串,请按原文内容回答,保留三角▲、五角★和序号(若有),不可擅自增删内容,尤其是不可擅自添加序号
要求与指南
1. 如果该货物没有相关采购要求或技术参数要求键值应为空列表
2. 如果存在嵌套结构且原文为Markdown 的表格语法'摄像机|有效像素|≥900W像素' 请不要返回该Markdown语法而是使用冒号':'将相关信息拼接在一起生成一条完整且清晰的技术参数或采购要求描述作为列表中的一个字符串"摄像机有效像素≥900W像素"
@ -498,14 +496,17 @@ def get_technical_requirements(file_path,invalid_path,processed_filepath):
queries.append(new_query)
# 处理 grouped_paths 中的项,应用 user_query_template_two
for grouped_key in grouped_paths:
# 将键中的 '.' 替换为 '下的'
modified_grouped_key = grouped_key.replace('.', '下的')
# 使用修改后的键填充第一个占位符,原始键填充第二个占位符
# full_text = read_txt_to_string(processed_filepath)
# new_query = user_query_template_two.format(modified_grouped_key, grouped_key, modified_grouped_key, full_text)
new_query = user_query_template_two.format(modified_grouped_key, grouped_key, modified_grouped_key)
queries.append(new_query)
for grouped_dict in grouped_paths:
for grouped_key, grouped_key_cnt in grouped_dict.items():
# 将键中的 '.' 替换为 '下的'
modified_grouped_key = grouped_key.replace('.', '下的')
# 使用修改后的键填充第一个占位符,原始键填充第二个占位符
# 如果需要使用 full_text可以取消注释并提供相应的实现
# full_text = read_txt_to_string(processed_filepath)
# new_query = user_query_template_two.format(modified_grouped_key, grouped_key, modified_grouped_key, full_text)
# 根据您的需求,生成新的查询字符串
new_query = user_query_template_two.format(modified_grouped_key, grouped_key_cnt,grouped_key, modified_grouped_key)
queries.append(new_query)
results = multi_threading(queries, "", file_id, 2) #通义
# results = multi_threading(queries, "", "", 3) #豆包
technical_requirements = []
@ -553,8 +554,8 @@ def test_all_files_in_folder(input_folder, output_folder):
if __name__ == "__main__":
start_time=time.time()
# truncate_file="C:\\Users\\Administrator\\Desktop\\fsdownload\\469d2aee-9024-4993-896e-2ac7322d41b7\\ztbfile_procurement.docx"
truncate_docfile=r"C:\Users\Administrator\Desktop\货物标\output1\招标文件(107国道)_procurement.docx"
truncate_file=r'C:\Users\Administrator\Desktop\fsdownload\e702f1e6-095d-443d-bb7d-ef2e42037cb1\ztbfile_procurement.pdf'
truncate_docfile=r"C:\Users\Administrator\Desktop\货物标\output1\6_2定版视频会议磋商文件_procurement.docx"
truncate_file=r'C:\Users\Administrator\Desktop\货物标\output1\6.2定版视频会议磋商文件_procurement.pdf'
# invalid_path="D:\\flask_project\\flask_app\\static\\output\\output1\\e7dda5cb-10ba-47a8-b989-d2993d34bb89\\ztbfile.pdf"
# truncate_file="D:\\flask_project\\flask_app\\static\\output\\output1\\e7dda5cb-10ba-47a8-b989-d2993d34bb89\\ztbfile_procurement.docx"
# output_folder="C:\\Users\\Administrator\\Desktop\\货物标\\output1\\tmp"

View File

@ -133,6 +133,14 @@ def restructure_data(data):
- 如果所有顶层键的值都是列表两层结构直接返回原数据
- 如果存在混合的两层和三层结构或更深层级则将所有数据统一为三层结构
"""
def get_max_depth(d, current_depth=1):
"""
计算字典的最大嵌套深度
"""
if not isinstance(d, dict) or not d:
return current_depth
return max(get_max_depth(v, current_depth + 1) for v in d.values())
# 检查是否所有顶层键的值都是列表(即两层结构)
all_two_layers = all(isinstance(value, list) for value in data.values())
@ -150,7 +158,7 @@ def restructure_data(data):
# 如果存在更深层级,展开至三层
for sub_key, sub_value in value.items():
if isinstance(sub_value, dict):
# 这里假设需要展开到三层,将其保留为三层
# 保留为三层
structured_data[sub_key] = sub_value
elif isinstance(sub_value, list):
# 将两层结构转换为三层结构
@ -165,7 +173,14 @@ def restructure_data(data):
structured_data[key] = {key: value}
else:
raise ValueError(f"'{key}'的数据格式异常: {type(value)}")
return structured_data
# 检查重构后的数据深度
max_depth = get_max_depth(structured_data)
if max_depth > 3:
# 递归调用以进一步重构
return restructure_data(structured_data)
else:
return structured_data
# 定义获取所有以''结尾的前缀的函数
@ -202,7 +217,7 @@ def remove_common_prefixes(string_list):
else:
new_string_list.append(s)
return new_string_list
#TODO:目前对于超过四层的数据无法平坦化为3层
if __name__ == "__main__":
# 示例数据
sample_data = {

View File

@ -62,7 +62,6 @@ def fetch_procurement_reqs(procurement_path, invalid_path):
# TODO:技术要求可以在技术参数之后执行,把完整的技术参数输入,问大模型,除了上述内容还有哪些,这样的话把技术标和其他的区分开。
# TODO:0362
if __name__ == "__main__":
start_time = time.time()