Files
Aislo/B08_Quantity/B08_Quantity_Router_Options.py
T
eomsangdonandClaude Opus 5.5 ef06cd01dc feat(497-0 [초기화] 서버): B07 · B08 · B09 reset 창구 셋 — 그 페이지 사용자 값 · 설정 지움 · 올린 파일 남김 · 단계 = 저장
- 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>
2026-10-20 20:14:45 +09:00

161 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""B08 좌측 패널 옵션 길(PLAN 122 · 124) — 배관 1본 길이 · 연암 / 경암 비율 · 할증 모래 · 자갈 · 시멘트.
GET /api/projects/{id}/quantity/options 지금 값
PUT /api/projects/{id}/quantity/options [저장] — 받은 칸만 정본 자리에 씀
POST /api/projects/{id}/quantity/complete [>] — 단계 5 완료 · 6 진행 중(요구 300 B1)
POST /api/projects/{id}/quantity/reset [초기화](요구 298-2 C1 · C3 · C4) — 사용자 값 ·
설정 지움 → 다음 GET = 지금 B05 · B06 저장본으로 셈한 기준 · 단계 = [저장]과 같음
[초기화] 지움 = `project_settings.json › quantity` 구획 통째(연암 · 할증 · 재료 칸 · 그 밖 → 기본)
· `hydro_inputs.json` · `milestone.json`(사용자 그림이 붙은 거래처 경로 줄만 남김 — 다음 [저장]이
그림을 안 지우게 · 그 줄 `to` = 시점) · 남김 = 1본 길이 · 조합 연결(M02 층) · 올린 그림 파일 ·
초안 넷 = 화면 몫.
1본 길이 = 프로젝트 층 M02 구조물집계표 변수 `파형강관_1본_m` 한 벌(98 `one_pipe_length` 이 읽는 자리 ·
M02 표 식 `[$파형강관_1본_m]` 도 같은 값) · 프로젝트 사본이 없으면 시스템 층 문서를 바탕으로 깔고 씀 ·
M02 [초기화]면 같이 되돌아감.
연암 % = `project_settings.json › quantity › rock_ratios_pct`(연암 · 경암 = 100 − 연암) · 기본 90 은
파일에 안 쓰고 읽을 때 채움(`soft_rock_pct`).
할증 % = `quantity › material_surcharge_pct`(모래 · 자갈 · 시멘트 · 받은 칸만 · 기본 12 · 5 · 3 은 읽을 때 채움
`panel_surcharge`) — 총괄자재수량집계표 그 세 줄이 씀.
"""
from __future__ import annotations
import asyncio
from pathlib import Path
from typing import Annotated, Any, Literal
from uuid import UUID
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
from B08_Quantity.B08_Quantity_Hydro_Site import SAVED_FILE as HYDRO_FILE
from B08_Quantity.B08_Quantity_Material_Calc import PANEL_KEY, panel_surcharge
from B08_Quantity.B08_Quantity_Milestone_Calc import SITE
from B08_Quantity.B08_Quantity_Router_Hydro import _project_root
from B08_Quantity.B08_Quantity_Router_Milestone import DATA_FILE as MILESTONE_FILE
from B08_Quantity.B08_Quantity_Router_Milestone import Milestone, images, read_data
from B08_Quantity.B08_Quantity_Router_Milestone import folder as milestone_folder
from B08_Quantity.B08_Quantity_Router_Summary import ONE_LENGTH_VAR, TABLE_NAME, one_pipe_length
from common_util.common_util_json import atomic_write_json
from common_util.common_util_project_settings import (
quantity_settings,
reset_section,
save_section,
soft_rock_pct,
)
from common_util.common_util_workflow_state import mark_stage
from M02_MasterTemplete import M02_Template_Layers as layers
router = APIRouter(prefix="/api/projects", tags=["B08 Options"])
class OptionsSave(BaseModel):
"""빈칸(None) = 그 칸은 그대로."""
one_pipe_length_m: float | None = Field(default=None, gt=0, le=100)
soft_rock_pct: float | None = Field(default=None, ge=0, le=100)
material_surcharge_pct: (
dict[Literal["sand", "gravel", "cement"], Annotated[float, Field(ge=0, le=100)]] | None
) = None
def read_options(root: str | Path) -> dict[str, Any]:
settings = quantity_settings(root)
soft = soft_rock_pct(settings)
return {
"one_pipe_length_m": one_pipe_length(root),
"rock_ratios_pct": {"연암": soft, "경암": 100.0 - soft},
PANEL_KEY: panel_surcharge(settings.get(PANEL_KEY)),
}
def _write_one_pipe(root: str | Path, value: float) -> None:
"""프로젝트 층 구조물집계표 변수 한 칸 — 판이 그새 바뀌었으면 `StaleTemplate`."""
folder = layers.project_dir(root)
found = layers.read_template(folder, "table", TABLE_NAME)
version = found["판"] if found else None
found = found or layers.read_template(layers.system_dir(), "table", TABLE_NAME)
if found is None:
raise HTTPException(status_code=404, detail=f"{TABLE_NAME} 양식이 없습니다 — M02 표부터.")
document = found["문서"]
document["변수"] = {**(document.get("변수") or {}), ONE_LENGTH_VAR: value}
layers.write_template(
folder, "table", TABLE_NAME, document, version=version, check_version=True
)
def save_options(root: str | Path, body: OptionsSave) -> dict[str, Any]:
if body.one_pipe_length_m is not None:
_write_one_pipe(root, body.one_pipe_length_m)
if body.soft_rock_pct is not None:
soft = float(body.soft_rock_pct)
ratios = {
**(quantity_settings(root).get("rock_ratios_pct") or {}),
"연암": soft,
"경암": 100.0 - soft,
}
save_section(
root, "quantity", {"rock_ratios_pct": ratios}, replace_keys=("rock_ratios_pct",)
)
if body.material_surcharge_pct:
stored = quantity_settings(root).get(PANEL_KEY)
merged = {**(stored if isinstance(stored, dict) else {}), **body.material_surcharge_pct}
save_section(root, "quantity", {PANEL_KEY: merged}, replace_keys=(PANEL_KEY,))
return read_options(root)
@router.get("/{project_id}/quantity/options")
async def options_read(project_id: UUID) -> dict[str, Any]:
root = await _project_root(project_id)
return await asyncio.to_thread(read_options, root)
@router.put("/{project_id}/quantity/options")
async def options_save(project_id: UUID, body: OptionsSave) -> dict[str, Any]:
root = await _project_root(project_id)
try:
out = await asyncio.to_thread(save_options, root, body)
except layers.StaleTemplate as error:
raise HTTPException(
status_code=409, detail="구조물집계표가 그새 바뀌었습니다 — 다시 열고 저장하세요."
) from error
await mark_stage(str(project_id), 5, "save") # [저장](B1)
return out
@router.post("/{project_id}/quantity/complete")
async def quantity_complete(project_id: UUID) -> dict[str, Any]:
"""B08 [>] — 단계 5 완료 · 6 진행 중(요구 300 B1 · 본문 없음)."""
await _project_root(project_id) # 없는 프로젝트는 404
await mark_stage(str(project_id), 5, "next")
return {"status": "success", "project_id": str(project_id)}
def reset_quantity(root: str | Path) -> dict[str, Any]:
"""[초기화] 파일 몫 — 지운 것 · 남긴 거래처 경로 줄 수를 옵션 꼴에 얹어 돌려줌."""
root = Path(root)
reset_section(root, "quantity")
hydro = root / HYDRO_FILE
cleared_hydro = hydro.is_file()
hydro.unlink(missing_ok=True)
kept_routes = 0
if (milestone_folder(root) / MILESTONE_FILE).is_file():
pictured = set(images(root)) # 사용자 그림 = 하위 폴더 밖 `<경로 id>.<확장자>`
routes = [
{**route, "to": SITE} for route in read_data(root)["routes"] if route["id"] in pictured
]
kept_routes = len(routes)
data = Milestone.model_validate({"routes": routes}).model_dump()
atomic_write_json(milestone_folder(root) / MILESTONE_FILE, data)
return {**read_options(root), "cleared_hydro": cleared_hydro, "kept_routes": kept_routes}
@router.post("/{project_id}/quantity/reset")
async def quantity_reset(project_id: UUID) -> dict[str, Any]:
root = await _project_root(project_id)
out = await asyncio.to_thread(reset_quantity, root)
await mark_stage(str(project_id), 5, "save") # [초기화] = 작업본 바꿈 = [저장]과 같음
return out