更新代码

This commit is contained in:
chentianrui
2025-10-14 16:13:18 +08:00
parent f5f26c5cf8
commit 0a4dedda1c
230 changed files with 7029 additions and 855114 deletions
+110 -43
View File
@@ -222,56 +222,114 @@ def process_project_children(children, software_type=None):
resource_nodes = []
# 内部工具:在人材机存在拆分时展开其子节点,删除中间父级人材机节点
def _flatten_resource_splits_in_place(node: dict):
"""
递归+迭代地展开任意深度的人材机拆分:
- 如果某个子节点是人材机(人工/材料/机械/2/3/4)且其自身有children
* 将其children上提为当前层的children
* 被上提的每个子节点的 类型/type 设为与父节点相同;
* 移除该父级人材机节点本身;
- 对非被移除的子节点递归处理;
通过 while 循环保证多层嵌套被完全展开。
递归+迭代地展开任意深度的人材机拆分,依据新规则
- 材料:仅当 "拆分"=1 时展开子项并删除父节点;否则保留父节点、删除 children
- 机械:仅当 "拆分"=0 时保留父节点、删除 children;否则展开子项并删除父节点
- 人工:始终展开(无拆分控制)
- 商品砼=1:最高优先级,保留父节点,删除 children
- 同时处理 node["children"] 和 node["材机列表"]
"""
if not isinstance(node, dict):
return
if "children" not in node or not node["children"]:
# 提前退出:无 children 且无 材机列表
if ("children" not in node or not node["children"]) and ("材机列表" not in node or not node["材机列表"]):
return
def _is_resource_type(t):
return t in ("人工", "材料", "机械", "2", "3", "4")
if t in ("人工", "材料", "机械"):
return True
return str(t) in ("2", "3", "4")
# 中英文类型映射
# 类型映射
str2code = {"人工": "2", "材料": "3", "机械": "4"}
code2str = {v: k for k, v in str2code.items()}
def _get_normalized_type(t):
"""将类型统一转为中文(用于判断材料/机械/人工)"""
if t in ("人工", "材料", "机械"):
return t
if str(t) == "2":
return "人工"
if str(t) == "3":
return "材料"
if str(t) == "4":
return "机械"
return None
def _process_node_list(node_list):
if not node_list:
return node_list, False
changed = False
new_list = []
for ch in node_list:
if isinstance(ch, dict):
t = ch.get("类型") or ch.get("type")
normalized_type = _get_normalized_type(t)
# 只处理人材机类型
if normalized_type and ch.get("children"):
# 规则1:商品砼=1 → 保留父节点,清空 children(最高优先级)
sg_concrete = ch.get("商品砼")
if sg_concrete == "1" or sg_concrete == 1:
ch.pop("children", None)
new_list.append(ch)
changed = True
continue
# 规则2:根据类型和“拆分”字段决定行为
split_flag = ch.get("拆分")
should_expand = False
if normalized_type == "人工":
# 人工:始终展开
should_expand = True
elif normalized_type == "材料":
# 材料:仅当 拆分=1 时展开
should_expand = split_flag == "1" or split_flag == 1
elif normalized_type == "机械":
# 机械:仅当 拆分≠0 时展开(即 拆分=0 时不展开)
should_expand = not (split_flag == "0" or split_flag == 0)
if should_expand:
# 展开:上提 children,丢弃父节点;保持子级原有 type/类型 不变
for gc in ch.get("children", []) or []:
new_list.append(gc)
changed = True
continue
else:
# 不展开:保留父节点,删除 children
ch.pop("children", None)
new_list.append(ch)
changed = True
continue
# 非人材机类型,或无人材机 children:递归处理并保留
_flatten_resource_splits_in_place(ch)
new_list.append(ch)
else:
new_list.append(ch)
return new_list, changed
# 迭代直到稳定
changed = True
while changed:
changed = False
new_children = []
for ch in node["children"]:
if isinstance(ch, dict):
t = ch.get("类型") or ch.get("type")
# 命中“人材机且存在children”,则将其子节点上提并继承类型
if _is_resource_type(t) and ch.get("children"):
parent_code = str2code.get(t, t) # 若 t 已是代码则保持
parent_str = code2str.get(t, t) # 若 t 已是中文则保持
for gc in ch.get("children", []) or []:
if isinstance(gc, dict):
gc["type"] = parent_code
gc["类型"] = parent_str
new_children.append(gc)
else:
new_children.append(gc)
# 丢弃父级人材机节点
changed = True
continue
else:
# 对未被移除的子节点继续递归展开
_flatten_resource_splits_in_place(ch)
new_children.append(ch)
else:
new_children.append(ch)
node["children"] = new_children
if "children" in node and node["children"]:
new_children, c1 = _process_node_list(node["children"])
if c1:
node["children"] = new_children
changed = True
if "材机列表" in node and node["材机列表"]:
new_materials, c2 = _process_node_list(node["材机列表"])
if c2:
node["材机列表"] = new_materials
changed = True
for child in children:
# 先复制两份:一份用于工程量树(quantity_node),一份用于资源提取(resource_node_src)
@@ -428,6 +486,8 @@ def process_project_children(children, software_type=None):
if new_type:
# 不改写原始字段,直接将条目拷贝加入,类型由下游对象构造派生
new_node = deepcopy(it)
# 修正:并入前应用拆分展开逻辑,确保 商品砼=1 的资源不再携带 children
_flatten_resource_splits_in_place(new_node)
quantity_nodes.append(new_node)
try:
nm = (
@@ -467,9 +527,12 @@ def process_project_children(children, software_type=None):
return True
return False
# 主材/设备直接计入(以标准化类型入列,避免后续 BCL 过滤不到)
if current_node_type in ("主材", "设备", "1", "5"):
# 主材/设备/一笔性费用直接计入(以标准化类型入列,避免后续 BCL 过滤不到)
if current_node_type in ("主材", "设备", "一笔性费用", "1", "5"):
to_append = deepcopy(original_quantity_node)
# 修正:对即将入列的节点应用人材机拆分展开逻辑,
# 以生效“商品砼=1:保留父节点并去除其 children”的规则
_flatten_resource_splits_in_place(to_append)
quantity_nodes.append(to_append)
try:
nm = (
@@ -486,16 +549,20 @@ def process_project_children(children, software_type=None):
# 定额按条件计入
elif is_quota_node:
if _children_have_no_children(quantity_node) or _mj_has_hit(quantity_node):
quantity_nodes.append(original_quantity_node)
to_append = deepcopy(original_quantity_node)
# 修正:定额节点入列时也先应用拆分展开逻辑,
# 保证“商品砼=1 的资源子项不含 children”
_flatten_resource_splits_in_place(to_append)
quantity_nodes.append(to_append)
try:
nm = (
original_quantity_node.get("项目名称")
or original_quantity_node.get("清单名称")
or original_quantity_node.get("name")
or original_quantity_node.get("材料名称")
to_append.get("项目名称")
or to_append.get("清单名称")
or to_append.get("name")
or to_append.get("材料名称")
or "<未命名>"
)
tp = original_quantity_node.get("类型") or original_quantity_node.get("type")
tp = to_append.get("类型") or to_append.get("type")
# print(f"[DEBUG] 入列工程量节点(定额): 名称={nm} | 类型={tp}")
except Exception:
pass