- 도번 SD · 표번 SC + [종류][분류1][분류2] — 제목행(열 머리 세 층) 차례 · 없는 층 00 · 한 열 두 번호 같은 6자리 · 준 번호 유지(새 열 = 그 무리 다음 번호 · 지운 번호 비움)
- 구조물 = 꼴=수 열만(NO · 측점 · 비고 빠짐 · 브레인 판정)
- 집계표 [저장] — 새 열 두 파일 만들고 지운 열 두 파일 지움
- 새 산출근거 = 진짜 빈 시트(머리 줄 없음) · renumber() = 옛 번호 한 번 옮김 + 옛 기본 머리 줄 지움
- POST /structures/{열}/copy?from= · /basis/{열}/copy?from= — 내용만 · 번호 · 열은 받는 쪽
- GET /structures/numbers = {열 id: {도번, 표번}} · /basis/numbers 뺌
- 시험: test_m02_structure · test_spreadsheet_store 고쳐 씀
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EUypcnp5d1gU2aeKh9F2H7
169 lines
8.5 KiB
Python
169 lines
8.5 KiB
Python
"""M02 구조물 도면(structure) — 새로 · 도번 SD+6자리 · 409 · 저장 · 지운 뒤 번호 ·
|
|
제목행 번호 매김 · 집계표 저장 때 만들기/지우기 · 복사 가져오기 · 프로젝트 복사.
|
|
산출근거는 따로 파일(basis) — `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(도번: str, 표번: str) -> dict:
|
|
return {"도번": 도번, "표번": 표번}
|
|
|
|
|
|
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",
|
|
"SD010101",
|
|
)
|
|
# 도각 없는 전용 화면 — 그린 만큼이 설계 영역 · 기본 도면층 하나(없으면 CAD 가 못 엶)
|
|
assert doc["도면"]["format"] == 6
|
|
assert doc["도면"]["entities"] == []
|
|
assert len(doc["도면"]["layers"]) == 1
|
|
# 산출근거는 구조물 문서 밖 — 같은 열 id 로 따로 한 통합문서
|
|
assert "산출근거" not in doc
|
|
assert client.get("/api/m02/templates/basis/pp_len").json()["문서"]["종류"] == "통합문서"
|
|
|
|
assert client.post("/api/m02/structures/rv").json()["문서"]["도번"] == "SD020101"
|
|
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("SD010101", "SC010101"),
|
|
"rv": _num("SD020101", "SC020101"),
|
|
}
|
|
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
|
|
|
|
|
|
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("", "SC020101") # 산출근거는 남아 번호를 쥠
|
|
assert numbers["ms"] == _num("SD030101", "SC030101") # 다른 열 번호는 그대로
|
|
# 같은 종류(관공) 새 열 = 그 무리 다음 번호 · 다시 만든 도면은 짝(산출근거) 번호를 이어 받음
|
|
assert client.post("/api/m02/structures/pp_cp").json()["문서"]["도번"] == "SD010102"
|
|
assert client.post("/api/m02/structures/rv").json()["문서"]["도번"] == "SD020101"
|
|
|
|
|
|
def test_제목행으로_번호_매김_한_번_옮김(client, tmp_path):
|
|
"""옛 구-NN 파일 · 글 열 파일 → 새 번호(열 차례 = 종류 · 분류1 · 분류2 차례) · 내용 그대로."""
|
|
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": "010101", # 돌기슭막이 · (형태마다) · H=(높이마다)
|
|
"pv_b": "020101", # 콘크리트포장 · T=(두께마다) · B
|
|
"pv_l": "020102", # 같은 종류 · 분류1 · 다음 분류2
|
|
"fb": "060100", # 세월교 · (관경 × 련수마다) · 분류2 없음 = 00
|
|
"be": "140000", # 소단 — 분류 둘 다 없음
|
|
"h_rip_c": "270100", # 돌붙임 · 찰붙임
|
|
"h_rip_m": "270200", # 돌붙임 · 메붙임 — 분류1 다음 번호
|
|
}
|
|
for column, code in expect.items():
|
|
assert numbers[column] == _num(f"SD{code}", f"SC{code}"), column
|
|
assert store.read("structure", "rv")["문서"]["도면"]["entities"] == [{"type": "line"}]
|
|
assert store.renumber() == {"지움": [], "바꿈": [], "새로": []} # 두 번 돌려도 같음
|
|
|
|
|
|
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, "꼴": "글"}
|
|
table["열"] += [new, kind, text]
|
|
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", "basis"):
|
|
assert client.get(f"/api/m02/templates/{kind_}/pv_a").status_code == 404
|
|
assert numbers["pv_x"] == _num("SD020106", "SC020106") # 지운 04 는 비워 둠 · 무리 다음
|
|
assert numbers["zz"] == _num("SD370000", "SC370000") # 새 종류 = 종류 다음 번호
|
|
|
|
|
|
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["열"]) == ("SD100101", "ms") # 번호 · 열은 받는 쪽 그대로
|
|
assert client.get("/api/m02/templates/structure/rv").json()["문서"]["도번"] == "SD010101"
|
|
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})
|