diff --git a/common_util/common_util_formula_doc.ts b/common_util/common_util_formula_doc.ts index 414575c3c..96ffe6a50 100644 --- a/common_util/common_util_formula_doc.ts +++ b/common_util/common_util_formula_doc.ts @@ -112,6 +112,7 @@ export interface FormulaDoc { 키?: string; 이름?: string; 결과단위?: string; + 부품?: { 갈래?: string }; 변수?: DocVar[]; 식?: { 기호?: string; 식?: string }[]; 결과?: string; diff --git a/common_util/common_util_line_stack_pick.ts b/common_util/common_util_line_stack_pick.ts index f03add116..165faf65f 100644 --- a/common_util/common_util_line_stack_pick.ts +++ b/common_util/common_util_line_stack_pick.ts @@ -26,6 +26,7 @@ import { type MoneyKind, type StackItem, } from "./common_util_line_stack"; +import { machineAlias } from "./common_util_machine_symbol_name"; type Row = Record; @@ -239,7 +240,7 @@ export function tableAlias(doc: FormulaDoc): Record { break; } } - return out; + return { ...out, ...machineAlias(doc) }; // 기계경비식 = 짧은 한글 이름(839-1 · 사전이 표 값 이름보다 먼저) } /** 식 글의 기호를 화면 이름으로(또는 `back` = 화면 이름을 기호로) */ diff --git a/common_util/common_util_machine_symbol_name.ts b/common_util/common_util_machine_symbol_name.ts new file mode 100644 index 000000000..1ec407f25 --- /dev/null +++ b/common_util/common_util_machine_symbol_name.ts @@ -0,0 +1,108 @@ +/* ============================================================================= + * common_util_machine_symbol_name.ts + * 기계경비식(부품 갈래) 기호 → 짧은 한글 이름(PLAN 839-1 · 요구 858 안 2) — 그림 전용 사전 + * 「P_기계」 → 「가격」 · 「k_기계상각비」 → 「상각」 · 「C_손료」 → 「손료」 · 「r_기계잡재료」 → 「잡품」 + * 화면 글만 바꿈 — 문서 자료 · 식 글 · 열쇠 · 계산 불변 · 식 글 고침 칸은 `tableAlias` 길이 이름 ↔ 기호로 오감 + * 기계경비식 밖 문서 · 사용자가 손으로 바꾼 기호(사전에 없는 꼴)는 그대로 · 이름이 겹치면 그 기호만 그대로 + * 화면 없음(시험이 Node 로 돌림) · 기계 제어 구역(836-1)도 `machineAlias` 로 같은 이름 + * ========================================================================== */ + +import { varId, type FormulaDoc } from "./common_util_formula_doc"; +import { symbols } from "./common_util_line_stack"; + +/** 기호 → 이름(품셈 말 줄임 — 가격 · 상각 · 정비 · 관리 · 손료 · 가산 · 잡품) */ +const NAME: Record = { + C_손료: "손료", + C_브레이커: "브레이커손료", + C_집게: "집게손료", + P_기계: "가격", + P_기계전력: "전력", + P_기계주연료: "주연료", + P_덤프트럭: "가격", + P_덤프트럭2: "주연료", + P_브레이커: "브레이커가격", + P_집게: "집게가격", + P_전력: "전력단가", + P_자동차용경유: "경유", + P_휘발유: "휘발유", + P_체인오일: "체인오일", + P_체인톱손료: "체인톱손료", + P_MO연료지역: "연료단가", + P_LB직종: "조종원노임", + Q_기계조종원: "조종원", + Q_덤프트럭: "조종원", + Q_시간환산: "환산", + "Q_분자^표": "환산분자", + "Q_분모^표": "환산분모", + "Q_주연료^표": "휘발유량", + "Q_주연료2^표": "오일량", + "Q_잡^표": "잡품", + S_기계: "기계", + S_지역: "지역", + S_보정작업: "보정작업", + S_보정기종: "보정기종", + S_브레이커: "브레이커", + S_집게: "집게", + S_연료: "연료", + S_직종: "직종", + S_조종원직종: "조종원직종", + S_전력계약종별: "전력계약", + k_기계상각비: "상각", + k_기계정비비: "정비", + k_기계관리비: "관리", + k_기계손료: "계수", + k_덤프트럭: "상각", + k_덤프트럭2: "정비", + k_덤프트럭3: "관리", + k_브레이커상각: "브레이커상각", + k_브레이커정비: "브레이커정비", + k_브레이커관리: "브레이커관리", + k_집게상각비: "집게상각", + k_집게정비비: "집게정비", + k_집게관리비: "집게관리", + k_체인톱: "체인톱계수", + r_가산: "가산", + "r_가산^표": "가산표", + r_기계잡재료: "잡품", + r_덤프트럭: "잡품", + r_비율: "연료비율", + r_조합잡재료: "조합잡품", + w_기계운전사: "운전사", + w_일반기계운전: "운전사", + w_화물차운전사: "운전사", +}; + +/** 「P_EQ기계…」(기계 한 줄 칸 읽기) = 읽는 칸 이름으로 */ +const CELL: Record = { + 가격: "가격", + 손료계수: "계수", + 주연료: "주연료", + 전력: "전력", + 조종원: "조종원", + 잡재료율: "잡품", + 노무비: "시간노무", + 재료비: "시간재료", + 경비: "시간경비", +}; + +/** 기계경비식 문서의 기호 → 화면 이름(이름 없는 · 겹치는 기호는 빠짐) */ +export function machineAlias(doc: FormulaDoc): Record { + if (doc.부품?.갈래 !== "기계경비식") return {}; + const ids = symbols(doc); + const cell = new Map((doc.변수 ?? []).map((v) => [varId(v), String(v.연결?.칸 ?? "")])); + const want = (id: string): string | undefined => + NAME[id] ?? (id.startsWith("P_EQ기계") ? CELL[cell.get(id) ?? ""] : undefined); + const count = new Map(); + const plain = new Set(ids); + for (const id of ids) { + const n = want(id); + if (n) count.set(n, (count.get(n) ?? 0) + 1); + } + const out: Record = {}; + for (const id of ids) { + const n = want(id); + // 겹침(같은 이름 둘 · 안 바뀌는 다른 기호가 이미 그 이름) = 그 기호는 그대로 + if (n && count.get(n) === 1 && !(plain.has(n) && !want(n))) out[id] = n; + } + return out; +} diff --git a/resources/tester/test_804_4_line_stack_take.py b/resources/tester/test_804_4_line_stack_take.py index 4c3422b0c..6ada98c13 100644 --- a/resources/tester/test_804_4_line_stack_take.py +++ b/resources/tester/test_804_4_line_stack_take.py @@ -247,6 +247,8 @@ def test_표값_화면이름_전수(): assert not g["틀림"], (k, g["틀림"][:3]) alias = g["별칭"] assert len(set(alias.values())) == len(alias), k # 겹치면 2 · 3 + if (docs[k].get("부품") or {}).get("갈래") == "기계경비식": + continue # 기계경비식 = 짧은 한글 이름(839-1 · 시험 test_839_1) assert all("^표" not in v and k.endswith("^표") for k, v in alias.items()), k named += len(alias) assert named > 500 diff --git a/resources/tester/test_839_1_machine_symbol_name.py b/resources/tester/test_839_1_machine_symbol_name.py new file mode 100644 index 000000000..d1bd81031 --- /dev/null +++ b/resources/tester/test_839_1_machine_symbol_name.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +"""PLAN 839-1(요구 858 안 2) — 기계경비식 16 문서 기호를 짧은 한글 이름으로 그림(그림만 · 자료 0). + +① 16 문서 · 기호 64 꼴 전부 이름 있음(이름 없는 기호 0) · 문서 안 이름 겹침 0 · 안 바뀐 기호와도 겹침 0 +② 식 글 · 금액 글 왕복(보이는 이름 → 기호 = 처음 식과 같은 나무) · 보이는 글에 옛 기호 안 남음 +③ 기계경비식 밖 문서 = 사전 안 탐 · 자료 json 안 바뀜(기호 · 식 · 열쇠 그대로 — 화면 길이 문서를 안 고침) +④ 사용자가 손으로 바꾼 기호 = 사전에 없는 꼴이라 그대로 +""" + +from __future__ import annotations + +import copy + +from M02_MasterTemplete import M02_Doc_Copy as dc +from resources.tester.test_804_2_line_stack import _ts + +MACHINE = "기계경비식" + + +def _ids(doc: dict) -> list[str]: + out = [] + for v in doc.get("변수", []): + out.append( + v["기호"] + + (f"_{v['아래']}" if v.get("아래") else "") + + (f"^{v['위']}" if v.get("위") else "") + ) + for line in doc.get("식", []): + if line["기호"] not in out: + out.append(line["기호"]) + return out + + +def _machines() -> dict[str, dict]: + docs = dc.folder() + got = {k: d for k, d in docs.items() if (d.get("부품") or {}).get("갈래") == MACHINE} + assert len(got) == 16 + return got + + +def test_16_문서_이름_전수_겹침_없음(): + docs = _machines() + keys = sorted(docs) + got = _ts("별칭", [{"문서": docs[k]} for k in keys]) + forms: set[str] = set() + for k, g in zip(keys, got): + alias = g["별칭"] + ids = _ids(docs[k]) + miss = [i for i in ids if i not in alias] + assert not miss, (k, miss) # 이름 없는 기호 0 + assert len(set(alias.values())) == len(alias), (k, alias) # 겹침 0 + assert not set(alias.values()) & set(ids), (k, alias) # 옛 기호와도 안 겹침 + assert all("_" not in v and "^" not in v for v in alias.values()), (k, alias) + forms |= {i for i in ids if not i.startswith("P_EQ기계")} + assert not g["틀림"], (k, g["틀림"][:3]) # 식 글 왕복 = 같은 식 + assert len(forms) == 58 # 64 꼴 = 사전 58 + 「P_EQ기계~6」 읽는 칸 이름 6 + + +def test_대표_이름(): + docs = _machines() + got = _ts("별칭", [{"문서": docs["GC000267"]}])[0]["별칭"] + assert got["P_기계"] == "가격" + assert [got[x] for x in ("k_기계상각비", "k_기계정비비", "k_기계관리비")] == [ + "상각", + "정비", + "관리", + ] + assert got["C_손료"] == "손료" and got["r_가산"] == "가산" and got["r_기계잡재료"] == "잡품" + assert got["P_기계주연료"] == "주연료" and got["Q_기계조종원"] == "조종원" + assert got["Q_시간환산"] == "환산" and got["Q_분자^표"] == "환산분자" + one = _ts("별칭", [{"문서": docs["GC001199"]}])[0]["별칭"] + assert [one[x] for x in ("P_EQ기계", "P_EQ기계2", "P_EQ기계3")] == [ + "시간노무", + "시간재료", + "시간경비", + ] + + +def test_밖_문서_그대로_자료_안_바뀜(): + docs = dc.folder() + outside = [k for k, d in docs.items() if (d.get("부품") or {}).get("갈래") != MACHINE] + pick = [k for k in sorted(outside) if k in ("GC001147", "GC000089")] or sorted(outside)[:2] + before = [copy.deepcopy(docs[k]) for k in pick] + got = _ts("별칭", [{"문서": docs[k]} for k in pick]) + for g in got: + assert not set(g["별칭"].values()) & {"가격", "상각", "정비", "관리", "손료", "잡품"} + assert [docs[k] for k in pick] == before # 문서 자료 안 바뀜 + machine = _machines() + snap = copy.deepcopy(machine) + _ts("별칭", [{"문서": d} for d in machine.values()]) + assert machine == snap + + +def test_손으로_바꾼_기호는_그대로(): + docs = _machines() + doc = copy.deepcopy(docs["GC000267"]) + for v in doc["변수"]: + if v["기호"] == "P" and v["아래"] == "기계": + v["아래"] = "차량" # 사용자가 손으로 고친 기호 = P_차량 + alias = _ts("별칭", [{"문서": doc}])[0]["별칭"] + assert "P_차량" not in alias # 그림 사전 안 탐 = 그 글 그대로