Files
Aislo/M02_MasterTemplete/M02_MyMachine_Look.py
T
eomsangdonandClaude Opus 5.5 f822a4c36a feat(840-2): 기계 보기 공용 — M01 기계 상세 · 내 기계를 왼쪽 제어 구역 + 오른쪽 시간당 경비 산출근거로 · 「복사해 내 기계로」 걷음 · 기계 줄 풀기 안 내놓음
- 기계 보기 부품(A00_Common/line_stack_machine_sheet) — 836-1 제어 구역 그대로(기계 표 꼴 훅 sheet · 잠금 lock) + 기본 정보 · 손료 근거 접이 카드 + 비슷한 기계에서 시작 · 값 바꾸면 브라우저 계산기로 즉시
- 서버 POST /api/m02/machine-sheet(M02_MyMachine_Sheet) — 식 고름 + 식 문서 + 처음 줄 값 + 연결 기록 · 쓰기 없음 · 파이썬 bind ↔ TS bindDoc 짝
- 덮음 ↔ 기계 줄(common_util_machine_sheet) — 고침 없음 = 줄 바이트 같음 · 내 기계 바로 · 직접 자리 맞춤
- 내 기계 = M01 에 없는 기계만 — 화면 만들기 · 고치기에서 M01 과 같은 이름 · 규격 409(불러오기 자동 생성은 그대로) · [새 기계] · 고침 열림
- 옛 기계 양식 부품(ui_template_machine_form) 지움 — 쓰는 곳 0(부품 모음 견본 줄도 걷음)
- 낡은 시험 test_821_3 test_stack_line_copy — 836-2 자동 따라옴(내 기계 고치면 SK 금액 따라감 · 갱신 할 것 없음)에 맞춤
- 지킴 시험 test_840_2 — M01 기계 655 대 박은 식 셈 = 요소 셈 · 줄 그대로 · 굴착기 덮음 · 내 기계 꼴 · M01 같은 이름 막음

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

149 lines
6.2 KiB
Python

"""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=` = 내 품목 — 그 층 단가산출 수식 문서 재료비 줄 가운데
단가가 우리 자재에 안 이어진 것(단가 변수 연결 = 숫자 · 입력) ·
(이름 · 규격 · 단위 · 값) 묶음마다 쓰는 단가산출.
POST `/api/m02/machine-sheet?owner=` 몸 {줄, 본?, 덮음?, 개인?} = 기계 보기(840-2 · `M02_MyMachine_Sheet` · 쓰기 없음 ·
개인 = 그 개인 층 · 아니면 M01 만).
권한 = 내 기계 길과 같음(`M02_MyMachine_Router._folder` — 개인 층 · 본인 · 맡은 회사 관리자 읽기).
⚠ 등록은 `main.py`(로그인만).
"""
from __future__ import annotations
import json
from typing import Any
from fastapi import APIRouter, Depends
from pydantic import BaseModel
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)}
class SheetBody(BaseModel):
줄: dict[str, Any]
본: dict[str, Any] | None = None
덮음: dict[str, Any] | None = None
개인: bool = False
@router.post("/machine-sheet")
def post_sheet(
body: SheetBody, owner: str | None = None, session: Session = Depends(verify_session)
) -> dict:
"""기계 보기(840-2 · `M02_MyMachine_Sheet`) — 개인 = 내 기계(그 층) · 아니면 M01 기계만 · 쓰기 없음."""
from M02_MasterTemplete import M02_MyMachine_Sheet as ms
folder = _folder(session, "personal", owner) if body.개인 else store.FOLDER
return _call(folder, ms.sheet, body.줄, body.본, body.덮음, body.개인)