"""M02 엑셀 불러오기 [수정] 보기(PLAN 809-1 · 요구 702 · 703) — 호표 하나 → 일위대가 문서 미리보기 + 줄 표지 · 읽기만. 경쟁사 로직 인정 · 요소만 연결 — 문서 줄 = 엑셀 줄 하나씩 고정 줄(엑셀 숫자 그대로 · `Fixed.line_of` · [내 일위대가로] 와 같은 이음 규칙 · 금액 = 엑셀). 줄 표지: · 로직 줄 = 단산 N 참조 → 「단가산출」(번 · 가져온 식 SK 로 엶) · 일위 N 참조 → 「일위대가」(번 · 하위 호표 보기) · 요소 줄 = 그 밖(인력 · 재료 · 기계 · 유가 · 율 · 고정 수) — 이음 있음 = 「연결됨」(이음) · 후보 있음 = 「후보」(키 · 이름) · 없음 = 「못 이음」(엑셀 값 그대로) · 엑셀 = 줄 금액(노무비 · 재료비 · 경비 · 계) 기초 판정 · 2차 풀이 없음 · 저장 없음. """ from __future__ import annotations from pathlib import Path from typing import Any from M02_MasterTemplete import M02_Hopyo_Build as hbuild from M02_MasterTemplete import M02_Hopyo_Fixed as hfix from M02_MasterTemplete import M02_Hopyo_Store as hopyo_store from M02_MasterTemplete import M02_Labor_Ref as refs from M02_MasterTemplete import M02_MasterTemplete_Store as store LOGIC, ELEMENT = "로직", "요소" SK, SC = "단가산출", "일위대가" LINKED, NEAR, NONE = "연결됨", "후보", "못 이음" MONEY = ("노무비", "재료비", "경비", "계") def _name(linker: hfix.Linker, code: str) -> str: row = (linker.whole.numbers.get(code[:2]) or {}).get(code) or {} return str(row.get("이름") or "") def mark(line: dict[str, Any], one: dict[str, Any], linker: hfix.Linker) -> dict[str, Any]: """엑셀 줄 하나 표지 — 로직(단가산출 · 일위대가) / 요소(연결됨 · 후보 · 못 이음).""" ref = line.get("참조") or {} money = line.get("금액") or {} out: dict[str, Any] = { "차례": int(line.get("차례", 0)), "엑셀": {k: money.get(k) for k in MONEY}, } if ref.get("종류") == "단산" and ref.get("번"): return {**out, "갈래": LOGIC, "표지": SK, "번": int(ref["번"])} if ref.get("종류") == "일위" or hfix.kind_of(line) == "호표": return {**out, "갈래": LOGIC, "표지": SC, "번": int(ref.get("번") or 0) or None} fx = one.get("고정") or {} if hfix.linked(one): link = {k: v for k, v in (fx.get("이음") or {}).items() if v} rate = fx.get("비율") return {**out, "갈래": ELEMENT, "표지": LINKED, "이음": link or {"비율": rate["기준"]}} near = [{"키": c, "이름": _name(linker, c)} for c in linker.candidates(line)] if near: return {**out, "갈래": ELEMENT, "표지": NEAR, "후보": near} return {**out, "갈래": ELEMENT, "표지": NONE} def view(source: str | Path, key: str, no: int) -> dict[str, Any]: """파일 층 HP 의 호표 하나 보기(`of`).""" return of(hopyo_store.read(Path(source), key), no) def of(book: dict[str, Any], no: int, linker: hfix.Linker | None = None) -> dict[str, Any]: """호표 하나 → `{번, 이름, 규격, 단위, 금액, 문서 {이름, 단위, 담은로직, 자재}, 줄 [표지]}` — 문서 줄 차례 = 엑셀 줄 차례.""" hopyo = next((h for h in book.get("호표") or [] if int(h["번"]) == int(no)), None) if hopyo is None: raise store.StoreError(404, f"없는 호표 「제{no}호표」") rows = hopyo.get("줄") or [] linker, taken = linker or hfix.Linker(True), set() where = {"파일": book.get("파일") or "", "호표": int(no)} lines, marks = [], [] for line in rows: one = hfix.line_of(line, [x for x in rows if x is not line], where, taken, linker) lines.append(one) marks.append(mark(line, one, linker)) name = " ".join(x for x in (hopyo.get("이름"), hopyo.get("규격")) if x) doc = {"이름": name, "단위": hopyo.get("단위") or None, "담은로직": lines, "자재": []} head = {k: hopyo.get(k) for k in ("번", "이름", "규격", "단위", "금액")} return {**head, "문서": doc, "줄": marks} def listing(folder: str | Path, key: str, to: str | Path) -> dict[str, Any]: """엑셀 보기 머리 · 목록 — 파일 줄 + 검산 · 호표마다 `맞춤` [연결됨 + 로직 줄, 줄 수] · `줄상태 [[차례, 표지, 이어짐]]` · `만든` SC(만들 층 `to` · 이 파일 층 것만).""" book = hopyo_store.read(Path(folder), key) made = hbuild.confirmed(to, refs.where(Path(folder))) linker, units = hfix.Linker(True), [] for h in book.get("호표") or []: no = int(h["번"]) marks = of(book, no, linker)["줄"] state = [ [i, m["표지"], m["갈래"] == LOGIC or m["표지"] == LINKED] for i, m in enumerate(marks) ] head = {k: h.get(k) for k in ("번", "이름", "규격", "단위", "금액", "행") if k in h} units.append({**head, "맞춤": [sum(x[2] for x in state), len(state)], "줄상태": state, "만든": made.get(hbuild.mark(key, no))}) # fmt: skip ready = [sum(h["맞춤"][0] for h in units), sum(h["맞춤"][1] for h in units)] head = {**hopyo_store.brief(book), "검산": book.get("검산"), "셈": {"끝": True}} return {**head, "호표": units, "맞춤": ready}