# Copyright (c) Opendatalab. All rights reserved. """ 数据模型定义 """ from typing import List class WeatherData: """气象数据模型""" def __init__(self): self.monitorAt: str = "" self.weather: str = "" self.temp: str = "" self.humidity: str = "" self.windSpeed: str = "" self.windDirection: str = "" self._auto_filled_weather: bool = False def to_dict(self): return { "monitorAt": self.monitorAt, "weather": self.weather, "temp": self.temp, "humidity": self.humidity, "windSpeed": self.windSpeed, "windDirection": self.windDirection } class NoiseData: """噪声数据模型""" def __init__(self): self.code: str = "" self.address: str = "" self.source: str = "" self.dayMonitorAt: str = "" self.dayMonitorValue: str = "" self.dayMonitorBackgroundValue: str = "" self.nightMonitorAt: str = "" self.nightMonitorValue: str = "" self.nightMonitorBackgroundValue: str = "" self.remark: str = "" def to_dict(self): return { "code": self.code, "address": self.address, "source": self.source, "dayMonitorAt": self.dayMonitorAt, "dayMonitorValue": self.dayMonitorValue, "dayMonitorBackgroundValue": self.dayMonitorBackgroundValue, "nightMonitorAt": self.nightMonitorAt, "nightMonitorValue": self.nightMonitorValue, "nightMonitorBackgroundValue": self.nightMonitorBackgroundValue, "remark": self.remark } class OperationalCondition: """工况信息数据模型(旧格式)""" def __init__(self): self.monitorAt: str = "" # 检测时间 self.project: str = "" # 项目名称 self.name: str = "" # 名称,如1#主变 self.voltage: str = "" # 电压范围 self.current: str = "" # 电流范围 self.activePower: str = "" # 有功功率 self.reactivePower: str = "" # 无功功率 def to_dict(self): return { "monitorAt": self.monitorAt, "project": self.project, "name": self.name, "voltage": self.voltage, "current": self.current, "activePower": self.activePower, "reactivePower": self.reactivePower } class OperationalConditionV2: """工况信息数据模型(新格式:表1检测工况)""" def __init__(self): self.monitorAt: str = "" # 检测时间 self.project: str = "" # 项目名称 self.name: str = "" # 名称,如500kV 江黄Ⅰ线 self.maxVoltage: str = "" # 电压最大值 self.minVoltage: str = "" # 电压最小值 self.maxCurrent: str = "" # 电流最大值 self.minCurrent: str = "" # 电流最小值 self.maxActivePower: str = "" # 有功功率最大值 self.minActivePower: str = "" # 有功功率最小值 self.maxReactivePower: str = "" # 无功功率最大值 self.minReactivePower: str = "" # 无功功率最小值 def to_dict(self): return { "monitorAt": self.monitorAt, "project": self.project, "name": self.name, "maxVoltage": self.maxVoltage, "minVoltage": self.minVoltage, "maxCurrent": self.maxCurrent, "minCurrent": self.minCurrent, "maxActivePower": self.maxActivePower, "minActivePower": self.minActivePower, "maxReactivePower": self.maxReactivePower, "minReactivePower": self.minReactivePower } class NoiseDetectionRecord: """噪声检测记录数据模型""" def __init__(self): self.project: str = "" self.standardReferences: str = "" self.soundLevelMeterMode: str = "" self.soundCalibratorMode: str = "" self.calibrationValueBefore: str = "" self.calibrationValueAfter: str = "" self.weather: List[WeatherData] = [] self.noise: List[NoiseData] = [] self.operationalConditions: List[OperationalCondition] = [] def to_dict(self): return { "project": self.project, "standardReferences": self.standardReferences, "soundLevelMeterMode": self.soundLevelMeterMode, "soundCalibratorMode": self.soundCalibratorMode, "calibrationValueBefore": self.calibrationValueBefore, "calibrationValueAfter": self.calibrationValueAfter, "weather": [w.to_dict() for w in self.weather], "noise": [n.to_dict() for n in self.noise], "operationalConditions": [oc.to_dict() for oc in self.operationalConditions] } class ElectromagneticWeatherData: """电磁检测气象数据模型""" def __init__(self): self.weather: str = "" self.temp: str = "" self.humidity: str = "" self.windSpeed: str = "" self.windDirection: str = "" def to_dict(self): return { "weather": self.weather, "temp": self.temp, "humidity": self.humidity, "windSpeed": self.windSpeed, "windDirection": self.windDirection } class ElectromagneticData: """电磁数据模型""" def __init__(self): self.code: str = "" self.address: str = "" self.height: str = "" self.monitorAt: str = "" self.powerFrequencyEFieldStrength1: str = "" self.powerFrequencyEFieldStrength2: str = "" self.powerFrequencyEFieldStrength3: str = "" self.powerFrequencyEFieldStrength4: str = "" self.powerFrequencyEFieldStrength5: str = "" self.avgPowerFrequencyEFieldStrength: str = "" self.powerFrequencyMagneticDensity1: str = "" self.powerFrequencyMagneticDensity2: str = "" self.powerFrequencyMagneticDensity3: str = "" self.powerFrequencyMagneticDensity4: str = "" self.powerFrequencyMagneticDensity5: str = "" self.avgPowerFrequencyMagneticDensity: str = "" def to_dict(self): return { "code": self.code, "address": self.address, "height": self.height, "monitorAt": self.monitorAt, "powerFrequencyEFieldStrength1": self.powerFrequencyEFieldStrength1, "powerFrequencyEFieldStrength2": self.powerFrequencyEFieldStrength2, "powerFrequencyEFieldStrength3": self.powerFrequencyEFieldStrength3, "powerFrequencyEFieldStrength4": self.powerFrequencyEFieldStrength4, "powerFrequencyEFieldStrength5": self.powerFrequencyEFieldStrength5, "avgPowerFrequencyEFieldStrength": self.avgPowerFrequencyEFieldStrength, "powerFrequencyMagneticDensity1": self.powerFrequencyMagneticDensity1, "powerFrequencyMagneticDensity2": self.powerFrequencyMagneticDensity2, "powerFrequencyMagneticDensity3": self.powerFrequencyMagneticDensity3, "powerFrequencyMagneticDensity4": self.powerFrequencyMagneticDensity4, "powerFrequencyMagneticDensity5": self.powerFrequencyMagneticDensity5, "avgPowerFrequencyMagneticDensity": self.avgPowerFrequencyMagneticDensity } class ElectromagneticDetectionRecord: """电磁检测记录数据模型""" def __init__(self): self.project: str = "" self.standardReferences: str = "" self.deviceName: str = "" self.deviceMode: str = "" self.deviceCode: str = "" self.monitorHeight: str = "" self.weather: ElectromagneticWeatherData = ElectromagneticWeatherData() self.electricMagnetic: List[ElectromagneticData] = [] def to_dict(self): return { "project": self.project, "standardReferences": self.standardReferences, "deviceName": self.deviceName, "deviceMode": self.deviceMode, "deviceCode": self.deviceCode, "monitorHeight": self.monitorHeight, "weather": self.weather.to_dict(), "electricMagnetic": [em.to_dict() for em in self.electricMagnetic] } class InvestmentItem: """投资项目数据模型""" def __init__(self): self.no: str = "" # 序号 self.name: str = "" # 工程或费用名称 self.level: str = "" # 明细等级 self.constructionScaleOverheadLine: str = "" # 建设规模-架空线(仅可研批复) self.constructionScaleBay: str = "" # 建设规模-间隔(仅可研批复) self.constructionScaleSubstation: str = "" # 建设规模-变电(仅可研批复) self.constructionScaleOpticalCable: str = "" # 建设规模-光缆(仅可研批复) self.staticInvestment: str = "" # 静态投资(元) self.dynamicInvestment: str = "" # 动态投资(元) # 新增费用字段(仅可研批复) self.constructionProjectCost: str = "" # 建筑工程费(元) self.equipmentPurchaseCost: str = "" # 设备购置费(元) self.installationProjectCost: str = "" # 安装工程费(元) self.otherExpenses: str = "" # 其他费用-合计(元) def to_dict(self, include_construction_scale: bool = False, include_cost_breakdown: bool = False): """ 转换为字典 Args: include_construction_scale: 是否包含建设规模字段(可研批复需要) include_cost_breakdown: 是否包含费用明细字段(可研批复需要) """ result = { "No": self.no, "name": self.name, "Level": self.level, "staticInvestment": self.staticInvestment, "dynamicInvestment": self.dynamicInvestment } # 如果需要建设规模字段,添加到输出(用于可研批复) if include_construction_scale: result["constructionScaleOverheadLine"] = self.constructionScaleOverheadLine result["constructionScaleBay"] = self.constructionScaleBay result["constructionScaleSubstation"] = self.constructionScaleSubstation result["constructionScaleOpticalCable"] = self.constructionScaleOpticalCable # 如果需要费用明细字段,添加到输出(用于可研批复) if include_cost_breakdown: result["constructionProjectCost"] = self.constructionProjectCost result["equipmentPurchaseCost"] = self.equipmentPurchaseCost result["installationProjectCost"] = self.installationProjectCost result["otherExpenses"] = self.otherExpenses return result class FeasibilityApprovalInvestment: """可研批复投资估算数据模型 返回结构与 designReview 保持一致,包含建设规模字段 三层嵌套结构: - Level 0: 顶层大类(如"山西晋城周村220千伏输变电工程") - Level 1: 二级分类(如"变电工程"、"线路工程"),有自己的 items - Level 2: 具体项目(如"周村220千伏变电站新建工程") 项目信息(可选,用于 safetyFsApproval 类型): - projectName: 工程(项目)名称 - projectUnit: 项目单位 - designUnit: 设计单位 """ def __init__(self): self.items: List[InvestmentItem] = [] # 项目基本信息(safetyFsApproval 专用) self.projectName: Optional[str] = None self.projectUnit: Optional[str] = None self.designUnit: Optional[str] = None def to_dict(self): """转换为嵌套结构,与 designReview 保持一致 Level="1" 的项目作为顶层大类(Level: 0) Level="2" 的项目作为二级分类(Level: 1),有自己的 items Level="3" 的项目作为具体项目(Level: 2),放入二级分类的 items Level="0" 的项目(合计)跳过 特殊处理:如果表格没有 Level=1 的顶层大类(如湖北省格式), 自动创建一个虚拟顶层大类来包含所有 Level=2 的项目 """ if not self.items: return [] # 检查是否有 Level=1 的顶层大类 has_level_1 = any(item.level == "1" for item in self.items) result = [] current_top_category = None # Level 0 顶层大类 current_sub_category = None # Level 1 二级分类 # 如果没有 Level=1 的顶层大类,创建一个虚拟的 if not has_level_1: current_top_category = { "name": "项目总表", "Level": 0, "constructionScaleSubstation": "", "constructionScaleBay": "", "constructionScaleOverheadLine": "", "constructionScaleOpticalCable": "", "staticInvestment": "", "dynamicInvestment": "", "constructionProjectCost": "", "equipmentPurchaseCost": "", "installationProjectCost": "", "otherExpenses": "", "items": [] } for item in self.items: if item.level == "1": # 顶层大类(如"山西晋城周村220千伏输变电工程") # 保存之前的二级分类和顶层大类 if current_sub_category is not None and current_top_category is not None: current_top_category["items"].append(current_sub_category) current_sub_category = None if current_top_category is not None: result.append(current_top_category) current_top_category = { "name": item.name, "Level": 0, "constructionScaleSubstation": item.constructionScaleSubstation or "", "constructionScaleBay": item.constructionScaleBay or "", "constructionScaleOverheadLine": item.constructionScaleOverheadLine or "", "constructionScaleOpticalCable": item.constructionScaleOpticalCable or "", "staticInvestment": self._parse_number(item.staticInvestment), "dynamicInvestment": self._parse_number(item.dynamicInvestment), "constructionProjectCost": self._parse_number(item.constructionProjectCost), "equipmentPurchaseCost": self._parse_number(item.equipmentPurchaseCost), "installationProjectCost": self._parse_number(item.installationProjectCost), "otherExpenses": self._parse_number(item.otherExpenses), "items": [] } elif item.level == "2" and current_top_category is not None: # 二级分类(如"变电工程"、"线路工程") # 保存之前的二级分类 if current_sub_category is not None: current_top_category["items"].append(current_sub_category) current_sub_category = { "No": self._parse_no(item.no), "name": item.name, "Level": 1, "constructionScaleSubstation": item.constructionScaleSubstation or "", "constructionScaleBay": item.constructionScaleBay or "", "constructionScaleOverheadLine": item.constructionScaleOverheadLine or "", "constructionScaleOpticalCable": item.constructionScaleOpticalCable or "", "staticInvestment": self._parse_number(item.staticInvestment), "dynamicInvestment": self._parse_number(item.dynamicInvestment), "constructionProjectCost": self._parse_number(item.constructionProjectCost), "equipmentPurchaseCost": self._parse_number(item.equipmentPurchaseCost), "installationProjectCost": self._parse_number(item.installationProjectCost), "otherExpenses": self._parse_number(item.otherExpenses), "items": [] } elif item.level == "3" and current_sub_category is not None: # 具体项目(如"周村220千伏变电站新建工程") current_sub_category["items"].append({ "No": self._parse_no(item.no), "name": item.name, "Level": 2, "constructionScaleSubstation": item.constructionScaleSubstation or "", "constructionScaleBay": item.constructionScaleBay or "", "constructionScaleOverheadLine": item.constructionScaleOverheadLine or "", "constructionScaleOpticalCable": item.constructionScaleOpticalCable or "", "staticInvestment": self._parse_number(item.staticInvestment), "dynamicInvestment": self._parse_number(item.dynamicInvestment), "constructionProjectCost": self._parse_number(item.constructionProjectCost), "equipmentPurchaseCost": self._parse_number(item.equipmentPurchaseCost), "installationProjectCost": self._parse_number(item.installationProjectCost), "otherExpenses": self._parse_number(item.otherExpenses), }) elif item.level == "0": # 合计行 - 跳过 if current_sub_category is not None and current_top_category is not None: current_top_category["items"].append(current_sub_category) current_sub_category = None if current_top_category is not None: result.append(current_top_category) current_top_category = None # 添加最后的分类 if current_sub_category is not None and current_top_category is not None: current_top_category["items"].append(current_sub_category) if current_top_category is not None: result.append(current_top_category) # 如果有项目信息,返回包含项目信息的字典;否则直接返回数据列表 if self.projectName or self.projectUnit or self.designUnit: return { "projectInfo": { "projectName": self.projectName or "", "projectUnit": self.projectUnit or "", "designUnit": self.designUnit or "" }, "data": result } else: return result @staticmethod def _parse_number(value: str) -> str: """将数字字符串格式化,保留原始精度""" if not value or not value.strip(): return "0" return value.strip() @staticmethod def _parse_no(value: str) -> int: if not value or not value.strip(): return 0 try: return int(value.strip()) except ValueError: return 0 class FeasibilityReviewInvestment: """可研评审投资估算数据模型 返回结构与 designReview 保持一致,不包含建设规模字段 三层嵌套结构: - Level 0: 顶层大类 - Level 1: 二级分类,有自己的 items - Level 2: 具体项目 """ def __init__(self): self.items: List[InvestmentItem] = [] def to_dict(self): """转换为嵌套结构,与 designReview 保持一致 Level="1" 的项目作为顶层大类(Level: 0) Level="2" 的项目作为二级分类(Level: 1),有自己的 items Level="3" 的项目作为具体项目(Level: 2),放入二级分类的 items Level="0" 的项目(合计)跳过 """ if not self.items: return [] result = [] current_top_category = None current_sub_category = None for item in self.items: if item.level == "1": # 顶层大类 if current_sub_category is not None and current_top_category is not None: current_top_category["items"].append(current_sub_category) current_sub_category = None if current_top_category is not None: result.append(current_top_category) current_top_category = { "name": item.name, "Level": 0, "staticInvestment": self._parse_number(item.staticInvestment), "dynamicInvestment": self._parse_number(item.dynamicInvestment), "items": [] } elif item.level == "2" and current_top_category is not None: # 二级分类 if current_sub_category is not None: current_top_category["items"].append(current_sub_category) current_sub_category = { "No": self._parse_no(item.no), "name": item.name, "Level": 1, "staticInvestment": self._parse_number(item.staticInvestment), "dynamicInvestment": self._parse_number(item.dynamicInvestment), "items": [] } elif item.level == "3" and current_sub_category is not None: # 具体项目 current_sub_category["items"].append({ "No": self._parse_no(item.no), "name": item.name, "Level": 2, "staticInvestment": self._parse_number(item.staticInvestment), "dynamicInvestment": self._parse_number(item.dynamicInvestment), }) elif item.level == "0": # 合计行 - 跳过 if current_sub_category is not None and current_top_category is not None: current_top_category["items"].append(current_sub_category) current_sub_category = None if current_top_category is not None: result.append(current_top_category) current_top_category = None # 添加最后的分类 if current_sub_category is not None and current_top_category is not None: current_top_category["items"].append(current_sub_category) if current_top_category is not None: result.append(current_top_category) return result @staticmethod def _parse_number(value: str) -> str: """将数字字符串格式化,保留原始精度""" if not value or not value.strip(): return "0" return value.strip() @staticmethod def _parse_no(value: str) -> int: if not value or not value.strip(): return 0 try: return int(value.strip()) except ValueError: return 0 class PreliminaryApprovalInvestment: """初设批复概算投资数据模型 返回结构与 designReview 保持一致: [{ "name": str, # 大类名称(如"变电工程"、"线路工程") "Level": 0, # 大类层级 "staticInvestment": float, # 静态投资总计 "dynamicInvestment": float, # 动态投资总计 "items": [ # 子项列表 { "No": int, # 序号 "name": str, # 工程名称 "Level": 1, # 子项层级 "staticInvestment": float, # 静态投资 "dynamicInvestment": float, # 动态投资 }, ... ] }, ...] """ def __init__(self): self.items: List[InvestmentItem] = [] def to_dict(self): """转换为嵌套结构,与 designReview 保持一致 Level="1" 的项目作为大类(变电工程、线路工程等) Level="2" 的项目作为子项 Level="0" 的项目(合计)跳过,不包含在输出中 """ if not self.items: return [] result = [] current_category = None for item in self.items: if item.level == "1": # 大类项目 - 创建新的类别 if current_category is not None: result.append(current_category) current_category = { "name": item.name, "Level": 0, "staticInvestment": self._parse_number(item.staticInvestment), "dynamicInvestment": self._parse_number(item.dynamicInvestment), "items": [] } elif item.level == "2" and current_category is not None: # 子项目 - 添加到当前类别的 items 中 current_category["items"].append({ "No": self._parse_no(item.no), "name": item.name, "Level": 1, "staticInvestment": self._parse_number(item.staticInvestment), "dynamicInvestment": self._parse_number(item.dynamicInvestment), }) elif item.level == "0": # 合计行 - 跳过,不包含在输出中 # 先保存当前类别 if current_category is not None: result.append(current_category) current_category = None # 添加最后一个类别 if current_category is not None: result.append(current_category) return result @staticmethod def _parse_number(value: str) -> str: """将数字字符串格式化,保留原始精度""" if not value or not value.strip(): return "0" return value.strip() @staticmethod def _parse_no(value: str) -> int: """将序号转换为整数""" if not value or not value.strip(): return 0 try: return int(value.strip()) except ValueError: return 0 class FinalAccountItem: """决算报告单项工程投资完成情况数据模型""" def __init__(self): self.no: int = 0 # 序号(项目序号) self.name: str = "" # 项目名称(审计内容) self.feeName: str = "" # 费用项目(建筑安装工程、设备购置、其他费用) self.estimatedCost: str = "" # 概算金额 self.approvedFinalAccountExcludingVat: str = "" # 决算金额审定不含税 self.vatAmount: str = "" # 增值税额 self.costVariance: str = "" # 超节支金额 self.varianceRate: str = "" # 超节支率 def to_dict(self, include_project_info: bool = True): """转换为字典 Args: include_project_info: 是否包含项目序号和名称(分组后不需要) """ result = { "feeName": self.feeName, "estimatedCost": self.estimatedCost, "approvedFinalAccountExcludingVat": self.approvedFinalAccountExcludingVat, "vatAmount": self.vatAmount, "costVariance": self.costVariance, "varianceRate": self.varianceRate } if include_project_info: result["No"] = self.no result["name"] = self.name return result class FinalAccountRecord: """决算报告记录数据模型 返回结构:按项目分组的嵌套数组 [{ "No": int, # 序号(项目序号,如1、2、3、4) "name": str, # 项目名称(如"周村220kV输变电工程变电站新建工程") "items": [{ # 费用明细列表 "feeName": str, # 费用项目(如"建筑安装工程"、"设备购置"、"其他费用") "estimatedCost": str, # 概算金额 "approvedFinalAccountExcludingVat": str, # 决算金额审定不含税 "vatAmount": str, # 增值税额 "costVariance": str, # 超节支金额 "varianceRate": str, # 超节支率 }, ...] }, ...] """ def __init__(self): self.items: List[FinalAccountItem] = [] def to_dict(self): """转换为按项目分组的嵌套结构""" from collections import OrderedDict # 按项目序号分组 grouped: OrderedDict[int, dict] = OrderedDict() for item in self.items: if item.no not in grouped: grouped[item.no] = { "No": item.no, "name": item.name, "items": [] } # 添加费用明细(不包含项目序号和名称) grouped[item.no]["items"].append(item.to_dict(include_project_info=False)) return list(grouped.values())