- 흠: 빈 칸을 거른 뒤 차례로 읽어 규격이 빈 머리(「굴삭기(0.2m3)」 시간 …) · 손료 줄(「대형브레이카(0.7m3)」 계수 천원 …)이 한 칸씩 밀림 → 호표 통째(머리) · 조합 둘째 손료를 못 읽음 · 규격 있는 운전사 · 규격 없는 연료 줄도 같은 까닭 - 고침: 첫 글 칸부터 [명칭, 규격, 수량, 단위, 단가] 칸 자리로 읽음(단위로 갈래) - 전후: STmate 엑셀 6 파일 호표 143 — 같음 109 · 새로 읽음 34(울진대흥 · 대흥공통 16 씩 · 울진소광 2) · 바뀜 · 잃음 0 · 새로 읽은 18(같은 책 둘 뺌) 모두 같은 공사 .STC 구성과 같음 · 두 공사 87 블록 불러오기 금액 검산 같음(전후 같음) · 이 견본은 기계가 모두 M01 에 이어져 내 기계 0 대(전후 같음) · 못 이었다면 몸 = 직접 → 바로 34 - 시험: test_842_4 ⑤ 더함 · test_828_1 (100, 118) → (118, 118) — 옛 단언이 이 흠(못 읽는 머리)을 박아 둔 낡은 칸 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
415 lines
20 KiB
Python
415 lines
20 KiB
Python
"""불러오기 못 이은 기계 → 내 기계 자동 생성(PLAN 821-4 · 요구서_796 4 장 · 방향 장부 Q1 · S) — 서버 단독.
|
||
|
||
구성 읽기(그 파일 값 → 우리 틀 값 · 경쟁사 조종원 노임 환산 ×16/12 ×25/20 은 안 가져옴):
|
||
EST 원파일 MachMainDat(`est_makes` · 올릴 때 둘 꼴 칸 `기계구성`) · EST 엑셀 「기계경비」 ·
|
||
STmate 엑셀 「중기사용료」(`xlsx_makes` · 둔 산근 시트에서 그때 읽음) → {열쇠: 구성}
|
||
구성 = {이름, 규격, 손료 [[계수 10⁻⁷, 가격 원, 달러?]], 주연료종류, 주연료 ℓ, 잡재료율 %, 조종원 인,
|
||
조종원직종(글), 전력 kWh} · 열쇠 = 자원 코드 · 「이름|규격」(표기 정규화 `M02_Machine_Find.norm`).
|
||
고름(`plan`) = 가져온 로직 호표 기계 줄 가운데 이음 없음 · 찾기 판정 「못 찾음」(별칭 · 표기 · 같은 파일 대조 뒤 ·
|
||
후보 여럿 = 사람이 고름이라 안 만듦) · 조합 기계 = 조각마다(첫 조각 = 첫 손료 + 연료 · 조종원 ·
|
||
다음 조각 = 그 차례 손료만 · 구성 없는 조합 = 안 만듦).
|
||
몸 = 구성 있음 → 방식 바로(가격 천원 · 달러 = × 환율 FX000001 + 비고 · 손료계수 · 주연료 → 우리 유가 이름 ·
|
||
잡품 % · 조종원 → 우리 직종 원문번호 · 전력) · 못 이은 연료 · 직종 = 칸 비움 + 비고 「확인 필요」 ·
|
||
구성 없음 → 직접(그 파일 시간당 노 · 재 · 경).
|
||
잇기(`attach` · 층 저장 길 `M02_Sangeun_FromHopyo.make` 만) = 그 층 내 기계 같은 이름 · 규격이면 그 줄 · 없으면
|
||
새로(`M02_MyMachine.create`) · 바로 = 기계 줄 이음 키 · 로직 줄 = 시간당 부름 글 / 직접 = 이음 없이 자원
|
||
`내기계` 키(셈 = 그 파일 값 · [우리 단가로] = 내 기계 시간당 · `M02_Sangeun_Imported.row_of`) ·
|
||
원본 `내기계` [{키, 이름, 규격, 새로}] · 개인 층 아님 = 안 만듦(원본 `내기계예정` [{이름, 규격}] 만).
|
||
금액(엑셀 단가 그대로)은 줄 값을 안 건드려 그대로.
|
||
기계 한 대 안 여러 줄(842-4) = 구성에 `인력줄` [[직종 글, 인]] · `연료줄` [[종류, ℓ]] · `잡품줄` [율 %] · `재료줄` [[이름, 수량, 단위]]
|
||
(읽은 차례 · 첫 줄 = 옛 단일 칸 그대로) → 몸 값 = `master_machine_crew` 꼴 `인력` · `잡품` 목록(옛 조종원 · 잡재료율 칸 없음) ·
|
||
둘째 연료부터 · 소모 재료 = 잡품 키 줄(우리 유가 · 자재 하나에 이어질 때 · 율 100) · 잡품 율 = 연료 줄 차례로 짝 ·
|
||
못 이은 직종 · 연료 · 재료 · 짝 없는 율 = 줄 안 만들고 비고 「확인 필요」(지어내지 않음 · 버린 줄 0).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import re
|
||
from typing import Any
|
||
|
||
from M02_MasterTemplete.M02_Machine_Find import NONE, norm
|
||
|
||
NOTE = "불러오기 자동 생성 — {file} · 값 확인 필요"
|
||
CHECK = "확인 필요"
|
||
COST = {"L": "노무비", "M": "재료비", "S": "경비"}
|
||
FUELS = ("경유", "휘발유", "실내등유")
|
||
|
||
|
||
def key_of(name: Any, spec: Any) -> str:
|
||
return f"{norm(name)}|{norm(spec)}"
|
||
|
||
|
||
def _num(v: Any) -> float | None:
|
||
if isinstance(v, bool) or v is None:
|
||
return None
|
||
if isinstance(v, (int, float)):
|
||
return float(v)
|
||
s = str(v).strip().replace(",", "")
|
||
return float(s) if re.fullmatch(r"-?\d+(?:\.\d*)?|-?\.\d+", s) else None
|
||
|
||
|
||
def _blank(name: str = "", spec: str = "") -> dict[str, Any]:
|
||
return {"이름": name, "규격": spec, "손료": []}
|
||
|
||
|
||
def _fuel(make: dict, kind: Any, litre: Any) -> None:
|
||
"""주연료 종류 · ℓ(처음 것 · 모두 `연료줄`) — 종류 「전력」 = 전력 kWh 칸."""
|
||
if re.sub(r"\s", "", str(kind or "")) == "전력" and (_num(litre) or 0) > 0:
|
||
make["전력"] = _num(litre)
|
||
elif (_num(litre) or 0) > 0:
|
||
make.setdefault("연료줄", []).append([str(kind or "").strip(), _num(litre)])
|
||
if make.get("주연료") is None:
|
||
make["주연료종류"], make["주연료"] = str(kind or "").strip(), _num(litre)
|
||
|
||
|
||
def _crew(make: dict, job: Any, n: Any) -> None:
|
||
"""운전사 줄 — 모두 `인력줄` · 첫 줄 = 조종원 · 조종원직종(842-4)."""
|
||
make.setdefault("인력줄", []).append([str(job).strip(), _num(n)])
|
||
if not make.get("조종원"):
|
||
make["조종원"], make["조종원직종"] = _num(n), str(job).strip()
|
||
|
||
|
||
def _misc(make: dict, rate: Any) -> None:
|
||
"""잡품 율 — 모두 `잡품줄` · 첫 율 = 잡재료율(842-4)."""
|
||
if _num(rate) is not None:
|
||
make.setdefault("잡품줄", []).append(_num(rate))
|
||
if make.get("잡재료율") is None:
|
||
make["잡재료율"] = _num(rate)
|
||
|
||
|
||
MISC_WORDS = ("잡품", "잡유", "잡비", "잡재료", "잡재료비")
|
||
|
||
|
||
# ── 구성 읽기 ─────────────────────────────────────────────────────────
|
||
def est_makes(t: dict[str, list[dict]]) -> dict[str, dict]:
|
||
"""EST 원파일 MachMainDat → {자원 코드: 구성} — TitleCode 머리 다음 줄부터 빈 줄까지 ·
|
||
Index -3 손료(GN 계수 · TU 가격 · 단위 「$」 = 달러) · -2 「주연료」(Size 종류) · 연료 이름 줄(선박용경유 등 ℓ) ·
|
||
잡품 · 잡유 % · 전력 KWH · -1 조종원(직종 · GN 인)."""
|
||
heads = {r.get("Code"): r for r in t.get("MachTitleDat", [])}
|
||
out: dict[str, dict] = {}
|
||
cur = None
|
||
for r in t.get("MachMainDat", []):
|
||
if r.get("TitleCode"):
|
||
head = heads.get(r["TitleCode"]) or r
|
||
cur = out.setdefault(
|
||
r["TitleCode"], _blank(head.get("Name") or "", head.get("Size") or "")
|
||
)
|
||
elif not r.get("Name"):
|
||
cur = None
|
||
elif cur is not None:
|
||
i, gn, word = r.get("Index"), r.get("GN"), re.sub(r"\s", "", str(r["Name"]))
|
||
unit = str(r.get("Unite") or "").strip().upper()
|
||
if i == -3 and gn:
|
||
cur["손료"].append([gn, r.get("TU"), unit == "$"])
|
||
elif i == -2 and word == "주연료":
|
||
_fuel(cur, r.get("Size"), gn)
|
||
elif i == -2 and word in MISC_WORDS:
|
||
_misc(cur, gn)
|
||
elif i == -2 and word == "전력":
|
||
cur["전력"] = gn
|
||
elif i == -2 and unit == "ℓ":
|
||
_fuel(cur, r["Name"], gn)
|
||
elif i == -2 and (gn or 0) > 0: # 소모 재료(치즐 EA 등 · 842-4)
|
||
cur.setdefault("재료줄", []).append([str(r["Name"]).strip(), gn, unit])
|
||
elif i == -1 and (gn or 0) > 0:
|
||
_crew(cur, r["Name"], gn)
|
||
return {k: v for k, v in out.items() if v["손료"]}
|
||
|
||
|
||
def _est_sheet(rows: list[list]) -> dict[str, dict]:
|
||
"""EST 엑셀 「기계경비」 — 「N. 이름 · 규격」 머리 아래 빈 줄까지: 「손료 가격 ₩×(달러 $×) 계수 × 10(-7)」 ·
|
||
「주연료 종류 ℓ」 · 「잡품 주연료의 % 」 · 「직종 인수 인」 · 「전력 … kWh」."""
|
||
out: dict[str, dict] = {}
|
||
cur = None
|
||
for row in rows:
|
||
v = [c for c in row if c not in (None, "")]
|
||
head = re.match(r"\d+\.\s*(.+)", str(v[0])) if v else None
|
||
if head:
|
||
spec = v[1] if len(v) > 1 and isinstance(v[1], str) else ""
|
||
cur = out.setdefault(key_of(head.group(1), spec), _blank(head.group(1).strip(), spec))
|
||
elif not v:
|
||
cur = None
|
||
elif cur is not None:
|
||
word = re.sub(r"\s", "", str(v[0]))
|
||
mark = next((x for x in v if x in ("₩×", "$×")), None)
|
||
if "손료" in word and mark and (at := v.index(mark) + 1) < len(v):
|
||
cur["손료"].append([_num(v[at]), _num(v[1]), mark == "$×"])
|
||
elif word == "주연료" and len(v) > 2:
|
||
_fuel(cur, v[1], v[2])
|
||
elif word in MISC_WORDS and len(v) > 2:
|
||
_misc(cur, v[2])
|
||
elif word == "전력" and len(v) > 2:
|
||
cur["전력"] = _num(v[1]) if _num(v[1]) is not None else _num(v[2])
|
||
elif len(v) > 2 and v[2] == "인" and (_num(v[1]) or 0) > 0:
|
||
_crew(cur, v[0], v[1])
|
||
elif len(v) > 3 and str(v[3]).upper() in ("ℓ", "L") and (_num(v[2]) or 0) > 0:
|
||
_fuel(cur, v[0], v[2])
|
||
return {k: m for k, m in out.items() if m["손료"] and None not in m["손료"][0][:2]}
|
||
|
||
|
||
def _stm_sheet(rows: list[list]) -> dict[str, dict]:
|
||
"""STmate 엑셀 「중기사용료」 — 칸 차례 [명칭, 규격, 수량, 단위, 단가 …](첫 글 칸부터 · 빈 규격도 칸 자리 지킴 ·
|
||
843-2 — 규격 빈 머리 · 손료 줄을 못 읽던 것) · 「제 N 호표」 뒤 머리(단위 「시간」 · 끝 칸 코드 X…) ·
|
||
손료(단위 「천원」 · 수량 = 계수(천원당) · 단가 = 가격 천원) · 조종원(「인」) · 연료(「L」) · 전력(「kWh」) ·
|
||
잡품(명칭 잡품 · 수량 = %) · 「합 계」 끝 → {코드 · 이름|규격: 구성}(손료계수 = 계수 × 10⁴ · 가격 = 천원 × 1000 원)."""
|
||
out: dict[str, dict] = {}
|
||
cur = None
|
||
for row in rows:
|
||
v = [c for c in row if c not in (None, "")]
|
||
first = re.sub(r"\s", "", str(v[0])) if v else ""
|
||
if re.fullmatch(r"제\d+호표", first):
|
||
cur = {}
|
||
continue
|
||
if cur is None or not v:
|
||
continue
|
||
if first == "합계":
|
||
cur = None
|
||
continue
|
||
at = next(i for i, c in enumerate(row) if c not in (None, ""))
|
||
name, spec, qty, unit, price = (*row[at : at + 5], *[None] * 5)[:5]
|
||
spec, qty, price, unit = spec or "", _num(qty), _num(price), str(unit or "").strip()
|
||
if not cur and unit in ("시간", "hr") and re.fullmatch(r"X\d+", str(v[-1])):
|
||
cur.update(_blank(str(name), str(spec)))
|
||
out[str(v[-1])] = out[key_of(name, spec)] = cur
|
||
elif not cur:
|
||
continue
|
||
elif unit == "천원" and qty and price:
|
||
cur["손료"].append([qty * 10_000, price * 1000, False])
|
||
elif unit == "인" and (qty or 0) > 0:
|
||
_crew(cur, name, qty)
|
||
elif first in MISC_WORDS and qty is not None:
|
||
_misc(cur, qty)
|
||
elif unit.upper() in ("L", "ℓ"):
|
||
_fuel(cur, name, qty)
|
||
elif unit.upper() == "KWH":
|
||
cur["전력"] = qty
|
||
return {k: m for k, m in out.items() if m.get("손료")}
|
||
|
||
|
||
def xlsx_makes(sheets: dict[str, list]) -> dict[str, dict]:
|
||
"""엑셀 산근 시트 → 구성(EST 「기계경비」 · STmate 「중기사용료」 · 없음 = {})."""
|
||
if sheets.get("기계경비"):
|
||
return _est_sheet(sheets["기계경비"])
|
||
if sheets.get("중기사용료"):
|
||
return _stm_sheet(sheets["중기사용료"])
|
||
return {}
|
||
|
||
|
||
# ── 고름 · 몸 ─────────────────────────────────────────────────────────
|
||
def _todo(logic: dict) -> list[tuple[dict, int]]:
|
||
"""(줄, 조각 차례) — 기계 줄 · 이음 없음 · 찾기 판정 「못 찾음」."""
|
||
out, n = [], 0
|
||
for line in logic.get("호표") or []:
|
||
res = line.get("자원") or {}
|
||
if res.get("갈래") != "기계":
|
||
continue
|
||
n = n + 1 if res.get("조합") else 0
|
||
if line.get("이음") or res.get("조합식") or res.get("내기계"):
|
||
continue
|
||
if (res.get("찾기") or {}).get("판정") == NONE:
|
||
out.append((line, n))
|
||
return out
|
||
|
||
|
||
def _make_of(res: dict, makes: dict) -> dict | None:
|
||
return makes.get(str(res.get("코드") or "")) or makes.get(
|
||
key_of(res.get("이름"), res.get("규격"))
|
||
)
|
||
|
||
|
||
def _kinds(make: dict, notes: list[str]) -> dict[str, Any]:
|
||
"""연료 · 조종원 → 우리 유가 이름 · 직종 원문번호(못 이음 = 칸 비움 + 비고)."""
|
||
from M01_MasterData import M01_MasterData_Store as m01
|
||
|
||
index, out = m01.loaded()[1], {}
|
||
fuel, litre = str(make.get("주연료종류") or ""), make.get("주연료") or 0
|
||
if litre:
|
||
ours = next(
|
||
(n for n in ("경유", "휘발유", "실내등유") if n == re.sub(r"\s", "", fuel)), None
|
||
)
|
||
ours = ours or next((n for n in ("경유", "휘발유", "실내등유") if fuel.startswith(n)), None)
|
||
if ours and f"{ours}:전국평균" in (index.numbers.get("MO") or {}):
|
||
out |= {"주연료": litre, "주연료종류": ours}
|
||
out |= {"잡재료율": make["잡재료율"]} if make.get("잡재료율") else {}
|
||
else:
|
||
rate = (
|
||
f"(잡품 {make['잡재료율']:g}%)" if make.get("잡재료율") else ""
|
||
) # 842-4 율도 적음
|
||
notes.append(f"주연료 「{fuel}」 {litre:g}ℓ{rate} — 우리 유가에 못 이음({CHECK})")
|
||
if make.get("조종원"):
|
||
job = norm(make.get("조종원직종"))
|
||
hit = [k for k, r in (index.numbers.get("LB") or {}).items() if norm(r.get("이름")) == job]
|
||
if len(hit) == 1:
|
||
out |= {"조종원": make["조종원"], "조종원직종": hit[0]}
|
||
else:
|
||
notes.append(
|
||
f"조종원 「{make.get('조종원직종')}」 {make['조종원']:g}인 — 우리 직종에 못 이음({CHECK})"
|
||
)
|
||
if make.get("전력"):
|
||
out["전력"] = make["전력"]
|
||
if not litre and make.get("잡재료율"): # 연료 없는 전력 기계 잡품 = 전력의 %(842-4)
|
||
out["잡재료율"] = make["잡재료율"]
|
||
crew, misc = _extra(make, out, index, notes)
|
||
if crew or misc: # 둘째 줄부터(842-4) — 첫 줄 몸은 옛 칸 그대로 바꿈 + 덧줄
|
||
import master_machine_crew as mcrew
|
||
|
||
out = mcrew.from_old(out)
|
||
out[mcrew.CREW] = [*out.get(mcrew.CREW, []), *crew]
|
||
out[mcrew.MISC] = [*out.get(mcrew.MISC, []), *misc]
|
||
return out
|
||
|
||
|
||
def _job(index: Any, name: Any) -> str | None:
|
||
hit = [
|
||
k for k, r in (index.numbers.get("LB") or {}).items() if norm(r.get("이름")) == norm(name)
|
||
]
|
||
return hit[0] if len(hit) == 1 else None
|
||
|
||
|
||
def _oil(index: Any, fuel: Any) -> dict | None:
|
||
t = re.sub(r"\s", "", str(fuel or ""))
|
||
ours = next((n for n in FUELS if n == t), None) or next(
|
||
(n for n in FUELS if t.startswith(n)), None
|
||
)
|
||
return (index.numbers.get("MO") or {}).get(f"{ours}:전국평균") if ours else None
|
||
|
||
|
||
def _goods(name: str, unit: str) -> dict | None:
|
||
"""소모 재료 → 우리 자재 하나(이름 표기 · 단위 같음) · 여럿 · 없음 = None."""
|
||
from M01_MasterData import M01_MasterData_Store as m01
|
||
|
||
q = re.sub(r"\(.*", "", re.sub(r"\s", "", name))
|
||
hit = [
|
||
x
|
||
for x in m01.pick("price", q, 200)["items"]
|
||
if norm(f"{x['이름']}{x.get('규격') or ''}") == norm(name)
|
||
and norm(x.get("단위")) == norm(unit)
|
||
]
|
||
return hit[0] if len(hit) == 1 else None
|
||
|
||
|
||
def _extra(make: dict, out: dict, index: Any, notes: list[str]) -> tuple[list, list]:
|
||
"""기계 한 대 안 둘째 줄부터(842-4) → (인력 줄, 잡품 줄) — 못 이음 = 줄 없이 비고 「확인 필요」."""
|
||
crew, misc = [], []
|
||
for job, n in (make.get("인력줄") or [])[1:]:
|
||
code = _job(index, job)
|
||
if code:
|
||
crew.append({"키": f"LB:{code}", "인": n, "이름": job})
|
||
else:
|
||
notes.append(f"인력 「{job}」 {n:g}인 — 우리 직종에 못 이음({CHECK})")
|
||
fuels = make.get("연료줄") or []
|
||
for fuel, litre in fuels[1:]:
|
||
row = _oil(index, fuel)
|
||
if row:
|
||
misc.append({"키": row["키"], "이름": str(fuel), "수량": litre, "율": 100})
|
||
else:
|
||
notes.append(f"연료 「{fuel}」 {litre:g}ℓ — 우리 유가에 못 이음({CHECK})")
|
||
for i, rate in enumerate((make.get("잡품줄") or [])[1:], 1):
|
||
row = _oil(index, fuels[i][0]) if i < len(fuels) else None
|
||
if row:
|
||
misc.append(
|
||
{"키": row["키"], "이름": f"{fuels[i][0]} 잡품", "수량": fuels[i][1], "율": rate}
|
||
)
|
||
elif out.get("주연료"): # 짝 연료 없음 = 주연료의 %
|
||
misc.append({"줄": "주연료", "율": rate})
|
||
else:
|
||
notes.append(f"잡품 {rate:g}% — 이을 연료 없음({CHECK})")
|
||
for name, qty, unit in make.get("재료줄") or []:
|
||
got = _goods(name, unit)
|
||
if got:
|
||
misc.append({"키": got["ref"], "이름": name, "수량": qty, "율": 100})
|
||
else:
|
||
notes.append(f"재료 「{name}」 {qty:g}{unit} — 우리 자재에 못 이음({CHECK})")
|
||
return crew, misc
|
||
|
||
|
||
def body(make: dict | None, n: int, line: dict, file: str) -> dict | None:
|
||
"""내 기계 몸(`M02_MyMachine.create`) — 구성 있음 = 바로 · 없음 = 직접(조합 조각 = 안 만듦)."""
|
||
from M01_MasterData import M01_MasterData_Store as m01
|
||
|
||
res = line.get("자원") or {}
|
||
found = res.get("찾기") or {}
|
||
name = str(found.get("이름") or res.get("이름") or line.get("이름") or "").strip()
|
||
spec = str((found.get("규격") if found.get("이름") else res.get("규격")) or "").strip()
|
||
head = {"이름": name, "규격": spec}
|
||
notes = [NOTE.format(file=file)]
|
||
if make is None:
|
||
if n or res.get("조합첫"):
|
||
return None
|
||
got = res.get("단가") or {}
|
||
pack = line.get("값묶음") or {}
|
||
hour = {v: _num(got.get(c)) or _num(pack.get(v)) or 0 for c, v in COST.items()}
|
||
return {**head, "방식": "직접", "시간당": hour, "비고": " · ".join(notes)}
|
||
if n >= len(make["손료"]):
|
||
return None
|
||
coef, price, usd = make["손료"][n]
|
||
if usd:
|
||
rate = float(m01.loaded()[1].index["FX"]["FX000001"]["값"])
|
||
notes.append(f"가격 ${price:g} × 환율 {rate:,} 원/USD")
|
||
price = price * rate
|
||
vals: dict[str, Any] = {"가격": round(float(price) / 1000, 4), "손료계수": coef}
|
||
if n == 0:
|
||
vals |= _kinds(make, notes)
|
||
return {**head, "방식": "바로", "값": vals, "비고": " · ".join(notes)}
|
||
|
||
|
||
def plan(logic: dict, makes: dict, file: str) -> list[dict]:
|
||
"""만들(또는 이을) 내 기계 [{줄, 열쇠, 몸}] — 글 · 층 안 건드림."""
|
||
out = []
|
||
for line, n in _todo(logic):
|
||
got = body(_make_of(line.get("자원") or {}, makes), n, line, file)
|
||
if got:
|
||
out.append({"줄": line, "열쇠": key_of(got["이름"], got["규격"]), "몸": got})
|
||
return out
|
||
|
||
|
||
def preview(out: dict, makes: dict) -> None:
|
||
"""미리 보기 · 분석 길 — 원본 `내기계예정` [{이름, 규격}](안 만듦)."""
|
||
todo = plan(out["로직"], makes, str(out["원본"].get("파일") or ""))
|
||
seen = {t["열쇠"]: {"이름": t["몸"]["이름"], "규격": t["몸"]["규격"]} for t in todo}
|
||
if seen:
|
||
out["원본"]["내기계예정"] = list(seen.values())
|
||
|
||
|
||
# ── 잇기(층 쓰기) ─────────────────────────────────────────────────────
|
||
def link(line: dict, row: dict) -> None:
|
||
"""가져온 줄 → 내 기계 — 바로 = 기계 줄 키 · 로직 줄 시간당 부름 글 / 직접 = 자원 `내기계` 만."""
|
||
from M02_MasterTemplete.M02_San_Link import hourly_text
|
||
|
||
line["자원"]["내기계"] = row["키"]
|
||
if row.get("방식") == "직접":
|
||
return
|
||
line["이음"] = hourly_text(row["키"]) if line.get("종류") == "로직" else row["키"]
|
||
|
||
|
||
def attach(out: dict, makes: dict) -> list[dict]:
|
||
"""층 저장 길 — 못 이은 기계 줄을 그 층 내 기계(같은 이름 · 규격) · 새 내 기계에 이음 · 원본 `내기계`."""
|
||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||
from M02_MasterTemplete import M02_MyMachine as mym
|
||
|
||
src = out["원본"]
|
||
todo = plan(out["로직"], makes, str(src.get("파일") or ""))
|
||
if not todo:
|
||
return []
|
||
try:
|
||
mym._user()
|
||
except store.StoreError: # 개인 층 아님 — 안 만듦(예정만)
|
||
preview(out, makes)
|
||
return []
|
||
done: dict[str, dict] = {}
|
||
for one in todo:
|
||
if one["열쇠"] not in done:
|
||
have = next(
|
||
(r for r in mym.rows() if key_of(r.get("이름"), r.get("규격")) == one["열쇠"]), None
|
||
)
|
||
row = have or mym.create(one["몸"])["줄"]
|
||
done[one["열쇠"]] = {"줄": row, "새로": have is None}
|
||
link(one["줄"], done[one["열쇠"]]["줄"])
|
||
made = [
|
||
{**{k: d["줄"][k] for k in ("키", "이름", "규격", "방식")}, "새로": d["새로"]}
|
||
for d in done.values()
|
||
]
|
||
src["내기계"] = made
|
||
src.pop("내기계예정", None)
|
||
return made
|