"""일위대가 실무자료 엑셀꼴 격자(PLAN 329-1 · `M02_Hopyo_Grid` · `GET hopyo-grid/{키}`). 북부청 임도 xls — 병합 11 · 15열 · B20 「제3호표」 · `#,##0` · 호표 68 행 범위 안 겹침 · 제3호표 [20, 24] · 올리면 격자도 · 200 / 옛 파일 404 · 지우면 격자도 · 역할 · xlsx 값만 · STmate 격자(일위대가표 · 단가산출근거 · 숨은 칸 글 뺌 · 338) · master_data 그대로. """ import pytest from M02_MasterTemplete import M02_Hopyo_Excel as hx from M02_MasterTemplete import M02_Hopyo_Grid as hg from M02_MasterTemplete import M02_Hopyo_Store as hs from M02_MasterTemplete import M02_Template_Layers as layers from resources.tester import test_m02_hopyo_excel as base from resources.tester.test_m02_hopyo_excel import ( NORTH, URL, _post, _xlsx, master_hash, ) client = base.client # 공용 고정물(임시 층) GRID = "/api/m02/layers/{}/hopyo-grid/{}" @pytest.fixture(scope="module") def north() -> dict: data = NORTH.read_bytes() return hg.read(NORTH.name, data, hx.read_book(NORTH.name, data)) def _sheet(grid: dict, name: str) -> dict: return next(s for s in grid["책"]["시트"] if s["이름"] == name) def test_격자_모양(north): book = north["책"] assert (book["종류"], book["판"], book["활성"]) == ("통합문서", 1, "s1") assert [s["이름"] for s in book["시트"]] == ["일위대가", "단가산출"] sheet = _sheet(north, "일위대가") assert len(sheet["병합"]) == 11 and "B1:O2" in sheet["병합"] assert len(sheet["열"]) == 15 and sheet["열"]["C"]["폭"] == round(7208 / 256, 2) assert sheet["틀고정"] == {"행": 4} assert sheet["칸"]["B20"]["값"] == "제3호표" assert sheet["칸"]["H5"]["값"] == 1040052 styles = book["서식"] assert styles[0] == {} and any(s.get("형식") == "#,##0" for s in styles) assert any(s.get("테두리") for s in styles) assert _sheet(north, "단가산출")["칸"]["B4"]["값"] == "1.유용토운반(무대)[㎥]" def test_호표_단산_행_범위(north): rows = [h["행"] for h in north["호표"]] assert len(rows) == 68 assert all(a[1] < b[0] for a, b in zip(sorted(rows), sorted(rows)[1:], strict=False)) assert next(h for h in north["호표"] if h["번"] == 3) == { "번": 3, "이름": "임도표지석", "행": [20, 24], } assert len(north["단산"]) == 95 assert next(b for b in north["단산"] if b["번"] == 93)["행"] == [4229, 4244] def test_줄_행(north): book = hx.read_book(NORTH.name, NORTH.read_bytes()) three = next(h for h in book["호표"] if h["번"] == 3) assert three["행"] == [20, 24] and [x["행"] for x in three["줄"]] == [21, 22, 23, 24] def test_올리기_격자_200_404_지우기(client): before = master_hash() assert _post(client).status_code == 200 res = client.get(GRID.format("personal", "HP-000001")) assert res.status_code == 200, res.text got = res.json() assert got["책"]["열"] == "HP-000001" and len(got["호표"]) == 68 folder = layers.personal_dir(7, 42) path = folder / hs.DIRNAME / hs.GRID / "HP-000001.json" assert path.is_file() assert [r["키"] for r in client.get(URL.format("personal")).json()["줄"]] == ["HP-000001"] path.unlink() # 옛 파일(324) = 격자 없음 old = client.get(GRID.format("personal", "HP-000001")) assert old.status_code == 404 and old.json()["detail"] == "격자 없음 — 다시 올리면 보임" assert _post(client).status_code == 200 assert client.get(GRID.format("personal", "HP-000002")).status_code == 200 assert client.delete(URL.format("personal") + "/HP-000002").status_code == 200 assert not (folder / hs.DIRNAME / hs.GRID / "HP-000002.json").exists() assert client.get(GRID.format("personal", "HP-000002")).status_code == 404 assert client.get(GRID.format("personal", "../x")).status_code == 404 assert client.get(URL.format("personal") + "/HP-000001/3").status_code == 200 # 길 안 부딪힘 assert master_hash() == before @pytest.mark.parametrize( ("role", "layer", "owner", "want"), [ ({"role": "USER"}, "personal", None, 200), ({"role": "USER"}, "personal", "7/43", 403), ({"role": "USER"}, "company", None, 200), ({"role": "USER"}, "system", None, 200), ], ) def test_역할_읽기(client, role, layer, owner, want): client.who.update({"role": "SYSTEM_ADMIN"}) assert _post(client, layer if want == 200 else "personal").status_code == 200 client.who.update(role) params = {"owner": owner} if owner else {} assert client.get(GRID.format(layer, "HP-000001"), params=params).status_code == want @pytest.mark.parametrize("layer", ["company", "system"]) def test_위_층_격자_읽기(client, layer): """아래 층 사용자가 고르기 줄(층 · 주인) 그대로 위 층 파일 격자를 읽음.""" client.who.update({"role": "SYSTEM_ADMIN"}) assert _post(client, layer).status_code == 200 client.who.update({"role": "USER"}) rows = client.get("/api/m02/layers/personal/hopyo-picks").json()["줄"] row = next(r for r in rows if r["층"] == layer) params = {"owner": row["주인"]} if row["주인"] else {} res = client.get(GRID.format(row["층"], row["키"]), params=params) assert res.status_code == 200 and len(res.json()["호표"]) == 68 def test_xlsx_값만(): data = _xlsx() grid = hg.read("a.xlsx", data, hx.read_book("a.xlsx", data)) sheet = grid["책"]["시트"][0] assert sheet["이름"] == "일위대가" and sheet["틀고정"] == {"행": 4} assert sheet["칸"]["B5"] == {"값": "제 1 호표"} and sheet["칸"]["E6"] == {"값": 0.5} assert "K6" not in sheet["칸"] # 머리 글(앞 4 줄) 마지막 열 J 까지 assert grid["호표"] == [{"번": 1, "이름": "표지석", "행": [5, 6]}] and grid["단산"] == [] def test_STmate_격자(): from resources.tester.test_m02_hopyo_stmate import _path path = _path("영월") data = path.read_bytes() parsed = hx.read_book(path.name, data) grid = hg.read(path.name, data, parsed) assert [s["이름"] for s in grid["책"]["시트"]] == ["일위대가표", "단가산출근거"] cells = _sheet(grid, "일위대가표")["칸"] one = grid["호표"][0] assert one == {"번": 1, "이름": "녹생토(종자배합)", "행": [5, 10]} assert cells[f"A{one['행'][0]}"]["값"].strip().startswith("제") assert cells["A10"]["값"] == "보통인부" calc = _sheet(grid, "단가산출근거")["칸"] block = next(b for b in grid["단산"] if b["번"] == 2) assert calc[f"B{block['행'][0] + 1}"]["값"].startswith("구조물터파기(인력)") assert not any(str(c.get("값")).startswith("_x000") for c in {**cells, **calc}.values())