Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
104 lines
4.6 KiB
Python
104 lines
4.6 KiB
Python
"""B08 좌측 패널 옵션 길(PLAN 122 · 124) — 배관 1본 길이 · 연암 / 경암 비율 · 할증 모래 · 자갈 · 시멘트.
|
||
|
||
GET /api/projects/{id}/quantity/options 지금 값
|
||
PUT /api/projects/{id}/quantity/options [저장] — 받은 칸만 정본 자리에 씀
|
||
|
||
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_Material_Calc import PANEL_KEY, panel_surcharge
|
||
from B08_Quantity.B08_Quantity_Router_Hydro import _project_root
|
||
from B08_Quantity.B08_Quantity_Router_Summary import ONE_LENGTH_VAR, TABLE_NAME, one_pipe_length
|
||
from common_util.common_util_project_settings import quantity_settings, save_section, soft_rock_pct
|
||
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:
|
||
return await asyncio.to_thread(save_options, root, body)
|
||
except layers.StaleTemplate as error:
|
||
raise HTTPException(
|
||
status_code=409, detail="구조물집계표가 그새 바뀌었습니다 — 다시 열고 저장하세요."
|
||
) from error
|