"""M02 내 기계 화면 읽기 길(PLAN 821-3) — 양식 목록 · 비슷한 기계 찾기 · 내 품목 · 쓰기 없음. GET `/api/m02/my-machine-lists` = 양식 고르기(연료 · 직종 · 전력 단가 · 시간환산) — M01 기계 화면과 같은 값(`M01_MasterData_Machine.lists` · 그 길은 시스템 관리자만). GET `/api/m02/layers/{층}/my-machine-find?q=&owner=` = 「비슷한 기계에서 시작」 · 「복사해 내 기계로」 — 그 층 내 기계(앞) + M01 기계(`M01_MasterData_Machine.find`) · q 가 M01 원문번호와 같으면 그 기계 먼저. GET `/api/m02/layers/{층}/my-items?owner=` = 내 품목 — 그 층 단가산출 수식 문서 재료비 줄 가운데 단가가 우리 자재에 안 이어진 것(단가 변수 연결 = 숫자 · 입력) · (이름 · 규격 · 단위 · 값) 묶음마다 쓰는 단가산출. 권한 = 내 기계 길과 같음(`M02_MyMachine_Router._folder` — 개인 층 · 본인 · 맡은 회사 관리자 읽기). ⚠ 등록은 `main.py`(로그인만). """ from __future__ import annotations import json from typing import Any from fastapi import APIRouter, Depends from common_util.common_util_auth import verify_session from M01_MasterData import M01_MasterData_Store as m01 from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_MyMachine as mym from M02_MasterTemplete.M02_MasterTemplete_Router_Items import Session, _call from M02_MasterTemplete.M02_MyMachine_Router import _folder router = APIRouter(prefix="/api/m02", tags=["M02 My Machine Look"]) DIRECT = ("숫자", "입력") # 단가 변수 연결 — 값을 줄에 바로 적은 것 def _m01m(): from M01_MasterData import M01_MasterData_Machine as m01m # M01 store 가 scripts 를 길에 둔 뒤 return m01m def lists() -> dict[str, Any]: got = _m01m().lists() return {k: got[k] for k in ("fuels", "jobs", "power", "hour")} def find(q: str, limit: int = 30) -> list[dict]: """내 기계(앞) + M01 기계 — 줄 {키, 원문번호, 이름, 규격, 구분, 값, 방식?, 시간당?, 출처}.""" words = [w for w in q.lower().split() if w] keep = ("키", "원문번호", "이름", "규격", "값", "방식", "시간당") out = [ {**{k: r[k] for k in keep if k in r}, "구분": "내 기계", "출처": "개인"} for r in mym.rows() if all(w in f"{r.get('이름')} {r.get('규격')} {r.get('키')}".lower() for w in words) ] exact = (m01.loaded()[1].numbers.get("EQ") or {}).get(q.strip()) head = [exact] if exact is not None and not mym.mine(exact.get("키")) else [] m01_rows = [*head, *_m01m().find(q, limit)] seen = set() for r in m01_rows: if r["키"] in seen: continue seen.add(r["키"]) brief = {k: r[k] for k in ("키", "원문번호", "이름", "규격", "값") if k in r} out.append({**brief, "구분": r.get("상세구분") or r.get("구분") or "", "출처": "M01"}) return json.loads(json.dumps(out[:limit], default=_m01m()._plain)) # Decimal → 수(글 아님) def _items_of(doc: dict) -> list[dict]: """수식 문서 재료비 줄 — 단가 = 바로 적은 값 변수(숫자 · 입력).""" import master_doc as md body = ((doc.get("수식") or {}).get("문서")) or {} vars_ = {md.var_id(v): v for v in body.get("변수") or [] if isinstance(v, dict)} out = [] for line in body.get("금액") or []: if not isinstance(line, dict) or line.get("비목") != "재료비": continue var = vars_.get(str(line.get("단가") or "").strip()) link = (var or {}).get("연결") or {} if link.get("갈래") not in DIRECT: continue out.append( { "이름": str(line.get("이름") or var.get("이름") or ""), "규격": str(line.get("규격") or ""), "단위": str(line.get("단위") or ""), "값": link.get("값"), } ) return out def items() -> list[dict]: """내 품목 — (이름 · 규격 · 단위 · 값) 묶음 · 쓰는곳 [{키, 이름}] · 이름 차례.""" from M02_MasterTemplete import M02_Sangeun_Doc as sd groups: dict[tuple, dict] = {} for path in sorted((store.root() / sd.KIND).glob("SK-*.json")): try: doc = json.loads(path.read_bytes()) except (OSError, ValueError): continue for it in _items_of(doc): got = groups.setdefault( (it["이름"], it["규격"], it["단위"], json.dumps(it["값"])), {**it, "쓰는곳": []} ) use = {"키": doc.get("키") or path.stem, "이름": doc.get("이름") or ""} if use not in got["쓰는곳"]: got["쓰는곳"].append(use) return sorted(groups.values(), key=lambda g: (g["이름"], g["규격"], g["단위"])) @router.get("/my-machine-lists") def get_lists() -> dict: return lists() @router.get("/layers/{layer}/my-machine-find") def get_find( layer: str, q: str = "", owner: str | None = None, session: Session = Depends(verify_session) ) -> dict: return {"rows": _call(_folder(session, layer, owner), find, q)} @router.get("/layers/{layer}/my-items") def get_items( layer: str, owner: str | None = None, session: Session = Depends(verify_session) ) -> dict: return {"rows": _call(_folder(session, layer, owner), items)}