refactor(M02): 일위대가 조합 남은 은퇴 — 갈라 내기 · 키 적기(use-combo) · 조합 만들기 · 지우기 · 알약 길 지움 · Branch.py 지움 · 시험은 저장소 함수 · 은퇴 길 404 · 405 로(39-1d)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013rJwJMVL4EE31y77TB11AP
This commit is contained in:
@@ -1,130 +0,0 @@
|
||||
"""조합 갈라 내기 — 구조물 열 하나가 가리키는 조합을 고친 내용으로 새로 만들어 그 열만 새 키를
|
||||
가리키게 함(PLAN 15장 · 검토 `14_조합편집검토.md` 1장).
|
||||
· 일위대가 조합(UA · UC · UP) — 새 줄은 지금 층 조합 저장소가 만듦(`Combo_Store` · PLAN 27-2c:
|
||||
마스터 층 = UA · 회사 층 = UC · 개인 층 = UP · 고르기는 세 층 중 이 층이 쓸 수 있는 것)
|
||||
|
||||
조합은 여러 열 · 모든 프로젝트가 같이 씀 — 제자리 고치지 않음. 담은 것(차례 · 메모까지)이
|
||||
이미 있는 조합과 같으면 새로 안 만들고 그 키(원래 키 먼저) · 새 줄의 본뜬키 = 원래 키 · 소유 현장.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Callable
|
||||
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Combo_Store as combos
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
|
||||
_OWNER = "현장"
|
||||
|
||||
|
||||
def _free_name(name: str, rows: list[dict]) -> str:
|
||||
taken = {str(r.get("이름") or "") for r in rows}
|
||||
n, out = 2, name
|
||||
while out in taken:
|
||||
out, n = f"{name} ({n})", n + 1
|
||||
return out
|
||||
|
||||
|
||||
def _branch(
|
||||
column: str,
|
||||
version: str,
|
||||
field: str,
|
||||
rows: list[dict],
|
||||
slot: str,
|
||||
ident: tuple[str, ...],
|
||||
items: list[dict],
|
||||
source: str,
|
||||
make: Callable[[str, list[dict]], dict],
|
||||
name: str,
|
||||
) -> dict[str, Any]:
|
||||
"""집계표 판 검사 → 같은 내용 찾기 · 없으면 `make(이름, 줄들)` 로 새 줄 → 집계표 그 열 `field`.
|
||||
`slot` = 담은 칸(담은로직) · `ident` = 담은 줄에서 견주는 칸(로직 · 메모)."""
|
||||
table = _table(column, version)
|
||||
if not isinstance(items, list) or not items:
|
||||
raise store.StoreError(400, "담은 것이 비었음")
|
||||
|
||||
def body(got) -> tuple: # 차례대로 · 빈 메모 = null
|
||||
return tuple(
|
||||
tuple(str(x.get(k) or "") for k in ident) for x in got or [] if isinstance(x, dict)
|
||||
)
|
||||
|
||||
same = [r for r in rows if body(r.get(slot)) == body(items)]
|
||||
hit = next((r for r in same if source and r.get("키") == source), same[0] if same else None)
|
||||
if hit:
|
||||
key, made_name, made = str(hit["키"]), str(hit.get("이름") or ""), False
|
||||
else:
|
||||
made_name = _free_name(name, rows)
|
||||
key, made = make(made_name, rows), True
|
||||
# ponytail: 조합 쓰기와 집계표 쓰기는 한 묶음 아님 — 사이에 집계표가 바뀌면 409 뒤 주인 없는
|
||||
# 조합 하나가 남음(다시 하면 같은 내용이라 그 키를 씀)
|
||||
판 = _point(table, column, field, key)
|
||||
return {"키": key, "이름": made_name, "새로만듦": made, "집계표판": 판}
|
||||
|
||||
|
||||
def _table(column: str, version: str) -> dict:
|
||||
"""집계표 판 검사 · 구조물 열 있는지 — 읽은 표(`{판, 문서}`)."""
|
||||
table = store.read("table", store.STRUCTURE_TABLE)
|
||||
if version and table["판"] != version:
|
||||
raise store.StoreError(409, {"stale": [store.STRUCTURE_TABLE], "판": table["판"]})
|
||||
if not any(c["id"] == column for c in store._structure_columns()):
|
||||
raise store.StoreError(404, f"{store.STRUCTURE_TABLE}에 없는 구조물 열 「{column}」")
|
||||
return table
|
||||
|
||||
|
||||
def _point(table: dict, column: str, field: str, key: str) -> str:
|
||||
"""집계표 그 열 `field` = `key` 로 저장 — 새 판."""
|
||||
doc = table["문서"]
|
||||
for c in doc.get("열", []):
|
||||
if isinstance(c, dict) and c.get("id") == column:
|
||||
c[field] = key
|
||||
return store.write("table", store.STRUCTURE_TABLE, table["판"], doc)["판"] # SS 도 여기서 맞춤
|
||||
|
||||
|
||||
def use_combo(column: str, field: str, key: str, version: str = "") -> dict[str, Any]:
|
||||
"""있는 조합을 이 열에 씀 — `field` 일위대가(UA) · 조합은 그대로(고르기만)."""
|
||||
if field == "일위대가":
|
||||
name = _combo(key).get("이름")
|
||||
else:
|
||||
raise store.StoreError(400, f"모르는 조합 종류 「{field}」")
|
||||
판 = _point(_table(column, version), column, field, key)
|
||||
return {"키": key, "이름": str(name or ""), "새로만듦": False, "집계표판": 판}
|
||||
|
||||
|
||||
def _combo(key: str) -> dict[str, Any]:
|
||||
"""이 층이 쓸 수 있는 조합(마스터 · 같은 회사 · 내 것) — 없으면 404 · 남의 층 것은 403."""
|
||||
row = combos.find(key)
|
||||
if row is None:
|
||||
raise store.StoreError(404, f"없는 조합 「{key}」")
|
||||
if not key.startswith(combos.heads()):
|
||||
raise store.StoreError(
|
||||
403, f"이 층에서 못 쓰는 조합 「{key}」 — 마스터 · 같은 회사 · 내 것만"
|
||||
)
|
||||
return row
|
||||
|
||||
|
||||
def branch_combo(
|
||||
column: str, source: str, items: list[dict], name: str, version: str = ""
|
||||
) -> dict[str, Any]:
|
||||
"""일위대가 조합 갈라 내기 — 구분 · 상세구분 · 단위 · 출처 = 본뜬 것 · 새 줄은 지금 층에."""
|
||||
was = _combo(source)
|
||||
items = [
|
||||
{"로직": str(x.get("로직") or ""), "메모": x.get("메모")}
|
||||
for x in items or []
|
||||
if isinstance(x, dict)
|
||||
]
|
||||
|
||||
def make(made_name: str, _rows) -> str:
|
||||
row = {
|
||||
"이름": made_name,
|
||||
**{k: was.get(k) for k in ("구분", "상세구분", "단위")},
|
||||
"담은로직": items,
|
||||
"본뜬키": source,
|
||||
"출처": was.get("출처"),
|
||||
}
|
||||
return str(combos.create({**row, "소유": _OWNER})["키"])
|
||||
|
||||
name = name.strip() or str(was.get("이름") or source)
|
||||
rows = combos.rows()
|
||||
return _branch(
|
||||
column, version, "일위대가", rows, "담은로직", ("로직", "메모"), items, source, make, name
|
||||
)
|
||||
@@ -10,7 +10,6 @@ from typing import Any
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from pydantic import BaseModel
|
||||
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Branch as branch
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Options as options
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
|
||||
@@ -22,13 +21,6 @@ class SaveBody(BaseModel):
|
||||
문서: Any
|
||||
|
||||
|
||||
class BranchBody(BaseModel):
|
||||
본뜬키: str
|
||||
담은로직: list[dict[str, Any]]
|
||||
이름: str = ""
|
||||
판: str = "" # 집계표 판 — 주면 어긋날 때 409
|
||||
|
||||
|
||||
class OptionsBody(BaseModel):
|
||||
판: str = "" # 옵션 표시 표 판 — 어긋나면 409
|
||||
표시: dict[str, Any] # {종류: {옵션 키: 참 · 거짓}} — 거짓만 남김
|
||||
@@ -63,24 +55,6 @@ def post_structure(column: str) -> dict:
|
||||
return _call(store.create_structure, column)
|
||||
|
||||
|
||||
@router.post("/structures/{column}/combo")
|
||||
def post_structure_combo(column: str, body: BranchBody) -> dict:
|
||||
"""조합 갈라 내기 — 고친 담은 로직으로 새 UA(같은 로직 집합이면 있는 키) · 이 열만 가리킴."""
|
||||
return _call(branch.branch_combo, column, body.본뜬키, body.담은로직, body.이름, body.판)
|
||||
|
||||
|
||||
class UseComboBody(BaseModel):
|
||||
종류: str # 일위대가
|
||||
키: str
|
||||
판: str = "" # 집계표 판 — 주면 어긋날 때 409
|
||||
|
||||
|
||||
@router.post("/structures/{column}/use-combo")
|
||||
def post_structure_use_combo(column: str, body: UseComboBody) -> dict:
|
||||
"""있는 조합을 이 열에 씀 — 집계표 그 열 일위대가 키만 바꿈(조합은 그대로)."""
|
||||
return _call(branch.use_combo, column, body.종류, body.키, body.판)
|
||||
|
||||
|
||||
@router.get("/structure-options")
|
||||
def get_structure_options() -> dict:
|
||||
"""`{판, 표시, 종류}` — 표시 = 숨긴 것만 · 종류 = 옵션 있는 종류와 그 옵션."""
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""M02 일위대가 조합 층 API(PLAN 27-2a) — `/api/m02/layers/{layer}/combos`(옛 `/api/m01/combo*`).
|
||||
|
||||
⚠ 등록은 `main.py`(로그인만) · 층 권한은 `M02_Layer_Access`(`Router_Items._root`) 한 규칙:
|
||||
system 읽기 = 로그인 누구나 · 쓰기 = 시스템 관리자(마스터 조합 · 키 UA)
|
||||
company 읽기 = 같은 회사 · 쓰기 = 회사 관리자(키 UC{회사}-######)
|
||||
personal 읽기 · 쓰기 = 본인(키 UP{사용자}-######)
|
||||
⚠ 등록은 `main.py`(로그인만) · 층 권한은 `M02_Layer_Access`(`Router_Items._root`) 한 규칙 ·
|
||||
읽기만(목록 · 키 하나) + 실무문서 통째 지우기 — 갈라 내기 · 키 적기 · 만들기 · 고치기 · 지우기 ·
|
||||
알약 길은 은퇴(39-1d · 정본 = 열마다 `labor/<열>.json`).
|
||||
저장소 = `M02_MasterTemplete_Combo_Store`.
|
||||
담을 로직 고르기 · 일위대가 입력 모달이 읽는 단가산출 로직(M01 길은 시스템 관리자 전용)은
|
||||
`/api/m02/logic-lib/…` 읽기만(로그인) — 모양은 M01 길과 같음.
|
||||
@@ -12,10 +11,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
from pydantic import BaseModel
|
||||
|
||||
from common_util.common_util_auth import verify_session
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
@@ -36,11 +32,6 @@ router = APIRouter(
|
||||
)
|
||||
|
||||
|
||||
class ComboBody(BaseModel):
|
||||
combo: dict[str, Any]
|
||||
판: str | None = None # 새로 만들 때 빈 글 = 지금 판 · 고칠 때 어긋나면 409
|
||||
|
||||
|
||||
@router.get("/combos/all")
|
||||
def get_all(session: Session = Depends(verify_session)) -> list[dict]:
|
||||
"""고르기(use-combo · 갈라 내기) — 세 층 `[{layer, owner, 판, 폴더, 줄}]` · 회사 없음 빈 층."""
|
||||
@@ -69,13 +60,6 @@ def get_by_key(key: str, session: Session = Depends(verify_session)) -> dict:
|
||||
return {"layer": layer, "owner": owner, **_call(_root(session, layer, owner), combos.one, key)}
|
||||
|
||||
|
||||
@router.get("/combos/key/{key}/inputs")
|
||||
def get_inputs_by_key(key: str, session: Session = Depends(verify_session)) -> dict:
|
||||
"""조합 알약(어느 층이든) — 구조물 조합 면적산출 연결이 부름."""
|
||||
layer, owner = _where(key)
|
||||
return _call(_root(session, layer, owner), combos.pills, key)
|
||||
|
||||
|
||||
@router.get("/layers/{layer}/combos")
|
||||
def get_combos(layer: Layer, owner: str | None = None, session: Session = Depends(verify_session)):
|
||||
return _call(_root(session, layer, owner), combos.listing)
|
||||
@@ -88,36 +72,6 @@ def get_combo(
|
||||
return _call(_root(session, layer, owner), combos.one, key)
|
||||
|
||||
|
||||
@router.get("/layers/{layer}/combos/{key}/inputs")
|
||||
def get_combo_inputs(
|
||||
layer: Layer, key: str, owner: str | None = None, session: Session = Depends(verify_session)
|
||||
):
|
||||
"""조합 알약 — 담은 로직 결과단위 분모마다 물량 하나(M02 면적산출이 받음)."""
|
||||
return _call(_root(session, layer, owner), combos.pills, key)
|
||||
|
||||
|
||||
@router.post("/layers/{layer}/combos")
|
||||
def post_combo(
|
||||
layer: Layer,
|
||||
body: ComboBody,
|
||||
owner: str | None = None,
|
||||
session: Session = Depends(verify_session),
|
||||
):
|
||||
folder = _root(session, layer, owner, write=True)
|
||||
return _call(folder, combos.create, body.combo, body.판 or None)
|
||||
|
||||
|
||||
@router.delete("/layers/{layer}/combos/{key}")
|
||||
def delete_combo(
|
||||
layer: Layer,
|
||||
key: str,
|
||||
판: str = "",
|
||||
owner: str | None = None,
|
||||
session: Session = Depends(verify_session),
|
||||
):
|
||||
return _call(_root(session, layer, owner, write=True), combos.delete, key, 판)
|
||||
|
||||
|
||||
@router.delete("/practice")
|
||||
def delete_practice(판: str = "", session: Session = Depends(verify_session)) -> dict:
|
||||
"""실무문서 통째 지우기(PLAN 39-2) — 시스템 관리자만 · 마스터 조합 표 줄을 모두 비움 ·
|
||||
|
||||
@@ -26,14 +26,9 @@ from common_util.common_util_auth import verify_session
|
||||
from config.config_db import get_db_pool
|
||||
from M02_MasterTemplete import M02_Item_Paste as paste_store
|
||||
from M02_MasterTemplete import M02_Layer_Access as access
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Branch as branch
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Items as items_store
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Tree as tree
|
||||
from M02_MasterTemplete.M02_MasterTemplete_Router import (
|
||||
BranchBody,
|
||||
UseComboBody,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
#: 이 요청이 쓴 남의 층(`others_write`) — 길이 성공해 답한 뒤에만 시스템 로그(`NotedRoute`)
|
||||
@@ -306,33 +301,6 @@ def post_structure(
|
||||
return _call(_root(session, layer, owner, write=True), store.create_structure, column)
|
||||
|
||||
|
||||
@router.post("/layers/{layer}/structures/{column}/combo")
|
||||
def post_combo(
|
||||
layer: Layer,
|
||||
column: str,
|
||||
body: BranchBody,
|
||||
owner: str | None = None,
|
||||
session: Session = Depends(verify_session),
|
||||
):
|
||||
"""일위대가 조합 갈라 내기 — 새 조합은 이 층 조합 표에(`Combo_Store`)."""
|
||||
folder = _root(session, layer, owner, write=True)
|
||||
return _call(
|
||||
folder, branch.branch_combo, column, body.본뜬키, body.담은로직, body.이름, body.판
|
||||
)
|
||||
|
||||
|
||||
@router.post("/layers/{layer}/structures/{column}/use-combo")
|
||||
def post_use_combo(
|
||||
layer: Layer,
|
||||
column: str,
|
||||
body: UseComboBody,
|
||||
owner: str | None = None,
|
||||
session: Session = Depends(verify_session),
|
||||
):
|
||||
folder = _root(session, layer, owner, write=True)
|
||||
return _call(folder, branch.use_combo, column, body.종류, body.키, body.판)
|
||||
|
||||
|
||||
def _copy_practice(column: str, key: str) -> dict[str, Any]:
|
||||
"""실무문서 조합(마스터 UA) → 이 열 일위대가 문서(입력값 빈 값 · 39-1b)."""
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
|
||||
Reference in New Issue
Block a user