# -*- coding: utf-8 -*- """견본 입력 — 마스터 화면 보기용 자동값(프로젝트 기본값 아님 · 저장 안 함). 고르기 = 목록 첫째 · 범위 = 최솟값 · 찾기 조건 값으로 쓰이는 수 = 그 표 그 칸 가장 낮은 줄 값 (범위 줄이면 그 줄에 드는 아래 끝) · 나누는 자리에 오는 수 = 1 · 그 밖 0. 찾기·나누기 쓰임은 로직 식(중간 · 결과 · 호표 수량·요소 · 덧줄)을 풀어 알아냄. """ from __future__ import annotations from decimal import Decimal import master_formula as mf def _texts(row: dict): for step in row.get("중간", []): yield step["식"] if "결과" in row: yield row["결과"]["식"] for item in row.get("호표", []): yield item["수량"] if isinstance(item.get("요소"), str): yield item["요소"] for extra in row.get("덧줄", []): yield extra["식"] def _names(node) -> set: return {part[1] for part in mf.walk(node) if part[0] == "name"} def _uses(row: dict) -> tuple[dict, set]: """(찾기 조건 값으로 바로 쓰인 이름 → [(표, 조건)], 나누는 자리에 든 이름) — 나누는 자리에 든 중간의 식에 쓰인 이름도 나누는 자리로 봄.""" finds, divs = {}, set() for text in _texts(row): try: node = mf.parse(text) except mf.FormulaError: continue for part in mf.walk(node): if part[0] == "bin" and part[1] == "/": divs |= _names(part[3]) if part[0] == "find": for cond, arg in part[2]: if arg[0] == "name": finds.setdefault(arg[1], []).append((part[1], cond)) for step in reversed(row.get("중간", [])): if step["이름"] in divs: divs |= _names(mf.parse(step["식"])) return finds, divs def _fits(master: mf.Master, ref: str, cond: str) -> list: """표 `ref` 조건 칸 `cond` 에 드는 수 — 낮은 차례 · 범위 칸이면 줄마다 그 줄에 드는 끝 (아래 끝이 열린 줄 「60 미만」 은 0).""" try: table = master.get(ref) except mf.FormulaError: return [] ranged = table.get("조건", {}).get(cond) == "범위" out = set() for r in table.get("줄", []): if r.get(cond) is None: # 빈 조건 칸은 무엇이든 맞음 — 값을 정하지 않음 continue low, high = r[cond] if ranged else (r[cond], None) ends = [Decimal(0) if low is None else low, high] out |= {v for v in ends if isinstance(v, Decimal) and mf._match(table, r, cond, v)} return sorted(out) def _lowest(master: mf.Master, uses: list): """찾기 여럿이 같은 입력을 쓰면 모든 표에 드는 가장 낮은 수 · 없으면 첫 표의 가장 낮은 수.""" sets = [_fits(master, ref, cond) for ref, cond in uses] both = sorted(set.intersection(*map(set, sets))) if sets else [] return (both or next((s for s in sets if s), [None]))[0] def values(master: mf.Master, row: dict) -> dict: """로직 줄 하나의 입력마다 자동값.""" finds, divs = _uses(row) out = {} for spec in row.get("입력", []): name = spec["이름"] if spec.get("고르기"): out[name] = spec["고르기"][0] elif spec.get("범위"): out[name] = spec["범위"][0] else: got = _lowest(master, finds.get(name, [])) out[name] = got if got is not None else Decimal(1 if name in divs else 0) return out