Files
Aislo/resources/master_data/scripts/master_auto.py
T
eomsangdonandClaude Opus 5.5 28da700d47 feat(M01): 흐름 로직 줄 = 옛 식 칸(입력 · 중간 · 호표 · 결과 · 덧줄) 지움 · 읽는 곳은 흐름의 설계 때 정할 것 · 출력에서(사용자 결정)
- master_formula.inputs_of · FLOW_INPUTS · OLD_FIELDS · mf.run 이 흐름 줄이면 스스로 서버 갈래를 깖(스크립트 · 시험도)
- check_master 틀 검사 · 시험입력 · 로직(…) 입력 검사 · master_auto 자동값 · 프로젝트 빈 입력 · 연결 · 다시 적용 값이 흐름 입력으로
- 프로젝트 단가 묶음(collect)이 흐름의 요소 · 표 · 다른 로직 · 기계 시간당 경비 로직을 모음
- 옛 엔진 갈래 결과 = 옛 꼴(돈 출력 줄 · 값들 · 알림) · 조합 · 프로젝트에서 안 준 입력은 빈 입력 알림(예시값 아님)
- /text 흐름 로직 = 출력 상자 줄 · GET /logic 은 보기용으로 설계 때 정할 것을 입력 꼴로 얹음(파일은 그대로)
- 찾을 값이 빈 조건 찾기는 그 값만 비움(옛 셈처럼)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL
2026-10-02 08:55:36 +09:00

99 lines
3.9 KiB
Python

# -*- 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:
"""로직 줄 하나의 입력마다 자동값 · 흐름 로직은 「설계 때 정할 것」 예시값."""
if isinstance(row.get("흐름"), dict):
return {s["이름"]: s.get("예시") for s in mf.inputs_of(row)}
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, []))
if got is None and "원" in str(spec.get("단위") or ""):
out[name] = None # 가격 입력 — 0 원처럼 보이지 않게 비워 둠
else:
out[name] = got if got is not None else Decimal(1 if name in divs else 0)
return out