258 lines
10 KiB
Python
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)
|