# -*- coding: utf-8 -*- """642-5(PLAN 785-5) 만들기 · 길 지킴 시험(설계 642-A 5-3 넷째 줄) — 임시 storage(`client` 마당). - 판정 길(목록 · 호표 · 블록) · 검산 길(입력값 = 엔진이 쓴 칸 모두 · 지역 · 자재지역 · 조종원직종 비움) - 옮기기 → SC 줄(고른 것마다 · 같은 별칭 · 본뜬 {호표, 차례, 행} · 수량 = 엑셀 × 몫) · 같은 기초 둘째 = 사본 · 별도계상 자재 · 입력 필요 = 그 줄만 막힘(고정 줄로 남음) · SK 더한줄(바탕 없음) · 장부 · 다시 옮김 = SC 안 늚 - 금액 · 품 칸 막음 · M01 원본 그대로 """ import json from M02_MasterTemplete import M02_Hopyo_Bind as hb from M02_MasterTemplete import M02_Hopyo_Build as hbuild from M02_MasterTemplete import M02_Hopyo_Judge as judge from M02_MasterTemplete import M02_Hopyo_Ledger as ledger from M02_MasterTemplete import M02_Labor_Doc as labor from M02_MasterTemplete import M02_Template_Layers as layers # 올리기 길 시험의 `client` 마당을 같이 씀 # ruff: noqa: F811 from resources.tester.test_m02_hopyo_excel import _post, client, master_hash # noqa: F401 API = "/api/m02/layers/personal" KEY = "HP-000001" def _mine(): return layers.personal_dir(7, 42) def _doc(kind, key): return json.loads((_mine() / kind / f"{key}.json").read_bytes()) def _at(unit, no): """호표 줄 판에서 단산 N 을 부르는 줄.""" return next(r for r in unit["줄"] if r.get("번") == no and "단가산출" in r["갈래"]) def _base(row, sec=0): """그 줄 풀이 절의 고른 기초 · 입력값.""" part = row["풀이"]["절"][sec] cand = next(c for c in part["후보"] if c["키"] == part["고름"]) return {"갈래": "기초", "키": cand["키"], "입력값": cand["바인딩"]["입력값"]} def _specs(code): m = hb.m01.cm.master() return {s["이름"] for s in hb.specs_of(m.logic(code))} - set(hb.SKIP) def test_판정_검산_옮기기(client): before = master_hash() assert _post(client).status_code == 200 got = client.get(f"{API}/hopyo-judge/{KEY}").json() assert got["셈중"] is True and got["블록"][9]["판정"] is None # 뒤 셈 전 assert judge.judged(_mine(), KEY) is None judge.solve_all(_mine(), KEY) # 올림 뒤 뒤 셈(시험은 바로) got = client.get(f"{API}/hopyo-judge/{KEY}").json() assert got["셈중"] is False and len(got["호표"]) == 68 and len(got["블록"]) == 95 assert got["블록"][9]["번"] == 10 and got["블록"][9]["판정"] == "기초로 풂" assert judge.judged(_mine(), KEY)[10] == {"판정": "기초로 풂", "고를수": 0} assert got["호표"][0]["판정"] == {"제안 있음": 2, "2차 필요": 4} two = client.get(f"{API}/hopyo-judge/{KEY}/2").json() ten = _at(two, 10) # 예시 1 야면석소운반 단산10 assert ten["이름"] and "단가" in ten and ten["풀이"]["판정"] == "기초로 풂" ex1 = _base(ten) assert ex1["키"] == "GC001140" and ex1["입력값"]["운반무게"] == 575 blk = client.get(f"{API}/hopyo-judge/{KEY}/block/10").json() assert blk["부름"] and "몫" in blk["절"][0] and "금액" in blk check = client.post( f"{API}/hopyo/{KEY}/check", json={"호표": 2, "차례": ten["차례"], "고른": [ex1]} ).json() one = check["고른"][0] assert one["판정"] == "품 같음" and one["입력필요"] == [] assert set(one["입력값"]) == _specs("GC001140") assert not set(one["입력값"]) & set(hb.SKIP) # 지역 · 자재지역 · 조종원직종 비움 assert int(one["금액"]["계"]) == 94227 # 엑셀 94,227 (원 미만 버림) assert check["수량"]["우리"] == ten["수량"]["엑셀"] first = client.get(f"{API}/hopyo-judge/{KEY}/1").json() rebar, form = _at(first, 47), _at(first, 24) # 예시 2 철근 · 예시 3 거푸집 a, b = _base(rebar, 0), _base(rebar, 1) b["입력값"] = {**b["입력값"], "구분": "토목"} # 엇갈림 = 사용자 고름 a["몫"] = 0.5 mat = first["줄"][0] # 레미콘 별도계상 — 786-1 후보 = 고정 줄 하나(품목은 프로젝트에서) assert mat["판정"] == "제안 있음" and len(mat["후보"]) == 1 cement = mat["후보"][0] assert (cement["갈래"], cement["키"], cement["별도계상"]) == ("고정", None, True) assert cement["품목"] == "프로젝트에서" stuck = {"갈래": "기초", "키": "GC000211", "입력값": {"유형": "보통"}} ex3 = _base(form) # 예시 3 GC000211 보통 · 4회 · 높이감률 · 구멍땜 = 입력 필요 url = f"{API}/hopyo/{KEY}/check" need = client.post(url, json={"호표": 1, "차례": form["차례"], "고른": [ex3]}).json() assert ( need["고른"][0]["입력필요"] == ["높이감률", "구멍땜"] and need["고른"][0]["금액"]["계"] is None ) ex3["입력값"] |= {"높이감률": 0, "구멍땜": 0} got3 = client.post(url, json={"호표": 1, "차례": form["차례"], "고른": [ex3]}).json()["고른"][0] assert ( got3["판정"] == "품 같음" and abs(got3["금액"]["노무비"] - 38291) <= 1 ) # 엑셀 노무 38,291 other = next(r for r in two["줄"] if r["차례"] > ten["차례"]) # 같은 기초 둘째 줄 twice = {**ex1, "입력값": {**ex1["입력값"], "운반거리": 12}} body = { "층": "personal", "호표": { "1": { str(rebar["차례"]): [a, b], str(form["차례"]): [stuck], "0": [cement], }, "2": {str(ten["차례"]): [ex1], str(other["차례"]): [twice]}, }, "블록": {"10": [{**ex1, "몫": 1}]}, } res = client.post(f"{API}/hopyo/{KEY}/move", json=body) assert res.status_code == 200, res.text out = res.json() assert [x["번"] for x in out["만듦"]] == [1, 2] and out["장부"]["씀"] >= 4 assert out["막힘"] == [ { "호표": 1, "차례": form["차례"], "까닭": out["막힘"][0]["까닭"], "칸": out["막힘"][0]["칸"], } ] assert out["막힘"][0]["까닭"].startswith("입력 필요") and out["막힘"][0]["칸"] sc2 = _doc("precombo", out["만듦"][1]["키"]) lines = sc2["담은로직"] mine = next(x for x in lines if x.get("로직") == "GC001140") assert mine["별칭"] == ten["이름"][: len(mine["별칭"])] assert mine["본뜬"] == {"호표": 2, "차례": ten["차례"], "행": ten["행"]} assert mine["수량"]["손"] == ten["수량"]["엑셀"] assert set(mine["입력값"]) == _specs("GC001140") wrap = next(x for x in lines if x.get("별칭") == other["이름"][: len(x.get("별칭") or "")]) inner = wrap["참조"]["몸"]["담은로직"][ 0 ] # 같은 기초 둘째 = 한 줄 SC 사본(본뜬 = 사본 출처 740-1) assert inner["로직"] == "GC001140" and inner["입력값"]["운반거리"] == 12 assert len(lines) == len(two["줄"]) # 확인 안 한 줄 = 고정(빠짐 0) sc1 = _doc("precombo", out["만듦"][0]["키"]) pair = [x for x in sc1["담은로직"] if (x.get("본뜬") or {}).get("차례") == rebar["차례"]] assert [x["로직"] for x in pair] == ["GC000206", "GC000207"] assert pair[0]["별칭"] == pair[1]["별칭"] # 한 엑셀 줄 = 우리 두 줄 · 같은 별칭 excel = rebar["수량"]["엑셀"] assert pair[0]["수량"]["손"] == excel * 0.5 and pair[1]["수량"]["손"] == excel assert pair[1]["입력값"]["구분"] == "토목" held = next( x for x in sc1["담은로직"] if (x.get("고정") or {}).get("출처", {}).get("줄") == form["차례"] ) assert "본뜬" not in held # 막힌 줄 = 고정 줄(엑셀 숫자) held_apart = next( x for x in sc1["담은로직"] if (x.get("본뜬") or {}).get("차례") == mat["차례"] ) fx = held_apart["고정"] # 별도계상 줄 = 합 밖 고정 줄(단가 0 · 자재 목록에 안 넣음) assert fx["이름"] == mat["이름"] and fx["규격"] == "별도계상" and not any(fx["단가"].values()) assert held_apart["수량"]["손"] == mat["수량"]["엑셀"] assert held_apart["메모"].startswith("별도계상 · 품목은 프로젝트에서") without = {**sc1, "담은로직": [x for x in sc1["담은로직"] if x is not held_apart]} got_sum, base_sum = labor.server(sc1)["계산값"], labor.server(without)["계산값"] assert got_sum["반영합"] == base_sum["반영합"] # 서버 정본 셈 — 별도계상 줄은 합에 안 듦 sk = _doc("sangeun", out["단가산출"][0]["키"]) assert sk["바탕"] is None and [x["로직"] for x in sk["더한줄"]] == ["GC001140"] assert sk["더한줄"][0]["입력값"]["운반무게"] == 575 book = ledger.read(_mine()) assert any( p.get("갈래") == "기초" and p.get("키") == "GC000207" for row in book.values() for p in row.get("짝") or [] ) scs = sorted(p.stem for p in (_mine() / "precombo").glob("SC-*.json")) stop = client.post(f"{API}/hopyo/{KEY}/move", json=body) # 이미 옮긴 것 = 덮음 없이 409 assert stop.status_code == 409 and [x["번"] for x in stop.json()["detail"]["호표"]] == [1, 2] assert sorted(p.stem for p in (_mine() / "precombo").glob("SC-*.json")) == scs again = client.post(f"{API}/hopyo/{KEY}/move", json={**body, "덮음": True}) assert again.status_code == 200, again.text again = again.json() assert [x["키"] for x in again["만듦"]] == [x["키"] for x in out["만듦"]] assert sorted(p.stem for p in (_mine() / "precombo").glob("SC-*.json")) == scs assert hbuild.confirmed(_mine())[hbuild.mark(KEY, 2)] == out["만듦"][1]["키"] assert client.delete(f"{API}/hopyo/{KEY}").status_code == 200 # [끝내기] = 저장본도 지움 assert not list((_mine() / "실무자료" / "_derived").glob(f"*{KEY}*")) assert master_hash() == before def test_막는_길(client): assert _post(client).status_code == 200 pick = {"갈래": "기초", "키": "GC001140", "입력값": {}, "금액": 1} bad = client.post( f"{API}/hopyo/{KEY}/move", json={"층": "personal", "호표": {"2": {"0": [pick]}}} ) assert bad.status_code == 422 # 금액 · 품 칸 = 서버가 셈 none = client.post(f"{API}/hopyo/{KEY}/move", json={"층": "personal", "호표": {"99": {}}}) assert none.status_code == 404 wrong = {"갈래": "인력", "키": "LB999999"} res = client.post( f"{API}/hopyo/{KEY}/move", json={"층": "personal", "호표": {"2": {"0": [wrong]}}} ) assert res.status_code == 400