feat(z01): 총칙 기본 감춤의 기준을 잣대로 · 목록에서는 「공종 아님 · 규정·기준」
랩탑 서브가 낸 기본 감춤(2ee6a7ed)·갈래 펴기(0ff186c1)를 받고, 판정 자리만 옮김. - 서버에 박혀 있던 `DEFAULTS` → `axis_policy.json` 의 `list_default` 를 읽음 (총칙 범위는 기관이 정하는 값 — 코드에 박으면 관리자가 못 고침 · 못 읽을 때만 대비값) - 구실마다 `list_label` — 거르기 상자에서는 「총칙」이 아니라 **「공종 아님 · 규정·기준」** · 기본 감춤이되 **지운 것이 아니라 감춘 것**임이 드러남 - 지금 수 — 산림 전부 627 중 공종 568 · 공종 아님 59 / 건설 전부 997 중 공종 983 · 공종 아님 14 ⚠ 갈래 열쇠 흠 하나 기록 — `@id` 가 `FW-00105#1.1∼1.5` 꼴로 갈래 문자열을 그대로 담음. 원문이 물결표를 둘(∼ U+223C 26 · ~ U+FF5E 8)로 쓰고 자간 공백만 다른 짝이 6쌍이라, 문자열을 손질하면 열쇠가 갈림. 잣대 `known_defects` 에 적고 수를 시험으로 못박음(갈래 335). 건설은 갈래 키가 비어 편 수 = 줄 수(997). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QsGw1Dz9HmhuAxGisxmDPF
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Z01 공종 축 — 산림·건설 두 kind 를 기초단가와 **같은 계약**으로(2026-09-17 브레인 · PLAN_공종축 8-4).
|
||||
|
||||
줄 = **품셈 표가 붙은 마디**(`axis_policy.json` `flat_list_filter: pum_tables > 0` — 잎으로 거르면 부모에 붙은 표가 안 뜸) ·
|
||||
갈래(`variant_keys`)는 칸으로(편 수 = `stats.units`).
|
||||
갈래(`variant_keys`)마다 한 줄로 폄 — 줄 수 = 편 수(`stats.units`) · 공종 수 = `stats.work_items`.
|
||||
이름 = 뿌리부터 전체 경로(「토공 › 토사깍기 › 기계」 · 건설은 부문부터) — 잎 이름만으론 겹침.
|
||||
계산 규칙 = 표 수준 `rules`(부모 마디 choose_one · sum_steps) + 줄 칸 `rule`·`step_weight`
|
||||
— 빠지면 발파암(절취 0.1 + 깍기 0.9 + 집토 1) 금액이 틀어짐.
|
||||
@@ -77,6 +77,8 @@ def base_rows(kind: str) -> list[dict[str, Any]]:
|
||||
return []
|
||||
d, name = found
|
||||
items, by, children = _tree(d)
|
||||
# 계산 규칙 이름 — 이름표 `value_overrides["work_item_master::<방식>"]`(「아래를 단계로 다 더함」) · 없으면 영문
|
||||
named = tables.load_labels().get("value_overrides") or {}
|
||||
rows = []
|
||||
for item in items:
|
||||
if not item.get("tables"):
|
||||
@@ -86,20 +88,28 @@ def base_rows(kind: str) -> list[dict[str, Any]]:
|
||||
(s["weight"] for s in parent.get("steps") or [] if s["code"] == item["work_item_code"]),
|
||||
None,
|
||||
)
|
||||
rows.append(
|
||||
{
|
||||
"@id": _key(item),
|
||||
"@source": name,
|
||||
"work_item_code": item["work_item_code"], # 목차 코드 — 열쇠 아님, 칸으로만
|
||||
"number": item["number"],
|
||||
"name": _path(item, by),
|
||||
"variant_keys": item.get("variant_keys") or [],
|
||||
"pum_tables": [t["pum_table_id"] for t in item["tables"]],
|
||||
"rule": parent.get("parent_mode"),
|
||||
"step_weight": weight,
|
||||
"effective_date": d.get("pum_edition") or d.get("effective_date"),
|
||||
}
|
||||
)
|
||||
key = _key(item)
|
||||
# 갈래마다 한 줄 — 일위대가가 갈래마다 달라 집는 단위가 갈래임(교목굴취 나무높이 9 · 2026-09-17 브레인 ③).
|
||||
# 열쇠 = 공종 열쇠#갈래 키(B08·B09 `코드#갈래` 꼴) · 갈래 키 글자는 마스터 것 그대로(조립 두 벌 금지).
|
||||
for variant in item.get("variant_keys") or [None]:
|
||||
rows.append(
|
||||
{
|
||||
"@id": f"{key}#{variant}" if variant else key,
|
||||
"@key": key, # 공종 열쇠 — 거르기·줄 수는 공종 단위로
|
||||
"@source": name,
|
||||
"work_item_code": item["work_item_code"], # 목차 코드 — 열쇠 아님, 칸으로만
|
||||
"number": item["number"],
|
||||
"name": _path(item, by),
|
||||
"variant": variant,
|
||||
"pum_tables": [t["pum_table_id"] for t in item["tables"]],
|
||||
"rule": (named.get(f"work_item_master::{parent.get('parent_mode')}") or {}).get(
|
||||
"name_ko"
|
||||
)
|
||||
or parent.get("parent_mode"),
|
||||
"step_weight": weight,
|
||||
"effective_date": d.get("pum_edition") or d.get("effective_date"),
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
@@ -148,6 +158,10 @@ def notice(kind: str) -> list[str]:
|
||||
#: ⚠ 「임도가 쓰는 것」 같은 쓰임 가름은 잣대가 아직 없어 안 냄(지어내지 않음).
|
||||
FILTER_KEYS = ("division", "axis_role")
|
||||
ALL = ""
|
||||
#: 값을 안 보냈을 때 거는 값 — **잣대가 정한다**(`axis_policy.json` 의 `list_default`).
|
||||
#: ⚠ 코드에 박지 않는다 — 총칙 범위는 기관이 정하는 것이라 관리자가 고칠 수 있어야 한다.
|
||||
#: 잣대를 못 읽을 때만 쓰는 대비값(총칙 줄은 기본 감춤 · 「전부」를 고르면 보임 · 2026-09-17 브레인).
|
||||
FALLBACK_DEFAULTS = {"axis_role": "work_item"}
|
||||
|
||||
|
||||
def _facets(kind: str) -> dict[str, dict[str, str]]:
|
||||
@@ -163,7 +177,7 @@ def _policy() -> dict[str, Any]:
|
||||
|
||||
|
||||
def _role_names() -> dict[str, str]:
|
||||
"""거르기 상자에 뜰 이름. 목록에서는 「총칙」이 아니라 「공종 아님 · 규정·기준」으로 적는다."""
|
||||
"""거르기 상자에 뜰 이름. 목록에서는 「총칙」이 아니라 **공종이 아님**을 드러내는 이름으로 적는다."""
|
||||
roles = _policy().get("roles") or {}
|
||||
return {
|
||||
key: str(role.get("list_label") or role.get("name_ko") or key)
|
||||
@@ -171,31 +185,28 @@ def _role_names() -> dict[str, str]:
|
||||
}
|
||||
|
||||
|
||||
def _default_of(key: str) -> str:
|
||||
"""이 가름의 기본값 — **잣대가 정한다**(화면도 서버도 스스로 가르지 않음).
|
||||
|
||||
총칙 줄은 표가 붙어 목록에 섞여 뜨지만 공종이 아니라 규정·기준이다. 기본은 안 보이되
|
||||
상자를 고르면 보인다 — ⚠ **지운 것이 아니라 감춘 것**이다(2026-09-17 브레인).
|
||||
"""
|
||||
def _defaults() -> dict[str, str]:
|
||||
"""안 고른 가름에 걸 값 — 잣대의 `list_default`. 못 읽으면 대비값."""
|
||||
wanted = _policy().get("list_default") or {}
|
||||
return str(wanted.get("value") or ALL) if wanted.get("filter") == key else ALL
|
||||
key = str(wanted.get("filter") or "")
|
||||
return {key: str(wanted.get("value") or ALL)} if key else dict(FALLBACK_DEFAULTS)
|
||||
|
||||
|
||||
def filters(kind: str, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""거르기 상자 — 값이 둘 이상 갈리는 가름만. 기본값은 **잣대**가 정한다(`list_default`)."""
|
||||
"""거르기 상자 — 값이 둘 이상 갈리는 가름만 · 기본은 **잣대**(`list_default`)가 정한다 — 그 값이 표에 있을 때만."""
|
||||
facets = _facets(kind)
|
||||
labels = {"division": ("부문", {}), "axis_role": ("줄 구실", _role_names())}
|
||||
out = []
|
||||
for key in FILTER_KEYS:
|
||||
counts: dict[str, int] = {}
|
||||
for row in rows:
|
||||
value = facets.get(row["@id"], {}).get(key, ALL)
|
||||
value = facets.get(row["@key"], {}).get(key, ALL)
|
||||
counts[value] = counts.get(value, 0) + 1
|
||||
values = [v for v in counts if v != ALL]
|
||||
if len(values) < 2:
|
||||
continue
|
||||
label, names = labels[key]
|
||||
default = _default_of(key)
|
||||
default = _defaults().get(key, ALL)
|
||||
out.append(
|
||||
{
|
||||
"key": key,
|
||||
@@ -211,16 +222,13 @@ def filters(kind: str, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
def apply_filters(
|
||||
kind: str, rows: list[dict[str, Any]], picked: dict[str, str]
|
||||
) -> list[dict[str, Any]]:
|
||||
"""고른 값으로 거른다. **안 고른 가름은 잣대의 기본값**을 쓴다.
|
||||
|
||||
⚠ 기본값을 서버가 매기는 까닭 — 화면만 기본값을 알면 「상자엔 공종이라 떠 있는데 목록엔
|
||||
총칙이 섞여 있는」 어긋남이 난다. 「전부」는 빈 값을 **적어 보내면** 된다.
|
||||
"""
|
||||
facets = _facets(kind)
|
||||
# 안 보낸 가름은 상자 기본값으로 — 상자에 보이는 값과 걸린 값이 같게(찾기 결과가 아니라 표 전체로 정함)
|
||||
defaults = {f["key"]: f["default"] for f in filters(kind, base_rows(kind))}
|
||||
for key in FILTER_KEYS:
|
||||
value = picked[key] if key in picked else _default_of(key)
|
||||
value = picked.get(key, defaults.get(key, ALL))
|
||||
if value != ALL:
|
||||
rows = [r for r in rows if facets.get(r["@id"], {}).get(key) == value]
|
||||
rows = [r for r in rows if facets.get(r["@key"], {}).get(key) == value]
|
||||
return rows
|
||||
|
||||
|
||||
@@ -231,7 +239,7 @@ def extra(kind: str) -> dict[str, Any]:
|
||||
"filters": filters(kind, rows),
|
||||
"rules": rules(kind),
|
||||
"stats": {
|
||||
"work_items": len(rows),
|
||||
"units": sum(max(1, len(r["variant_keys"])) for r in rows),
|
||||
"work_items": len({r["@key"] for r in rows}),
|
||||
"units": len(rows),
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user