- 번호 [종류][분류1][분류2][분류3] · 없는 층 00 · 12장 6자리는 끝에 00 을 붙여 읽음
- 양식 종류 basis → area(면적산출) · material(재료산출) 새로 · 집계표 저장 때 세 파일 자동 만들기/지우기
- 복사 길 POST /structures · /area · /material/{받는 열}/copy?from= · 알약 연결은 문서와 같이 복사
- 번호 목록 {열 id: {도번, 면적번, 재료번}} · 저장 때 번호 칸은 서버 것 그대로
- renumber: basis/ → area/ 옮김(표번 → 면적번 · 시트 이름 면적산출) · 빠진 재료산출 만듦
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EUypcnp5d1gU2aeKh9F2H7
183 lines
9.3 KiB
Python
183 lines
9.3 KiB
Python
"""M02 구조물 도면(structure) — 새로 · 도번 SD+8자리 · 409 · 저장 · 지운 뒤 번호 ·
|
|
제목행 번호 매김(분류3 까지) · 집계표 저장 때 만들기/지우기 · 복사 가져오기 · 프로젝트 복사.
|
|
면적산출(area) · 재료산출(material)은 따로 파일 —
|
|
`resources/tester/spreadsheet/test_spreadsheet_store.py`."""
|
|
|
|
import json
|
|
import shutil
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete import M02_Template_Layers as layers
|
|
from M02_MasterTemplete.M02_MasterTemplete_Router import router
|
|
|
|
REAL = store.FOLDER
|
|
|
|
|
|
@pytest.fixture()
|
|
def client(tmp_path, monkeypatch):
|
|
for kind in store.KINDS:
|
|
(tmp_path / kind).mkdir()
|
|
shutil.copy(REAL / "table/구조물집계표.json", tmp_path / "table")
|
|
monkeypatch.setattr(store, "FOLDER", tmp_path)
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
return TestClient(app)
|
|
|
|
|
|
def _num(code: str) -> dict:
|
|
"""세 번호 — 같은 8자리."""
|
|
return {"도번": f"SD{code}", "면적번": f"SA{code}", "재료번": f"SM{code}"}
|
|
|
|
|
|
def test_새로_도번_자동_409_없는_열(client):
|
|
made = client.post("/api/m02/structures/pp_len")
|
|
assert made.status_code == 200
|
|
doc = made.json()["문서"]
|
|
assert (doc["양식"], doc["종류"], doc["열"], doc["도번"]) == (
|
|
"구조물도면",
|
|
"structure",
|
|
"pp_len",
|
|
"SD01010100",
|
|
)
|
|
# 도각 없는 전용 화면 — 그린 만큼이 설계 영역 · 기본 도면층 하나(없으면 CAD 가 못 엶)
|
|
assert doc["도면"]["format"] == 6
|
|
assert doc["도면"]["entities"] == []
|
|
assert len(doc["도면"]["layers"]) == 1
|
|
# 면적산출 · 재료산출은 구조물 문서 밖 — 같은 열 id 로 따로 한 통합문서씩
|
|
assert "산출근거" not in doc
|
|
for kind in ("area", "material"):
|
|
assert client.get(f"/api/m02/templates/{kind}/pp_len").json()["문서"]["종류"] == "통합문서"
|
|
|
|
assert client.post("/api/m02/structures/rv").json()["문서"]["도번"] == "SD02010100"
|
|
assert client.post("/api/m02/structures/pp_len").status_code == 409
|
|
assert client.post("/api/m02/structures/없는열").status_code == 404
|
|
assert client.post("/api/m02/structures/no").status_code == 404 # 글 열은 구조물 아님
|
|
assert client.get("/api/m02/structures/numbers").json() == {
|
|
"pp_len": _num("01010100"),
|
|
"rv": _num("02010100"),
|
|
}
|
|
rows = client.get("/api/m02/templates").json()
|
|
assert {(r["종류"], r["이름"]) for r in rows if r["종류"] == "structure"} == {
|
|
("structure", "pp_len"),
|
|
("structure", "rv"),
|
|
}
|
|
|
|
|
|
def test_저장_뼈대_거름(client):
|
|
got = client.post("/api/m02/structures/pp_len").json()
|
|
doc = got["문서"]
|
|
doc["도면"]["entities"] = [{"type": "line"}]
|
|
saved = client.put("/api/m02/templates/structure/pp_len", json={"판": got["판"], "문서": doc})
|
|
assert saved.status_code == 200 and saved.json()["판"] != got["판"]
|
|
for bad in ({}, {**doc, "도면": {}}, {**doc, "도면": {"entities": "x"}}, {**doc, "도면": []}):
|
|
r = client.put(
|
|
"/api/m02/templates/structure/pp_len", json={"판": saved.json()["판"], "문서": bad}
|
|
)
|
|
assert r.status_code == 400
|
|
assert client.get("/api/m02/templates/structure/pp_len").json()["문서"] == doc
|
|
now = client.get("/api/m02/templates/structure/pp_len").json()["판"]
|
|
client.put(
|
|
"/api/m02/templates/structure/pp_len", json={"판": now, "문서": {**doc, "도번": "SD9"}}
|
|
)
|
|
got = client.get("/api/m02/templates/structure/pp_len").json()["문서"]
|
|
assert got["도번"] == "SD01010100" # 번호는 서버 것 — 화면 값을 안 받음
|
|
|
|
|
|
def test_지워도_번호를_당기지_않음_짝은_같은_번호(client):
|
|
for column in ("pp_len", "rv", "ms"):
|
|
client.post(f"/api/m02/structures/{column}")
|
|
assert client.delete("/api/m02/templates/structure/rv").status_code == 200
|
|
numbers = client.get("/api/m02/structures/numbers").json()
|
|
assert numbers["rv"] == {**_num("02010100"), "도번": ""} # 면적 · 재료는 남아 번호를 쥠
|
|
assert numbers["ms"] == _num("03010100") # 다른 열 번호는 그대로
|
|
# 같은 종류(관공) 새 열 = 그 무리 다음 번호 · 다시 만든 도면은 짝(면적산출) 번호를 이어 받음
|
|
assert client.post("/api/m02/structures/pp_cp").json()["문서"]["도번"] == "SD01010200"
|
|
assert client.post("/api/m02/structures/rv").json()["문서"]["도번"] == "SD02010100"
|
|
|
|
|
|
def test_제목행으로_번호_매김_한_번_옮김(client, tmp_path):
|
|
"""옛 구-NN 파일 · 글 열 파일 → 새 번호(열 차례 = 종류 · 분류1 · 분류2 · 분류3 차례) ·
|
|
내용 그대로."""
|
|
for column in ("no", "rv", "pv_l"):
|
|
doc = {"양식": "구조물도면", "종류": "structure", "판": 1, "열": column, "도번": "구-07"}
|
|
doc["도면"] = {"format": 6, "entities": [{"type": "line"}], "layers": []}
|
|
(tmp_path / "structure" / f"{column}.json").write_text(
|
|
json.dumps(doc, ensure_ascii=False), encoding="utf-8"
|
|
)
|
|
done = store.renumber()
|
|
assert done["지움"] == ["structure/no"] # 글 열(NO · 측점 · 비고)은 구조물 아님
|
|
numbers = client.get("/api/m02/structures/numbers").json()
|
|
assert set(numbers) == set(store._column_ids()) and "sta" not in numbers
|
|
expect = {
|
|
"rv": "01010100", # 돌기슭막이 · (형태마다) · H=(높이마다) · 분류3 없음 = 00
|
|
"c1": "01010200", # 같은 종류 · 분류1 · 다음 분류2
|
|
"pv_b": "02010100", # 콘크리트포장 · T=(두께마다) · B
|
|
"pv_l": "02010200",
|
|
"fb": "06010000", # 세월교 · (관경 × 련수마다) · 분류2 없음 = 00
|
|
"be": "14000000", # 소단 — 분류 모두 없음
|
|
"h_rip_c": "27010000", # 돌붙임 · 찰붙임
|
|
"h_rip_m": "27020000", # 돌붙임 · 메붙임 — 분류1 다음 번호
|
|
}
|
|
for column, code in expect.items():
|
|
assert numbers[column] == _num(code), column
|
|
assert store.read("structure", "rv")["문서"]["도면"]["entities"] == [{"type": "line"}]
|
|
again = {"옮김": [], "지움": [], "바꿈": [], "새로": []}
|
|
assert store.renumber() == again # 두 번 돌려도 같음
|
|
|
|
|
|
def test_집계표_저장_때_새_열은_만들고_지운_열은_같이_지움(client):
|
|
store.renumber()
|
|
got = client.get("/api/m02/templates/table/구조물집계표").json()
|
|
table = got["문서"]
|
|
table["열"] = [c for c in table["열"] if c["id"] != "pv_a"]
|
|
new = {"id": "pv_x", "머리": ["콘크리트포장", "T=(두께마다)", "X"], "단위": "m", "꼴": "수"}
|
|
kind = {"id": "zz", "머리": ["새 구조물", None, None], "단위": "개소", "꼴": "수"}
|
|
text = {"id": "note2", "머리": ["메모", None, None], "단위": None, "꼴": "글"}
|
|
third = {"id": "pv_y", "머리": ["콘크리트포장", "T=(두께마다)", "B", "H=1"], "꼴": "수"}
|
|
table["열"] += [new, kind, text, third]
|
|
saved = client.put(
|
|
"/api/m02/templates/table/구조물집계표", json={"판": got["판"], "문서": table}
|
|
)
|
|
assert saved.status_code == 200
|
|
numbers = client.get("/api/m02/structures/numbers").json()
|
|
assert "pv_a" not in numbers and "note2" not in numbers
|
|
for kind_ in ("structure", "area", "material"):
|
|
assert client.get(f"/api/m02/templates/{kind_}/pv_a").status_code == 404
|
|
assert numbers["pv_x"] == _num("02010600") # 지운 04 는 비워 둠 · 무리 다음
|
|
assert numbers["zz"] == _num("37000000") # 새 종류 = 종류 다음 번호
|
|
assert numbers["pv_y"] == _num("02010101") # pv_b 무리 안 분류3 다음 번호
|
|
|
|
|
|
def test_다른_열_도면을_복사해_가져옴(client):
|
|
store.renumber()
|
|
src = client.get("/api/m02/templates/structure/rv").json()
|
|
src["문서"]["도면"]["entities"] = [{"type": "circle"}]
|
|
client.put("/api/m02/templates/structure/rv", json=src)
|
|
r = client.post("/api/m02/structures/ms/copy", params={"from": "rv"})
|
|
assert r.status_code == 200
|
|
ms = client.get("/api/m02/templates/structure/ms").json()["문서"]
|
|
assert ms["도면"]["entities"] == [{"type": "circle"}]
|
|
assert (ms["도번"], ms["열"]) == ("SD10010100", "ms") # 번호 · 열은 받는 쪽 그대로
|
|
assert client.get("/api/m02/templates/structure/rv").json()["문서"]["도번"] == "SD01010100"
|
|
bad = client.post("/api/m02/structures/ms/copy", params={"from": "없는열"})
|
|
assert bad.status_code == 404
|
|
|
|
|
|
def test_프로젝트_복사와_층_저장에_실림(client, tmp_path, monkeypatch):
|
|
doc = client.post("/api/m02/structures/pp_len").json()["문서"]
|
|
monkeypatch.setattr(layers, "SYSTEM_ROOT", tmp_path)
|
|
project = tmp_path / "project"
|
|
copied = layers.seed_project(project)
|
|
assert "structure/pp_len" in copied["작업본"] and "structure/pp_len" in copied["초기"]
|
|
work = layers.project_dir(project)
|
|
assert layers.read_template(work, "structure", "pp_len")["문서"] == doc
|
|
assert layers.read_template(layers.initial_dir(project), "structure", "pp_len")["문서"] == doc
|
|
assert ("structure", "pp_len") in {(r["종류"], r["이름"]) for r in layers.list_templates(work)}
|
|
layers.check_skeleton("structure", doc)
|
|
with pytest.raises(ValueError):
|
|
layers.check_skeleton("structure", {**doc, "도면": None})
|