Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1
# Conflicts: # M01_MasterData/M01_MasterData_UI_LogicLab_Detail.ts
This commit is contained in:
@@ -322,3 +322,103 @@ def causes(v: dict, mach: list[dict], rec: dict | None) -> list[list[str]]:
|
||||
]
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
# ── 판 표지(PLAN 794-2b · 분석 652-794-2a 5 장) — 근거후보 까닭 「판 차이」에 판 번호 ──────────
|
||||
STRONG = ("판 차이", "현장 값", "그 밖") # 둘 이상 파일 같은 배면 「판 차이 강함」 으로 올림
|
||||
_TEXT_YEAR = re.compile(r"(?<!\d)((?:20)?\d\d)\s*년\s*(?:도\s*)?[^\d\n]{0,12}?품셈")
|
||||
_RATIO = re.compile(r"×([0-9.]+(?:e[-+]?\d+)?)")
|
||||
|
||||
|
||||
def _year(m: re.Match | None) -> int | None:
|
||||
y = int(m.group(1)) % 100 if m else None
|
||||
return None if y is None else y + (2000 if y < 50 else 1900)
|
||||
|
||||
|
||||
def file_year(name: Any) -> int | None:
|
||||
"""파일 이름 연도 — 「2024년 …」 · 「00.25년_…」 · 「20250527 2025년 …」."""
|
||||
s = str(name or "")
|
||||
return _year(re.search(r"(20\d\d)\s*년", s) or re.search(r"(?<!\d)(\d\d)\s*년", s))
|
||||
|
||||
|
||||
def text_year(blk: dict) -> int | None:
|
||||
"""블록 글 속 판 — 「2017년 품셈」 · 「24년 건설표준품셈」(가장 옛 것)."""
|
||||
text = " ".join(str(t) for t in [blk.get("이름"), blk.get("규격"), *(blk.get("글") or [])])
|
||||
found = [_year(m) for m in _TEXT_YEAR.finditer(text)]
|
||||
return min(found) if found else None
|
||||
|
||||
|
||||
def edition_tag(key: str, rev: list, year: int, by: str) -> str:
|
||||
"""기초 절 개정 가운데 블록 판 뒤 것 — 「'24 보완 · 파일 2024」 · 없으면 빈 글.
|
||||
|
||||
글 속 판 해 = 그 해 개정을 담음(뒤 것만) · 파일 해 = 같은 해 개정이 앞일 수도(같은 해 포함)."""
|
||||
later = [int(y) for y in rev or [] if int(y) > year or (by == "파일" and int(y) == year)]
|
||||
if not later:
|
||||
return ""
|
||||
word = "제정" if key.startswith("GF") else "보완"
|
||||
return f"{' · '.join(f'{chr(39)}{y % 100:02d}' for y in later)} {word} · {by} {year}"
|
||||
|
||||
|
||||
def edition(out: dict, file_name: Any, blk: dict) -> dict:
|
||||
"""제안 근거후보마다 판 표지 `판`(덧칸) · 까닭 「판 차이」 → 「판 차이(판 표지)」.
|
||||
|
||||
셈 · 단 · 꼴 안 바뀜(표지 글만)."""
|
||||
by_text, by_file = text_year(blk), file_year(file_name)
|
||||
if by_text and (not by_file or by_text <= by_file):
|
||||
year, by = by_text, "글"
|
||||
else:
|
||||
year, by = by_file, "파일"
|
||||
if not year:
|
||||
return out
|
||||
index = hb.m01.cm.master().index
|
||||
for c in out.get("근거후보") or []:
|
||||
key = str(c.get("기초") or "")
|
||||
tag = edition_tag(key, (index.get(key[:2], {}).get(key) or {}).get("개정"), year, by)
|
||||
if tag:
|
||||
c["판"] = tag
|
||||
c["까닭"] = [
|
||||
[f"판 차이({tag})" if lab == "판 차이" else lab, txt] for lab, txt in c["까닭"]
|
||||
]
|
||||
return out
|
||||
|
||||
|
||||
def _ratios(c: dict) -> list[float]:
|
||||
return [
|
||||
float(m.group(1))
|
||||
for lab, txt in c.get("까닭") or []
|
||||
if lab.startswith(STRONG) and (m := _RATIO.search(str(txt)))
|
||||
]
|
||||
|
||||
|
||||
def strong(got: dict, others: list[dict]) -> dict:
|
||||
"""같은 기초 · 같은 배(±2 %)가 다른 파일에도 판 표지와 함께 있으면 「판 차이 강함(…)」."""
|
||||
seen = [
|
||||
(c["기초"], r)
|
||||
for o in others
|
||||
for p in o.values()
|
||||
for c in p.get("근거후보") or []
|
||||
if c.get("판")
|
||||
for r in _ratios(c)
|
||||
]
|
||||
if not seen:
|
||||
return got
|
||||
|
||||
def hit(key: str, lab: str, text: Any) -> bool:
|
||||
m = _RATIO.search(str(text)) if lab.startswith(STRONG) else None
|
||||
return bool(m) and any(
|
||||
k == key and r and abs(float(m.group(1)) / r - 1) <= 0.02 for k, r in seen
|
||||
)
|
||||
|
||||
out = {}
|
||||
for no, p in got.items():
|
||||
cs = []
|
||||
for c in p.get("근거후보") or []:
|
||||
if c.get("판"):
|
||||
why = [
|
||||
[f"판 차이 강함({c['판']})", t] if hit(c["기초"], lab, t) else [lab, t]
|
||||
for lab, t in c["까닭"]
|
||||
]
|
||||
c = {**c, "까닭": why}
|
||||
cs.append(c)
|
||||
out[no] = {**p, "근거후보": cs}
|
||||
return out
|
||||
|
||||
@@ -20,10 +20,11 @@ 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 = "793-3a.1" # 제안 꼴이 바뀌면 올림
|
||||
VERSION = "794-2b.1" # 제안 꼴이 바뀌면 올림
|
||||
SHEET = "단가산출근거"
|
||||
_LOCK, _RUNNING = threading.Lock(), set()
|
||||
|
||||
@@ -50,7 +51,8 @@ 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)
|
||||
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:
|
||||
@@ -110,12 +112,27 @@ def shortfall(got: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
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)}
|
||||
|
||||
|
||||
@@ -123,7 +140,7 @@ 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
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user