Files
Aislo/resources/tester/test_797_1a_import_flags.py
T
eomsangdonandClaude Opus 5.5 61bb15fbde refactor(812-1): 옛 엑셀 엔진 걷기 2 · 실무 11 금액 100 % · NativeEst 자리 dict 묶음 고침 · 옛 시험 정리
- M02_Doc_NativeEst.triple: 세 줄 수량 식이 같은지 = repr 로 맞댐(자리 규칙 dict 가 set 에 안 들어가 TypeError — 엑셀 줄은 보인 값이 반올림 자리를 타 원파일에 없던 꼴 · 812-3 끝나 브레인이 맡김)
- 엑셀 읽기기: 절 소계 덧셈이 뺀 오타 줄 = 비목 없음(블록 금액도 그래야 맞을 때만) · 세 줄 계 금액 칸 비목 하나 = 옮김 · 소계 61 = 금액 칸 비목 하나 · 「.1」 수 · 총괄표 당초 칸 표지 · STmate 엑셀 줄 금액 · 계 꼬리(검산 줄)
- 화면: 가져온 식 규격 칸 꼬리표 = 원파일 · 엑셀 갈림(line_stack_sourced)
- 시험: 793_1 지움(옛 EST · STmate 엑셀 글 추정 엔진 · 812_1 골든이 대신) · 793_2_san_emit EST 시험 지움(STmate 남김) · 797_1a 옛 표지 ① ② · 줄 단위 합격 · 표본 밖 수 · 버림1 감시 지움(옛 엔진) · 810_1 (c) 엑셀 엔진 대조 지움 · 793_2 실무 블록 · 793_3b 끝전 = 새 길 · 812_1 금액 엇갈림 없음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 08:38:12 +09:00

132 lines
6.6 KiB
Python

"""797-1a 지킴 — 엑셀 불러오기 빠진 정보 표지(요구 658) · 812-1 뒤 남은 것.
③ 당초 / 적용 칸(`M02_San_Book.applied_how` → 엑셀 읽기기 블록 표지 → 원본 표지) ④ 조정 비율 · 낙찰률
미지원(`M02_San_Emit.stmate`) ⑤ 읽기 불가 사유(`M02_Sangeun_FromHopyo.keep`) — 작은 견본(엑셀 없이).
옛 EST 글 추정 표지(① 금액 줄 못 읽음 · ② 단가 후보 여럿) · 줄 단위 합격 · 표본 밖 수 · 버림1 감시는
옛 엔진과 함께 걷음 — 812-1 `test_812_1_xlsx_golden`(실제 짝 · 합성 · 실무 11 금액)이 지킴.
`FILES`(실무 11) = 812-1 시험이 씀.
"""
from __future__ import annotations
from pathlib import Path
import helper_m02_hopyo_golden as golden
from M02_MasterTemplete import M02_Doc_FromNative as fn
from M02_MasterTemplete import M02_San_Book as book
from M02_MasterTemplete import M02_San_Emit as em
from M02_MasterTemplete import M02_San_Stmate as stm
from M02_MasterTemplete import M02_San_Xlsx as sx
from M02_MasterTemplete import M02_Sangeun_FromHopyo as fh
NOTE = em.NOTE
DOCS = golden.DOCS
def notes(out: dict) -> list[str]:
return [m for m in out["원본"]["표지"] if NOTE in m]
def hard(out: dict) -> list[str]:
"""못 옮김 표지(참고 표지 뺌)."""
return [m for m in out["원본"]["표지"] if NOTE not in m]
# ── ③ 당초 / 적용 칸 ───────────────────────────────────────────────────
def _row(applied: tuple, first: tuple) -> list:
r = [None] * 29
for j, v in zip((5, 6, 7, 8), applied, strict=True):
r[j] = v
for j, v in zip((25, 26, 27, 28), first, strict=True):
r[j] = v
return r
def test_applied_or_first_column_is_flagged() -> None:
same = book.applied_how(_row((100, 40, 30, 30), (100, 40, 30, 30)))
assert same == ({"T": 100, "L": 40, "M": 30, "S": 30}, None)
diff = book.applied_how(_row((100, 40, 30, 30), (90, 40, 30, 20)))
assert diff[0]["T"] == 100
assert diff[1] == "적용 칸 사용 — 당초와 다름 (계 적용 100 · 당초 90 · 경비 적용 30 · 당초 20)"
empty = book.applied_how(_row((None, None, None, None), (90, 40, 30, 20)))
assert empty[0] == {"T": 90, "L": 40, "M": 30, "S": 20}
assert empty[1] == "당초 칸 사용 — 적용 칸 비어 있음"
sums = book.applied_how(_row((100, 40, 30, 10), (90, 40, 30, 20)))
assert sums[0]["T"] == 90
assert (
sums[1]
== "당초 칸 사용 — 적용 칸 합이 안 맞음 (계 적용 100 · 당초 90 · 경비 적용 10 · 당초 20)"
)
nothing = book.applied_how(_row((None,) * 4, (0, 0, 0, 0)))
assert nothing[1] is None # 쓸 값이 없는 줄
assert book.applied(_row((100, 40, 30, 30), (90, 40, 30, 20)))["T"] == 100
def test_first_column_note_reaches_block_flags() -> None:
head = [[None] * 29 for _ in range(3)]
total = [*head, [None, "1", "견본", "", "m"] + [None] * 24]
total[2][1:5] = ["No", "품명", "규격", "단위"]
total[3][25:29] = [90, 40, 30, 20]
sheet = [[None, "단가산출"], [], [None, "산출근거", "합계", "노무비", "재료비", "경비"]]
sheet += [
[None, "1.견본[m]", 90, "40", "30", "20"],
[None, " 계 : 90", 90, "40", "30", "20"],
]
got = sx.read_sheets({"단가산출": sheet, "단가산출총괄표": total})
out = fn.emit(fn.pack(got, "견본.xls"), 1)
assert f"{NOTE}당초 칸 사용 — 적용 칸 비어 있음" in out["원본"]["표지"]
assert out["원본"]["엑셀"] is True
# ── ④ 조정 비율 · 낙찰률 미지원 ────────────────────────────────────────
def _stmate_out(meta: dict) -> dict:
prices = {
"L00007": {"T": 100, "L": 100, "M": 0, "S": 0, "meta": {}, "이름": "보통인부"},
"D00001": {"T": 0, "L": 0, "M": 0, "S": 0, "meta": meta, "단위": "식"},
}
rows = [{"행": 0, "G": "'보통인부' &L00007& * 1 :=A01"}]
block = {"코드": "D00001", "이름": "견본", "꼬리": [], "몸": rows}
block["머리"] = {"A": "", "B": "", "C": 0, "D": 0, "E": 0, "F": 0}
return em.stmate(block, stm.parse_block(rows), prices, "비목버림")
def test_adjust_ratio_and_bid_rate_are_flagged() -> None:
base = {"ADJ": "T", "SBM": " ", "ADJL": "1", "ADJM": "1", "ADJS": "1", "UC": "F", "NAG": "0"}
assert _stmate_out(base)["원본"]["표지"] == [] # 비율 1 · 낙찰률 없음 = 표지 없음
odd = _stmate_out({**base, "ADJL": "0.9"})["원본"]["표지"]
assert odd == [f"{NOTE}조정 비율 미지원 · 사람 확인 (노무비 0.9)"]
sbm = _stmate_out({**base, "SBM": "S", "ADJL": "0.9"})
assert sbm["로직"]["조정"] == {"옮김": "경비"} and sbm["원본"]["표지"] == []
bid = _stmate_out({**base, "UC": "F", "NAG": "88.5"})["원본"]["표지"]
assert bid == [f"{NOTE}낙찰률 88.5 있으나 계약단가 아님(UC) — 낙찰률 미적용 · 사람 확인"]
used = _stmate_out({**base, "UC": "T", "NAG": "88.5"})
assert used["원본"]["표지"] == [] and used["원본"]["계약"]["낙찰률"] == 88.5
# ── ⑤ 읽기 불가 사유 ───────────────────────────────────────────────────
def test_unreadable_reason_is_returned(tmp_path: Path) -> None:
def keep(name: str, data: bytes) -> str | None:
return fh.keep(tmp_path, "HP990001", name, data)
assert keep("a.xlsx", b"") == "빈 파일"
assert keep("a.xlsx", b"\x9b DRMONE\x00\x01") == "DRM 암호 파일 — 암호 풀린 사본을 올림"
assert (keep("a.xlsx", b"PK\x03\x04 not a zip") or "").startswith("엑셀 형식 오류")
assert (keep("a.xls", b"plain text") or "").startswith("엑셀 형식 오류")
assert not fh.has(tmp_path, "HP990001") # 못 읽은 파일은 안 둠
# ── 실무 엑셀 11(812-1 시험이 씀) ──────────────────────────────────────
def _oos() -> dict[str, Path]:
pack = DOCS / "2026_실무일위대가 모음"
common = DOCS / "2024년 간선임도(기번3-울진.대흥)(공통)/1. 설계원본/01 내역자료"
found = {
"2016사방": next(pack.glob("2016*.xls"), None),
"2026수목": next(pack.glob("2026_상반*.xls"), None),
"대흥공통": next((p for p in common.glob("*.xlsx") if not p.name.startswith("~$")), None),
}
return {k: v for k, v in found.items() if v}
FILES = {**golden.FILES, **_oos()}