import json def equipment_calculation(json_file_path): # 读取JSON文件 with open(json_file_path, "r", encoding="utf-8") as f: data = json.load(f) # 获取projectDivision project_division = data["projectData"]["projectDivision"] def collect_nodes_by_type(parent_name): # 初始化五个列表,分别存储不同类型的节点 zhucai_nodes = [] # 主材 shebei_nodes = [] # 设备 rengong_nodes = [] # 人工 cailiao_nodes = [] # 材料 jixie_nodes = [] # 机械 found_parent = [False] # 使用列表作为可变引用 # 递归查找函数 - 处理嵌套结构 def find_parent_and_collect(node, target_name): # 如果节点是字典 if isinstance(node, dict): # 检查当前节点是否为目标父节点 node_name = node.get("项目名称", node.get("名称", "")) if node_name == target_name: print(f"找到目标节点: {target_name}") # 找到目标父节点,开始收集子节点 collect_children(node) found_parent[0] = True return True # 处理所有可能的子节点路径 for key, value in node.items(): if find_parent_and_collect(value, target_name): return True # 如果节点是列表 elif isinstance(node, list): for item in node: if find_parent_and_collect(item, target_name): return True return False # 递归收集子节点中的不同类型节点 def collect_children(node, parent_quantity=1.0): # 检查当前节点类型并添加到对应列表 node_type = node.get("类型") node_name = node.get("项目名称", node.get("名称", "未知")) node_quantity = float(node.get("数量", 1.0)) # 只有当前节点是五类特定节点之一时,才考虑其父节点数量 if node_type: print(f"发现节点: {node_name}, 类型: {node_type}, 数量: {node_quantity}, 父节点数量: {parent_quantity}") # 创建节点的副本,避免修改原始数据 node_copy = node.copy() # 保存原始数量 node_copy["原始数量"] = node_quantity # 保存父节点数量,用于后续计算 node_copy["父节点数量"] = parent_quantity if node_type == "主材": zhucai_nodes.append(node_copy) elif node_type == "设备": shebei_nodes.append(node_copy) elif node_type == "人工": rengong_nodes.append(node_copy) elif node_type == "材料": cailiao_nodes.append(node_copy) elif node_type == "机械": jixie_nodes.append(node_copy) # 计算当前节点传递给子节点的数量 # 如果当前节点不是特定五类节点之一,则将其数量与父节点数量相乘传递给子节点 current_parent_quantity = ( node_quantity * parent_quantity if node_type not in ["主材", "设备", "人工", "材料", "机械"] else parent_quantity ) # 递归处理children子节点 children = node.get("children", []) for child in children: collect_children(child, current_parent_quantity) # 递归处理材机列表子节点 caiji_list = node.get("材机列表", []) for item in caiji_list: collect_children(item, current_parent_quantity) # 从根节点开始查找目标父节点 find_parent_and_collect(project_division, parent_name) if not found_parent[0]: print(f"警告: 未找到名为 '{parent_name}' 的节点") # 尝试查找相似名称的节点 all_names = [] def collect_names(node): if isinstance(node, dict): if "项目名称" in node: all_names.append(node["项目名称"]) if "名称" in node: all_names.append(node["名称"]) # 遍历所有子节点 for key, value in node.items(): collect_names(value) elif isinstance(node, list): for item in node: collect_names(item) collect_names(project_division) # 打印一些可能的节点名称 print("可能的节点名称示例:") unique_names = sorted(set(all_names)) for name in unique_names[:20]: # 只显示前20个,去重并排序 print(f"- {name}") # 返回五个分类列表 return { "主材": zhucai_nodes, "设备": shebei_nodes, "人工": rengong_nodes, "材料": cailiao_nodes, "机械": jixie_nodes, } # 合并同类型节点并计算价格差异 def merge_same_nodes(nodes_dict): merged_results = {"主材": [], "设备": [], "人工": [], "材料": [], "机械": []} # 遍历每种类型的节点 for node_type, nodes in nodes_dict.items(): # 用于存储合并的节点 merged_nodes = {} # 遍历每个节点 for node in nodes: # 获取节点属性 name = node.get("名称", node.get("项目名称", "未知")) budget_price = str(node.get("预算价不含税", "0")) market_price = str(node.get("市场价不含税", "0")) # 获取当前节点的数量和父节点数量 node_quantity = float(node.get("原始数量", node.get("数量", "0"))) parent_quantity = float(node.get("父节点数量", 1.0)) # 计算实际数量 = 节点数量 * 父节点数量 actual_quantity = node_quantity * parent_quantity print( f"合并节点: {name}, 节点数量: {node_quantity}, 父节点数量: {parent_quantity}, 实际数量: {actual_quantity}" ) # 创建唯一键以识别相同节点 - 使用名称、预算价和市场价作为键 key = f"{name}_{budget_price}_{market_price}" # 如果已存在相同键的节点,则合并数量 if key in merged_nodes: merged_nodes[key]["数量"] += actual_quantity else: # 深拷贝节点,避免修改原始数据 merged_node = node.copy() merged_node["数量"] = actual_quantity # 移除辅助字段 if "原始数量" in merged_node: del merged_node["原始数量"] if "父节点数量" in merged_node: del merged_node["父节点数量"] merged_nodes[key] = merged_node # 计算合并后的预算价合价、市场价合价和价差 for key, merged_node in merged_nodes.items(): quantity = float(merged_node["数量"]) budget_price = float(merged_node.get("预算价不含税", 0)) market_price = float(merged_node.get("市场价不含税", 0)) # 计算合价 budget_total = budget_price * quantity market_total = market_price * quantity price_diff = market_total - budget_total # 添加计算结果到节点属性 merged_node["预算价合价"] = round(budget_total, 2) merged_node["市场价合价"] = round(market_total, 2) merged_node["价差"] = round(price_diff, 2) # 将合并后的节点添加到结果中 merged_results[node_type].append(merged_node) return merged_results # 打印合并结果的统计信息 def print_merge_stats(merged_results): for node_type, nodes in merged_results.items(): total_budget = sum(node.get("预算价合价", 0) for node in nodes) total_market = sum(node.get("市场价合价", 0) for node in nodes) total_diff = sum(node.get("价差", 0) for node in nodes) print(f"{node_type}节点数量: {len(nodes)}") print(f"{node_type}总预算价合价: {round(total_budget, 2)}") print(f"{node_type}总市场价合价: {round(total_market, 2)}") print(f"{node_type}总价差: {round(total_diff, 2)}") print("-" * 30) # 主函数流程 parent_name = "基础工程材料工地运输" result = collect_nodes_by_type(parent_name) print("\n原始节点统计:") print(f"主材节点数量: {len(result['主材'])}") print(f"设备节点数量: {len(result['设备'])}") print(f"人工节点数量: {len(result['人工'])}") print(f"材料节点数量: {len(result['材料'])}") print(f"机械节点数量: {len(result['机械'])}") print("\n开始合并相同节点...") merged_results = merge_same_nodes(result) print("\n合并后节点统计及价格信息:") print_merge_stats(merged_results) # 输出样例节点详情 for node_type, nodes in merged_results.items(): if nodes: sample_node = nodes[0] print(f"\n{node_type}节点样例:") for key, value in sample_node.items(): if key in [ "名称", "项目名称", "数量", "预算价不含税", "市场价不含税", "预算价合价", "市场价合价", "价差", ]: print(f" {key}: {value}") break return merged_results if __name__ == "__main__": json_file_path = "主网预算/架空_clean.json" equipment_calculation(json_file_path)