增加了知识图谱导出excel

This commit is contained in:
chentianrui
2025-08-18 15:14:37 +08:00
parent ce2986fbe2
commit 3fd0b2af0c
610 changed files with 6062 additions and 4932473 deletions
+174 -202
View File
@@ -1,10 +1,4 @@
"""
第二步:计算bcl结果
"""
import os
import argparse
import re
import json
from typing import Dict, List, Any, Optional, Tuple
from equipment_calculation.software_types import (
@@ -16,78 +10,23 @@ from equipment_calculation.software_types import (
from equipment_calculation.software_calculators import get_calculator
def parse_arguments():
"""解析命令行参数"""
parser = argparse.ArgumentParser(description="工程量取费和人材机合价计算程序")
# 软件类型参数
parser.add_argument(
"--category", choices=["主网", "配网", "技改"], default="主网", help="软件类别(主网/配网/技改)"
)
parser.add_argument("--engineering-type", choices=["预算", "清单"], default="清单", help="工程类型(预算/清单)")
# 计算类型参数
parser.add_argument(
"--calculate",
choices=["all", "quantity", "resource"],
default="quantity",
help="计算类型(all: 全部, quantity: 工程量取费, resource: 人材机合价)",
)
# 项目参数
parser.add_argument("--project", help="项目名称,如果不指定则处理所有项目")
parser.add_argument("--adjustment-type", default="调差", help="调差类型,默认为'调差'")
# 输入文件参数
parser.add_argument(
"--input-file",
default="测试案例/主网清单变电.json",
help="输入JSON文件路径,如果不指定则使用默认路径",
)
# 添加输入和输出文件夹参数
parser.add_argument("--input-folder", help="输入文件夹路径,包含要处理的JSON文件")
parser.add_argument("--output-folder", default="计算结果", help="输出文件夹路径,用于保存计算结果")
return parser.parse_args()
def parse_filename(filename: str) -> Tuple[str, str]:
"""
从文件名中解析软件类别和工程类型(备用方法)
:param filename: JSON文件名,例如"主网预算变电.json"
:return: (category, engineering_type) 元组,例如 ("主网", "预算")
"""
# 移除扩展名
basename = os.path.splitext(filename)[0]
# 查找类别(主网/配网/技改)
category = None
for cat in ["主网", "配网", "技改"]:
if cat in basename:
category = cat
break
# 查找工程类型(预算/清单)
engineering_type = None
for eng_type in ["预算", "清单"]:
if eng_type in basename:
engineering_type = eng_type
break
# 如果未找到,使用默认值
if not category:
print(f"警告: 无法从文件名 '{filename}' 中解析软件类别,使用默认值 '主网'")
category = "主网"
if not engineering_type:
print(f"警告: 无法从文件名 '{filename}' 中解析工程类型,使用默认值 '清单'")
engineering_type = "清单"
return category, engineering_type
# 软件类别名称映射字典,将各种变体映射到标准类别
CATEGORY_MAPPING = {
# 主网及其变体
"主网": "主网",
"主网工程": "主网",
"主网项目": "主网",
# 配网及其变体
"配网": "配网",
"配网造价": "配网",
"配网清单": "配网",
# 技改及其变体
"技改": "技改",
"技改工程": "技改",
"技改项目": "技改",
"技改造价": "技改",
"技改清单": "技改",
}
def parse_json_content(json_file_path: str) -> Tuple[Optional[str], Optional[str]]:
@@ -102,8 +41,8 @@ def parse_json_content(json_file_path: str) -> Tuple[Optional[str], Optional[str
data = json.load(f)
# 定义阶段类型映射表
budget_types = ["概预算", "定额", "", ""]
list_types = ["清单", "结算", "招标控制价", "招投标工程"]
budget_types = ["概预算", "定额", "定额计价", "", "预算工程"]
list_types = ["清单", "结算", "招标控制价", "招投标工程", "清单计价"]
# 从division字段获取软件名称和阶段类型
if "division" in data:
@@ -112,28 +51,39 @@ def parse_json_content(json_file_path: str) -> Tuple[Optional[str], Optional[str
# 使用-分割division字段
parts = division.split("-")
if len(parts) >= 2:
if len(parts) == 2:
category = parts[0].strip()
stage_type = parts[1].strip()
# 验证软件类别
if category not in ["主网", "配网", "技改"]:
print(f"警告: division中的软件名称 '{category}' 不是有效值,将使用默认值 '主网'")
category = "主网"
# 映射阶段类型
if any(budget_type in stage_type for budget_type in budget_types):
engineering_type = "预算"
elif any(list_type in stage_type for list_type in list_types):
engineering_type = "清单"
else:
print(f"警告: division中的阶段类型 '{stage_type}' 无法映射到预算或清单,将使用默认值 '清单'")
engineering_type = "清单"
print(f"从division解析: 软件名称={category}, 阶段类型={engineering_type} (原始值: {stage_type})")
return category, engineering_type
elif len(parts) == 3:
category = parts[0].strip()
stage_type = parts[2].strip()
else:
print(f"警告: division字段 '{division}' 格式不正确,无法分割")
# 可以选择返回默认值或抛出异常,这里以返回默认值为例
category = "主网"
engineering_type = "清单"
print(f"使用默认值: 软件名称={category}, 阶段类型={engineering_type}")
return category, engineering_type
# 使用映射字典规范化软件类别
if category in CATEGORY_MAPPING:
category = CATEGORY_MAPPING[category]
else:
print(f"警告: division中的软件名称 '{category}' 不是有效值,将使用默认值 '主网'")
category = "主网"
# 映射阶段类型
if any(budget_type in stage_type for budget_type in budget_types):
engineering_type = "预算"
elif any(list_type in stage_type for list_type in list_types):
engineering_type = "清单"
else:
print(f"警告: division中的阶段类型 '{stage_type}' 无法映射到预算或清单,将使用默认值 '清单'")
engineering_type = "清单"
print(f"从division解析: 软件名称={category}, 阶段类型={engineering_type} (原始值: {stage_type})")
return category, engineering_type
else:
print(f"警告: JSON文件中未找到division字段,尝试从basicData中解析")
@@ -145,8 +95,10 @@ def parse_json_content(json_file_path: str) -> Tuple[Optional[str], Optional[str
# 验证解析结果
if category and engineering_type:
# 确保category是有效值
if category not in ["主网", "配网", "技改"]:
# 使用映射字典规范化软件类别
if category in CATEGORY_MAPPING:
category = CATEGORY_MAPPING[category]
else:
print(f"警告: basicData中的软件名称 '{category}' 不是有效值,将使用默认值 '主网'")
category = "主网"
@@ -170,146 +122,166 @@ def parse_json_content(json_file_path: str) -> Tuple[Optional[str], Optional[str
def process_json_file(
input_file: str,
base_output_dir: str = "计算结果",
category: Optional[str] = None,
engineering_type: Optional[str] = None,
project: Optional[str] = None,
adjustment_type: str = "调差",
json_file_path: str, output_dir: str, calculate_type: str = "all", project_name: str = None
) -> bool:
"""
处理单个JSON文件
:param input_file: 输入JSON文件路径
:param base_output_dir: 基础输出目录路径,实际结果会保存在此目录下的子文件夹中
:param category: 软件类别,如果为None则从JSON内容中解析
:param engineering_type: 工程类型,如果为None则从JSON内容中解析
:param project: 项目名称,如果为None则处理所有项目
:param adjustment_type: 调差类型
:return: 处理是否成功
Args:
json_file_path: JSON文件路径
output_dir: 输出目录
calculate_type: 计算类型(all: 全部, quantity: 工程量取费, resource: 人材机合价)
project_name: 项目名称,如果不指定则处理所有项目
Returns:
bool: 处理是否成功
"""
try:
# 获取文件名(不含扩展名)作为输出子文件夹的名称
filename = os.path.basename(input_file)
file_basename = os.path.splitext(filename)[0]
output_dir = os.path.join(base_output_dir, file_basename)
# 从JSON文件内容中解析软件类别和工程类型
category, engineering_type = parse_json_content(json_file_path)
# 创建输出子文件夹
os.makedirs(output_dir, exist_ok=True)
print(f"创建输出目录: {output_dir}")
# 如果解析失败,使用默认值
if category is None:
category = "主网"
print(f"无法从文件中解析软件类别,使用默认值: {category}")
# 如果未指定category或engineering_type,从JSON内容中解析
if category is None or engineering_type is None:
# 首先尝试从JSON内容中解析
json_category, json_engineering_type = parse_json_content(input_file)
if category is None:
if json_category:
category = json_category
else:
# 如果从JSON内容中解析失败,尝试从文件名中解析(作为备用)
print("从JSON内容解析软件名称失败,尝试从文件名解析...")
parsed_category, _ = parse_filename(filename)
category = parsed_category
if engineering_type is None:
if json_engineering_type:
engineering_type = json_engineering_type
else:
# 如果从JSON内容中解析失败,尝试从文件名中解析(作为备用)
print("从JSON内容解析阶段类型失败,尝试从文件名解析...")
_, parsed_engineering_type = parse_filename(filename)
engineering_type = parsed_engineering_type
print(f"处理文件: {input_file}")
print(f" 软件类别: {category}")
print(f" 工程类型: {engineering_type}")
if engineering_type is None:
engineering_type = "预算"
print(f"无法从文件中解析工程类型,使用默认值: {engineering_type}")
# 获取软件类型
software_type = get_software_type(category, engineering_type)
print(f" 使用软件类型: {software_type.name}")
print(f"使用软件类型: {software_type.name}")
# 获取计算器
calculator = get_calculator(software_type)
if not calculator:
print(f" 错误: 未找到软件类型 {software_type.name} 的计算器")
print(f"错误: 未找到软件类型 {software_type.name} 的计算器")
return False
# 设置输出目录
calculator.set_output_dir(output_dir)
# 获取文件名(不含扩展名),并替换空格和特殊字符
base_filename = os.path.basename(json_file_path).replace(".json", "")
# 创建安全的目录名(替换空格和特殊字符)
safe_dirname = base_filename.replace(" ", "_").replace("\\", "_").replace("/", "_")
# 执行计算
print(" 开始计算工程量取费表...")
calculator.calculate_quantity_fee_tables(json_file_path=input_file, project_name=project)
# 设置自定义输出目录
custom_output_dir = os.path.join(output_dir, safe_dirname)
print(" 开始计算人材机合价...")
calculator.calculate_resource_fee_tables(json_file_path=input_file, project_name=project)
# 确保输出目录存在
try:
os.makedirs(custom_output_dir, exist_ok=True)
print(f"创建输出目录: {custom_output_dir}")
except Exception as e:
print(f"创建输出目录失败: {str(e)}")
# 尝试使用更安全的目录名
safe_dirname = f"project_{hash(base_filename) % 10000}"
custom_output_dir = os.path.join(output_dir, safe_dirname)
os.makedirs(custom_output_dir, exist_ok=True)
print(f"使用备用输出目录: {custom_output_dir}")
print(f" 处理完成: {input_file}")
print(f" 结果保存在: {output_dir}")
# 检查计算器是否有set_output_dir方法,如果有则直接设置输出目录
if hasattr(calculator, "set_output_dir"):
calculator.set_output_dir(custom_output_dir)
print(f"已设置计算器输出目录: {custom_output_dir}")
# 根据计算类型执行计算
if calculate_type in ["all", "quantity"]:
print("开始计算工程量取费表...")
calculator.calculate_quantity_fee_tables(
json_file_path=json_file_path,
project_name=project_name,
)
if calculate_type in ["all", "resource"]:
print("开始计算人材机合价...")
calculator.calculate_resource_fee_tables(json_file_path=json_file_path, project_name=project_name)
else:
# 创建一个新的计算器,复制原始计算器的属性
class CustomOutputCalculator:
def __init__(self, original_calculator, custom_dir):
self.original_calculator = original_calculator
self.custom_dir = custom_dir
def get_output_dir(self):
return self.custom_dir
def __getattr__(self, name):
return getattr(self.original_calculator, name)
# 创建自定义输出目录的计算器
custom_calculator = CustomOutputCalculator(calculator, custom_output_dir)
# 根据计算类型执行计算
if calculate_type in ["all", "quantity"]:
print("开始计算工程量取费表...")
custom_calculator.calculate_quantity_fee_tables(
json_file_path=json_file_path,
project_name=project_name,
)
if calculate_type in ["all", "resource"]:
print("开始计算人材机合价...")
custom_calculator.calculate_resource_fee_tables(
json_file_path=json_file_path, project_name=project_name
)
print(f"文件 {json_file_path} 处理完成")
return True
except Exception as e:
print(f" 处理文件 {input_file} 时出错: {str(e)}")
print(f"处理文件 {json_file_path} 时出错: {str(e)}")
import traceback
traceback.print_exc()
return False
def process_BCL_calculate(input_folder: str, output_folder: str) -> List[Tuple[str, bool]]:
def process_directory(input_dir: str, output_dir: str, calculate_type: str = "all") -> None:
"""
处理指定文件夹中的所有JSON文件
批量处理目录中的JSON文件
:param input_folder: 输入文件夹路径,包含要处理的JSON文件
:param output_folder: 输出文件夹路径,用于保存计算结果
:return: 处理结果列表,格式为 [(文件路径, 是否成功), ...]
Args:
input_dir: 输入目录路径
output_dir: 输出目录路径
calculate_type: 计算类型(all: 全部, quantity: 工程量取费, resource: 人材机合价)
"""
# 确保基础输出目录存在
os.makedirs(output_folder, exist_ok=True)
# 确保输出目录存在
os.makedirs(output_dir, exist_ok=True)
# 查找所有JSON文件
json_files = []
for file in os.listdir(input_folder):
if file.lower().endswith(".json"):
json_files.append(os.path.join(input_folder, file))
# 获取目录中的所有JSON文件
json_files = [f for f in os.listdir(input_dir) if f.lower().endswith(".json")]
if not json_files:
print(f"警告: 在目录 {input_folder}没有找到JSON文件")
return []
print(f"警告: 在目录 {input_dir}找到JSON文件")
return
print(f"找到 {len(json_files)} 个JSON文件,开始处理...")
# 处理每个JSON文件
results = []
for input_file in json_files:
success = process_json_file(
input_file=input_file,
base_output_dir=output_folder, # 传递基础输出目录
category=None, # 从JSON内容中解析
engineering_type=None, # 从JSON内容中解析
project=None, # 处理所有项目
adjustment_type="调差",
)
success_count = 0
for i, json_file in enumerate(json_files, 1):
json_file_path = os.path.join(input_dir, json_file)
print(f"处理文件 {i}/{len(json_files)}: {json_file}")
results.append((input_file, success))
if process_json_file(json_file_path, output_dir, calculate_type):
success_count += 1
return results
print(f"处理完成,成功: {success_count}/{len(json_files)}")
def main():
"""程序入口"""
def bcl_calculate(input_dir: str, output_dir: str, calculate_type: str = "all") -> None:
"""
主函数,处理指定目录中的所有JSON文件
input_folder = "project2json/outputs/json"
output_folder = "project2json/outputs/bcl_results"
Args:
input_dir: 输入目录路径
output_dir: 输出目录路径
calculate_type: 计算类型(all: 全部, quantity: 工程量取费, resource: 人材机合价)
"""
print(f"开始处理目录: {input_dir}")
print(f"输出目录: {output_dir}")
print(f"计算类型: {calculate_type}")
results = process_BCL_calculate(input_folder, output_folder)
process_directory(input_dir, output_dir, calculate_type)
# 显示处理结果
success_count = sum(1 for _, success in results if success)
print(f"\n处理完成: 成功 {success_count}/{len(results)} 个文件")
if success_count < len(results):
print("处理失败的文件:")
for file_path, success in results:
if not success:
print(f" - {os.path.basename(file_path)}")
if __name__ == "__main__":
main()
print("所有文件处理完成")