merge(511-B B-5 follow 합치기): origin/sub_laptop_3(follow 줄 엶 · /calc follow 서버 맥락) 받음 — 공유 파일 충돌 풂
- /calc 창구 차례 = 격자 경로 막기 → 단계 · 화면 길 서버 맥락(B-4) → follow 저장 정본 맥락(B-5 · stages 다시 짬) - 까닭 글 = cross 그대로 · pair_B5 걷음(follow 엶) - 시험 511_b1 · 309_2 · 309_2_browser = 잠긴 단계 줄 없음 · 잠금 예시 = form_default · 화면 열린 줄에 P5 P6 F1 F2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
브라우저가 계산 자리 판(`calc_sites.json`)에서 묶음을 「서버」로 둔 때만 부름
|
||||
(`design_calc_server.ts`).
|
||||
권한 = 프로젝트 접근(`protected_with_company`) · 호출량 = `main.py` 미들웨어 · 본문 상한 `MAX_BODY`.
|
||||
파일 경로 = 서버가 프로젝트 id 로 정함(그 storage 안 DTM 격자) · 입력에 경로 칸 = 400.
|
||||
파일 경로 = 서버가 프로젝트 id 로 정함(그 storage 안 DTM 격자) · 입력에 경로 칸 = 400 ·
|
||||
follow 「서버」 맥락(저장 노선 · 관 · 구조물 …) = 서버가 모음(`B06_Section_Router_Calc_Context`).
|
||||
실행 = 늘 떠 있는 Node 하나(`common_util_calc_runner.run_calc_served`) ·
|
||||
정본 저장 길은 이 창구를 안 씀.
|
||||
단계(`stage.*`) · 화면 길(`cross.refresh`) = 서버가 DTM 격자 경로 · 구조물 정의를 채움 —
|
||||
@@ -20,6 +21,7 @@ from uuid import UUID
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from B06_Section.B06_Section_Router_Calc_Context import attach_follow_context
|
||||
from common_util.common_util_calc_runner import (
|
||||
PAGE_IDS,
|
||||
SERVER_ONLY_IDS,
|
||||
@@ -97,6 +99,10 @@ async def run_calc_endpoint(project_id: UUID, request: Request) -> dict | JSONRe
|
||||
return _error(400, problem)
|
||||
if set(run) & (STAGE_IDS | PAGE_IDS):
|
||||
await _server_context(project_id, inputs)
|
||||
# follow 「서버」 = 저장 정본 맥락은 서버가 모음(브라우저 정본 칸 버림 · 511-B B-5)
|
||||
problem = await attach_follow_context(project_id, inputs)
|
||||
if problem:
|
||||
return _error(409, problem)
|
||||
if {"stations.list", "stations.plan"} & set(run) and isinstance(inputs.get("stations"), dict):
|
||||
# 심는 측점 · 카드 이름 표 = 서버 구조물 정의(서버 측점 생성과 같은 표 · 511-B B-2)
|
||||
from B05_Profile.B05_Profile_Engine_Sections_Node import structure_table
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
"""계산 창구 `/calc` 의 서버 맥락(PLAN 511-B B-5) — follow 「서버」(`stage.follow` · until
|
||||
follow)에 붙이는 저장 정본을 프로젝트 id 로 서버가 모음. 브라우저가 보낸 정본 칸은 버림(경로
|
||||
막기와 같은 원칙).
|
||||
|
||||
plan · types · definition · saved_pipes · saved_structures · saved_plus · interval_m ·
|
||||
longitudinal
|
||||
= 화면이 받는 값과 같은 길(GET 노선 계획 · 구조물 종류 · 정의 · 관 지점 · 구조물 · 추가 측점 ·
|
||||
측점 간격 기본 · 최신 노선 저장 종단) — 정본 쓰기 없음(관 지점 = 화면 GET 과 같은 파생 저장본만).
|
||||
브라우저 몫 = `BROWSER_KEYS`(묶음 입력 · 초안 전 시설 · 관 세션 장부 · 세션 계획고 초안).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
BROWSER_KEYS = frozenset({"input", "before_draft", "pipe_ids", "saved_edits", "until"})
|
||||
PLAN_KEYS = ("planned", "curves", "min_radius_m", "station_snap", "station_snap_step")
|
||||
|
||||
|
||||
async def follow_context(project_id: UUID) -> dict[str, Any] | str:
|
||||
"""follow 두 단계 맥락 — 못 모으면 까닭 글."""
|
||||
from B04_PreProcess.B04_PreProcess_Router_Basins import get_pipe_points
|
||||
from B05_Profile.B05_Profile_Engine_Sections_Core import SectionGenerationOptions
|
||||
from B05_Profile.B05_Profile_Engine_Sections_Node import structure_table
|
||||
from B05_Profile.B05_Profile_Plus_Stations import load_plus_stations
|
||||
from B05_Profile.B05_Profile_Router_Replan import _project_paths, read_route_plan
|
||||
from B05_Profile.B05_Profile_Structures_Router import read_structures
|
||||
|
||||
paths = await _project_paths(project_id)
|
||||
if paths is None:
|
||||
return "프로젝트 저장 경로 없음"
|
||||
root = paths[0]
|
||||
plan, pipes, structures, (types, definition) = await asyncio.gather(
|
||||
read_route_plan(project_id),
|
||||
get_pipe_points(project_id),
|
||||
read_structures(project_id),
|
||||
asyncio.to_thread(structure_table),
|
||||
)
|
||||
for name, got in (("노선", plan), ("관", pipes)):
|
||||
if not isinstance(got, dict):
|
||||
return f"{name} 정본을 못 읽음"
|
||||
if not hasattr(structures, "model_dump"):
|
||||
return "구조물 정본을 못 읽음"
|
||||
return {
|
||||
"plan": {key: plan.get(key) for key in PLAN_KEYS},
|
||||
"types": types,
|
||||
"definition": definition,
|
||||
"saved_pipes": [
|
||||
{k: v for k, v in pipe.items() if k != "lonlat"} for pipe in pipes["pipe_points"]
|
||||
],
|
||||
"saved_structures": structures.model_dump(mode="json")["structures"],
|
||||
"saved_plus": await asyncio.to_thread(load_plus_stations, root),
|
||||
"interval_m": SectionGenerationOptions().station_interval_m,
|
||||
"longitudinal": await _saved_longitudinal(project_id, root),
|
||||
}
|
||||
|
||||
|
||||
async def _saved_longitudinal(project_id: UUID, root: Path) -> dict[str, Any] | None:
|
||||
"""최신 노선의 저장 종단(화면 상세 `longitudinal`) — 없거나 못 읽으면 None."""
|
||||
from B05_Profile.B05_Profile_Repository import get_latest_route
|
||||
from B06_Section.B06_Section_Repository import get_longitudinal_section
|
||||
from config.config_db import get_db_pool
|
||||
|
||||
async with get_db_pool().acquire() as connection:
|
||||
route = await get_latest_route(connection, project_id)
|
||||
row = route and await get_longitudinal_section(connection, project_id, int(route["id"]))
|
||||
rel = (row or {}).get("longitudinal_file_path")
|
||||
if not rel:
|
||||
return None
|
||||
path = (root / str(rel)).resolve()
|
||||
if not path.is_relative_to(root.resolve()) or not path.is_file():
|
||||
return None
|
||||
data = json.loads(await asyncio.to_thread(path.read_text, encoding="utf-8"))
|
||||
return data if isinstance(data, dict) else None
|
||||
|
||||
|
||||
async def attach_follow_context(project_id: UUID, inputs: dict[str, Any]) -> str | None:
|
||||
"""`inputs.stages`(until follow) — 브라우저 몫만 남기고 서버 맥락을 붙임 · 못 붙이면 까닭 글."""
|
||||
stages = inputs.get("stages")
|
||||
if not isinstance(stages, dict) or stages.get("until") != "follow":
|
||||
return None
|
||||
context = await follow_context(project_id)
|
||||
if isinstance(context, str):
|
||||
return context
|
||||
inputs["stages"] = {
|
||||
**{k: v for k, v in stages.items() if k in BROWSER_KEYS},
|
||||
**context,
|
||||
}
|
||||
return None
|
||||
Reference in New Issue
Block a user