"""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*)?(? 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]