Files
Aislo/resources/tester/test_812_9_machine_link.py
T
eomsangdonandClaude Opus 5.5 0392cd12ad feat(820-1): 품셈에 없는 소형·보조 기계 13 줄 · 파워트로웰 기계경비식 GC001195 · 가격 = EST 씨앗(갱신 필요)
- 기계.json EQ000656~668 더함 — 발전기 5.5·7·10㎾(손료계수 2362 발전기 계열) · 대형 브레이커 0.18㎥(0.2㎥ 계수·가격) · 에어 호스 1.91㎝ · 모노펌프 5-20㎥/hr · 파워트로웰 3.73㎾ · 콘크리트펌프용 파이프 여섯($ × 환율 FX000001 · 비고에 $ 값)
- 원문번호 「자체 이름 규격」 · 출처 가격 = 자체 · 비고 = EST 단가 구성 → 우리 줄 · 가격 갱신 필요
- 계산식 — 손료만 = GC000273 · 발전기 5.5 = GC000268 고르기 끝에 더함(814-1 빌더 · 로직 + 수식 문서) · 파워트로웰 = 새 GC001195(휘발유 + 잡품(주연료 줄 %) + 전력 + 손료)
- 발전기 7·10㎾ = 2016 사방품셈(안) 기계경비 가격 · 그 표대로 손료만
- M02_Machine_Hourly 시간당 부름에서 빈칸 든 입력 이름(전력 계약종별) 따옴표 — 파워트로웰 시간당 이음
- 기술정보 기계경비_산정 7 절(표)
- 낡은 시험 고침: 812_9 test_no_guess · 815_1 test_alias_machine 이 「에어 호스 · 모노펌프 = 우리 표에 없음」 예로 씀 → 아직 없는 기종(진공흡입기 · 스키드로더)으로
- 기존 수식 문서 1,514 기본 금액 전후 같음 · check_master 전후 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 22:51:49 +09:00

208 lines
12 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.
"""812-9 지킴 — 가져온 기계 잇기(`M02_Machine_Find` · 요구 718 · 719).
(a) 기계표 전체에서 찾음 — 낱말표 밖 기종(로우더 · 공기 압축기 · 램 머 · 라인마커 …) · 후보 여럿이던 것
(물탱크 · 크레인(무한궤도) · 머캐덤 · 콘크리트 펌프차 …) · 지어 맞추기 없음(3.0Ton = 우리 표에 없음)
(b) 표기 정규화 · 괄호 낱말(무한궤도 · 타이어 · 일반용 · 표에 없는 낱말 발파 = 안 봄) · 범위 · 복수 규격
(c) 조합 기계 = 둘(대형브레이카 +굴착기 · 0.7+브레이카 · +부착용집게 · +진동콤팩터 · 덤프트럭(자동덮개))
(d) 별칭 먼저 — M01 기계 줄 「별칭」 칸(815-1 · 시험 안 임시 별칭) · 이름 + 규격 통째 = 그 EQ · 이름만 =
그 기종으로 · 후보 여럿 표지(옛 층 장부 쓰기 `learn` = 815-2 장부 걷기로 지움)
(e) 금액 전후 같음 — 조합 줄 둘(첫 줄 엑셀 값 · 다음 줄 0) · 원파일 저장 금액 그대로
"""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
HERE = Path(__file__).resolve().parent
for p in (ROOT, HERE):
if str(p) not in sys.path:
sys.path.insert(0, str(p))
from M02_MasterTemplete import M02_Elem_Alias as ea # noqa: E402
from M02_MasterTemplete import M02_Machine_Find as mf # noqa: E402
from test_815_1_import_link import aliases, no_aliases # noqa: E402 — M01 별칭 칸 임시
@pytest.fixture(autouse=True)
def _blank_aliases():
"""진짜 마스터 기계 별칭 내용 · 수와 무관 — 별칭이 필요한 시험은 스스로 넣음."""
with no_aliases("EQ"):
yield
def one(name: str, spec: str = "") -> tuple[str, str, str]:
got = mf.find(name, spec)
first = got["후보"][0] if got["후보"] else {}
return got["판정"], str(first.get("이름") or ""), str(first.get("규격") or "")
LINKED = [ # 낱말 모름 · 후보 여럿이던 예(812-8) → 하나
("로우더(타이어)", "0.57㎥", "로더(타이어)", "0.57㎥"),
("로우더(타이어)", "1.72㎥", "로더(타이어)", "1.72㎥"),
("공기 압축기", "21.0㎥/min", "공기압축기(이동식)", "21.0㎥/min"),
("공기 압축기", "7.1㎥/min", "공기압축기(이동식)", "7.1㎥/min"),
("라인마커", "10Km/hr", "라인 마커", "10km/hr"),
("램 머", "80kg", "래 머", "80㎏"),
("진동파일해머", "60kW", "진동파일 해머(전동식)", "60㎾"),
("진동파일해머", "45kW", "진동파일 해머(전동식)", "45㎾"),
("진동파일해머", "40kW", "진동파일 해머(전동식)", "40㎾"),
("노면파쇄기", "2.0m", "노면파쇄기", "2.0m"),
("아스팔트 디스트리뷰터", "3800ℓ", "아스팔트 디스트리뷰터", "3,800ℓ"),
("고소작업차", "3Ton", "고소작업차", "3ton"),
("고소작업차", "5Ton", "고소작업차", "5ton"),
("취부기(녹생토)", "18.65kW", "취부기(녹생토 암절개면 보호식재용)", "18.65㎾"),
("아스팔트 스프레이어", "400ℓ", "아스팔트 스프레이어", "400ℓ"),
("커 터", "320~400mm", "커터(콘크리트 및 아스팔트용)", "320~400"),
("경운기", "1000kg", "경운기", "1,000㎏"),
("건설용펌프(자흡식)", "50mm", "건설용펌프(자흡식)", "50(1.49×10)"),
("강연선인장기", "120Ton", "강연선인장기", "120ton"),
("물탱크", "5500ℓ", "물탱크(살수차)", "5,500ℓ"),
("물탱크", "16000ℓ", "물탱크(살수차)", "16,000ℓ"),
("콘크리트 펌프차", "32m,80~95㎥/hr", "콘크리트 펌프차", "32 [80∼95]"),
("머캐덤 로울러(자주식)", "10~12Ton", "머캐덤 롤러(자주식)", "10∼12"),
("타이어 로울러(자주식)", "8~15Ton", "타이어 롤러(자주식)", "8∼15"),
("크레인(무한궤도)", "40Ton", "크레인(무한궤도)", "40 (1.53)"),
("크레인(무한궤도)", "35Ton", "크레인(무한궤도)", "35 (1.33)"),
("크로울러 드릴(공기식)", "17㎥/min", "크롤러드릴(공기식)", "17 (120㎜)"),
("모터 그레이더", "3.6m(일반용)", "모터그레이더(일반용)", "3.6m"),
("콘크리트 믹서", "0.3㎥", "콘크리트 믹서", "0.30㎥"),
("진동 로울러(자주식)", "12Ton", "진동롤러(자주식)", "12ton"),
("굴삭기(무한궤도)", "0.7㎥", "굴착기(무한궤도)", "0.7㎥"),
("굴착기(타이어형)", "0.6㎥", "굴착기(타이어)", "0.6㎥"),
("굴착기(무한궤도)", "0.4㎥(발파)", "굴착기(무한궤도)", "0.4㎥"),
("불도우저(무한궤도)", "32Ton", "불도저(무한궤도)", "32ton"),
]
@pytest.mark.parametrize("name,spec,our,size", LINKED)
def test_linked(name: str, spec: str, our: str, size: str) -> None:
kind, *got = one(name, spec)
assert kind in (mf.LINKED, mf.ALIAS) and got == [our, size] # 승인 별칭(디젤 엔진) = 별칭
MASTER = [ # 마스터 층 승인 별칭(요구 735-5 · 736) — 이름 별칭 + 규격은 수로 맞댐
("아스팔트페이버(피니셔)", "3.0m", "EQ000193"),
("아스팔트페이버(피니셔)", "1.7m", "EQ000192"),
("디젤 엔진", "11.19kW", "EQ000500"),
("디젤 엔진", "26.11kW", "EQ000503"),
("디젤 엔진", "52.22kW", "EQ000504"),
("워터제트", "96kW", "EQ000400"),
("버킷준설기", "7.46kW", "EQ000425"),
("바이브레이트", "봉상후렉시블", "EQ000248"),
]
# 마스터 기계 별칭 다섯(815-2 가 기계 줄 「별칭」 칸으로 옮김) — 칸이 비었으면 시험 동안만 넣음
FIVE = {"EQ000193": "아스팔트페이버(피니셔)", "EQ000500": "디젤 엔진", "EQ000400": "워터제트",
"EQ000425": "버킷준설기", "EQ000248": '"바이브레이트 봉상후렉시블"'} # fmt: skip
@pytest.mark.parametrize("name,spec,eq", MASTER)
def test_master_alias(name: str, spec: str, eq: str) -> None:
rows = ea.book()["줄"]["EQ"]
with aliases("EQ", {k: v for k, v in FIVE.items() if not rows[k].get("별칭")}):
got = mf.find(name, spec)
assert got["키"] == eq and got["판정"] in (mf.LINKED, mf.ALIAS)
def test_no_guess() -> None:
"""우리 표에 없는 규격 = 못 찾음(가까운 규격으로 안 이음) · 기종 모름 = 별칭 몫 · 여럿 = 여럿."""
got = mf.find("덤프트럭", "3.0Ton")
assert got["판정"] == mf.NONE and got["까닭"] == "우리 표에 없음(규격)" and got["키"] is None
assert got["후보"][0]["규격"] == "2.5ton" # 보여주기만 — 가까운 규격 먼저
for name, spec in (
("진공흡입기", "5kW"),
("스키드로더", "0.3㎥"),
): # 우리 표에 없는 기종 = 별칭 몫
got = mf.find(name, spec)
assert (got["판정"], got["까닭"]) == (mf.NONE, "기종 모름 — 별칭")
got = mf.find("크레인(트럭)", "10Ton") # 「트럭」 = 기계표 괄호에 없는 낱말 → 안 봄
assert got["판정"] == mf.MANY and got["수"] == 2
assert mf.mark(got, "크레인(트럭)", "10Ton").startswith("참고 · 기계 후보 2 — 고르기")
assert "기계 못 찾음" in mf.mark(mf.find("덤프트럭", "3.0Ton"), "덤프트럭", "3.0Ton")
assert mf.mark(mf.find("물탱크", "5500ℓ"), "물탱크", "5500ℓ") is None
def test_norm_paren_spec() -> None:
assert mf.norm("로우더") == mf.norm("로더") and mf.norm("로울러") == mf.norm("롤러")
assert mf.norm("브레이카") == mf.norm("브레이커") and mf.norm("굴삭기") == mf.norm("굴착기")
assert mf.norm("불도우저") == mf.norm("불도저") and mf.norm("램 머") == mf.norm("래 머")
assert mf.tokens("10~12Ton") == [(10.0, 12.0, "ton")]
assert mf.tokens("32m,80~95㎥/hr") == [(32.0, 32.0, "m"), (80.0, 95.0, "m3/hr")]
assert mf.tokens("5,500ℓ") == [(5500.0, 5500.0, "l")]
assert mf.spec_fit(mf.tokens("0.7㎥"), mf.tokens("0.6∼0.8")) == "안"
assert one("굴착기(타이어)", "0.6㎥")[1] == "굴착기(타이어)" # 괄호 낱말이 가름
assert one("굴착기(무한궤도)", "0.6㎥")[1] == "굴착기(무한궤도)"
COMBO = [ # 조합 기계 = 둘 · 규격 없는 쪽 = 앞 규격
("굴삭기(무한궤도)", "0.7+브레이카", [("굴착기(무한궤도)", "0.7㎥"), ("대형 브레이커", "0.7㎥")]),
("대형브레이카", "+굴착기(0.7㎥)", [("대형 브레이커", "0.7㎥"), ("굴착기(무한궤도)", "0.7㎥")]),
("대형브레이카", "+굴착기(0.4㎥)", [("대형 브레이커", "0.4㎥"), ("굴착기(무한궤도)", "0.4㎥")]),
("굴삭기+진동콤팩터", "0.7㎥", [("굴착기(무한궤도)", "0.7㎥"),
("유압식 진동콤팩터(굴착기 부착용)", "0.7㎥")]),
("굴삭기(무한궤도)", "0.2㎥+부착용집게", [("굴착기(무한궤도)", "0.2㎥"), ("부착용 집게", "0.2㎥")]),
("굴삭기(무한궤도)", "0.6㎥+부착용집게", [("굴착기(무한궤도)", "0.6㎥"), ("부착용 집게", "0.6∼0.8")]),
("로우더(타이어)+소형노면파쇄기", "0.95㎥", [("로더(타이어)", "0.95㎥"), ("소형노면파쇄기", "0.95㎥")]),
("덤프트럭(자동덮개)", "15Ton", [("덤프트럭", "15ton"), ("덤프트럭 자동덮개시설", "15톤용")]),
("굴삭기(무한궤도)", "0.7㎥,(브레이커조합)", [("굴착기(무한궤도)", "0.7㎥"), ("대형 브레이커", "0.7㎥")]),
] # fmt: skip
@pytest.mark.parametrize("name,spec,want", COMBO)
def test_combo_two(name: str, spec: str, want: list) -> None:
got = mf.find_all(name, spec)
assert [g["판정"] for g in got] == [mf.LINKED, mf.LINKED]
assert [(g["후보"][0]["이름"], g["후보"][0]["규격"]) for g in got] == want
def test_combo_many_part() -> None:
"""대형브레이카 +굴착기(1.0㎥) — 굴착기 무한궤도 · 타이어 둘 다 1.0 = 그 쪽만 후보 여럿(사용자 고름)."""
got = mf.find_all("대형브레이카", "+굴착기(1.0㎥)")
assert [g["판정"] for g in got] == [mf.LINKED, mf.MANY]
assert mf.find_all("덤프트럭", "15Ton") == [{**mf.find("덤프트럭", "15Ton"), "이름": "덤프트럭", "규격": "15Ton"}] # fmt: skip
def test_alias_first_master_column() -> None:
"""마스터 기계 줄 「별칭」 칸(815-2 옮김) — 이름만 별칭 = 그 기종 → 규격으로 하나 ·
통째 별칭 · 할증 글 · 후보 여럿 = `test_815_1_import_link.test_alias_machine`."""
with aliases("EQ", FIVE): # 다섯만 넣음(더 있어도 비워져 있음)
got = mf.find("아스팔트페이버(피니셔)", "3.0m")
assert (got["판정"], got["후보"][0]["이름"], got["후보"][0]["규격"]) == (
mf.ALIAS, "아스팔트 피니셔", "3m") # fmt: skip
assert mf.find("크레인(트럭)", "10Ton")["판정"] == mf.MANY # 크레인 별칭은 비움 · 여럿
def test_combo_money_same() -> None:
"""원파일 거창 측구터파기 연암 — 「굴삭기(무한궤도) 0.7+브레이카」 = 줄 둘 · 금액 = 원파일 저장 그대로."""
from test_810_2_native_doc import doc_and_money, native
from M02_MasterTemplete import M02_Doc_FromNative as fn
saved = native("EST", "거창")
blk = fn.numbers(saved)[2]
sk, doc, money = doc_and_money(fn.emit(saved, 2))
names = [(m["이름"], m.get("규격")) for m in doc["금액"]]
assert ("굴삭기(무한궤도)", "0.7") in names and (
"대형 브레이커",
"0.7㎥",
) in names # 첫 줄 규격 = 그 기계(814-1)
assert float(money["계"]) == pytest.approx(float(blk["저장"]["T"]), abs=1)
found = {x["원래"]["이름"]: x for x in doc["가져온"]["기계"]}
assert found["브레이카"]["조합"] is True and found["브레이카"]["찾기어"] == "대형 브레이커"
assert any("조합 기계 — 경비는 한 덩이" in m for m in doc["가져온"]["표지"])
from M02_MasterTemplete import M02_Doc_NativeLayout as lay
from test_812_3_native_layout import _show
groups = [(g[0], g[2]) for g in _show(doc)] # 조합 기계 = 기계마다 항목(요구 738-3)
assert ("굴착기(무한궤도) 0.7㎥", ["=Q", "굴삭기(무한궤도)"]) in groups
assert ("대형 브레이커 0.7㎥", ["대형 브레이커"]) in groups
owned = lay.owned_of(doc) # 못 이은 기계 줄(시간당 자료 없음 = 숫자 묶음)도 기계 = 이름 단추
at = next(i for i, m in enumerate(doc["금액"]) if m["이름"] == "대형 브레이커")
assert owned.get(at) == found["브레이카"]["기호"]