feat(z01): 연결고리 표가 확정으로 이은 줄에 「연결됨」 — 감추지 않고 표시만(기본 다 보임) · 「연결된 것만」 거르기 · 판정은 work_item_link 표(코드에 목록 없음 · 미판정 안 이음) · 건설 11 · 산림 5 · 이름표 열 「연결」 · 금액 불변 시험에 이행 전 코드로 뜬 원가계산서 금액 못박음(내역서·원가계산서 지문이 이행 전후 한 글자 같았음)(브레인)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
2026-09-17 16:46:58 +09:00
co-authored by Claude Opus 5
parent c044726d41
commit 337e42314c
4 changed files with 123 additions and 11 deletions
+43 -7
View File
@@ -97,12 +97,36 @@ def base_rows(kind: str) -> list[dict[str, Any]]:
"pum_tables": [t["pum_table_id"] for t in item["tables"]],
"rule": parent.get("parent_mode"),
"step_weight": weight,
# 「임도가 쓰는 것」 — 연결고리 표가 확정으로 이은 줄(판정은 그 표 · 감추지 않고 표시만)
"link": LINKED if _key(item) in linked_keys() else "",
"effective_date": d.get("pum_edition") or d.get("effective_date"),
}
)
return rows
LINK_FOLDER = tables.RESOURCES / "data_work_item_link"
LINKED = "연결됨"
def linked_keys() -> frozenset[str]:
"""연결고리 표(`work_item_link_*.json` 끝 판 · 코덱스)가 **확정**으로 이은 열쇠(산림·건설).
⚠ 목록을 코드에 안 둠 — 판정은 그 표 · 미판정은 안 이음(표의 `unconfirmed_action: do_not_link`).
"""
found = sorted(LINK_FOLDER.glob("work_item_link_*.json")) if LINK_FOLDER.is_dir() else []
if not found:
return frozenset()
links = _read(str(found[-1]), found[-1].stat().st_mtime_ns).get("links") or []
return frozenset(
key
for link in links
if link.get("status") == "확정"
for key in (link.get("forest_key"), link.get("const_key"))
if key
)
def rules(kind: str) -> list[dict[str, Any]]:
"""부모 마디 계산 규칙 — choose_one(아래 중 하나) · sum_steps(아래를 무게대로 더해야 한 단위)."""
found = doc(kind)
@@ -144,16 +168,24 @@ def notice(kind: str) -> list[str]:
return lines
#: 거르기 — **자료에 있는 가름만**(건설 부문 · 줄 구실). 어느 줄을 거를지는 서버 한 곳 · 화면은 고른 값만 보냄.
#: ⚠ 「임도가 쓰는 것」 같은 쓰임 가름은 잣대가 아직 없어 안 냄(지어내지 않음).
FILTER_KEYS = ("division", "axis_role")
#: 거르기 — **자료에 있는 가름만**(건설 부문 · 줄 구실 · 연결고리 표). 어느 줄을 거를지는 서버 한 곳 ·
#: 화면은 고른 값만 보냄 · 기본은 늘 「전부」(감추지 않음 — 사용자 「어디에 쓸지 모르니 건설 전체」 · 브레인).
FILTER_KEYS = ("link", "division", "axis_role")
ALL = ""
def _facets(kind: str) -> dict[str, dict[str, str]]:
found = doc(kind)
items = found[0].get("work_items") or [] if found else []
return {_key(i): {key: str(i.get(key) or "") for key in FILTER_KEYS} for i in items}
linked = linked_keys()
return {
_key(i): {
"link": LINKED if _key(i) in linked else ALL,
"division": str(i.get("division") or ""),
"axis_role": str(i.get("axis_role") or ""),
}
for i in items
}
def _role_names() -> dict[str, str]:
@@ -166,9 +198,13 @@ def _role_names() -> dict[str, str]:
def filters(kind: str, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
"""거르기 상자 — 이 둘 이상 갈리는 가름만 · 기본은 「전부」(안 거름)."""
"""거르기 상자 — 이 둘 이상으로 갈리는 가름만 · 기본은 「전부」(안 거름)."""
facets = _facets(kind)
labels = {"division": ("부문", {}), "axis_role": ("줄 구실", _role_names())}
labels = {
"link": ("연결", {LINKED: "연결된 것만"}),
"division": ("부문", {}),
"axis_role": ("줄 구실", _role_names()),
}
out = []
for key in FILTER_KEYS:
counts: dict[str, int] = {}
@@ -176,7 +212,7 @@ def filters(kind: str, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
value = facets.get(row["@id"], {}).get(key, ALL)
counts[value] = counts.get(value, 0) + 1
values = [v for v in counts if v != ALL]
if len(values) < 2:
if len(counts) < 2: # 다 같은 값이면 거를 것이 없음(연결은 「연결됨」 · 빈칸 둘로 갈림)
continue
label, names = labels[key]
out.append(