Files
Aislo/M02_MasterTemplete/M02_San_Basis_Store.py
eomsangdonandClaude Opus 5.5 616dfa698a feat(794-2b): 근거 제안 까닭 「판 차이」에 판 번호 · 둘 이상 파일 같은 배 = 「판 차이 강함」
- M02_San_Basis_Check 새 함수 edition · strong(causes 안 고침 · 셈 · 단 · 꼴 그대로) — 블록 판 = 글 속 「20xx년 품셈」 › 파일 이름 연도 · 기초 로직 줄 「개정」 가운데 그 뒤 것 → 근거후보 덧칸 `판` · 까닭 「판 차이('24 보완 · 파일 2024)」
- 같은 기초 · 같은 배(±2 %)가 같은 폴더 다른 HP 저장본에도 판 표지와 함께 있으면 판 차이 · 현장 값 · 그 밖 → 「판 차이 강함(…)」(읽을 때 · 저장본 안 고침)
- M02_San_Basis_Store — one 끝 edition(HP 파일 이름) · listing · block 끝 strong · 저장본 VERSION 794-2b.1
- 지킴 시험 test_794_2b_edition 7 — 개정 칸 = 빌더 값 · 셈 지문 건너뜀 없음 · 잔토처리 ㎥당 보통인부 0.2 · 봉화#98 철근공 ×0.9714 「판 차이('24 보완 · 파일 2024)」 · 봉화#8 · 거창#28 뒤채움 ×1.98 판 차이 강함

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

152 lines
5.8 KiB
Python
Raw Permalink 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_Check as chk
from M02_MasterTemplete import M02_San_Basis_Index as bi
KIND = "san_basis_"
VERSION = "794-2b.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))
out = basis.suggest(blk, book.get("꼴") or "", line, same, g)
return chk.edition(out, book.get("파일"), blk) # 794-2b 판 표지
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 _others(folder: Path, key: str) -> list[dict[str, Any]]:
"""같은 폴더 다른 HP 저장본(셈 끝난 것만) — 판 차이 강함(794-2b) 대조."""
out = []
for r in hopyo_store.rows(folder):
if r.get("키") and r["키"] != key:
try:
got = solved(folder, r["키"])
except Exception: # noqa: BLE001 — 못 읽는 HP = 대조에서 뺌
got = None
if got:
out.append(got)
return out
def listing(folder: Path, key: str) -> dict[str, Any]:
"""{셈중, 블록 {번: 제안}, 보강} — 셈 중 = 블록 {} · 뒤 셈 시작."""
got = solved(folder, key)
if got is None:
ensure(folder, key)
return {"키": key, "셈중": True, "블록": {}, "보강": []}
got = chk.strong(got, _others(folder, key))
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 chk.strong({str(no): got}, _others(folder, key))[str(no)]
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)