Files
Aislo/B06_Section/B06_Section_Router_CrossTable.py
T
eomsangdonandClaude Opus 5.5 48c4f7d7d4 feat(B06): 횡단 카드 [상세] 모달 — 구조물 글 · CAD 꼴 그림 · B07 횡단 수량표(138-4)
- 측구 줄 맨 오른쪽 [상세] · 투명 막 · 카드 안 가리는 넓은 쪽 · 카드 다시 그리면 갱신
- 구조물 글 structure_note — USER_TOUCHED_KEYS 두 벌 · CrossSectionPatch · 세션 초안 → [저장]
- 표 값 = 서버 cross-quantity-table 한 길(B07 _quantity_table 을 캐시 설계로) · 칸 배치 B07 그대로
- test_138_cross_detail

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JywHxQuWPk6UYXTebyrBoU
2026-10-09 15:01:01 +09:00

95 lines
3.8 KiB
Python

"""B06 횡단 [상세] 모달의 **B07 횡단 수량표 값** 창구(138-4).
값은 B07 횡단도가 쓰는 `_quantity_table` 한 벌을 **브라우저 캐시 설계**로 돌린 것이다 —
화면이 TS 로 다시 짜지 않는다(CLAUDE.md 5장 ③ 서버 단독). 칸 배치도 B07 표 정의를 그대로
실어 보내 화면은 그리기만 한다. **표시 전용** — 정본을 쓰지 않는다. 모달을 열 때 부른다.
"""
from __future__ import annotations
import asyncio
import logging
from pathlib import Path
from typing import Any
from uuid import UUID
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B06_Section.B06_Section_Repository import get_longitudinal_section
from B06_Section.B06_Section_Router_Design import read_cross_design_inputs
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import _format
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Table import (
_CROSS_HEADER_KEYS,
_CROSS_LEFT_ROWS,
_CROSS_MIDDLE_ROWS,
_CROSS_RIGHT_ROWS,
)
from B07_DesignDetail.B07_DesignDetail_Router_Support import _quantity_table
from common_util.common_util_storage import resolve_stored_project_path
from config.config_db import get_db_pool
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/projects", tags=["B06 Profile Cross"])
class CrossQuantityTableRequest(BaseModel):
chainage_m: float = Field(..., ge=0)
# 브라우저 캐시의 그 측점 설계(조작 반영본) — 없으면 원본 칸만.
design: dict[str, Any] | None = None
def cross_quantity_sheet(source: dict[str, Any], design: dict[str, Any] | None) -> dict[str, Any]:
"""B07 횡단 수량표 한 장 — 값(숫자) · 글(B07 표와 같은 꼴) · 칸 배치."""
table = _quantity_table(source, design)
text = {
key: _format(value) if isinstance(value, (int, float)) else "-"
for key, value in table.items()
}
return {
"quantity_table": table,
"text": text,
"layout": {
"header": [list(row) for row in _CROSS_HEADER_KEYS],
"left": [list(row) for row in _CROSS_LEFT_ROWS],
"middle": [list(row) for row in _CROSS_MIDDLE_ROWS],
"right": [list(row) for row in _CROSS_RIGHT_ROWS],
},
}
@router.post("/{project_id}/sections/{route_id}/cross-quantity-table", response_model=None)
async def cross_quantity_table(
project_id: UUID, route_id: int, request: CrossQuantityTableRequest
) -> JSONResponse:
pool = get_db_pool()
async with pool.acquire() as connection:
longitudinal = await get_longitudinal_section(connection, project_id, route_id)
stored_path = await get_project_storage_relative_path(connection, project_id)
if not longitudinal or not stored_path:
return JSONResponse(
status_code=404,
content={"status": "error", "message": "종횡단 상세 결과가 없습니다."},
)
try:
*_, source = await asyncio.to_thread(
read_cross_design_inputs,
Path(resolve_stored_project_path(stored_path)),
str(longitudinal["longitudinal_file_path"]),
request.chainage_m,
)
sheet = cross_quantity_sheet(source, request.design)
except (FileNotFoundError, ValueError) as error:
return JSONResponse(status_code=404, content={"status": "error", "message": str(error)})
except Exception:
logger.exception(
"횡단 수량표 계산 실패: project_id=%s chainage=%s", project_id, request.chainage_m
)
return JSONResponse(
status_code=500,
content={"status": "error", "message": "횡단 수량표를 만들지 못했습니다."},
)
return JSONResponse(content={"status": "success", **sheet})