"""재료 자재품목 열 한 벌 · 상하차(요구 828 · 830 · 831 · 834 · 836 · 837 · PLAN 830-9). build_재료_자재품목 이 부름 · 시험 = test_830_2_material_merge. 줄 목록 = EST 그대로(지역 · 관급 · 사급 · 제조사 · 조합 글이 든 원래 이름 · 줄마다 따로) — 합치지 않음. 열 한 벌 = COLUMNS — 상하차 = 단위 바로 뒤 · 가격 열 = 조달청(관급) · 물가지 넷 · 일반가격(사급). 사급 / 관급 고르기는 재료 표 열이 아님 — 단가산출 쪽 몫(요구 837). 상하차 칸 = 상차 · 하차 · -(기본 · 모름) — 이름 · 규격 끝 인도 조건 글로 채우고 그 글은 이름 · 규격에서 뺌(요구 836) 「도착도」 · 「하차도」 = 하차 · 「채취장상차(도)」 · 「하치장 · 생산공장 상차도」 · 「(상차도)」 = 상차 조달청 칸 값은 모두 비움(EST 「가격정보」 — 요구 827 · 831) · 일반가격 = 시스템 관리자가 넣는 칸. 상하차 · 일반가격 손 값은 빌더가 이어 받음. """ import re MARKET = ("물가자료", "유통물가", "물가정보", "거래가격") LOAD = "상하차" # 표지 칸(값 칸 아님) LOADS = ("-", "상차", "하차") # 첫째 = 기본(모름) GOVT = "조달청" # 관급 가격 칸 — 가장 낮은 값 고르기에 안 넣음(요구 826 · 830) OWN = "일반가격" # 시스템 관리자 손 입력 칸(사급) HAND = (LOAD, OWN) # 빌더가 이어 받는 손 값 칸 COLUMNS = ( # 열 한 벌 · 차례(요구 830 · 831) "키", "구분", "상세구분", "이름", "규격", "단위", LOAD, GOVT, *MARKET, OWN, "출처", "비고", "면수", ) NAME_LOAD = re.compile(r",\s*(도착도|하차도|채취장상차도|채취장상차)$") # 모 래, 서울, 도착도 SPEC_LOAD = re.compile( r"\s*(?:\((상차도)\)|((?:하치장|생산공장)\s*상차도))$" ) # HD=10mm 하치장 상차도 def split_load(row: dict) -> tuple[str, str, str, str | None]: """(인도 조건 글을 뺀 이름, 규격, 상하차 값, 뺀 글) — 도착 · 하차 = 하차 · 상차 = 상차 · 글 없음 = -.""" name, spec = row.get("이름") or "", row.get("규격") or "" m = NAME_LOAD.search(name) if m: name, text = name[: m.start()], m[1] else: m = SPEC_LOAD.search(spec) if not m: return name, spec, LOADS[0], None spec, text = spec[: m.start()], m[1] or m[2] return name, spec, "하차" if text.startswith(("도착", "하차")) else "상차", text def place(row: dict) -> dict: """COLUMNS 차례의 줄 — 첫 칸(열쇠 · 키)은 그대로 · 인도 조건 글은 상하차 칸으로 · 손 값(상하차)이 있으면 그 값 · 조달청 = 빔 · 옛 칸(관급)은 뺌.""" name, spec, load, _ = split_load(row) first = next(iter(row)) fix = { "이름": name, "규격": spec, LOAD: row.get(LOAD) or load, GOVT: None, } return {first: row[first], **{c: fix[c] if c in fix else row.get(c) for c in COLUMNS[1:]}}