Merge remote-tracking branch 'origin/sub_laptop_5' into main_laptop_1
This commit is contained in:
@@ -22,7 +22,7 @@ from fastapi.responses import JSONResponse
|
||||
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Analyze import preview_stages
|
||||
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Expand import AnalysisStopped
|
||||
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Export import drainage_dir
|
||||
from B04_PreProcess.B04_PreProcess_Router_Basins import _build, _payload
|
||||
from B04_PreProcess.B04_PreProcess_Router_Basins import AUTO_MODES, _build, _payload
|
||||
from B04_PreProcess.B04_PreProcess_Router_Watershed import _prepare, router
|
||||
from common_util.common_util_drainage_context import _open_sampler, load_drainage_context
|
||||
from common_util.common_util_drainage_detail import RoadRouting
|
||||
@@ -78,7 +78,8 @@ def live_routing(preview: Any, route_xy: list[tuple[float, float]], to_lonlat: A
|
||||
async def post_drainage_live(
|
||||
project_id: UUID, payload: dict[str, Any] | None = None
|
||||
) -> dict[str, Any] | JSONResponse:
|
||||
"""몸통 = {generation, route: RouteDraft | null(저장 노선), points: 관 목록 | null(자동)}."""
|
||||
"""몸통 = {generation, route: RouteDraft | null(저장 노선),
|
||||
points: 관 목록 | null(세류선 교차만), auto: [자동 제안] 방식(all · add · 369) | 없음}."""
|
||||
body = payload or {}
|
||||
generation = int(body.get("generation") or 0)
|
||||
key = str(project_id)
|
||||
@@ -87,6 +88,9 @@ async def post_drainage_live(
|
||||
def superseded() -> bool:
|
||||
return _LATEST_GENERATION.get(key) != generation
|
||||
|
||||
auto = body.get("auto")
|
||||
if auto not in AUTO_MODES:
|
||||
return JSONResponse(status_code=400, content={"status": "error", "message": "auto 틀림"})
|
||||
try:
|
||||
raw = body.get("points")
|
||||
points = None if raw is None else parse_pipe_points(raw)
|
||||
@@ -139,7 +143,7 @@ async def post_drainage_live(
|
||||
[list(to_lonlat(x, y)) for x, y in line.coords] for line in preview.region.split.upstream
|
||||
]
|
||||
detail, carried = await asyncio.to_thread(
|
||||
_build, context.stored_path, live, points, routing, upstream
|
||||
lambda: _build(context.stored_path, live, points, routing, upstream, auto=auto)
|
||||
)
|
||||
if detail is None:
|
||||
return JSONResponse(
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
* - 덮개 · 잠금 없음 — 다른 칸 조작은 그대로.
|
||||
* - [자동 제안](369) — 고름 창 ① 전체 자동(all) ② 지정 포함 추가(add) · [배관 배치 초기화] = 세류선 교차
|
||||
* 관만 · 셋 다 초안(세션) · [저장] 때 정본 · 답이 오면 사진첩 한 걸음(늦은 답도 앞 걸음에 안 묻힘) ·
|
||||
* 낡은 동안 [자동 제안] 끔.
|
||||
* 낡은 동안 [자동 제안] 끔 · 노선 초안 중이거나 저장 분석이 낡았으면(옛 노선) 「지금 노선으로 셈」 길에
|
||||
* 방식을 실어 보냄(저장 분석으로 셈 = 옛 노선 자리 · 409).
|
||||
* ========================================================================== */
|
||||
|
||||
import { API_BASE_URL } from "@config/config_frontend";
|
||||
@@ -76,7 +77,7 @@ export interface DrainageLive {
|
||||
/** 「지금 노선으로 셈」 — 서버 `post_drainage_live` · 작업본에 안 씀. */
|
||||
async function fetchLive(
|
||||
projectId: string,
|
||||
body: ReturnType<typeof liveRequestBody>,
|
||||
body: ReturnType<typeof liveRequestBody> | (Proposal & { generation: number }),
|
||||
signal: AbortSignal,
|
||||
): Promise<DetailBasinResponse & { status?: string; generation?: number }> {
|
||||
const response = await fetch(`${API_BASE_URL}/projects/${projectId}/drainage/live`, {
|
||||
@@ -97,6 +98,9 @@ export function readDrainageMode(): DrainageMode {
|
||||
|
||||
const L = (key: keyof typeof ui_locales): string => ui_locales[key][currentLanguageIndex];
|
||||
|
||||
/** [자동 제안] · [배관 배치 초기화] 요청 — 관 목록 없음(null) = 세류선 교차만. */
|
||||
type Proposal = { points: DetailPipeInput[] | null; auto?: "all" | "add" };
|
||||
|
||||
export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
const viewport = deps.root.querySelector<HTMLElement>(".b05-drainage__viewport") as HTMLElement;
|
||||
const header = deps.root.querySelector<HTMLElement>(".b05-drainage__header") as HTMLElement;
|
||||
@@ -124,6 +128,8 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
let drafted = false;
|
||||
/** 저장분 다시 받기 답이 「낡음」이었나(`markStale` 이 세움). */
|
||||
let savedStale = false;
|
||||
/** 저장 분석이 옛 노선(서버 「낡음」 답 · [확정] 전까지) — [자동 제안]은 지금 노선으로 셈. */
|
||||
let analysisStale = false;
|
||||
let generation = 0;
|
||||
/** 마지막 단계 입력 지문 — 같으면 묶음이 다시 돈 것뿐. */
|
||||
let key = "";
|
||||
@@ -169,7 +175,8 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
inflight = null;
|
||||
}
|
||||
|
||||
function refresh(): void {
|
||||
/** 지금 노선으로 셈 — `proposal` 이면 그 관 목록 · 방식으로(답 = 관 초안 · 사진첩 한 걸음). */
|
||||
function refresh(proposal?: Proposal): void {
|
||||
const projectId = deps.runner.projectId();
|
||||
if (!projectId) return;
|
||||
cancel();
|
||||
@@ -177,7 +184,7 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
const controller = new AbortController();
|
||||
inflight = controller;
|
||||
paint();
|
||||
const body = liveRequestBody(sent.generation, route, deps.pipeRequest());
|
||||
const body = { ...liveRequestBody(sent.generation, route, deps.pipeRequest()), ...proposal };
|
||||
void deps.runner
|
||||
.run(async () => {
|
||||
const response = await fetchLive(projectId, body, controller.signal);
|
||||
@@ -190,10 +197,20 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
if (inflight !== controller) return;
|
||||
inflight = null;
|
||||
if (applied) stale = false;
|
||||
if (applied && proposal) {
|
||||
deps.rememberPipes();
|
||||
pageHistory()?.record();
|
||||
}
|
||||
paint();
|
||||
});
|
||||
}
|
||||
|
||||
/** [자동 제안] · [배관 배치 초기화] — 저장 분석이 지금 노선과 같으면 빠른 길(detail-basins). */
|
||||
function proposeDraft(proposal: Proposal): void {
|
||||
if (analysisStale || draftMakesStale(route)) return refresh(proposal);
|
||||
draft((projectId) => computeDetailBasins(projectId, proposal.points, proposal.auto));
|
||||
}
|
||||
|
||||
/** 평면 · 관 자리 조작 — 처음으로(요청 끊기 · 다시 흐림 또는 다시 3 s). */
|
||||
function invalidate(): void {
|
||||
cancel();
|
||||
@@ -264,7 +281,7 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
input.name = "b05-drainage-propose";
|
||||
input.addEventListener("change", () => {
|
||||
close();
|
||||
draft((projectId) => computeDetailBasins(projectId, deps.pipeRequest(), value));
|
||||
proposeDraft({ points: deps.pipeRequest(), auto: value });
|
||||
});
|
||||
const text = document.createElement("span");
|
||||
text.textContent = `${order === 0 ? "①" : "②"} ${label} — ${hint}`;
|
||||
@@ -284,7 +301,7 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
paint();
|
||||
}
|
||||
|
||||
update.addEventListener("click", refresh);
|
||||
update.addEventListener("click", () => refresh());
|
||||
gear.addEventListener("click", openSettings);
|
||||
propose?.addEventListener("click", openPropose);
|
||||
|
||||
@@ -300,10 +317,11 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
else if (wasDraft) backToSaved();
|
||||
return null;
|
||||
},
|
||||
refresh,
|
||||
refresh: () => refresh(),
|
||||
suggest,
|
||||
markStale() {
|
||||
savedStale = true;
|
||||
analysisStale = true;
|
||||
if (stale) return;
|
||||
stale = true;
|
||||
if (mode === "auto" && !inflight) timer = window.setTimeout(refresh, QUIET_MS);
|
||||
@@ -312,9 +330,10 @@ export function createDrainageLive(deps: DrainageLiveDeps): DrainageLive {
|
||||
markFresh() {
|
||||
cancel();
|
||||
stale = false;
|
||||
analysisStale = false;
|
||||
paint();
|
||||
},
|
||||
resetToStreams: () => draft((projectId) => computeDetailBasins(projectId, null)),
|
||||
resetToStreams: () => proposeDraft({ points: null }),
|
||||
get stale() {
|
||||
return stale;
|
||||
},
|
||||
|
||||
@@ -348,6 +348,30 @@ async function main() {
|
||||
remembered: remembered - remembered0,
|
||||
records: records - records0,
|
||||
};
|
||||
// 저장 분석이 옛 노선(서버 낡음 답) → [업데이트] 뒤 [자동 제안] = 지금 노선으로 셈 길에 방식 실음
|
||||
await runner.run(async () => ({ basins: [], pipe_points: pipes, stale: "배수유역 낡음" }));
|
||||
update.fire("click");
|
||||
answer(calls[calls.length - 1]);
|
||||
await flush();
|
||||
const viaDetail = detailCalls.length;
|
||||
modeRow = 0;
|
||||
propose.fire("click");
|
||||
const liveCall = calls[calls.length - 1];
|
||||
answer(liveCall);
|
||||
await flush();
|
||||
live.resetToStreams();
|
||||
const resetCall = calls[calls.length - 1];
|
||||
answer(resetCall);
|
||||
await flush();
|
||||
out.proposeLive = {
|
||||
auto: liveCall.body.auto,
|
||||
points: liveCall.body.points.length,
|
||||
resetPoints: resetCall.body.points,
|
||||
resetAuto: resetCall.body.auto ?? null,
|
||||
detailCalls: detailCalls.length - viaDetail,
|
||||
remembered: remembered - remembered0,
|
||||
records: records - records0,
|
||||
};
|
||||
// 입력 지문 — 판 번호 · 관 차례는 안 봄 · 꼭짓점 · 관 자리(cm) · id 는 봄
|
||||
const { inputKey } = load(path.join(B05, "logic", "B05_Profile_Drainage_Live.ts"));
|
||||
const a = { id: "a1", chainage_m: 100 };
|
||||
|
||||
@@ -81,6 +81,16 @@ def test_자동_제안_두_방식_초기화_세류선만_한_걸음(r):
|
||||
"remembered": 3, # 셋 다 초안
|
||||
"records": 3, # 답마다 사진첩 한 걸음
|
||||
}
|
||||
# 저장 분석이 옛 노선 — 지금 노선으로 셈 길(detail-basins 안 부름) · 같은 초안 · 한 걸음
|
||||
assert r["proposeLive"] == {
|
||||
"auto": "all",
|
||||
"points": 1,
|
||||
"resetPoints": None,
|
||||
"resetAuto": None,
|
||||
"detailCalls": 0,
|
||||
"remembered": 5,
|
||||
"records": 5,
|
||||
}
|
||||
|
||||
|
||||
def test_move_while_busy_cancels(r):
|
||||
|
||||
Reference in New Issue
Block a user