- 새 M02_San_MatPick: 종 열쇠(이름 · 규격 · 단위 · 단서) · 고름 자리 = 개인 층 실무자료/재료고름/<파일 지문>.json([끝내기] 뒤 남음 · 같은 파일 다시 올리면 그대로) · 판(블록마다 재료찾기 판정 → 종 · 첫 판 한 번 셈 → 산근 저장본 칸) · 만들 때 얹기 · 이미 만든 SK 얹기(가져온 그대로인 줄만 · 바꿈 · 따로 고른 줄 그대로 · 풀면 가져온 그대로)
- 만들기: from-hopyo 가 그 파일 고름째 만듦(저장 안 한 판 고름 = 만들 때 저장) · 원본 지문 · 고른 줄 = 이음 + 재료찾기.고름 · 문서 셈 우리 값(엑셀 값 기억 없음) · 별도계상 = 품목만(줄 이음 · 단가 0 그대로)
- 문서 머리 가져온.재료(판정 · 후보 · 단서 · 처음 연결) · 가져온.지문
- 길: GET/POST hopyo-materials/{HP}(판 · [저장] · 만든 것 적용은 물음 예일 때만) · GET/POST material-picks/{지문}(SK 상세 물음)
- 화면: 단가산출 목록 위 「고를 재료」 판(후보 고름 · 찾기 · 고름 풀기 · n 블록에 적용 · 이어진 재료 접힘 · [저장]) · 저장 안 한 고름 = 떠날 때 물음 · 줄 쌓기 재료 줄 판정 배지 · 풍선말(판정 · 단서 · 후보) · 이름 단추 · [이음 고르기] 뒤 「같은 파일 n 줄」 물음
- 별도계상 설명 = 「별도계상 · 품목: 이름 규격」(엔진 이은 품목은 자재표 이름) — test_827_2 기대 글 고침(품목 이음 → 품목 이름)
- 시험 test_827_3 셋(파일 고름 블록 둘 · 다른 파일 0 · 별칭 · 마스터 그대로 · 풀기 · 다시 올림 유지 · 물음 예일 때만 · 따로 바꾼 줄 보존 · 별도계상 품목 + 단가 0)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
436 lines
18 KiB
Python
436 lines
18 KiB
Python
"""M02 일위대가 실무자료 길(PLAN 785-5 · 요구 642 · 설계 642-A 2-9) — `/api/m02/layers/{층}/hopyo…`.
|
|
|
|
[수정] 보기 `hopyo-view/{키}/{번}`(809-1 · `M02_Hopyo_View` · 경쟁사 로직 인정 · 요소만 연결 · 읽기만) ·
|
|
엑셀 보기 머리 · 목록 = `View.listing`(줄 표지) · 옮기기 = [내 일위대가로] `hopyo-build-all {번}`(807-1).
|
|
옛 판정 · 검산 · 옮기기 · 근거 제안 길(785-5 `hopyo-judge` · `check` · `move` · `price` · 793-3a `hopyo-basis`)은
|
|
809-1 에서 지움(요구 703 — 기초로 바꾸기 없음).
|
|
그대로 쓰는 길(목록 · 올림 · 엑셀 보기 머리 · 격자 · 지우기 · 모두 고정 · 붙여넣기 · 다시 잇기 ·
|
|
구조물 조합 · 칠 = B09 · M01 이 씀) · 옛 맞춤 길(호표 하나 맞춤 · 미리 셈 · 실무 호표 · 확인 만들기 · 안 이은 줄 ·
|
|
단가산출 만들기)은 785-7 에서 지움.
|
|
층 권한 = `Router_Items._root` · ⚠ 등록은 `main.py`(로그인만).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from typing import Any, Literal
|
|
|
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
from common_util.common_util_auth import verify_session
|
|
from M02_MasterTemplete import M02_Doc_FromNative as from_native
|
|
from M02_MasterTemplete import M02_Hopyo_Build as hopyo_build
|
|
from M02_MasterTemplete import M02_Hopyo_Excel as hopyo_excel
|
|
from M02_MasterTemplete import M02_Hopyo_Forget as hopyo_forget
|
|
from M02_MasterTemplete import M02_Hopyo_Grid as hopyo_grid
|
|
from M02_MasterTemplete import M02_Hopyo_Item as hopyo_item
|
|
from M02_MasterTemplete import M02_Hopyo_Marks as hopyo_marks
|
|
from M02_MasterTemplete import M02_Hopyo_Paste as hopyo_paste
|
|
from M02_MasterTemplete import M02_Hopyo_Relink as hopyo_relink
|
|
from M02_MasterTemplete import M02_Hopyo_Store as hopyo_store
|
|
from M02_MasterTemplete import M02_Hopyo_View as hopyo_view_
|
|
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
|
from M02_MasterTemplete import M02_San_MatPick as mat_pick
|
|
from M02_MasterTemplete import M02_Sangeun_FromHopyo as from_hopyo
|
|
from M02_MasterTemplete.M02_MasterTemplete_Router_Items import Layer, Session, _call, _root
|
|
|
|
router = APIRouter(prefix="/api/m02", tags=["M02 Hopyo"])
|
|
|
|
|
|
# ── [수정] 보기(809-1) — 경쟁사 로직 인정 · 요소만 연결 · 읽기만 ─────────────────
|
|
|
|
|
|
@router.get("/layers/{layer}/hopyo-view/{key}/{no}")
|
|
def hopyo_view(
|
|
layer: Layer,
|
|
key: str,
|
|
no: int,
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""호표 하나 → 일위대가 문서 미리보기(엑셀 숫자 그대로 고정 줄) + 줄 표지(로직 · 요소) — 저장 없음."""
|
|
folder = _root(session, layer, owner)
|
|
return _call(folder, hopyo_view_.view, folder, key, no)
|
|
|
|
|
|
# ── 그대로 쓰는 길 ─────────────────────────────────────────────────────
|
|
HOPYO_MAX_BYTES = 20 * 1024 * 1024
|
|
|
|
|
|
def _who(session: Session) -> dict[str, Any]:
|
|
return {"id": session.get("user_id"), "이름": session.get("name")}
|
|
|
|
|
|
def _upload(name: str, data: bytes, who: dict[str, Any]) -> dict[str, Any]:
|
|
# 경쟁사 원파일(810-2) — 같은 공사 엑셀 HP 에 붙음 · 원파일 우선
|
|
if from_native.is_native(name):
|
|
return from_hopyo.upload_native(store.root(), name, data, who)
|
|
mark = hopyo_store.fingerprint(data)
|
|
hopyo_forget.sweep(store.root()) # 하루 지난 작업 중 HP 끝냄(743-1)
|
|
if same := next((r for r in hopyo_store.rows(store.root()) if r.get("지문") == mark), None):
|
|
cannot = None
|
|
if not from_hopyo.has(store.root(), same["키"]): # 793-3b 전에 올린 HP
|
|
cannot = from_hopyo.keep(store.root(), same["키"], name, data)
|
|
return {**same, "이어서": True, **({"읽기불가": cannot} if cannot else {})}
|
|
try:
|
|
parsed = hopyo_excel.read_book(name, data)
|
|
except hopyo_excel.ExcelError as error:
|
|
raise store.StoreError(400, str(error)) from error
|
|
try:
|
|
grid = hopyo_grid.read(name, data, parsed)
|
|
except Exception: # 격자는 보기용 덤 — 못 만들어도 올리기는 됨(보기 창 404)
|
|
grid = None
|
|
# 산근 시트(793-3b 가져온 식 열기) = HP 를 목록에 보이기 전에 보관(814-1) · 못 씀 사유 → 화면 토스트(797-1)
|
|
keep = lambda key: from_hopyo.keep(store.root(), key, name, data) # noqa: E731
|
|
return hopyo_store.save(store.root(), name, parsed, who, grid, mark, keep)
|
|
|
|
|
|
@router.get("/layers/{layer}/hopyo")
|
|
def hopyo_list(
|
|
layer: Layer, owner: str | None = None, session: Session = Depends(verify_session)
|
|
) -> dict:
|
|
"""실무자료 목록 `{줄: [{키, 파일, 올린때, 올린이, 시트, 호표수, 어긋남}]}`."""
|
|
return {"줄": hopyo_store.rows(_root(session, layer, owner))}
|
|
|
|
|
|
@router.post("/layers/{layer}/hopyo")
|
|
def hopyo_upload(
|
|
layer: Layer,
|
|
file: UploadFile = File(...),
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""엑셀(xls · xlsx) 올리기 — 「일위대가」 시트를 읽어 결과 · 격자 저장 · 20 MB ·
|
|
엉터리 · 시트 없음 400 · 원파일(.estx · .stc) = 산근 원파일 우선(810-2 `upload_native`)."""
|
|
folder = _root(session, layer, owner, write=True)
|
|
data = file.file.read(HOPYO_MAX_BYTES + 1)
|
|
if len(data) > HOPYO_MAX_BYTES:
|
|
raise HTTPException(status_code=400, detail="파일이 너무 큼(20 MB 넘음)")
|
|
name = Path(file.filename or "실무자료").name[:120]
|
|
return _call(folder, _upload, name, data, _who(session))
|
|
|
|
|
|
@router.get("/layers/{layer}/hopyo/{key}")
|
|
def hopyo_read(
|
|
layer: Layer, key: str, owner: str | None = None, session: Session = Depends(verify_session)
|
|
) -> dict:
|
|
"""실무자료 하나(엑셀 보기 머리 · 목록) — 검산 · 호표마다 `맞춤` [연결됨 + 로직 줄, 줄 수] · `줄상태` ·
|
|
`만든`(`M02_Hopyo_View.listing` · 809-1)."""
|
|
folder = _root(session, layer, owner)
|
|
return _call(folder, hopyo_view_.listing, folder, key, folder)
|
|
|
|
|
|
@router.get("/layers/{layer}/hopyo-grid/{key}")
|
|
def hopyo_grid_read(
|
|
layer: Layer, key: str, owner: str | None = None, session: Session = Depends(verify_session)
|
|
) -> dict:
|
|
"""엑셀꼴 격자(329-1) `{책: Workbook, 호표: [{번, 이름, 행}], 단산: [{번, 이름, 행}]}` ·
|
|
옛 파일 404."""
|
|
folder = _root(session, layer, owner)
|
|
return _call(folder, hopyo_store.grid, folder, key)
|
|
|
|
|
|
class _Strict(BaseModel):
|
|
model_config = ConfigDict(extra="forbid") # 금액 · 품 칸 = 400(서버가 셈)
|
|
|
|
|
|
class BuildTarget(_Strict):
|
|
"""대상 하나 — 로직 · 자재 · 호표 중 하나 · 배 = 우리 수량 ÷ 엑셀 수량 · 몫 = 섞인 줄 몫."""
|
|
|
|
로직: str | None = None
|
|
자재: str | None = None
|
|
호표: int | None = None
|
|
전국: bool | None = None # 「전국」 자재(361-1 · 키 없음 · 서버가 줄에서 다시 셈)
|
|
노임: str | None = None # 729-2 사용자 이음 — LB 키(엑셀 숫자 + 이음)
|
|
기계: str | None = None # EQ 키
|
|
비율: Literal["노무비", "재료비", "경비"] | None = None # % 줄 밑수
|
|
입력값: dict[str, Any] = Field(default_factory=dict)
|
|
배: float = 1
|
|
몫: float | None = None
|
|
|
|
|
|
class BuildLine(_Strict):
|
|
"""확인한 줄 — `차례` = 호표 안 줄(0부터) · `대상` 목록 또는 `뺌` 까닭."""
|
|
|
|
차례: int
|
|
대상: list[BuildTarget] = Field(default_factory=list)
|
|
뺌: Literal["우리에 없음", "안 씀"] | None = None
|
|
|
|
|
|
class BuildWhole(_Strict):
|
|
대상: list[BuildTarget]
|
|
|
|
|
|
class BuildBody(_Strict):
|
|
"""실무 호표 → 이 층 사전조합 — 파일 층 `층` · `주인` · `키` + 호표 `번` + 확인한 `줄` ·
|
|
직접 줄 호표 = `통째` · 같은 본뜬은 늘 덮음."""
|
|
|
|
층: Layer
|
|
주인: str | None = None
|
|
키: str
|
|
번: int
|
|
줄: list[BuildLine] = Field(default_factory=list, max_length=500)
|
|
통째: BuildWhole | None = None
|
|
|
|
|
|
class BuildAllBody(_Strict):
|
|
"""시트 통째(705-3) — 파일 층 `층` · `주인` · `키` 만(숫자 · 줄 칸 없음 · 서버가 파일에서 읽음) ·
|
|
`번` = 그 호표 하나 + 하위(807-1 [내 일위대가로])."""
|
|
|
|
층: Layer
|
|
주인: str | None = None
|
|
키: str
|
|
번: int | None = None
|
|
|
|
|
|
@router.post("/layers/{layer}/hopyo-build-all")
|
|
def hopyo_make_all(
|
|
layer: Layer,
|
|
body: BuildAllBody,
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""[모두 고정으로 들여오기] — 시트 호표 모두 고정 줄로 · 하위 먼저 · 확인된 호표 그대로 →
|
|
`{호표, 줄, 품셈, 고정, 호표참조, 빠짐, 이음, 이음후보, 같음, 그대로, 다름, 실패}`."""
|
|
folder = _root(session, layer, owner, write=True)
|
|
source = _root(session, body.층, body.주인)
|
|
args = (
|
|
folder,
|
|
source,
|
|
body.키,
|
|
_who(session),
|
|
True,
|
|
body.번,
|
|
) # 729-2 남은 줄 = 실패(번 = 고정)
|
|
return _call(folder, hopyo_build.build_all, *args)
|
|
|
|
|
|
@router.delete("/layers/{layer}/hopyo/{key}")
|
|
def hopyo_delete(
|
|
layer: Layer, key: str, owner: str | None = None, session: Session = Depends(verify_session)
|
|
) -> dict:
|
|
"""실무자료 지우기 = [끝내기](718-1 · 743-1 · 화면 떠남도) — HP · 격자 · 맞춤셈 캐시 지움 ·
|
|
만든 일위대가 · 단가산출은 엑셀 사본만 걷고 남음 · 별칭 장부 그대로 → `{지움, 만든}`."""
|
|
folder = _root(session, layer, owner, write=True)
|
|
got = _call(folder, hopyo_forget.finish, folder, key)
|
|
return got
|
|
|
|
|
|
class PasteBody(_Strict):
|
|
"""엑셀 붙여넣기(705-2) — `글` = 붙인 글 그대로 · `열` = 기억한 열 짝(없으면 머리 글 · 짐작 + 물음) ·
|
|
`줄만` = 열린 문서에 줄로 · `있음` = 문서의 고정 줄 가름(이름 겹침 피함)."""
|
|
|
|
글: str = Field(max_length=hopyo_paste.TEXT_MAX)
|
|
열: dict[str, int] | None = None
|
|
줄만: bool = False
|
|
있음: list[str] = Field(default_factory=list, max_length=500)
|
|
|
|
|
|
@router.post("/layers/{layer}/hopyo-paste")
|
|
def hopyo_paste_run(
|
|
layer: Layer,
|
|
body: PasteBody,
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""[Ctrl+V] — `갈래` 물음(열 짝) · 줄(고정 줄 · 저장은 문서 [저장]) · 만듦(실무자료 + 호표마다 사전조합 ·
|
|
층 쓰기 · `build_all` 답 + 키)."""
|
|
folder = _root(session, layer, owner, write=True)
|
|
args = (folder, body.글, body.열, _who(session), body.줄만, body.있음)
|
|
return _call(folder, hopyo_paste.paste, *args)
|
|
|
|
|
|
class RelinkRowsBody(BaseModel):
|
|
"""다시 잇기 표(705-2) — `키` = 이 층 사전조합들 · `문서` = 열린 문서(저장 전 · 줄 · 후보만)."""
|
|
|
|
키: list[str] = Field(default_factory=list, max_length=500)
|
|
문서: dict[str, Any] | None = None
|
|
|
|
|
|
@router.post("/layers/{layer}/hopyo-relink/rows")
|
|
def hopyo_relink_rows(
|
|
layer: Layer,
|
|
body: RelinkRowsBody,
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""「이 공사 단가로 다시 잇기」 표 — 고정 줄마다 붙인 단가 · 이은 단가 · 후보(층 읽기)."""
|
|
folder = _root(session, layer, owner)
|
|
return _call(folder, hopyo_relink.rows, body.키, body.문서)
|
|
|
|
|
|
class RelinkBody(_Strict):
|
|
"""바꿈 `[{키, 줄, 노임?, 자재?}]` — 이음 키만(숫자 칸 없음 · 값 null = 풂)."""
|
|
|
|
바꿈: list[dict[str, Any]] = Field(max_length=hopyo_relink.CHANGES_MAX)
|
|
|
|
|
|
@router.post("/layers/{layer}/hopyo-relink")
|
|
def hopyo_relink_apply(
|
|
layer: Layer,
|
|
body: RelinkBody,
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""[바꿈] · [되돌리기] — 이 층 사전조합 이음 칸 고쳐 씀(층 쓰기) → `{바뀜, 문서, 되돌림}`."""
|
|
folder = _root(session, layer, owner, write=True)
|
|
return _call(folder, hopyo_relink.apply, body.바꿈)
|
|
|
|
|
|
@router.get("/layers/{layer}/hopyo/{key}/{no}/siblings")
|
|
def hopyo_siblings(
|
|
layer: Layer,
|
|
key: str,
|
|
no: int,
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""규격 묶음 제안(704-4) — 이름 · 단위 · 줄 구성 같고 규격만 다른 호표 `{묶음 [{번, 규격, 열}], 이름}`."""
|
|
folder = _root(session, layer, owner)
|
|
return _call(folder, hopyo_item.siblings, folder, key, no)
|
|
|
|
|
|
class ItemBody(BuildBody):
|
|
"""[구조물 조합으로](704-4) — 확인 창 몸 그대로 + `묶음` = 규격 열로 묶을 호표 번 · `이름` = 항목 이름."""
|
|
|
|
묶음: list[int] = Field(default_factory=list, max_length=20)
|
|
이름: str | None = Field(None, max_length=120)
|
|
새로: bool = False # 같은 내용 항목 409 뒤 — 그래도 새로(733-2)
|
|
씀: str | None = Field(None, max_length=80) # 같은 내용 409 뒤 — 그 항목 씀
|
|
|
|
|
|
@router.post("/layers/{layer}/hopyo-item")
|
|
def hopyo_to_item(
|
|
layer: Layer,
|
|
body: ItemBody,
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""실무 호표 → 이 층 구조물 조합 항목(사전조합 안 만듦 · 이 층 쓰기 · 파일 층 읽기) →
|
|
`{key, 열, 이름, 줄수, 열수, 빠진줄}`."""
|
|
folder = _root(session, layer, owner, write=True)
|
|
source = _root(session, body.층, body.주인)
|
|
lines = [one.model_dump(exclude_none=True) for one in body.줄]
|
|
whole = body.통째.model_dump(exclude_none=True) if body.통째 else None
|
|
args = (folder, source, body.키, body.번, lines, whole, body.묶음, body.이름, _who(session))
|
|
args += (body.새로, body.씀)
|
|
return _call(folder, hopyo_item.make_item, *args)
|
|
|
|
|
|
@router.get("/layers/{layer}/hopyo-marks/{key}")
|
|
def hopyo_marks_read(
|
|
layer: Layer,
|
|
key: str,
|
|
owner: str | None = None,
|
|
to: Layer | None = None,
|
|
to_owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""하이라이트 재료(779-2 · 읽기만) — `블록 [{번, 판정, SK, 갈래, 못옮김}]` · `to` = SK 를 볼 층."""
|
|
folder = _root(session, layer, owner)
|
|
target = _root(session, to, to_owner) if to else folder
|
|
return _call(folder, lambda: {"블록": hopyo_marks.blocks(folder, key, target)})
|
|
|
|
|
|
# ── 불러오기 재료 고름(827-3 · 별칭 아님 · 고름 자리 = 개인 층) ──────────────────
|
|
class MatPickBody(_Strict):
|
|
"""`고름` {종: 고른 것 | null(풂)} · `만든적용` = 이미 만든 SK 에도(물음 예) · `종` = 얹을 종(없으면 `고름` 의 종)."""
|
|
|
|
고름: dict[str, dict[str, Any] | None] = Field(default_factory=dict, max_length=500)
|
|
만든적용: bool = False
|
|
종: list[str] | None = Field(default=None, max_length=500)
|
|
|
|
|
|
@router.get("/layers/{layer}/hopyo-materials/{key}")
|
|
def hopyo_materials(
|
|
layer: Layer,
|
|
key: str,
|
|
owner: str | None = None,
|
|
to: Layer | None = None,
|
|
to_owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""고를 재료 판 — `{지문, 파일, 종 [판정 · 후보 · 단서 · 블록 · 고름?], 만든 [SK]}`(첫 판 = 블록 셈 한 번)."""
|
|
folder = _root(session, layer, owner)
|
|
target = _root(session, to, to_owner) if to else folder
|
|
mine = _root(session, "personal", None)
|
|
|
|
def run() -> dict:
|
|
fp, name = mat_pick.fingerprint(folder, key)
|
|
picks = mat_pick.read(mine, fp)["고름"]
|
|
kinds = [g | ({"고름": picks[g["종"]]} if g["종"] in picks else {}) for g in mat_pick.census(folder, key)] # fmt: skip
|
|
return {"지문": fp, "파일": name, "종": kinds, "만든": mat_pick.made(target, fp)}
|
|
|
|
return _call(folder, run)
|
|
|
|
|
|
@router.post("/layers/{layer}/hopyo-materials/{key}")
|
|
def hopyo_materials_save(
|
|
layer: Layer,
|
|
key: str,
|
|
body: MatPickBody,
|
|
owner: str | None = None,
|
|
to: Layer | None = None,
|
|
to_owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""[저장] — 고름을 개인 층 그 파일 자리에 · 만든적용 = 그 파일 SK(만들 층)의 가져온 그대로인 줄에도 → `{고름, 고침?, 그대로?}`."""
|
|
folder = _root(session, layer, owner)
|
|
mine = _root(session, "personal", None, write=True)
|
|
fp, name = _call(folder, mat_pick.fingerprint, folder, key)
|
|
out: dict[str, Any] = {"고름": _call(mine, mat_pick.write, mine, fp, name, body.고름)}
|
|
if body.만든적용:
|
|
target = _root(session, to or layer, to_owner if to else owner, write=True)
|
|
kinds = set(body.종 if body.종 is not None else body.고름)
|
|
out |= _call(target, mat_pick.apply_made, fp, out["고름"], kinds)
|
|
return out
|
|
|
|
|
|
class SkPickBody(_Strict):
|
|
"""SK 상세에서 고른 재료 줄 → 같은 파일 같은 종(물음 예) — `빼기` = 지금 SK(화면 [저장] 몫)."""
|
|
|
|
종: str = Field(max_length=4000)
|
|
고름: dict[str, Any] | None = None
|
|
파일: str = Field(default="", max_length=200)
|
|
빼기: list[str] = Field(default_factory=list, max_length=20)
|
|
|
|
|
|
@router.get("/layers/{layer}/material-picks/{fp}")
|
|
def material_pick_uses(
|
|
layer: Layer,
|
|
fp: str,
|
|
종: str,
|
|
owner: str | None = None,
|
|
me: str = "",
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""SK 상세 물음 — `{수 {블록, 줄}(작업 중 HP 판 · 없으면 null), 만든 [그 종 줄 있는 다른 SK], 고름?}`."""
|
|
folder = _root(session, layer, owner)
|
|
mine = _root(session, "personal", None)
|
|
|
|
def run() -> dict:
|
|
n = mat_pick.tally(folder, fp, 종)
|
|
n = n if n is not None or mine == folder else mat_pick.tally(mine, fp, 종)
|
|
return {"수": n, "만든": mat_pick.uses(fp, 종, (me,)), "고름": mat_pick.read(mine, fp)["고름"].get(종)} # fmt: skip
|
|
|
|
return _call(folder, run)
|
|
|
|
|
|
@router.post("/layers/{layer}/material-picks/{fp}")
|
|
def material_pick_save(
|
|
layer: Layer,
|
|
fp: str,
|
|
body: SkPickBody,
|
|
owner: str | None = None,
|
|
session: Session = Depends(verify_session),
|
|
) -> dict:
|
|
"""그 종 고름을 개인 층 파일 자리에 + 이 층 그 파일 다른 SK 의 가져온 그대로인 줄에 → `{고름, 고침, 그대로}`."""
|
|
folder = _root(session, layer, owner, write=True)
|
|
mine = _root(session, "personal", None, write=True)
|
|
picks = _call(mine, mat_pick.write, mine, fp, body.파일, {body.종: body.고름})
|
|
return {
|
|
"고름": picks,
|
|
**_call(folder, mat_pick.apply_made, fp, picks, {body.종}, tuple(body.빼기)),
|
|
}
|