Files
Aislo/common_util/common_util_work_item_link.py
T

88 lines
3.4 KiB
Python

"""연결고리 표(`work_item_link`)를 읽는 **한 자리** — 산림·건설 어느 품셈 품을 쓸지 **표가 정함**
(2026-09-17 브레인 · 코덱스 크로스체크 ② 「연결고리 미소비」).
고시 총칙 7 「본 품셈에 명시되지 않은 품은 타 부문 표준품셈 적용 · 유사 공종은 본 품셈 우선」 —
그 규칙은 표의 `policy`(`both_precedence` · `unconfirmed_action`)에 있고 코드는 **읽기만** 함(「산림 우선」을 코드에 안 박음).
확정 · both → `both_precedence` 쪽 열쇠(지금 표는 forest)
확정 · 한쪽만 → 그 열쇠 그대로
미판정 → **안 고름** · 사유를 붙여 막음(`unconfirmed_action: do_not_link`)
표에 없음 → 제 품셈 그대로(품셈에 명시된 품)
⚠ 미판정 행 가운데 열쇠가 빈 행(여러 절 묶음)은 줄에 못 닿음 — `undecided_links()` 로 목록만 드러냄.
"""
from __future__ import annotations
import json
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from typing import Any
FOLDER = Path(__file__).resolve().parent.parent / "resources" / "data_work_item_link"
CONFIRMED = "확정"
#: 표 `policy.both_precedence` 낱말 → 그 행의 열쇠 칸
_KEY_COLUMN = {"forest": "forest_key", "construction": "const_key"}
@dataclass(frozen=True)
class Choice:
"""쓸 열쇠 · 막힌 까닭(있으면 금액을 안 세움)."""
key: str | None
blocked: str = ""
@lru_cache(maxsize=4)
def _read(path: str, _mtime_ns: int) -> dict[str, Any]:
return json.loads(Path(path).read_text(encoding="utf-8"))
def _doc() -> dict[str, Any]:
found = sorted(FOLDER.glob("work_item_link_*.json")) if FOLDER.is_dir() else []
return _read(str(found[-1]), found[-1].stat().st_mtime_ns) if found else {}
def choose(key: str | None) -> Choice:
"""열쇠 하나(FW-·CW-) → 실제로 쓸 열쇠. 미판정 행에 걸리면 안 고르고 막음."""
if not key:
return Choice(key)
doc = _doc()
policy = doc.get("policy") or {}
rows = [
link
for link in doc.get("links") or []
if key in (link.get("forest_key"), link.get("const_key"))
]
for link in rows:
if link.get("status") != CONFIRMED:
return Choice(
None,
"연결고리 미판정 — 산림·건설 어느 품을 쓸지 근거가 없어 고르지 않음"
f"({link.get('evidence') or '근거 없음'})",
)
for link in rows:
if link.get("branch") == "both":
column = _KEY_COLUMN.get(str(policy.get("both_precedence") or ""))
chosen = link.get(column) if column else None
if not chosen:
return Choice(
None, "연결고리 표에 둘 다 있는 줄의 우선 품셈이 안 적혔음 — 고르지 않음"
)
return Choice(chosen)
return Choice(key)
def undecided_links() -> list[dict[str, str]]:
"""미판정 행 — 열쇠가 비어 줄에 못 닿는 행도 드러냄(조용히 안 버림)."""
return [
{
"branch": str(link.get("branch") or ""),
"forest_key": str(link.get("forest_key") or ""),
"const_key": str(link.get("const_key") or ""),
"evidence": str(link.get("evidence") or ""),
}
for link in _doc().get("links") or []
if link.get("status") != CONFIRMED
]