- B07 POST /design-drawings/reset — 저장본 확정 풂(번호 · json 남김) · B07 이 올린 측점 확정 → 잠정 · 축척 · 횡단 배치 · 평면도 종류 · 표식 파일 지움 · 새 _Router_Reset.py(main.py 물림) - B08 POST /quantity/reset — quantity 설정 구획 기본 · hydro_inputs 지움 · milestone 기본(사용자 그림 붙은 거래처 경로 줄만 남김 · to = 시점) · 1본 길이 · 그림 남김 - B09 POST /estimation/reset — 사용자 값 · 단가 책 · estimation 설정 구획 지움 · 안 쓰는 서고 사본 정리 · 견적서 남김 · 답 = GET 꼴 - 공용 reset_section(구획 하나 기본으로) · 새 시험 test_298_2_page_reset Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
229 lines
9.2 KiB
Python
229 lines
9.2 KiB
Python
"""요구 298-2 · 300 C1 · C3 · C4 · C5 — B07 · B08 · B09 [초기화] 서버 창구(PLAN 497-0).
|
|
|
|
셋 다: 지운 것(사용자 값 · 설정) · 남긴 것(도면번호 · 올린 파일 · 남의 구획) · 단계(= [저장]) ·
|
|
두 번 눌러도 같음. 임시 storage · 단계 표는 메모리 흉내(`test_298_2_stage_state`).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
from pathlib import Path
|
|
from uuid import uuid4
|
|
|
|
from test_298_2_stage_state import _Pool, _Table
|
|
|
|
from common_util import common_util_project_settings as ps
|
|
|
|
C, P, S = "COMPLETE", "IN_PROGRESS", "STALE"
|
|
|
|
|
|
def _write(path: Path, data) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
|
|
|
|
|
def _read(path: Path):
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
|
|
|
|
def _mark_calls(monkeypatch, mod, root: Path) -> list:
|
|
calls = []
|
|
|
|
async def project_root(_pid):
|
|
return root
|
|
|
|
async def mark(_project_id, no, action, cursor=None):
|
|
calls.append((no, action))
|
|
|
|
monkeypatch.setattr(mod, "_project_root", project_root)
|
|
monkeypatch.setattr(mod, "mark_stage", mark)
|
|
return calls
|
|
|
|
|
|
# ── B07 ─────────────────────────────────────────────
|
|
|
|
|
|
def _b07_root(tmp_path: Path) -> Path:
|
|
b07 = tmp_path / "B07_DesignDetail"
|
|
drawings = {
|
|
"cross_sheet_1": {"kind": "cross", "confirmed": True, "number": 7},
|
|
"longitudinal": {"kind": "longitudinal", "confirmed": True, "number": 3},
|
|
"mass_haul": {"kind": "mass_haul", "confirmed": False, "number": 9},
|
|
}
|
|
_write(b07 / "manifest.json", {"drawings": drawings})
|
|
_write(b07 / "drawings" / "cross_sheet_1.json", {"entities": [], "layers": []})
|
|
_write(b07 / "drawing_scales.json", {"cross": {"x": 200, "y": 200}})
|
|
_write(b07 / "cross_layout.json", {"default": [3, 3], "sheets": {}, "table_scale": 1.5})
|
|
_write(b07 / "plan_marks.json", {"marks": [{"kind": "shelter"}]})
|
|
_write(b07 / "plan_kinds" / "k1.json", {"name": "사용자 종류", "pieces": []})
|
|
return tmp_path
|
|
|
|
|
|
def test_b07_reset(monkeypatch, tmp_path):
|
|
from B07_DesignDetail import B07_DesignDetail_Router_Reset as reset
|
|
|
|
root = _b07_root(tmp_path)
|
|
designs = {10: {"status": "confirmed"}, 20: {"status": "provisional"}, 30: {}}
|
|
merged = []
|
|
table = _Table([C, C, C, C, C, C, P])
|
|
|
|
async def source(_pid):
|
|
return 1, root, root / "l.json", False
|
|
|
|
async def by_chainage(_route_id):
|
|
return designs
|
|
|
|
async def merge(_connection, *, route_id, chainage, patch):
|
|
merged.append((chainage, patch["status"]))
|
|
designs[chainage] = {**designs[chainage], **patch}
|
|
|
|
monkeypatch.setattr(reset, "_confirmed_source", source)
|
|
monkeypatch.setattr(reset, "_designs_by_chainage", by_chainage)
|
|
monkeypatch.setattr(reset, "merge_cross_section_design_by_round", merge)
|
|
monkeypatch.setattr(reset, "get_db_pool", lambda: _Pool(table))
|
|
|
|
out = asyncio.run(reset.reset_design_drawings(uuid4()))
|
|
assert out["status"] == "success"
|
|
assert out["released"] == 2 and out["provisional"] == 1
|
|
assert sorted(out["cleared"]) == sorted(
|
|
["drawing_scales.json", "cross_layout.json", "plan_marks.json", "plan_kinds"]
|
|
)
|
|
manifest = _read(root / "B07_DesignDetail" / "manifest.json")["drawings"]
|
|
assert not any(entry["confirmed"] for entry in manifest.values())
|
|
assert {k: v["number"] for k, v in manifest.items()} == {
|
|
"cross_sheet_1": 7,
|
|
"longitudinal": 3,
|
|
"mass_haul": 9,
|
|
}
|
|
assert (root / "B07_DesignDetail" / "drawings" / "cross_sheet_1.json").is_file()
|
|
assert not (root / "B07_DesignDetail" / "plan_kinds").exists()
|
|
assert merged == [(10, "provisional")]
|
|
assert table.states == [C, C, C, C, P, S, S]
|
|
|
|
again = asyncio.run(reset.reset_design_drawings(uuid4()))
|
|
assert (again["released"], again["provisional"], again["cleared"]) == (0, 0, [])
|
|
assert _read(root / "B07_DesignDetail" / "manifest.json")["drawings"] == manifest
|
|
assert table.states == [C, C, C, C, P, S, S]
|
|
|
|
|
|
def test_b07_reset_wired():
|
|
main = (Path(__file__).parents[2] / "main.py").read_text(encoding="utf-8")
|
|
assert "app.include_router(b07_reset_router, dependencies=protected_with_company)" in main
|
|
|
|
|
|
# ── B08 ─────────────────────────────────────────────
|
|
|
|
|
|
def _b08_root(tmp_path: Path) -> Path:
|
|
ps.save_section(
|
|
tmp_path,
|
|
"quantity",
|
|
{
|
|
"rock_ratios_pct": {"연암": 70, "경암": 30},
|
|
"material_surcharge_pct": {"sand": 20},
|
|
"material_sheets": {"units": {"x": 1}},
|
|
"topsoil_thickness_m": 0.3,
|
|
},
|
|
)
|
|
ps.save_section(tmp_path, "estimation", {"rate_dataset": "keep"})
|
|
_write(tmp_path / "B08_Quantity" / "quantities" / "hydro_inputs.json", {"stations": {"0": {}}})
|
|
milestone = tmp_path / "B08_Quantity" / "milestone"
|
|
_write(
|
|
milestone / "milestone.json",
|
|
{
|
|
"site": {"mid_station_m": 120, "mid_road": "비포장"},
|
|
"bases": [{"id": "b1", "name": "기준점"}],
|
|
"routes": [
|
|
{"id": "r1", "name": "그림 업체", "to": "b1", "rows": [{"item": "골재"}]},
|
|
{"id": "r2", "name": "그림 없는 업체"},
|
|
],
|
|
},
|
|
)
|
|
(milestone / "r1.png").write_bytes(b"png")
|
|
(milestone / "auto").mkdir()
|
|
(milestone / "auto" / "r2.png").write_bytes(b"png")
|
|
return tmp_path
|
|
|
|
|
|
def test_b08_reset(monkeypatch, tmp_path):
|
|
from B08_Quantity import B08_Quantity_Router_Milestone as milestone
|
|
from B08_Quantity import B08_Quantity_Router_Options as options
|
|
|
|
root = _b08_root(tmp_path)
|
|
calls = _mark_calls(monkeypatch, options, root)
|
|
|
|
out = asyncio.run(options.quantity_reset(uuid4()))
|
|
assert out["rock_ratios_pct"] == {"연암": 90.0, "경암": 10.0}
|
|
assert out["material_surcharge_pct"] == options.panel_surcharge(None)
|
|
assert out["cleared_hydro"] is True and out["kept_routes"] == 1
|
|
settings = ps.load_settings(root)
|
|
assert settings["quantity"] == ps.default_settings()["quantity"]
|
|
assert settings["estimation"]["rate_dataset"] == "keep" # 남의 구획
|
|
assert not (root / "B08_Quantity" / "quantities" / "hydro_inputs.json").exists()
|
|
data = milestone.read_data(root)
|
|
assert data["bases"] == [] and data["site"]["mid_station_m"] is None
|
|
assert [(r["id"], r["to"], r["name"]) for r in data["routes"]] == [("r1", "site", "그림 업체")]
|
|
assert calls == [(5, "save")]
|
|
|
|
# C4 — 다음 [저장](그 GET 꼴 그대로)이 올린 그림을 안 지움
|
|
milestone.save_data(root, milestone.Milestone.model_validate(data))
|
|
assert (root / "B08_Quantity" / "milestone" / "r1.png").read_bytes() == b"png"
|
|
|
|
again = asyncio.run(options.quantity_reset(uuid4()))
|
|
assert again["kept_routes"] == 1 and again["cleared_hydro"] is False
|
|
assert milestone.read_data(root) == data
|
|
assert ps.load_settings(root) == settings
|
|
assert calls == [(5, "save"), (5, "save")]
|
|
|
|
|
|
def test_b08_reset_no_files(monkeypatch, tmp_path):
|
|
"""한 번도 저장 안 한 프로젝트 = 파일 안 만듦."""
|
|
from B08_Quantity import B08_Quantity_Router_Options as options
|
|
|
|
_mark_calls(monkeypatch, options, tmp_path)
|
|
out = asyncio.run(options.quantity_reset(uuid4()))
|
|
assert out["kept_routes"] == 0 and out["cleared_hydro"] is False
|
|
assert list(tmp_path.iterdir()) == []
|
|
|
|
|
|
# ── B09 ─────────────────────────────────────────────
|
|
|
|
|
|
def test_b09_reset(monkeypatch, tmp_path):
|
|
from B09_Estimation import B09_Estimation_Router as b09
|
|
from B09_Estimation import B09_Estimation_Store as store
|
|
|
|
root = tmp_path
|
|
v1 = store.folder(root)
|
|
_write(v1 / store.USER, {"estimate": {"duration_days": 90}, "bill": {"v": 2, "rows": []}})
|
|
_write(v1 / store.BOOK, {"made": "t", "rows": {}})
|
|
quote = store.quote_dir(root, "MT-1") / "견적.pdf"
|
|
quote.parent.mkdir(parents=True)
|
|
quote.write_bytes(b"pdf")
|
|
copy = v1 / "library" / "combo@system@0@abc"
|
|
copy.mkdir(parents=True)
|
|
ps.save_section(root, "estimation", {"rate_dataset": "old"})
|
|
ps.save_section(root, "quantity", {"topsoil_thickness_m": 0.3})
|
|
calls = _mark_calls(monkeypatch, b09, root)
|
|
|
|
async def payload(_pid, _root, draft=None):
|
|
return {"user": store.read_user(_root)}
|
|
|
|
monkeypatch.setattr(b09, "payload", payload)
|
|
|
|
out = asyncio.run(b09.estimation_reset(uuid4()))
|
|
assert out["user"]["estimate"] == {} and "bill" not in out["user"]
|
|
assert out["user"]["materials"] == {"MT-1": {"files": ["견적.pdf"]}} # 견적서 남음
|
|
assert not (v1 / store.USER).exists() and not (v1 / store.BOOK).exists()
|
|
assert quote.read_bytes() == b"pdf"
|
|
assert not copy.exists()
|
|
settings = ps.load_settings(root)
|
|
assert settings["estimation"] == ps.default_settings()["estimation"]
|
|
assert settings["quantity"]["topsoil_thickness_m"] == 0.3 # 남의 구획
|
|
assert calls == [(6, "save")]
|
|
|
|
again = asyncio.run(b09.estimation_reset(uuid4()))
|
|
assert again == out and ps.load_settings(root) == settings
|
|
assert calls == [(6, "save"), (6, "save")]
|