feat(713-3): 로직 목록 덧칸 서버 — /logics 줄마다 묶음(같은 절) · 작업량 부모 · 부르는/불리는 수 · 관련(비고 절 번호) · 정본 그대로 · 새 파일 Store_List · 인재경 줄 수는 712-1 뒤
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ from pydantic import BaseModel
|
||||
from M01_MasterData import M01_MasterData_Store as store
|
||||
from M01_MasterData import M01_MasterData_Store_Combo as combo_store
|
||||
from M01_MasterData import M01_MasterData_Store_Flow as flow
|
||||
from M01_MasterData import M01_MasterData_Store_List as store_list
|
||||
from M01_MasterData import M01_MasterData_Store_Make as make
|
||||
from M01_MasterData import M01_MasterData_Store_Revise as revise
|
||||
|
||||
@@ -193,7 +194,8 @@ def get_sections(
|
||||
def get_logics(
|
||||
sub: str = "", detail: str = "", q: str = "", blocked: int | None = None, owner: str = ""
|
||||
) -> dict:
|
||||
return {"logics": _call(store.logics, sub, detail, q, blocked, owner)}
|
||||
"""줄마다 덧칸(묶음 · 작업량 · 부르는/불리는 수 · 관련 · 713-3) — 읽기만."""
|
||||
return {"logics": store_list.attach(_call(store.logics, sub, detail, q, blocked, owner))}
|
||||
|
||||
|
||||
@router.get("/logic")
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
"""M01 로직 목록 덧칸 (PLAN 713-3 · 요구서 내역12 4장 7) — 정본은 그대로 · 목록에서만 묶음.
|
||||
|
||||
`/logics` 줄마다 붙이는 읽기 칸(파일에 안 씀):
|
||||
- `묶음` = 같은 파일 · 같은 원문번호 로직이 둘 넘으면 그 번호 · `묶음수` = 몇 개.
|
||||
- `작업량` = 돈 아닌 로직(`결과` 칸) · `부모` = 그 작업량을 부르는 로직(목록에서 그 밑으로 접음).
|
||||
- `부르는수` · `불리는수` = 로직끼리 부르기(기계경비 포함 · `Store_Cache.counted`).
|
||||
- `관련` = 비고 글의 절 번호(「1-5-6(주③)」 · 「별도」 참조)가 가리키는 로직 — 계산 밖 링크.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as store
|
||||
from M01_MasterData import M01_MasterData_Store_Cache as cache
|
||||
|
||||
_REF = re.compile(
|
||||
r"(?:(산림품셈|건설품셈)\s*(공통|토목|건축|기계설비|유지관리)?\s*)?(?<![\d.-])(\d+-\d+(?:-\d+)*)"
|
||||
)
|
||||
|
||||
|
||||
# ponytail: 넓은 절 거름은 수 하나 — 잘못 걸리면 절 깊이 규칙으로
|
||||
_WIDE = 12
|
||||
|
||||
|
||||
def _book(data: dict) -> tuple[str, str]:
|
||||
return str(data.get("원문") or ""), str(data.get("부문") or "")
|
||||
|
||||
|
||||
def _related(row: dict, book: tuple[str, str], by_section: dict) -> list[str]:
|
||||
"""비고 글 절 번호 → 같은 원문(책 이름이 앞에 있으면 그 책) 로직 키 · 자기 빼고 · 차례대로."""
|
||||
out: dict[str, None] = {}
|
||||
clauses = [c for c in str(row.get("비고") or "").split(" · ") if not c.startswith("해석")]
|
||||
for name, division, number in _REF.findall(" · ".join(clauses)):
|
||||
where = (name, division or ("" if name == "산림품셈" else book[1])) if name else book
|
||||
keys = by_section.get((*where, number), [])
|
||||
if len(keys) > _WIDE: # 장 · 큰 절 통째(「1-1」)는 링크 아님
|
||||
continue
|
||||
out |= dict.fromkeys(k for k in keys if k != str(row.get("키")))
|
||||
return list(out)
|
||||
|
||||
|
||||
def extras() -> dict[str, dict]:
|
||||
"""로직 키 → 덧칸 — 목록 전체로 셈(거른 목록이어도 묶음 수 · 부르는 수는 전체 기준)."""
|
||||
files, _ = store.loaded()
|
||||
rows = [
|
||||
(name, data, row)
|
||||
for name, data in files.items()
|
||||
if data.get("그룹") == "로직"
|
||||
for row in data.get("줄") or []
|
||||
]
|
||||
calls = {key: got[1] for key, got in cache.counted(store.FOLDER).items()}
|
||||
called = Counter(k for got in calls.values() for k in got)
|
||||
parents: dict[str, list[str]] = defaultdict(list)
|
||||
for key, got in calls.items():
|
||||
for k in got:
|
||||
parents[k].append(key)
|
||||
groups = Counter((name, str(row.get("원문번호") or "")) for name, _, row in rows)
|
||||
by_section: dict[tuple, list[str]] = defaultdict(list)
|
||||
for _, data, row in rows:
|
||||
number = str(row.get("원문번호") or "")
|
||||
for at in range(1, number.count("-") + 1): # 「1-5-6」 은 「1-5」 로도 찾힘
|
||||
by_section[(*_book(data), "-".join(number.split("-")[: at + 1]))].append(
|
||||
str(row.get("키"))
|
||||
)
|
||||
out = {}
|
||||
for name, data, row in rows:
|
||||
key, number = str(row.get("키")), str(row.get("원문번호") or "")
|
||||
many = groups[(name, number)] if number else 1
|
||||
work = "결과" in row
|
||||
out[key] = {
|
||||
"묶음": number if many > 1 else "",
|
||||
"묶음수": many if many > 1 else 0,
|
||||
"작업량": work,
|
||||
"부모": sorted(parents.get(key, [])) if work else [],
|
||||
"부르는수": len(calls.get(key, ())),
|
||||
"불리는수": called.get(key, 0),
|
||||
"관련": _related(row, _book(data), by_section),
|
||||
}
|
||||
return out
|
||||
|
||||
|
||||
def attach(logics: list[dict]) -> list[dict]:
|
||||
"""`/logics` 줄에 덧칸을 붙임(없는 키 = 저장 안 한 초안 — 빈 칸)."""
|
||||
more = extras()
|
||||
blank = {
|
||||
"묶음": "",
|
||||
"묶음수": 0,
|
||||
"작업량": False,
|
||||
"부모": [],
|
||||
"부르는수": 0,
|
||||
"불리는수": 0,
|
||||
"관련": [],
|
||||
}
|
||||
return [{**one, **more.get(str(one.get("키")), blank)} for one in logics]
|
||||
@@ -0,0 +1,31 @@
|
||||
"""713-3 지킴 — 로직 목록 덧칸(묶음 · 작업량 부모 · 부르는/불리는 수 · 관련) · 정본 그대로 · 줄 수 그대로."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from M01_MasterData import M01_MasterData_Router as router
|
||||
from M01_MasterData import M01_MasterData_Store_List as store_list
|
||||
|
||||
COLS = {"묶음", "묶음수", "작업량", "부모", "부르는수", "불리는수", "관련"}
|
||||
|
||||
|
||||
def test_목록_줄_수_그대로_덧칸_다_있음() -> None:
|
||||
got = router.get_logics()["logics"]
|
||||
assert len(got) == 1469 # 묶음은 화면 접기만 — 목록 줄은 그대로
|
||||
assert all(COLS <= set(x) for x in got)
|
||||
|
||||
|
||||
def test_묶음_작업량_부르기() -> None:
|
||||
more = store_list.extras()
|
||||
assert sum(1 for v in more.values() if v["묶음"]) == 323 # 한 절에 로직 둘 넘음(조사 12-B ㉯)
|
||||
work = {k for k, v in more.items() if v["작업량"]}
|
||||
assert len(work) == 27
|
||||
assert more["GF000102"]["부모"] == ["GF000103"] # 구조물터파기 작업량 → 단가 로직 밑
|
||||
assert more["GC000268"]["불리는수"] >= 300 # 기계경비 로직
|
||||
assert sum(1 for v in more.values() if v["부르는수"]) == 506
|
||||
assert more["GC000294"]["관련"] == ["GC000292"] # 비고 「1-5-6(주③)」
|
||||
|
||||
|
||||
def test_관련은_해석_글과_넓은_절을_뺌() -> None:
|
||||
more = store_list.extras()
|
||||
assert all(len(v["관련"]) <= store_list._WIDE for v in more.values())
|
||||
assert not any(k in v["관련"] for k, v in more.items()) # 자기 자신 없음
|
||||
Reference in New Issue
Block a user