- 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>
152 lines
5.8 KiB
Python
152 lines
5.8 KiB
Python
"""근거 제안 길 · 저장본(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)
|