Files
Aislo/M02_MasterTemplete/M02_Hopyo_Marks.py
T
eomsangdonandClaude Opus 5.5 4231902d57 feat(778-5 D-6): 엑셀 셈 어긋남 띠 — 불리는 블록 재현 못 한 줄 · 덮음 실패 · 금액 차 · 까닭
- Marks 블록 덧칸 `어긋남 {줄합, 금액, 차, 덮음, 줄 [{행, 이름, 묶음, 셈, 금액, 차, 까닭}], 까닭}` · 읽기만
- SheetMap `missesOf` = 상세 시트 행 차례(덮음 실패 = 블록 머리 행 · 재현 못 한 줄 = 그 줄 행) · 칠 안 바꿈
- SheetNav 머리 막대 아래 띠(부품 createBanner) 「엑셀 셈 어긋남 n」 + [보기 i/m] 차례로 뛰기 · 그 행 까닭은 막대 글
- 북부청 임도 = 2 · 33(엑셀 합이 진동로울러 노무 뺌 · 차 2,637.1 · 3,418.7) · 64 · 95(글 꼴 밖 줄) · 81 맞음 · 영월 0
- 시험 새 test_778_5_hopyo_excel_misses · test_779_2 어긋남 한 줄 · helper_779_4 missesOf

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 13:34:02 +09:00

116 lines
6.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.
"""M02 불러오기 엑셀 보기 하이라이트 재료(PLAN 779-2 · 요구서 621 3장 5) — 읽기만(파일 · storage 변화 0).
블록 `[{번, 판정, SK, 갈래, 못옮김}]` = 단가산출 만들기 결과(`Sangeun.made`) + 맞춤셈 캐시(`Fit.result`)를 읽기만.
- `판정` = 그 블록을 부르는 첫 줄의 맞춤셈 판정 상태(맞음 · 다른 점 · 후보 …) · 안 불리거나 셈 전이면 None
- `SK` = 만든 SK 키 · 없으면 None / `갈래` · `못옮김` = 그 SK 본뜬(출처별) · 없으면 None · []
줄 상태는 `Match.view` 호표마다 `줄상태 [[차례, 상태, 체크]]` · `만든`(SC 키)로 실려 감.
칠 꼴(화면 779-4 가 정함 · 상태 이름 맞춤표 — 「이어짐」 = 확인 창과 한 규칙 `체크`):
| 칠 | 줄 상태 (`줄상태`) | 블록 (`판정` · `SK` · `못옮김`) |
| 초록 이어짐 | 맞음 · 별칭 · 호표(`체크`) · 체크한 다른 점 | SK 있음 + 못옮김 [] (갈래 「기초」 포함) |
| 주황 엑셀과 다름 | 다른 점 + `체크` | SK 있음 + 갈래 「SK」(차이) |
| 파랑 고를 것 | 후보 · `체크` 아닌 다른 점 · 호표(`체크` 아님) | SK 없음 + 판정 후보 · 고름 대기 |
| 빨강 못 이음 | 없음 · 노임 · (안 이은 줄 = 마법사 몫) | SK 있음 + 못옮김 남음 |
| 흐림 밖 | % · 뺌 | 판정 None (안 불리는 블록) |
| 띠 내가 고름 | 화면 캐시(서버 아님) | — |
최종 판정은 화면(`SheetMap`)이 이 재료 + 캐시 고름으로 내림 — 여기는 상태 이름을 그대로 실음.
`어긋남`(778-5 D-6 · 요구 623 · 624) = 불리는 블록의 엑셀 셈 어긋남(엑셀 = 과거 값 · 사용자가 보고 판단 ·
칠 · 판정에 안 씀) — `{줄합, 금액, 차, 덮음, 줄 [{행, 이름, 묶음, 셈, 금액, 차, 까닭}], 까닭}` · 없으면 None.
줄 = 재현 못 한 단위 줄(`UnitLine.redo` — 엑셀 단가 × 매개값 ≠ 엑셀 줄 금액 · 셈 밖 뺌) · 덮음 실패 = 줄 합 ≠ 블록 금액.
"""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from M02_MasterTemplete import M02_Hopyo_Fit as fit
from M02_MasterTemplete import M02_Hopyo_Sangeun as sangeun
from M02_MasterTemplete import M02_Hopyo_Store as hopyo_store
from M02_MasterTemplete import M02_Hopyo_UnitLine as unit_line
from M02_MasterTemplete import M02_Labor_Ref as refs
def _origin(to: Path, sk: str, mark: str, here: refs.Here) -> dict[str, Any]:
try:
doc = json.loads((to / sangeun.KIND / f"{sk}.json").read_bytes())
except (OSError, ValueError):
return {}
return sangeun.origin_of(doc, mark, here)
def _won(v: float) -> str:
return f"{v:,.1f}".removesuffix(".0")
def _calc(one: dict[str, Any]) -> float | None:
"""엑셀 단가 × 매개값(× 계수) — `UnitLine.redo` 와 같은 셈 · 매개값 · 단가 없으면 None."""
q, price = one.get("품"), one.get("단가") or {}
if q is None:
return None
if one["묶음"] == "R":
return float(one.get("기준값") or 0) * q
if one["묶음"] == "X":
ratio = one.get("계수") or {}
return sum(float(price[c]) * q * ratio.get(c, 1) for c in unit_line.COSTS if price.get(c))
unit = unit_line.money(price)
return None if unit is None else unit * q
def misses(block: dict[str, Any]) -> dict[str, Any] | None:
"""블록 엑셀 셈 어긋남 — 재현 못 한 줄 · 덮음 실패 · 없으면 None."""
lines = []
for one in block.get("항목") or []:
if one.get("재현") or one.get("밖"):
continue
calc, got = _calc(one), one.get("금액")
if calc is None or got is None:
why = "매개값 · 단가를 못 읽음(글 꼴 밖)" if got is not None else "줄 금액 없음"
else:
why = f"엑셀 단가 × 매개값 = {_won(calc)} ≠ 엑셀 줄 금액 {_won(float(got))}"
diff = round(float(got) - calc, 1) if calc is not None and got is not None else None
name = one.get("이름") or ""
row = {"행": one.get("행"), "이름": name, "묶음": one["묶음"], "셈": calc, "금액": got}
lines.append({**row, "차": diff, "까닭": why})
total, have = unit_line.money(block.get("금액")), block.get("줄합")
cover = block.get("덮음")
if not lines and cover is not False:
return None
diff = round(float(have) - total, 1) if total is not None and have is not None else None
why = (
f"줄 합 {_won(float(have))} ≠ 엑셀 블록 금액 {_won(total)} — 엑셀 합이 줄을 뺐거나 더함"
if cover is False and diff is not None
else f"재현 못 한 줄 {len(lines)}"
)
return {"줄합": have, "금액": total, "차": diff, "덮음": cover, "줄": lines, "까닭": why}
def blocks(folder: Path, key: str, to: Path | None = None) -> list[dict[str, Any]]:
"""블록마다 `{번, 판정, SK, 갈래, 못옮김, 어긋남}` — `to` = 만든 SK 를 볼 층(없으면 파일 층)."""
folder, to = Path(folder), Path(to or folder)
hp = hopyo_store.read(folder, key)
here = refs.where(folder)
cache = (fit.result(folder, key) or {}).get("블록") or {}
have = sangeun.made(to, key, here)
all_, first = sangeun.calls(hp)
out = []
for no in sorted(all_):
line = first.get(no)
entry = cache.get(fit.unit_key(line)) if line else None
mark = sangeun.mark(key, no)
sk = have.get(mark)
origin = _origin(to, sk, mark, here) if sk else {}
out.append(
{
"번": no,
"판정": ((entry or {}).get("판정") or {}).get("상태"),
"SK": sk,
"갈래": origin.get("갈래"),
"못옮김": origin.get("못옮김") or [],
"어긋남": misses(all_[no]) if line else None,
}
)
return out