- Basis.merge 같은 점수는 이름 겹침 큰 쪽 · 이름 · 낱말(괄호 글 포함 · 인력 · 기계 · 장비 뺌) 안 겹치는 근거 후보는 근거 맞음(조항 · 직종 · 단위)이 아니면 겹치는 근거 후보 뒤로 - Basis.pick 다섯 안에 이름 1순위 하나 늘 남김 · Match match_line 이 씀 - 시험 basis(제3호표 구조물터파기 다섯 = 굴착(인력/토사) 1위 · 육상토사 안 · 터널굴착 밖 · pick) · match(이름 1순위 자리) · grid(고르기 줄 층 · 주인 그대로 회사 · 시스템 격자 읽기) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
204 lines
10 KiB
Python
204 lines
10 KiB
Python
"""일위대가 실무자료 맞춤(PLAN 324-2 · `M02_Hopyo_Match`) — 규격 → 입력값 · 단위 · 환산 ·
|
||
맞춤 차례 · 기억 우선 · 길(보기 · 미리 채움 · 확정) · master_data 원본 그대로. 층은 임시 폴더.
|
||
"""
|
||
|
||
import pytest
|
||
|
||
from M02_MasterTemplete import M02_Hopyo_Match as hm
|
||
from M02_MasterTemplete import M02_Template_Layers as layers
|
||
|
||
# 올리기 길 시험의 `client` 마당을 같이 씀
|
||
# ruff: noqa: F811
|
||
from resources.tester.test_m02_hopyo_excel import URL, _post, client, master_hash # noqa: F401
|
||
|
||
mm = hm.mm
|
||
|
||
|
||
# ── 규격 · 단위 · 환산 ─────────────────────────────────────────────────
|
||
def test_규격_이름_붙은_수():
|
||
assert hm.numbers("D=1,000mm,일반형") == {"D": (1000, "mm")}
|
||
assert hm.numbers("H:1.2m") == {"H": (hm.Decimal("1.2"), "m")}
|
||
assert {k: float(v) for k, (v, _) in hm.numbers("B=2.3,H=0.4,야면석").items()} == {
|
||
"B": 2.3,
|
||
"H": 0.4,
|
||
}
|
||
assert hm.numbers("L3=35cm적용,장비사용") == {"L3": (35, "cm")}
|
||
assert hm.numbers("Φ40×200") == {} and mm.plain_spec("Φ40×200") == "ø40x200"
|
||
|
||
|
||
def test_단위_정규화():
|
||
got = [mm.plain_unit(u) for u in ("㎥", "M", "Ton", "TON", "EA", "㎡")]
|
||
assert got == ["m3", "m", "톤", "톤", "개", "m2"]
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("have", "want", "spec", "times", "kind"),
|
||
[
|
||
("㎥", "㎥", "", 1, "같음"),
|
||
("M", "m", "", 1, "같음"),
|
||
("kg", "ton", "", 0.001, "단위표"),
|
||
("㎡", "㎥", "T=150mm", 0.15, "추정"),
|
||
("M", "㎡", "B=2.3,H=0.4", 2.3, "추정"),
|
||
("M", "㎥", "B=2.3,T=0.2", 0.46, "추정"),
|
||
("개소", "㎥", "", None, "입력"),
|
||
("㎥", "㎡ · ㎥", "", 1, "두 단위"),
|
||
("M", "㎡ · ㎥", "", None, "입력"),
|
||
],
|
||
)
|
||
def test_환산(have, want, spec, times, kind):
|
||
assert hm.factor(have, want, spec) == {"배": times, "갈래": kind}
|
||
|
||
|
||
SPECS = [
|
||
{"이름": "뒷길이", "단위": "㎝", "고르기": [25, 35, 45], "자동값": None},
|
||
{"이름": "관경", "단위": "mm", "자동값": None},
|
||
{"이름": "설치높이", "단위": "m", "자동값": None},
|
||
{
|
||
"이름": "기계",
|
||
"고르기": ["0201-0020", "0201-0060"],
|
||
"고르기이름": {
|
||
"0201-0020": "굴착기(무한궤도) 0.2㎥",
|
||
"0201-0060": "굴착기(무한궤도) 0.6㎥",
|
||
},
|
||
"자동값": None,
|
||
},
|
||
{"이름": "횟수", "고르기": ["3회사용시", "4회사용시"], "자동값": None},
|
||
{"이름": "단위당", "고르기": ["㎡당", "㎥당"], "자동값": None},
|
||
{"이름": "지역", "고르기": ["전국평균", "서울"], "자동값": "전국평균"},
|
||
{"이름": "보정작업", "고르기": ["해당 없음", "전석섞인토사"], "자동값": None},
|
||
]
|
||
|
||
|
||
def test_규격에서_입력값():
|
||
line = {"이름": "찰붙임(4회)", "규격": "L3=35cm적용,굴삭기0.6㎥,D=1,000mm,H:1.2m", "단위": "㎥"}
|
||
values, unread = hm.spec_inputs(line, SPECS)
|
||
assert values == {
|
||
"뒷길이": 35,
|
||
"관경": 1000,
|
||
"설치높이": 1.2,
|
||
"기계": "0201-0060",
|
||
"횟수": "4회사용시",
|
||
"단위당": "㎥당",
|
||
}
|
||
assert unread == ["보정작업"] # 지역 = 자동값 · 못 읽은 것만 표시
|
||
other = {"이름": "찰붙임", "규격": "L3=40cm", "단위": "㎡"}
|
||
values, unread = hm.spec_inputs(other, SPECS[:1])
|
||
assert values == {} and unread == ["뒷길이"] # 고르기 밖 수 = 빈 값 + 표시
|
||
|
||
|
||
# ── 맞춤 차례 ────────────────────────────────────────────────────────
|
||
def _hopyo(client, no, layer="personal"):
|
||
res = client.get(f"{URL.format(layer)}/HP-000001/{no}")
|
||
assert res.status_code == 200, res.text
|
||
return {one["이름"]: one["맞춤"] for one in res.json()["호표"]["줄"]}
|
||
|
||
|
||
def test_이름_같음_후보_호표_퍼센트(client):
|
||
before = master_hash()
|
||
assert _post(client).status_code == 200
|
||
first = _hopyo(client, 1)
|
||
plywood = first["합판거푸집"]
|
||
assert plywood["상태"] == "같음" and plywood["대상"]["로직"] == "GF000160"
|
||
assert plywood["입력값"] == {"횟수": "4회사용시"} and plywood["들어감"] is True
|
||
remicon = first["레미콘(25-21-120)"]
|
||
assert remicon["상태"] == "후보" and remicon["들어감"] is False
|
||
assert all(c["규격"] == "25-21-120" for c in remicon["후보"][:3]) # 규격 같은 자재 먼저
|
||
dig = _hopyo(client, 3)["구조물터파기(인력100%)"]
|
||
assert dig["상태"] == "후보" and len(dig["후보"]) == 5
|
||
assert dig["후보"][0]["이름"] == "굴착(인력/토사)" # 330-2 근거(건설 3-2-1 · 특별인부) 1순위
|
||
assert any(c["이름"].startswith("구조물터파기") for c in dig["후보"]) # 이름 1순위 자리
|
||
ref = _hopyo(client, 45)["고화매트재료비"]
|
||
assert ref["상태"] == "호표" and ref["대상"] == {"호표": 46, "단위": "㎡"} and ref["들어감"]
|
||
pct = _hopyo(client, 46)["잡재료비"]
|
||
assert pct["상태"] == "%" and pct["들어감"] is False
|
||
whole = client.get(f"{URL.format('personal')}/HP-000001").json()
|
||
assert whole["맞춤"][1] == 415 and 0 < whole["맞춤"][0] < 415
|
||
assert next(h for h in whole["호표"] if h["번"] == 61)["맞춤"] == [0, 0]
|
||
assert master_hash() == before
|
||
|
||
|
||
def test_미리_채움_확정_기억_우선(client):
|
||
assert _post(client).status_code == 200
|
||
url = f"{URL.format('personal')}/HP-000001/3/guess"
|
||
got = client.post(url, json={"차례": 1, "대상": {"로직": "GF000101"}}).json()
|
||
assert got["환산"] == {"배": 1, "갈래": "같음"} and got["대상"]["단위"] == "㎥"
|
||
assert client.post(url, json={"차례": 1, "대상": {"로직": "XX"}}).status_code == 400
|
||
key = _hopyo(client, 3)["구조물터파기(인력100%)"]["열쇠"]
|
||
assert key == "구조물터파기(인력100%)|토사|m3"
|
||
body = {"줄": {key: {"로직": "GF000101", "입력값": {"지역": "서울"}, "환산": 1}}}
|
||
assert client.post("/api/m02/layers/personal/hopyo-memory", json=body).status_code == 200
|
||
dig = _hopyo(client, 3)["구조물터파기(인력100%)"]
|
||
assert (dig["상태"], dig["대상"]["로직"], dig["입력값"], dig["들어감"]) == (
|
||
"기억",
|
||
"GF000101",
|
||
{"지역": "서울"},
|
||
True,
|
||
)
|
||
# 이름 같음보다 기억이 앞섬 · 빼기
|
||
plywood = _hopyo(client, 1)["합판거푸집"]["열쇠"]
|
||
out = {"줄": {plywood: {"빼기": True}}}
|
||
assert client.post("/api/m02/layers/personal/hopyo-memory", json=out).status_code == 200
|
||
assert _hopyo(client, 1)["합판거푸집"]["상태"] == "빼기"
|
||
clear = {"줄": {plywood: None}}
|
||
assert client.post("/api/m02/layers/personal/hopyo-memory", json=clear).status_code == 200
|
||
assert _hopyo(client, 1)["합판거푸집"]["상태"] == "같음"
|
||
|
||
|
||
def test_확정_검사와_위_층_기억(client):
|
||
bad = [
|
||
{"로직": "GC000422", "환산": 1}, # 돈 아닌 로직
|
||
{"로직": "GF000101", "환산": 0},
|
||
{"로직": "GF000101", "자재": "MT004258", "환산": 1},
|
||
{"환산": 1},
|
||
]
|
||
for one in bad:
|
||
res = client.post("/api/m02/layers/personal/hopyo-memory", json={"줄": {"a|b|c": one}})
|
||
assert res.status_code == 400, one
|
||
assert client.post("/api/m02/layers/system/hopyo-memory", json={"줄": {}}).status_code == 403
|
||
client.who.update(role="SYSTEM_ADMIN")
|
||
key = "구조물터파기(인력100%)|토사|m3"
|
||
body = {"줄": {key: {"로직": "GF000103", "환산": 1}}}
|
||
assert client.post("/api/m02/layers/system/hopyo-memory", json=body).status_code == 200
|
||
client.who.update(role="USER")
|
||
assert _post(client).status_code == 200
|
||
assert _hopyo(client, 3)["구조물터파기(인력100%)"]["대상"]["로직"] == "GF000103" # 위 층
|
||
mine = {"줄": {key: {"로직": "GF000101", "환산": 1}}}
|
||
assert client.post("/api/m02/layers/personal/hopyo-memory", json=mine).status_code == 200
|
||
assert _hopyo(client, 3)["구조물터파기(인력100%)"]["대상"]["로직"] == "GF000101" # 같은 층 먼저
|
||
assert hm.chain(layers.personal_dir(7, 42))[1] == layers.company_dir(7)
|
||
|
||
|
||
def test_로직_추가_고르기_줄은_이_층과_위_층(client):
|
||
client.who.update(role="SYSTEM_ADMIN")
|
||
assert _post(client, "system").status_code == 200
|
||
client.who.update(role="USER")
|
||
assert _post(client).status_code == 200
|
||
rows = client.get("/api/m02/layers/personal/hopyo-picks").json()["줄"]
|
||
assert [r["층"] for r in rows].count("personal") == 68
|
||
assert [r["층"] for r in rows].count("system") == 68
|
||
three = next(r for r in rows if r["층"] == "system" and r["번"] == 3)
|
||
assert {k: three[k] for k in ("주인", "키", "이름", "단위")} == {
|
||
"주인": None,
|
||
"키": "HP-000001",
|
||
"이름": "임도표지석",
|
||
"단위": "개소",
|
||
}
|
||
assert three["맞춤"][1] == 4
|
||
system = client.get("/api/m02/layers/system/hopyo-picks").json()["줄"]
|
||
assert {r["층"] for r in system} == {"system"} # 마스터 = 마스터만
|
||
|
||
|
||
def test_뜻_낱말이_안_겹치면_후보_아님():
|
||
assert hm.words("면 목") == {"면목"} and hm.words("구조물되메우기(인력100%)") == {
|
||
"구조물되메우기"
|
||
}
|
||
assert hm.words("되메우기 및 다짐(소형장비)") == {"되메우기", "다짐"}
|
||
soil = {"이름": "잔토처리(인력100%)", "규격": "", "단위": "㎥", "갈래": "단가산출"}
|
||
assert hm.candidates(soil, "로직") == [] # 「처리」 두 글자만 겹침(소금처리 · 바탕처리)
|
||
stone = {"이름": "임도표지석(받침,글새김포함)", "규격": "", "단위": "㎥", "갈래": "자재"}
|
||
assert hm.candidates(stone, "자재") == [] # 속도표지판 · 도로표지용페인트
|
||
got = hm.match_line(soil, [], {})
|
||
assert got["상태"] == "없음" and got["후보"] == []
|
||
fill = {"이름": "구조물되메우기(인력100%)", "규격": "", "단위": "㎥", "갈래": "단가산출"}
|
||
assert hm.candidates(fill, "로직")[0]["이름"] == "되메우기" # 한쪽 낱말에 듦 = 겹침
|