Files
Aislo/B09_Estimation/B09_Estimation_Router_Link.py
T
eomsangdonandClaude Opus 5.5 fce824b471 refactor(815-2): 옛 별칭 장부 걷기 — M02 장부 화면 · 장부 길 · 불러오기 확인 장부 쓰기 · 공사 내역 맞춤 기억
- M02 왼쪽 [별칭 장부] · M02_MasterTemplete_Hopyo_Ledger.ts · 장부 타입 · css 지움
- 길 GET · PUT /api/m02/layers/{층}/hopyo-ledger 지움 · M02_Hopyo_Ledger 의 장부 화면 줄(rows · 승인 상태) 지움
- 일위대가 불러오기 확인(M02_Hopyo_Build) = 장부에 안 적음 · 가져온 식 저장(M02_Doc_Router) = 기계 장부에 안 적음
- B09 공사 내역 맞춤 기억(내역맞춤.json 읽기 · 쓰기 · 이 짝 잊기 길 · 단추) 걷음 — 제안은 짝 표만
- storage 옛 장부 파일(맞춤 · 내역맞춤 · 별칭반려)은 읽지 않게만 · 지우지 않음
- M02_Hopyo_Ledger 모듈은 남김 — 짝 검사 · 열쇠 도우미 · 요소 잇기의 옛 장부 읽기(M02_Machine_Find · M02_Hopyo_Units · M02_Hopyo_Sangeun — 815-1 몫)
- 시험: test_718_2_alias_gather · test_739_1_alias_central · test_m02_hopyo_build 지움(걷은 길 · 화면 지킴) · test_707_4 기억 층 · 잊기 시험을 「걷음」 시험으로 · test_m02_hopyo_ledger rows 줄 · test_m02_hopyo_screen · test_721 장부 화면 줄 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 15:02:00 +09:00

121 lines
4.9 KiB
Python

"""B09 내역 줄 ↔ 우리 공종 잇기 길(PLAN 707-4) — 저장 안 함(화면 캐시로 → [저장] 때 서버 정본).
POST /api/projects/{id}/estimation/link/suggest {user?}
→ {줄: 제안(`B09_Estimation_Bill_Link.suggest` · 구조물 묶음에 딸린 줄 뺌) ·
저절로: 지난번 이음 · 하나 수 · 남은 · 자리 · 이은(`board`)}
POST /api/projects/{id}/estimation/link/apply
{pairs [{line, target, mode?}], auto, mode, counts?, user?} · 알림 남은 = 안 이은 항목 수
잇기(`link`) → {user(새 캐시), 알림, payload(그 캐시로 셈)}
POST …/link/undo {target, user?} → {user, 알림, payload} — 잇기 풀기
공사 내역 맞춤 기억(`실무자료/내역맞춤.json` · 지난번 이음 · 잊기)은 걷음(815-2 · 요구 765-2) —
제안은 짝 표만 · 옛 기억 파일은 읽지도 쓰지도 않음(지우지 않음).
끼워 넣기 = `B09_Estimation_Router`(같은 머리 · 같은 권한) · 화면 = `B09_Estimation_UI_Link`.
"""
from __future__ import annotations
import asyncio
from pathlib import Path
from typing import Any
from uuid import UUID
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from B08_Quantity.B08_Quantity_Router_Hydro import _project_root
from B09_Estimation import B09_Estimation_Bill_Link as linker
from B09_Estimation import B09_Estimation_Store as store
from common_util.common_util_auth import verify_session
router = APIRouter()
PATH = "/{project_id}/estimation/link"
def _draft(raw: Any) -> dict[str, Any] | None:
return store.clean_user(raw) if isinstance(raw, dict) else None
class SuggestBody(BaseModel):
user: dict[str, Any] | None = None
class Pair(BaseModel):
line: str = Field(min_length=1, max_length=80)
target: str = Field(min_length=1, max_length=200)
mode: str | None = None
class UndoBody(BaseModel):
target: str = Field(min_length=1, max_length=200)
user: dict[str, Any] | None = None
class ApplyBody(BaseModel):
pairs: list[Pair] = Field(default_factory=list, max_length=2000)
auto: bool = False
mode: str = "take"
counts: dict[str, float] | None = None
user: dict[str, Any] | None = None
async def _rows(project_id: UUID, root: Path, user: dict[str, Any]) -> list[dict[str, Any]]:
from B09_Estimation.B09_Estimation_Router import payload # 서로 부름 — 부를 때만
return (await payload(project_id, root, user, True))["sheets"]["bill"]["rows"]
@router.post(PATH + "/suggest")
async def suggest(
project_id: UUID, body: SuggestBody, session: dict[str, Any] = Depends(verify_session)
) -> dict[str, Any]:
root = await _project_root(project_id)
draft = _draft(body.user) or await asyncio.to_thread(store.read_user, root)
rows = await _rows(project_id, root, draft)
board = linker.board(draft, rows)
left = {r["key"] for r in board["남은"]}
lines = [s for s in linker.suggest(rows, {}) if s["key"] in left]
return {"줄": lines, "저절로": len(linker.auto_pairs(lines)), **board}
@router.post(PATH + "/apply")
async def apply(
project_id: UUID, body: ApplyBody, session: dict[str, Any] = Depends(verify_session)
) -> dict[str, Any]:
from B09_Estimation.B09_Estimation_Router import payload # 서로 부름 — 부를 때만
modes = {p.line: p.mode for p in body.pairs if p.mode}
if any(m not in linker.MODES for m in (body.mode, *modes.values())):
raise HTTPException(status_code=400, detail="수량 갈래는 take · price 중 하나")
root = await _project_root(project_id)
draft = _draft(body.user) or await asyncio.to_thread(store.read_user, root)
rows = await _rows(project_id, root, draft)
pairs = [(p.line, p.target) for p in body.pairs]
if body.auto:
pairs += [p for p in linker.auto_pairs(linker.suggest(rows, {})) if p not in pairs]
made, report = linker.link(draft, rows, pairs, body.mode, body.counts, modes)
made = store.clean_user(made)
report.pop("기억")
out = await payload(project_id, root, made, True)
left = linker.board(made, out["sheets"]["bill"]["rows"])["남은"]
report["남은"] = sum(1 for r in left if r["kind"] == "item")
return {"user": made, "알림": report, "payload": out}
@router.post(PATH + "/undo")
async def undo(
project_id: UUID, body: UndoBody, session: dict[str, Any] = Depends(verify_session)
) -> dict[str, Any]:
from B09_Estimation.B09_Estimation_Router import payload # 서로 부름 — 부를 때만
root = await _project_root(project_id)
draft = _draft(body.user) or await asyncio.to_thread(store.read_user, root)
made, why = linker.unlink(draft, await _rows(project_id, root, draft), body.target)
if why:
raise HTTPException(status_code=400, detail=why)
made = store.clean_user(made)
return {
"user": made,
"알림": {"풀음": 1},
"payload": await payload(project_id, root, made, True),
}