feat: add pcf analysis web app and api

This commit is contained in:
2026-06-30 14:29:02 +08:00
commit e600a73e48
44 changed files with 12412 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
from __future__ import annotations
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
@dataclass
class PcfBlock:
"""PCF 中的一个顶层段。"""
block_type: str
value: str = ""
fields: dict[str, list[str]] = field(default_factory=dict)
line_no: int = 0
def first(self, key: str, default: str = "") -> str:
values = self.fields.get(key, [])
return values[0] if values else default
@dataclass
class WeldRecord:
"""内部标准焊口记录。"""
source_file: str
pipeline_reference: str = ""
piping_spec: str = ""
line_no: str = ""
weld_no: str = ""
diameter: str = ""
outside_diameter: str = ""
wall_thickness: str = ""
spec: str = ""
weld_area_raw: str = ""
weld_area: str = ""
contractor_raw: str = ""
component_identifier: str = ""
master_component_identifier: str = ""
skey: str = ""
uci: str = ""
material_1: str = ""
material_2: str = ""
issues: list[str] = field(default_factory=list)
raw_fields: dict[str, list[str]] = field(default_factory=dict)
def set_if_empty(self, field_name: str, value: str) -> None:
if hasattr(self, field_name) and not getattr(self, field_name) and value:
setattr(self, field_name, value)
@dataclass
class UnknownFieldCandidate:
"""等待 DeepSeek 识别的非标准字段候选。"""
source_field: str
section: str
sample_values: list[str]
context: str
@dataclass
class MappingDecision:
"""DeepSeek 字段映射结果。"""
source_field: str
target_field: str
confidence: float
reason: str = ""
sample_values: list[str] = field(default_factory=list)
@property
def is_usable(self) -> bool:
return bool(self.source_field and self.target_field and self.confidence >= 0)
@dataclass
class JobResult:
job_id: str
input_files: int
weld_count: int
warning_count: int
error_count: int
output_xls: Path
issues_csv: Path
report_json: Path
messages: list[str] = field(default_factory=list)
llm_enabled: bool = False
llm_mappings: list[MappingDecision] = field(default_factory=list)
import_headers: list[str] = field(default_factory=list)
import_columns: list[dict[str, str]] = field(default_factory=list)
import_rows: list[dict[str, str]] = field(default_factory=list)
issue_rows: list[dict[str, str]] = field(default_factory=list)
def to_public_dict(self) -> dict[str, Any]:
return {
"job_id": self.job_id,
"input_files": self.input_files,
"weld_count": self.weld_count,
"warning_count": self.warning_count,
"error_count": self.error_count,
"messages": self.messages,
"llm_enabled": self.llm_enabled,
"tables": {
"import_headers": self.import_headers,
"import_columns": self.import_columns,
"import_rows": self.import_rows,
"issue_rows": self.issue_rows,
},
"downloads": {
"xls": f"/api/jobs/{self.job_id}/download/xls",
"issues": f"/api/jobs/{self.job_id}/download/issues",
"report": f"/api/jobs/{self.job_id}/download/report",
},
}