feat(785-5): 만들기 · 길 — 확인한 고른 것 → SC · SK · 장부 · 새 M02_Hopyo_Router · 판정 · 검산 · 옮기기
- 새 M02_Hopyo_Router — 옛 hopyo 길 모두 M02_Labor_Router 에서 옮김(옛 화면 785-7 까지 돎) · 새 길 hopyo-judge 목록 · 호표 · 블록 · hopyo/{키}/check · hopyo/{키}/move · main 등록 · 시동 warm · Fit.ensure 걷음 · 올림 때 엑셀 사전(Row.lexicon) · 블록 풀이 뒤 셈 · 끝내기 때 저장본 지움
- 새 M02_Hopyo_Judge — 1차 줄 판(Row) + 단산 줄 2차 풀이(Clause → Solve) · 엑셀 칸(이름 · 규격 · 단위 · 단가) · 블록 금액 · 절 몫 · 블록 풀이 파생 저장본(HP 폴더 _derived · 입력 지문 · 지문 다를 때만) · judged(folder, key) · 검산(입력값 = 엔진이 쓴 칸 모두 · 지역 · 자재지역 · 조종원직종 비움 · 맡은 절 단위 줄 · 몫과 품 대조 · 금액 = 일위대가 로직 줄과 같은 셈)
- 새 M02_Hopyo_Make — 고른 것 → SC 줄(같은 별칭 · 줄 본뜬 {호표, 차례, 행} · 수량 = 엑셀 × 몫 × 배 · 같은 기초 둘째 = 한 줄 SC 사본 · 별도계상 자재 · 이음 · 율 · 고정 · 참조) · SK 더한줄(바탕 없음 · SkUnit.adds · Sangeun.Maker.write) · 하위 먼저 · 입력 필요 · 단위 환산 못 정함 = 그 줄(블록)만 막힘 · 장부 고른 것 꼴 짝 · 이미 옮긴 것 = 덮음 없으면 409(남의 SC 덮지 않음) · 확인한 줄 몸(Lines) = Build 에서 옮김
- Build 나눔 — 모두 고정 길 · Builder · _order · _origins · build_all 그대로(B09 빌림) · 확인한 줄 몸 → Make.Lines
- SkUnit · Sangeun — SkPatch · Fit · Basis 부름 걷음(기계경비 기초 차례 SkUnit 으로 · 근거 꼬리표 = Clause.basis · 맞춤 셈 409 없앰 · 재료 고름 MO 됨) · Kids 가 _same_mark 가짐
- 시험 새 test_642_5_make(판정 · 검산 · 옮기기 · 예시 1 · 2 · 3 · 덮음 409 · 막는 길) · test_m02_hopyo_excel 마당에 새 Router 더함 · HOPYO_MAX_BYTES 자리(길 옮긴 까닭)
- 낡은 시험 고침: test_718_1 · test_729 「맞춤 셈 전 409」 뺌(맞춤 뒤 셈 안 기다림) · test_628 맞춤셈 캐시 넣기 뺌(근거 = 조항 색인) · test_729 근거 없음 꼴 · 고정 이음 유가 칸(785-3 LINKS 늘어난 까닭)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,399 @@
|
||||
"""M02 엑셀 불러오기 만들기(PLAN 785-5 · 요구 642 · 설계 642-A 2-7 ⑥) — 확인한 것 → 우리 SC · SK · 장부.
|
||||
|
||||
옮기기 몸 = `{호표 {번: {차례: [고른 것]}}, 블록 {번: [고른 것]}}` · 고른 것 = 785-3 꼴
|
||||
`{갈래 기초|단가산출|일위대가|인력|재료|유가|기계|율|고정, 키, 입력값, 몫, 배?, 비목?, 기준?, 별도계상?, 층?,
|
||||
주인?, 항목?}` · 서버가 다시 셈(브라우저 금액 · 품 안 받음 · 배 = `Row.factor` · 사용자 배 우선 ·
|
||||
입력값 = 엔진이 쓴 칸 모두 `Judge.inputs`) · 「입력 필요」 · 배 못 정함 · 없는 키 = 그 줄(블록)만 `막힘`.
|
||||
SC(`M02_Hopyo_Build.Builder` 가 줄 차례 · 확인 안 한 줄 = 모두 고정 길 그대로) — 한 엑셀 줄 = 고른 것마다
|
||||
우리 줄(같은 별칭 = 엑셀 품명 · 줄 `본뜬 {호표, 차례, 행}`) · 수량 {손 = 엑셀 수량 × 몫 × 배} ·
|
||||
기초 = 로직 줄(같은 로직 둘째 = 한 줄 SC 사본) · 재료 · 유가 = 자재 목록(별도계상) · 인력 · 기계 · 유가 이음 ·
|
||||
율 = 고정 줄 `비율 {기준, 비목}` · 고정 = 엑셀 단가 고정 줄 · 단가산출 · 일위대가 = 참조
|
||||
(`단산N` = 같은 옮기기 블록 SK · `제N호표` = 같은 HP 호표 SC — 하위 먼저 · 확인 안 된 하위 = 모두 고정).
|
||||
SK(블록) = 바탕 없음 + 더한줄 — 기초 `{종류 로직, 입력값, 수량 = 몫}` · 참조 · 단위 줄(`항목`)에 이은
|
||||
인력 · 기계 · 재료 · 유가 · 율 = `SkUnit.adds` · 고정 = 엑셀 금액 · 쓰기 = `Sangeun.Maker.write`.
|
||||
같은 내용 재사용(`Kids.save` · `Sangeun.Maker.write`) · 장부 = 고른 것 꼴 짝(`Ledger.check_pair`) ·
|
||||
엑셀 대조 = `Compare.compare`(참고 · 비목마다 1 원).
|
||||
`Lines` = 확인한 줄 몸(옛 대상 꼴 `put` 은 옛 길 몫 · 785-7 지움).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from decimal import Decimal
|
||||
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_Judge as judge
|
||||
from M02_MasterTemplete import M02_Hopyo_Kids as hkids
|
||||
from M02_MasterTemplete import M02_Hopyo_Ledger as ledger
|
||||
from M02_MasterTemplete import M02_Hopyo_Material as hmat
|
||||
from M02_MasterTemplete import M02_Hopyo_Sangeun as sangeun
|
||||
from M02_MasterTemplete import M02_Hopyo_SkLink as sklink
|
||||
from M02_MasterTemplete import M02_Hopyo_SkUnit as skunit
|
||||
from M02_MasterTemplete import M02_Hopyo_Store as hopyo_store
|
||||
from M02_MasterTemplete import M02_Hopyo_Units as units
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
from M02_MasterTemplete import M02_Library_Same as lsame
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
from M02_MasterTemplete.M02_Hopyo_Compare import compare
|
||||
|
||||
_SUB = {"단가산출": re.compile(r"^단산(\d+)$"), "일위대가": re.compile(r"^제(\d+)호표$")}
|
||||
LINK = {"인력": "노임", "기계": "기계", "유가": "유가"}
|
||||
OLD_PICK = {"인력": "노임", "기계": "기계", "재료": "자재", "유가": "자재"}
|
||||
NEED = "입력 필요"
|
||||
COVER = "이미 옮긴 것이 있음 — 덮으려면 [덮어 옮기기](덮음)"
|
||||
|
||||
|
||||
class Stuck(Exception):
|
||||
"""그 줄(블록)만 못 옮김 — `{까닭, 칸?}`."""
|
||||
|
||||
def __init__(self, why: str, cells: list[str] | None = None) -> None:
|
||||
super().__init__(why)
|
||||
self.why, self.cells = why, cells or []
|
||||
|
||||
|
||||
def _num(value: Decimal) -> int | float:
|
||||
return hbuild._num(value)
|
||||
|
||||
|
||||
# ── 확인한 줄 몸(SC) ─────────────────────────────────────────────────────
|
||||
class Lines:
|
||||
"""한 호표 줄 모음 — `Builder.build` 가 줄 차례대로 부름 · 확인 안 한 줄은 Builder 가 고정 길로."""
|
||||
|
||||
def __init__(self, b: Any, rows: list[dict], where: dict, stamp: str, linker: Any) -> None:
|
||||
self.b, self.rows, self.where, self.stamp, self.linker = b, rows, where, stamp, linker
|
||||
self.lines: list[dict] = []
|
||||
self.mats: dict[str, dict] = {}
|
||||
self.missing: list[dict] = []
|
||||
self.used: set[str] = set()
|
||||
self.mat_lines: dict[str, dict] = {}
|
||||
self.nation: dict[tuple, dict] = {}
|
||||
self.taken: set[str] = set()
|
||||
self.kids: set[int] = set()
|
||||
self.left: list[dict] = []
|
||||
|
||||
def put(self, line: dict, one: dict, alias: str | None, qty: Decimal | None = None,
|
||||
mark: dict | None = None) -> None: # fmt: skip
|
||||
"""대상 하나 → 줄 · 자재 — 옛 꼴 `{로직|자재|호표|전국|이음, 입력값, 배, 몫}` · `qty` = 새 길 수량
|
||||
(엑셀 × 몫 × 배) · `mark` = 새 길 줄 `본뜬`."""
|
||||
keep = len(self.lines)
|
||||
if "이음" in one:
|
||||
others = [x for x in self.rows if x is not line]
|
||||
fx = hfix.line_of(line, others, self.where, self.taken, self.linker, one["이음"])
|
||||
self.lines.append(fx)
|
||||
else:
|
||||
qty = (
|
||||
qty
|
||||
if qty is not None
|
||||
else Decimal(str(line.get("수량") or 0)) * Decimal(str(one["배"]))
|
||||
)
|
||||
self._put(line, one, alias, qty, mark is not None)
|
||||
for row in self.lines[keep:]:
|
||||
row.update({"본뜬": mark} if mark else {})
|
||||
|
||||
def _put(self, line: dict, one: dict, alias: str | None, qty: Decimal, new: bool) -> None:
|
||||
b = self.b
|
||||
memo = hbuild._memo(line, one)
|
||||
if "전국" in one:
|
||||
at = (one["이름"], one["규격"], one["단위"])
|
||||
have = self.nation.get(at)
|
||||
hand = qty + Decimal(str(have["수량"]["손"])) if have else qty
|
||||
head = {"이름": at[0], "규격": at[1], "단위": at[2], "수량": {"손": _num(hand)}}
|
||||
self.nation[at] = {**head, **(hmat.marks(line, at[0]) if not have else {
|
||||
k: have[k] for k in labor.MARKS if k in have
|
||||
})} # fmt: skip
|
||||
self.missing.append(hbuild._missing(line, hbuild.NATIONAL))
|
||||
return
|
||||
if "자재" in one:
|
||||
code = one["자재"]
|
||||
have = self.mats.get(code)
|
||||
hand = qty + Decimal(str(have["수량"]["손"])) if have else qty
|
||||
name = b.mat_names.get(code, "")
|
||||
marks = (
|
||||
have and {k: have[k] for k in labor.MARKS if k in have} or hmat.marks(line, name)
|
||||
)
|
||||
if one.get("별도계상") is not None: # 세 칸 값(빈 칸 = 기본 「별도계상」)
|
||||
marks = {**marks, "별도계상": "별도계상" if one["별도계상"] else "직접계산"}
|
||||
self.mats[code] = {"코드": code, "수량": {"손": _num(hand)}, **marks}
|
||||
if not have:
|
||||
self.mat_lines[code] = line
|
||||
return
|
||||
if "호표" in one or "참조" in one:
|
||||
if "호표" in one:
|
||||
self.kids.add(one["호표"])
|
||||
key = b.done.get(hbuild.mark(b.key, one["호표"]), "SC-미리")
|
||||
ref = {"층": refs.here()[0], "주인": refs.here()[1], "키": key}
|
||||
else:
|
||||
ref = one["참조"]
|
||||
self.lines.append({"참조": ref, "별칭": alias, "메모": memo, "수량": {"손": _num(qty)}})
|
||||
return
|
||||
row = {"로직": one["로직"], "입력값": one["입력값"], "메모": memo}
|
||||
if not new:
|
||||
kept, why = sklink.kept(b, line, one, qty, alias, memo)
|
||||
if why:
|
||||
self.left.append(sklink.left(line, why))
|
||||
if kept is not None:
|
||||
self.lines.append(kept)
|
||||
return
|
||||
if one["로직"] not in self.used:
|
||||
self.used.add(one["로직"])
|
||||
self.lines.append({**row, "별칭": alias, "수량": {"손": _num(qty)}})
|
||||
return
|
||||
at = int(line.get("차례", 0))
|
||||
hopyo, file = b.hopyo, b.doc.get("파일") or ""
|
||||
wrap = { # 같은 로직 둘째 — 한 줄 사전조합으로 감쌈
|
||||
"이름": f"{hopyo['이름']} — {line.get('이름')}",
|
||||
"단위": line.get("단위") or None,
|
||||
"구분": hbuild.ORIGIN,
|
||||
"상세구분": file,
|
||||
"담은로직": [{**row, "별칭": alias, "수량": {"손": 1}}],
|
||||
"자재": [],
|
||||
"본뜬": b._origin(f"{hbuild.mark(b.key, b.no, at)} {one['로직']}", self.stamp),
|
||||
}
|
||||
ref = {"키": "SC-미리"} if b.dry else hkids.save(wrap, b.same)[0] # 미리 보기 = 쓰기 없음
|
||||
self.lines.append({"참조": ref, "별칭": alias, "메모": memo, "수량": {"손": _num(qty)}})
|
||||
|
||||
def picks(self, at: int, line: dict, picks: list[dict]) -> None:
|
||||
"""고른 것(검사 · 다시 셈 끝난 것) → 그 엑셀 줄의 우리 줄 · 자재."""
|
||||
alias = str(line.get("이름") or "")[: labor.ALIAS_MAX].strip() or None
|
||||
mark = {"호표": self.b.no, "차례": at, "행": line.get("행")}
|
||||
for p in picks:
|
||||
kind, code = p["갈래"], p.get("키")
|
||||
one = {"배": p["배"], "몫": p.get("몫")}
|
||||
qty = p["수량"]
|
||||
if kind == "기초":
|
||||
self.put(line, {**one, "로직": code, "입력값": p["입력값"]}, alias, qty, mark)
|
||||
elif kind in ("재료", "유가") and not p.get("이음"):
|
||||
self.put(
|
||||
line, {**one, "자재": code, "별도계상": p.get("별도계상")}, alias, qty, mark
|
||||
)
|
||||
elif kind in ("단가산출", "일위대가"):
|
||||
self.put(line, {**one, "참조": p["참조"]}, alias, qty, mark)
|
||||
elif kind == "율":
|
||||
self.put(line, {"이음": {"비율": p["비목"]}}, alias, None, mark)
|
||||
self.lines[-1]["고정"]["비율"] = {"기준": p["기준"], "비목": p["비목"]}
|
||||
elif kind == "고정":
|
||||
self.put(line, {"이음": dict.fromkeys(hfix.fixed.LINKS)}, alias, None, mark)
|
||||
else:
|
||||
self.put(line, {"이음": {LINK[kind]: code}}, alias, None, mark)
|
||||
|
||||
|
||||
def ledger_pick(p: dict[str, Any]) -> dict[str, Any]:
|
||||
"""장부 짝(고른 것 꼴 · `Ledger.check_pair`) — 참조 = 만든 SK · SC 키."""
|
||||
keep = ("갈래", "키", "입력값", "배", "몫", "비목", "기준", "별도계상")
|
||||
out = {k: p[k] for k in keep if p.get(k) is not None}
|
||||
return out | ({"키": p["참조"]["키"]} if "참조" in p else {})
|
||||
|
||||
|
||||
# ── 고른 것 검사 · 다시 셈 ──────────────────────────────────────────────
|
||||
class Move:
|
||||
"""한 번 옮기기 — 파일 · M01 · 만든 SC · SK 를 한 번 읽음(쓰기 = `store.root()`)."""
|
||||
|
||||
def __init__(self, folder: Path, source: Path, key: str, who: Any) -> None:
|
||||
self.folder, self.source, self.key, self.who = Path(folder), Path(source), key, who
|
||||
self.hp = hopyo_store.read(source, key)
|
||||
self.units = {int(h["번"]): h for h in self.hp.get("호표") or []}
|
||||
self.m = judge.master()
|
||||
self.known = ledger._known()
|
||||
self.sk = sangeun.Maker(source, key, who)
|
||||
self.same = lsame.Same()
|
||||
self.out: dict[str, Any] = {"만듦": [], "단가산출": [], "막힘": [], "하위": []}
|
||||
self.out["장부"] = {"씀": 0}
|
||||
|
||||
def _ref(self, p: dict) -> dict[str, Any]:
|
||||
"""단가산출 · 일위대가 키 → 참조 칸(같은 HP 블록 · 호표 = 만든 것) · 없으면 막힘."""
|
||||
code, kind = str(p.get("키") or ""), p["갈래"]
|
||||
hit = _SUB[kind].match(code)
|
||||
here = refs.here()
|
||||
if hit:
|
||||
n = int(hit.group(1))
|
||||
made = (
|
||||
self.sk.keys.get(sangeun.mark(self.key, n))
|
||||
if kind == "단가산출"
|
||||
else hbuild.confirmed(self.folder, refs.where(self.source)).get(
|
||||
hbuild.mark(self.key, n)
|
||||
)
|
||||
)
|
||||
if not made:
|
||||
raise Stuck(f"{code} 먼저 옮김")
|
||||
return {"층": here[0], "주인": here[1], "키": made}
|
||||
head = "SK-" if kind == "단가산출" else "SC-"
|
||||
ref = {"층": p.get("층") or here[0], "주인": p.get("주인"), "키": code}
|
||||
if p.get("층") is None:
|
||||
ref["주인"] = here[1]
|
||||
if not code.startswith(head) or refs.read_ref(ref) is None:
|
||||
raise Stuck(f"없는 {kind} 「{code}」")
|
||||
return ref
|
||||
|
||||
def pick(self, p: dict, line: dict | None, where: str) -> dict[str, Any]:
|
||||
"""고른 것 하나 재검사 · 다시 셈 → `{갈래, 키, 입력값, 배, 몫, 수량, 참조?, …}`."""
|
||||
kind = p["갈래"]
|
||||
got = {k: p[k] for k in ("갈래", "키", "몫", "비목", "기준", "별도계상") if k in p}
|
||||
if kind == "기초":
|
||||
full, need = judge.inputs(self.m, str(p.get("키") or ""), p.get("입력값"))
|
||||
if need:
|
||||
raise Stuck(f"{NEED} {len(need)}", need)
|
||||
got["입력값"] = full
|
||||
if kind in _SUB:
|
||||
got["참조"] = self._ref(p)
|
||||
else:
|
||||
plain = {k: v for k, v in got.items() if k in ledger.PICK_KEYS}
|
||||
ledger.check_pair(plain, where, self.known)
|
||||
if kind in ("재료", "유가") and not got.get("키"):
|
||||
raise Stuck(f"{kind} 키 없음")
|
||||
if line is not None:
|
||||
qty = judge.quantity(self.m, line, p)
|
||||
if qty["배"] is None:
|
||||
raise Stuck("단위 환산 입력")
|
||||
got["배"], got["수량"] = qty["배"], Decimal(str(qty["우리"] or 0))
|
||||
return got
|
||||
|
||||
# ── SC ──
|
||||
def hopyo(self, no: int, rows: dict[int, list[dict]]) -> None:
|
||||
unit = self.units[no]
|
||||
lines = unit.get("줄") or []
|
||||
checked = {}
|
||||
for at, picks in sorted(rows.items()):
|
||||
if not 0 <= at < len(lines):
|
||||
raise store.StoreError(400, f"없는 줄 「제{no}호표 {at}」")
|
||||
where = f"제{no}호표 {at + 1}째 줄"
|
||||
if not picks:
|
||||
continue
|
||||
try:
|
||||
checked[at] = {"고른": [self.pick(p, lines[at], where) for p in picks]}
|
||||
except Stuck as e:
|
||||
self.out["막힘"].append({"호표": no, "차례": at, "까닭": e.why, "칸": e.cells})
|
||||
b = hbuild.Builder(self.folder, self.source, self.key, no, self.who)
|
||||
b.strict, b.same = False, self.same
|
||||
made = b.build(checked, [])
|
||||
ref = made["참조"]
|
||||
head = {"번": no, "키": ref["키"], "이름": unit.get("이름")}
|
||||
head |= {k: made[k] for k in ("다시씀", "재사용")}
|
||||
self.out["만듦"].append({**head, "비교": compare(ref, unit)})
|
||||
self.out["장부"]["씀"] += made["장부"]["씀"]
|
||||
|
||||
def kid(self, no: int) -> None:
|
||||
"""확인 안 된 하위 호표 — 모두 고정(705-3 길)으로 먼저."""
|
||||
b = hbuild.Builder(self.folder, self.source, self.key, no, self.who)
|
||||
b.strict, b.same = False, self.same
|
||||
if b.done.get(hbuild.mark(self.key, no)):
|
||||
return
|
||||
made = b.build({}, [])
|
||||
self.out["하위"].append({"번": no, "키": made["참조"]["키"], "새로": True})
|
||||
|
||||
# ── SK ──
|
||||
def block(self, no: int, picks: list[dict]) -> None:
|
||||
blk = self.sk.blocks[no]
|
||||
items = blk.get("항목") or []
|
||||
us = self.sk.units()
|
||||
made: list[dict] = []
|
||||
rows: list[dict] = []
|
||||
for p in picks:
|
||||
got = self.pick(p, None, f"단산{no}")
|
||||
name = str(p.get("이름") or "").strip() or str(blk.get("이름") or f"단산{no}")
|
||||
share = got.get("몫") or 1
|
||||
at = p.get("항목")
|
||||
item = items[at] if isinstance(at, int) and 0 <= at < len(items) else None
|
||||
if got["갈래"] == "기초":
|
||||
made.append({"종류": "로직", "로직": got["키"], "입력값": got["입력값"],
|
||||
"이름": name, "수량": share}) # fmt: skip
|
||||
continue
|
||||
if got["갈래"] in _SUB:
|
||||
qty = float(item.get("품") or 1) if item else share
|
||||
made.append({"참조": got["참조"], "이름": name, "수량": qty})
|
||||
continue
|
||||
if item is None:
|
||||
raise Stuck(f"{got['갈래']} — 블록 단위 줄(항목) 고름")
|
||||
if got["갈래"] == "고정":
|
||||
parts = hfix._parts(item.get("금액"))
|
||||
fx = {"이름": name, "단가": {k: float(v) for k, v in parts.items()}, "이음": {}}
|
||||
made.append({"고정": fx, "이름": name, "수량": 1})
|
||||
continue
|
||||
old = {"기준": got.get("기준"), "비목": got.get("비목")}
|
||||
if got["갈래"] in OLD_PICK:
|
||||
old = {OLD_PICK[got["갈래"]]: got["키"], "별도계상": got.get("별도계상")}
|
||||
unit = skunit._picked(item, old, us)["단위"]
|
||||
rows.append({"차례": at, "행": item.get("행"), "상태": units.LINKED, "단위": unit})
|
||||
extra, _ = skunit.adds(blk, rows, us, self.sk.ref_of)
|
||||
doc = {"키": "SK-000000", "이름": "옮김", "단위": blk.get("단위") or "식", "바탕": None}
|
||||
try:
|
||||
adds = sd.clean({**doc, "더한줄": made + extra}, None, self.sk.whole)["더한줄"]
|
||||
except (ValueError, store.StoreError) as e:
|
||||
raise Stuck(f"더한줄 틀림 — {e}") from e
|
||||
plan = {"갈래": "고름", "바탕": None, "입력값": {}, "덮개": {}, "자재표시": {}, "뺀줄": []}
|
||||
plan |= {"더한줄": adds, "못옮김": [], "놓음": []}
|
||||
mark_ = sangeun.mark(self.key, no)
|
||||
key = self.sk.write(no, plan, self.sk.keys.get(mark_))
|
||||
self.sk.keys[mark_] = key
|
||||
self.out["단가산출"].append({"번": no, "키": key, "이름": blk.get("이름")})
|
||||
|
||||
def blocks(self, sure: dict[int, list[dict]]) -> None:
|
||||
"""하위 단산(블록 단위 줄 하위) 먼저."""
|
||||
done: set[int] = set()
|
||||
|
||||
def visit(no: int, path: tuple = ()) -> None:
|
||||
if no in done or no in path:
|
||||
return
|
||||
for one in self.sk.blocks[no].get("항목") or []:
|
||||
sub = one.get("하위") or {}
|
||||
if sub.get("종류") == "단산" and int(sub.get("번") or 0) in sure:
|
||||
visit(int(sub["번"]), (*path, no))
|
||||
done.add(no)
|
||||
try:
|
||||
self.block(no, sure[no])
|
||||
except Stuck as e:
|
||||
self.out["막힘"].append({"블록": no, "까닭": e.why, "칸": e.cells})
|
||||
|
||||
for no in sorted(sure):
|
||||
visit(no)
|
||||
|
||||
|
||||
def move(
|
||||
folder: Path,
|
||||
source: Path,
|
||||
key: str,
|
||||
units_: dict[int, dict[int, list[dict]]],
|
||||
blocks: dict[int, list[dict]],
|
||||
who: Any = None,
|
||||
cover: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
"""[옮기기] → `{만듦, 단가산출, 막힘, 하위, 장부}` — 블록 SK 먼저 · 호표 하위 먼저 ·
|
||||
이미 옮긴 호표 · 블록(같은 HP 출처 SC · SK)은 `cover`(덮음) 때만 덮음 · 아니면 409 아무것도 안 씀."""
|
||||
mv = Move(folder, source, key, who)
|
||||
bad = [f"제{n}호표" for n in units_ if n not in mv.units]
|
||||
bad += [f"단산{n}" for n in blocks if n not in mv.sk.blocks]
|
||||
if bad:
|
||||
raise store.StoreError(404, f"없는 {bad[0]}")
|
||||
done = hbuild.confirmed(mv.folder, refs.where(mv.source))
|
||||
had = {
|
||||
"호표": [
|
||||
{"번": n, "키": done[k]} for n in sorted(units_) if (k := hbuild.mark(key, n)) in done
|
||||
]
|
||||
}
|
||||
had["블록"] = [
|
||||
{"번": n, "키": mv.sk.keys[k]}
|
||||
for n in sorted(blocks)
|
||||
if (k := sangeun.mark(key, n)) in mv.sk.keys
|
||||
]
|
||||
if not cover and (had["호표"] or had["블록"]):
|
||||
raise store.StoreError(409, {"까닭": COVER, **had})
|
||||
mv.blocks(blocks)
|
||||
wanted = {
|
||||
int(hit.group(1))
|
||||
for rows in units_.values()
|
||||
for picks in rows.values()
|
||||
for p in picks
|
||||
if p.get("갈래") == "일위대가" and (hit := _SUB["일위대가"].match(str(p.get("키") or "")))
|
||||
}
|
||||
order = hbuild._order(mv.units)
|
||||
for no in order: # 고른 것이 부르는 확인 안 된 하위 = 모두 고정으로 먼저
|
||||
if no in wanted and no not in units_:
|
||||
mv.kid(no)
|
||||
for no in order:
|
||||
if no in units_:
|
||||
mv.hopyo(no, units_[no])
|
||||
return mv.out
|
||||
Reference in New Issue
Block a user