refactor(776-2): 엑셀 한 번 열기(격자 = 읽은 책) · 줄 갈래 한 규칙 · EST 블록 금액 · 항목 · 옛 판 HP 409 「다시 올림」
- Excel — read_book 이 연 책(xls = formatting_info · 목록 시트까지)을 Grid.read 가 이어 씀(opened) · 거창 여는 횟수 2 → 1 · 여덟 파일 올림 시간 17.8 → 7.8 초 - Excel.kind_of = 두 형식 · 붙여넣기 한 규칙(STmate 코드 머리 · EST 칸 · 끝에 노임 올림) · Stmate._kind 걷음 - Excel — EST 블록에 금액 · 항목(ReadEst) 실음 · Basis 대신 ReadEst 직접 - Grid — 읽은 책 그대로 · xlsx 열 폭은 보기 시트 머리 <cols> 만(sheetData 에서 멈춤) · 여덟 파일 격자 결과 전과 같음 - Store.read 다른 판 = 409 「다시 올림」 · Forget.finish 는 옛 판도 끝냄(old=True) - S1 · S2 해시 그대로 · test_718_1 옛 판 열기 409 · 끝내기 시험 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -29,11 +29,12 @@ import re
|
||||
from decimal import ROUND_FLOOR, Decimal, InvalidOperation
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Basis as hopyo_basis
|
||||
from M02_MasterTemplete import M02_Hopyo_ReadEst as hopyo_basis # EST 단가산출 어댑터(776-2)
|
||||
from M02_MasterTemplete import M02_Hopyo_Stmate as hopyo_stmate
|
||||
|
||||
SHEET = "일위대가"
|
||||
SUMMARY = "일위대가총괄표"
|
||||
LISTS = ("단가산출총괄표", "일위대가목록표") # 엑셀 보기 목록 시트(779-1 격자) — 같이 읽음
|
||||
COSTS = ("노무비", "재료비", "경비")
|
||||
HEADS = ("호표", "품명", "규격", "수량", "단위", "비고")
|
||||
NO_SHEET = "일위대가 시트 없음"
|
||||
@@ -42,6 +43,16 @@ _REF = re.compile(r"^(?:(단산|산근|단가산출)|(일위대가|일위|대가
|
||||
DRM = "문서 보안(DRM)으로 잠긴 파일 — 보안을 푼 사본으로 저장해 올림"
|
||||
_OLE = b"\xd0\xcf\x11\xe0"
|
||||
_MACHINE_UNITS = ("hr", "시간", "h")
|
||||
_CODE_KIND = {
|
||||
"D": "단가산출",
|
||||
"B": "호표",
|
||||
"L": "노임",
|
||||
"X": "기계",
|
||||
"S": "별도계상",
|
||||
"W": "별도계상",
|
||||
}
|
||||
# ponytail: 마지막 연 책 하나(올림 길 read_book → Grid.read 가 같은 바이트) — 여럿 동시면 다시 엶
|
||||
_OPENED: tuple[bytes, dict[str, list[list[Any]]], Any] | None = None
|
||||
|
||||
|
||||
class ExcelError(ValueError):
|
||||
@@ -82,6 +93,22 @@ def _out(value: Decimal | None) -> int | float | None:
|
||||
# ── 파일 → 시트 표(줄마다 값 목록) ────────────────────────────────────────
|
||||
def _sheets(name: str, data: bytes, wanted: tuple[str, ...] = ()) -> dict[str, list[list[Any]]]:
|
||||
"""시트 이름 → 줄 목록 — 일위대가 · 총괄표 · 단가산출만 읽음(`wanted` = 더 읽을 시트)."""
|
||||
return _open(name, data, wanted)[0]
|
||||
|
||||
|
||||
def opened(name: str, data: bytes, wanted: tuple[str, ...] = ()) -> tuple[dict[str, Any], Any]:
|
||||
"""(시트 줄, xls 책 | None) — 방금 `read_book` 이 연 같은 바이트면 그 책(한 번 열기 · 776-2) ·
|
||||
아니면 엶. xls 책 = xlrd `formatting_info`(격자 서식)."""
|
||||
global _OPENED
|
||||
got, _OPENED = _OPENED, None
|
||||
if got is not None and got[0] is data: # read_book 은 목록 시트(LISTS)까지 읽어 둠
|
||||
return got[1], got[2]
|
||||
return _open(name, data, wanted)
|
||||
|
||||
|
||||
def _open(
|
||||
name: str, data: bytes, wanted: tuple[str, ...] = ()
|
||||
) -> tuple[dict[str, list[list[Any]]], Any]:
|
||||
keep = (SHEET, SUMMARY, hopyo_basis.SHEET, *wanted)
|
||||
if not data:
|
||||
raise ExcelError("빈 파일")
|
||||
@@ -100,15 +127,21 @@ def _sheets(name: str, data: bytes, wanted: tuple[str, ...] = ()) -> dict[str, l
|
||||
if ws.title.strip() in keep
|
||||
}
|
||||
book.close()
|
||||
return out
|
||||
return out, None
|
||||
import xlrd
|
||||
|
||||
book = xlrd.open_workbook(file_contents=data, logfile=io.StringIO())
|
||||
return {
|
||||
try:
|
||||
book = xlrd.open_workbook(
|
||||
file_contents=data, formatting_info=True, logfile=io.StringIO()
|
||||
)
|
||||
except Exception: # 서식 표가 깨진 파일 — 값만(격자는 그때 서식 없이 다시 엶)
|
||||
book = xlrd.open_workbook(file_contents=data, logfile=io.StringIO())
|
||||
rows = {
|
||||
sh.name.strip(): [sh.row_values(r) for r in range(sh.nrows)]
|
||||
for sh in book.sheets()
|
||||
if sh.name.strip() in keep
|
||||
}
|
||||
return rows, book
|
||||
except ExcelError:
|
||||
raise
|
||||
except Exception as error: # 라이브러리마다 다른 예외 — 깨진 파일 · 엑셀 아님
|
||||
@@ -143,7 +176,29 @@ def _money(row: list[Any], cols: dict[str, int], offset: int) -> dict[str, Decim
|
||||
|
||||
# ── 갈래 · 검산 ─────────────────────────────────────────────────────────
|
||||
def kind_of(line: dict[str, Any]) -> str:
|
||||
"""구성 줄 갈래 — 단가산출 · 자재 · 기계 · 호표 · % · 별도계상 · 기타."""
|
||||
"""구성 줄 갈래 한 규칙(776-2 · EST · STmate · 붙여넣기) — 단가산출 · 자재 · 기계 · 노임 ·
|
||||
호표 · % · 별도계상 · 기타. STmate 코드가 있으면 코드 머리 글자 · 없으면 칸(비고 참조 ·
|
||||
단위 · 단가) · 끝에 노임 올림(단위 「인」 + 우리 직종 · 괄호 글 풂)."""
|
||||
kind = _code_kind(line) if line.get("코드") else _cell_kind(line)
|
||||
if kind in ("기타", "자재", "노임") and _flat(line.get("단위")) == "인":
|
||||
return "노임" if hopyo_basis.trade(line.get("이름")) else kind
|
||||
return kind
|
||||
|
||||
|
||||
def _code_kind(line: dict[str, Any]) -> str:
|
||||
code, price = str(line["코드"]), line["단가"]
|
||||
if code.startswith("PR") or _flat(line.get("단위")) == "%":
|
||||
return "%"
|
||||
if code[0] == "M":
|
||||
apart = str(line.get("비고") or "").startswith("별산")
|
||||
apart = apart or "별도계상" in f"{line.get('규격')}{line.get('이름')}"
|
||||
return "별도계상" if apart or not any(price.values()) else "자재"
|
||||
if code[0] == "D" and price.get("재료비") and not price.get("노무비") and not price.get("경비"):
|
||||
return "자재" # 재료비만 = 자재(358-2 · 엑셀꼴과 같음)
|
||||
return _CODE_KIND.get(code[0], "기타")
|
||||
|
||||
|
||||
def _cell_kind(line: dict[str, Any]) -> str:
|
||||
ref = line.get("참조") or {}
|
||||
unit = _flat(line.get("단위")).lower()
|
||||
price = line["단가"]
|
||||
@@ -241,7 +296,9 @@ def _by_name(raw: list[list[dict[str, Any]]], blocks: dict[int, dict[str, Any]])
|
||||
# ── 읽기 ──────────────────────────────────────────────────────────────
|
||||
def read_book(name: str, data: bytes) -> dict[str, Any]:
|
||||
"""엑셀 → `{시트, 호표: [...], 검산: {어긋남: [글 …], 총괄표: 맞춘 수}}` — 값은 JSON 수."""
|
||||
sheets = _sheets(name, data)
|
||||
global _OPENED
|
||||
sheets, xls = _open(name, data, LISTS)
|
||||
_OPENED = (data, sheets, xls) # 격자(`Grid.read`)가 이어 씀 — 한 번 열기
|
||||
if hopyo_stmate.is_stmate(sheets):
|
||||
return hopyo_stmate.read(sheets)
|
||||
rows = sheets.get(SHEET)
|
||||
@@ -306,7 +363,7 @@ def read_book(name: str, data: bytes) -> dict[str, Any]:
|
||||
{**_json(hopyo), "줄": [_json(line) for line in lines]}
|
||||
for hopyo, lines in zip(hopyos, raw, strict=True)
|
||||
]
|
||||
keep = ("번", "이름", "단위", "행", "출처", "직종", "글")
|
||||
keep = ("번", "이름", "단위", "행", "출처", "직종", "글", "금액", "항목")
|
||||
basis = [{k: b[k] for k in keep} for b in blocks.values()]
|
||||
return {
|
||||
"꼴": "엑셀꼴",
|
||||
|
||||
Reference in New Issue
Block a user