Files
Aislo/M02_MasterTemplete/M02_San_Basis_Store.py
T
eomsangdonandClaude Opus 5.5 532cc52c8e feat(793-3a): 근거 제안 엔진 — 번호 ∩ 값 ∩ 제목 ∩ 기계 상수 → M01 기초 + 옵션 · 품 검산 · 세 단
- M01 값 역색인 · 숫자 정규화 한 벌(N1~N10 · 단위 기준 · % ↔ 소수 · t ↔ kg · 인/양 몫) · M01 지문마다 한 번 · 서버 시작 때 뒤에서 지음
- 값 길(상수 지문 → 로직 · 우연 보정 유의 4) · 합친 길(분석 648-D2 차례 · 후보 2~6 엔진 품 대조) · 검산 · 옵션(B1~B6 → 값 길 V → 고르기 맞댐 → 입력 필요)
- 세 단 확정 · 근접(판 차이 · 현장 값 · 감률 · 할증 배수 · 단위 기준 차이) · 사람(+ 값 약한 1 등) · 없음(M01 보강 목록)
- 길 GET hopyo-basis/{키} · hopyo-basis/{키}/{번} · 파생 저장본 · 올림 때 HP 숨은식(STmate G 식) · 끝내기 때 지움 · 판정 길 그대로
- 보정 자료 = 여덟 파일 상수 1,094 · 시험 test_793_3a 11(459 블록 기초 260 · 옵션 전부 52 · 품 같음 141 · 없음 85)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:19:26 +09:00

135 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""근거 제안 길 · 저장본(PLAN 793-3a) — 서버 단독 · 읽기만(HP 는 안 고침).
올림 = HP 에 `숨은식 {번: [G 식]}`(STmate 단가산출근거 숨은 G 열 · `hidden`) + 뒤 셈(`ensure`).
저장본 = HP 폴더 `_derived/san_basis_{키}` · 입력 지문 = HP(지문 · 판 · 올린때) + M01 역색인 지문
+ 판 ·
지문 다를 때만 다시 셈 · GET 은 안 지움 · [끝내기] = `forget`.
답 꼴 = `listing` {셈중, 블록 {번: 제안}, 보강 [{이름, 품셈, 까닭, 블록 [번]}]} · `block` = 제안
하나
(`M02_San_Basis.suggest` 꼴 — 단 · 꼴 · 근거후보 · 절 · 보강?).
"""
from __future__ import annotations
import shutil
import threading
from pathlib import Path
from typing import Any
from common_util import common_util_derived_store as derived
from M02_MasterTemplete import M02_Hopyo_Judge as judge
from M02_MasterTemplete import M02_Hopyo_Store as hopyo_store
from M02_MasterTemplete import M02_San_Basis as basis
from M02_MasterTemplete import M02_San_Basis_Index as bi
KIND = "san_basis_"
VERSION = "793-3a.1" # 제안 꼴이 바뀌면 올림
SHEET = "단가산출근거"
_LOCK, _RUNNING = threading.Lock(), set()
def hidden(book: dict[str, Any], sheets: dict[str, list[list[Any]]]) -> dict[str, list[str]]:
"""STmate 블록마다 숨은 G 열 원문 식(「_x0007_」 머리 줄만) · 그 밖 = {}."""
rows = sheets.get(SHEET) or []
if book.get("꼴") != "STmate" or not rows:
return {}
out = {}
for blk in book.get("단산") or []:
start, end = blk["행"]
got = []
for r in range(start, end + 1):
row = rows[r - 1] if 0 < r <= len(rows) else []
v = str(row[6]) if len(row) > 6 and row[6] is not None else ""
if v.startswith("_x0007_") and v != "_x0007_":
got.append(v.replace("_x0007_", "").strip())
out[str(blk["번"])] = got
return out
def one(book: dict[str, Any], no: int) -> dict[str, Any]:
blk = next(b for b in book.get("단산") or [] if int(b["번"]) == int(no))
line, same = judge._caller(book, int(no))
g = (book.get("숨은식") or {}).get(str(no))
return basis.suggest(blk, book.get("꼴") or "", line, same, g)
def _stamp(key: str, book: dict[str, Any]) -> str:
return derived.derived_key(
VERSION, key, book.get("지문"), book.get("판"), book.get("올린때"), bi.stamp()
)
def solved(folder: Path, key: str) -> dict[str, Any] | None:
book = hopyo_store.read(folder, key)
got = derived.read_derived(hopyo_store._dir(folder), KIND + key, _stamp(key, book))
return got["value"] if got else None
def solve_all(folder: Path, key: str) -> dict[str, Any]:
book = hopyo_store.read(folder, key)
out = {
str(b["번"]): one(book, int(b["번"]))
for b in book.get("단산") or []
if b.get("번") is not None
}
derived.write_derived(hopyo_store._dir(folder), KIND + key, _stamp(key, book), out, keep=1)
return out
def _work(folder: Path, key: str) -> None:
try:
solve_all(folder, key)
except Exception: # noqa: BLE001 — 뒤 셈 실패 = 다음 읽기에서 다시(파생 저장본)
pass
finally:
with _LOCK:
_RUNNING.discard(f"{folder}|{key}")
def ensure(folder: Path, key: str) -> bool:
"""저장본 있음 = 참 · 없으면 뒤 셈 시작(같은 파일 한 번) = 거짓."""
if solved(folder, key) is not None:
return True
tag = f"{folder}|{key}"
with _LOCK:
if tag in _RUNNING:
return False
_RUNNING.add(tag)
threading.Thread(target=_work, args=(Path(folder), key), daemon=True).start()
return False
def shortfall(got: dict[str, Any]) -> list[dict[str, Any]]:
"""없음 블록 → M01 보강 목록(794-2 근거) — 이름 · 품셈 묶음마다 블록 번."""
out: dict[tuple, dict] = {}
for no, r in got.items():
if r.get("보강"):
b = r["보강"]
one_ = out.setdefault((b["이름"], tuple(b["품셈"])), {**b, "블록": []})
one_["블록"].append(int(no))
return sorted(out.values(), key=lambda x: (-len(x["블록"]), x["이름"]))
def listing(folder: Path, key: str) -> dict[str, Any]:
"""{셈중, 블록 {번: 제안}, 보강} — 셈 중 = 블록 {} · 뒤 셈 시작."""
got = solved(folder, key)
if got is None:
ensure(folder, key)
return {"키": key, "셈중": True, "블록": {}, "보강": []}
return {"키": key, "셈중": False, "블록": got, "보강": shortfall(got)}
def block(folder: Path, key: str, no: int) -> dict[str, Any]:
"""블록 하나 — 저장본 › 그 자리 셈."""
got = (solved(folder, key) or {}).get(str(no))
if got is not None:
return got
book = hopyo_store.read(folder, key)
if not any(b.get("번") is not None and int(b["번"]) == int(no) for b in book.get("단산") or []):
raise judge._bad(f"없는 블록 「단산{no}」", 404)
return one(book, no)
def forget(folder: Path, key: str) -> None:
shutil.rmtree(derived.derived_dir(hopyo_store._dir(folder), KIND + key), ignore_errors=True)