Files
Aislo/resources/tester/test_812_9_machine_link.py
T
eomsangdonandClaude Opus 5.5 bed35e3bc3 feat(814-1 3·4·6·7·8·9·10): 불러오기 결정 반영 — 세 줄 묶기 · 꼬리 뗀 맞대기 · 유가 · 재료 · 까닭 · 가벼움
- 3 연결 찾기 = 엑셀 이름 규격 · 단위 꼬리 뗀 이름으로 맞댐(`M02_Doc_Match_Note.bare` · 보이는 이름 그대로) · STmate 블록 규격도 가져온 칸에
- 4 기계 세 줄 = 기계 한 줄 — STmate X 코드 노 · 재 · 경 세 참조 줄(같은 수량 · 번지) 묶음(묶어서 셈 못 하는 세 줄만 그대로) ·
  문서 단계 묶기 넓힘(차례 아무렇게 · 시간당 경비 이음 없으면 엑셀 값 묶음 · 번지 같을 때) · 조합 다음 줄 = 세 줄 뒤 · 같은 기계 한 항목
- 6 공구손료 율 비목 차이(재료비 ↔ 경비) = 참고(`M02_Doc_Match_Value.tool_cost`) — 확정 안 막음
- 7 못 이은 줄 까닭(`M02_San_Link.why` — 자재 없음 · 운임표 없음 · 기계 표에 없음 · 별칭 대기 · 후보 여럿 · 시간당 경비 없음 · 조합) =
  문서 설명 · 이음 없는 자원 띠 · 연결 「없음」 띠 = 「우리 기초 없음 — 우리 정보로 셈 n/m 줄」 · 형제만 = 「조합」
- 8 유가 = 전국평균 줄 + 지역 옵션(`MO:경유:{S_지역}`) · 전력 = 일반용(갑)Ⅰ 저압 + 계약종별 옵션 · 엑셀 값 = 검산 칸
- 9 재료 = 지역 꼬리 뗀 이름 · 규격 정확 일치면 이음(지역 여럿 = 줄 지역 › 서울)
- 10 조합 첫 줄 규격 = 그 기계 · 얼린 줄도 소계 식에(북부청임도 33) · 기계 줄 찾기 판정 늘 붙음 · 올린 직후 409(HP 보이기 전 산근 보관) ·
  엑셀 블록 「엑셀 읽기 확정」 글 · 전수 도우미 str() 뗌 · 줄 쌓기 이름에 규격이 들면 한 번만
- M02_San_Link 새 파일(San_Emit 700 줄 막음 — 요소 잇기 · 시간당 · 조합 · 까닭)
- 시험: 새 test_814_1 · 812-2 · 810-4 거푸집 41 = 근접 → 확정(공구손료 비목 = 값 · 요구 744 C1) · 바꾸기 거절 견본 = 19 ·
  812-9 조합 첫 줄 규격 「0.7+브레이카」 → 「0.7」(칩 글 · 요구 738 흠 12)

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

219 lines
13 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.
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
"""812-9 지킴 — 가져온 기계 잇기(`M02_Machine_Find` · 요구 718 · 719).
(a) 기계표 전체에서 찾음 — 낱말표 밖 기종(로우더 · 공기 압축기 · 램 머 · 라인마커 …) · 후보 여럿이던 것
(물탱크 · 크레인(무한궤도) · 머캐덤 · 콘크리트 펌프차 …) · 지어 맞추기 없음(1.0Ton = 우리 표에 없음)
(b) 표기 정규화 · 괄호 낱말(무한궤도 · 타이어 · 일반용 · 표에 없는 낱말 발파 = 안 봄) · 범위 · 복수 규격
(c) 조합 기계 = 둘(대형브레이카 +굴착기 · 0.7+브레이카 · +부착용집게 · +진동콤팩터 · 덤프트럭(자동덮개))
(d) 별칭 먼저 — 지금 층 장부 기계 짝 = 그 EQ · 이름만 열쇠 = 그 기종으로 · 후보 여럿 표지 ·
[저장] 때 고른 EQ = 지금 층 장부 줄(`learn`)
(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_Hopyo_Ledger as ledger # noqa: E402
from M02_MasterTemplete import M02_Machine_Find as mf # noqa: E402
from M02_MasterTemplete import M02_MasterTemplete_Store as store # noqa: E402
from resources.tester.test_m02_labor_ref import world # noqa: E402, F401 — 같은 임시 층
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"),
("디젤 엔진", "11.19kW", "엔진(디젤)", "기종 디젤 엔진 · 규격 11.19㎾"),
("물탱크", "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"),
]
@pytest.mark.parametrize("name,spec,eq", MASTER)
def test_master_alias(name: str, spec: str, eq: str) -> None:
got = mf.find(name, spec)
assert got["키"] == eq and got["판정"] in (mf.LINKED, mf.ALIAS)
def test_no_guess() -> None:
"""우리 표에 없는 규격 = 못 찾음(가까운 규격으로 안 이음) · 기종 모름 = 별칭 몫 · 여럿 = 여럿."""
got = mf.find("덤프트럭", "1.0Ton")
assert got["판정"] == mf.NONE and got["까닭"] == "우리 표에 없음(규격)" and got["키"] is None
assert got["후보"][0]["규격"] == "2.5ton" # 보여주기만 — 가까운 규격 먼저
for name, spec in (("에어 호스", "1.91cm"), ("모노펌프", "5-20㎥/hr")): # 승인 전 = 별칭 몫
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("덤프트럭", "1.0Ton"), "덤프트럭", "1.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 _pair(eq: str, name: str, spec: str, unit: str = "hr") -> dict:
full = ledger.key({"이름": name, "규격": spec, "단위": unit})
row = {"원래": {"품명": name, "규격": spec, "단위": unit}, "갈래": "짝"}
return {full: row | {"짝": [{"갈래": "기계", "키": eq, "입력값": {}, "배": 1}]}}
def test_alias_first_and_learn(world) -> None: # noqa: F811
me = world["personal"]
me.mkdir(parents=True, exist_ok=True)
with store.at(me):
assert mf.find("아스팔트페이버(피니셔)", "3.0m")["판정"] == mf.NONE
ledger.write(me, _pair("EQ000193", "아스팔트페이버(피니셔)", ""), {"id": "sub3임시"})
got = mf.find("아스팔트페이버(피니셔)", "3.0m") # 이름만 열쇠 = 그 기종 → 규격으로 하나
assert (got["판정"], got["후보"][0]["이름"], got["후보"][0]["규격"]) == (
mf.ALIAS, "아스팔트 피니셔", "3m") # fmt: skip
ledger.write(me, _pair("EQ000129", "크레인(트럭)", "15Ton"), {"id": "sub3임시"})
got = mf.find("크레인(트럭)", "15Ton") # 이름 + 규격 열쇠 = 그 EQ(찾기보다 먼저)
assert (got["판정"], got["키"]) == (mf.ALIAS, "EQ000129")
assert (
mf.find("크레인(트럭)", "15Ton(할증125%)")["키"] == "EQ000129"
) # 규격 속 할증 글 = 안 봄
assert mf.same_kind("아스팔트페이버(피니셔)", "아스팔트 피니셔") is True
doc = {
"변수": [{"열쇠": "P_크", "기호": "P", "아래": "크", "연결": {
"갈래": "단가산출", "키": "GC000270", "입력": {"기계": "'2104-0010'"}}}],
"가져온": {"기계": [{"기호": "P_크", "판정": mf.MANY, "키": None,
"원래": {"이름": "크레인(트럭)", "규격": "10Ton", "단위": "hr"}}]},
} # fmt: skip
assert mf.learn(doc, {"id": "sub3임시"}) == 1
got = mf.find("크레인(트럭)", "10Ton")
assert got["판정"] == mf.ALIAS and got["후보"][0]["원문번호"] == "2104-0010"
assert mf.learn(doc, {"id": "sub3임시"}) == 0 # 이미 같은 EQ = 안 씀
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["브레이카"]["기호"]