Files
chentianrui 9609bb67b4 上传文件
2025-08-01 15:31:56 +08:00

258 lines
10 KiB
Python

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)