Files
Aislo/resources/tester/test_800_1_var_symbols.py
T
eomsangdonandClaude Opus 5.5 58a3e039b3 feat(813-2): 보조 문서 부품 표지 73 · 작업량당 기계경비 6 지움 · 단가산출 목록에서 뺌 · 기계 → 부품 길
- 표지 = 수식 문서 머리 · 옛 로직 줄 칸 「부품」 — 기계경비식 9 · 작업량 틀 60(기종 = 기계표 상세구분 · 규격 열쇠) · 계수식 4 · 변환기가 옮김 · 틀 문서 9 장
- 지움 = 작업량당 기계경비 6(GC001145 · 1148 · 1151 · 1156 · 1159 · 1166 · 부르는 곳 0 · 요구 734-4 · 735-4) — 수식 문서 · 옛 로직 · 키대장 폐기 칸 · 빌더 · 변수표
- 이름 꼬리 4 = 원문 제목(지게운반 · 인력운반(기계설비) · 벽돌운반 · 수중펌프 설치 및 해체) · 키 폐기 0
- 서버 = `M01_MasterData_Parts` — M01 `/logics` · M02 `/logic-lib/logics` 목록에서 부품 뺌(1,440) · 키로 열기 · 부르기 그대로 · `/m01/parts`(기종 계산식 · 틀 · 계수식) · `/m02/logic-lib/machine-parts` · 연결 찾기 과녁에서 부품 뺌(1,444 → 1,435 · 판정 코드 그대로)
- 금액 전후 = 문서 1,513 · 셈 9,626(기본 + 옵션 표본) 모두 같음 · 791_1 지문 남은 1,513 같음 → 지문 다시 뜸 · 802_2 변환 대조 1,513 · 목록 xlsx 다시 씀(요약 수)
- 낡은 시험 = 1,519 · 1,518 기대 → 1,513 · GC001148 견본 → 같은 꼴 GF000089 토사깍기(기계)(블록 Q 39.293 · 합계 2,190) · 지운 6 을 셈하던 줄 걷음 · 목록 수 1,440 · 묶음 328 · 부르는 수 507
- 새 지킴 시험 test_813_2_parts

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

212 lines
8.3 KiB
Python

"""800-1 변수 기호 기본값 지킴 (요구 660-1 · 659-2) — 수식 탭 왼쪽 식에 한글 변수가 안 보임.
① 규칙(`master_symbol.rule`) 표본 — 끝말 → 머리글자 · 아래첨자 · 갈래별(찾기 위 「표」 · 요소 · 합 · 결과).
② 입력 · 중간 변수 — 기호에 한글 0 · 한 로직 안 겹침 0 · 같은 이름 같은 기호(원문 796-3 제외).
③ 빌더 — 채운 정본에 쓸 것 없음 · 채운 것을 걷으면 같은 수만큼 다시 채움.
④ 수식 내보내기 — 표본 로직 전 변수 기호에 한글 0(찾기 · 요소 · 합 · 결과 포함) · 한 로직 안 겹침 없음.
⑤ 보이기만 — 변수표를 걷어도 엔진 셈 · 끝수 지문 같음(금액 전후 같음).
"""
from __future__ import annotations
import copy
import hashlib
import importlib
import json
import re
from functools import lru_cache
import pytest
from M01_MasterData import M01_MasterData_Store as store
meq = importlib.import_module("master_equation")
mf = importlib.import_module("master_formula")
mcut = importlib.import_module("master_cut")
ma = importlib.import_module("master_auto")
sym = importlib.import_module("master_symbol")
build = importlib.import_module("build_변수기호_800_1")
build796 = importlib.import_module("build_변수표_796_3")
HAN = re.compile(r"[가-힣]")
@lru_cache(maxsize=1)
def loaded():
return store.loaded()
def logic_rows() -> list[dict]:
return [r for d in loaded()[0].values() if d.get("그룹") == "로직" for r in d.get("줄", [])]
def test_규칙_표본():
rule = sym.rule
assert rule("합판율")[:3] == ("r", "합판", "")
assert rule("사용횟수")[:3] == ("n", "사용", "")
assert rule("지형계수")[:3] == ("k", "지형", "")
assert rule("작업효율")[:3] == ("E", "작업", "")
assert rule("운반거리")[:3] == ("L", "운반", "")
assert rule("교량받침중량")[:3] == ("W", "교량받침", "") # 「량」 보다 「중량」 먼저
assert rule("버킷용량")[:3] == ("q", "버킷", "")
assert rule("보통인부")[:3] == ("Q", "보통인부", "")
assert rule("지역")[:3] == ("S", "지역", "") # 고르는 말
assert rule("E입력")[:3] == ("E", "입력", "") # 앞 영문 = 기호
assert rule("폼타이", hint="고름")[0] == "S" and rule("구멍땜")[0] == "X"
assert rule("시공량", "찾기")[:3] == ("Q", "시공", "표") # 표 칸 = 위 「표」
assert rule("보통인부노임", "요소")[:3] == ("w", "보통인부", "")
assert rule("합판단가", "요소")[:3] == ("P", "합판", "")
assert rule("형틀목공", "합")[:3] == ("C", "형틀목공", "")
assert rule("원/㎡", "결과")[:3] == ("P", "", "") and rule("㎥/hr", "결과")[:3] == ("Q", "", "")
assert sym.short("폼타이 12.7㎜") == "폼타이127" and len(sym.short("플랜트기계설치공")) <= 6
assert sym.is_symbol("K") and sym.is_symbol("V_1") and not sym.is_symbol("합판율")
def test_기호에_한글_0_한_로직_겹침_0():
for r in logic_rows():
seen: dict[str, str] = {}
for v in meq.table(r):
assert not HAN.search(v["기호"]), (r["키"], v["열쇠"], v["기호"])
for word in {meq.token(v), v["이름"]}:
assert seen.get(word, v["열쇠"]) == v["열쇠"], (r["키"], word)
seen.setdefault(word, v["열쇠"])
def test_같은_이름_같은_기호():
orig = build796.decisions()
got: dict[str, set[str]] = {}
for r in logic_rows():
for key, look in (r.get(meq.FIELD) or {}).items():
if (orig.get(r["키"]) or {}).get(key, {}).get("기호") or not look.get("기호"):
continue # 원문 · 추정(796-3) · 기호 없음
if HAN.search(key) or not sym.is_symbol(key):
got.setdefault(key, set()).add(look["기호"])
rules = {sym.rule(key, "변수", h)[:2] for h in ("", "고름", "중간")}
assert look["기호"] in {a for a, _ in rules}, (r["키"], key)
assert any(look.get("아래", "").startswith(b[:4]) for _, b in rules), (r["키"], key)
assert len(got) >= 1000
assert all(len(v) == 1 for v in got.values())
def test_빌더_쓸것_없음_그리고_걷으면_다시_채움():
batch, stat = build.plan(loaded()[0])
assert batch == [] and stat["변수"] == 0
orig, files, cut = build796.decisions(), copy.deepcopy(loaded()[0]), 0
for data in files.values():
if data.get("그룹") != "로직":
continue
for r in data["줄"]:
for key in list((r.get(meq.FIELD) or {}).keys()):
if not (orig.get(r["키"]) or {}).get(key, {}).get("기호"):
look = r[meq.FIELD][key]
if look.get("기호") and not set(look) - {"기호", "아래", "위"}:
del r[meq.FIELD][key]
cut += 1
_, again = build.plan(files)
assert cut >= 6000 and again["변수"] == cut
def _words(tree, out: list) -> None:
if isinstance(tree, dict):
if tree.get("꼴") == "변수":
out.append(tree)
for v in tree.values():
_words(v, out)
elif isinstance(tree, list):
for v in tree:
_words(v, out)
SAMPLE = ("GC000211", "GF000089", "GC001147", "GC001140", "GC001144", "GC000087", "GC000303")
def test_수식_내보내기_표본_한글_기호_0():
whole = loaded()[1]
rows = [r["키"] for r in logic_rows()]
for code in (*SAMPLE, *rows[::60]):
eq = meq.export(whole, str(code))
found: list[dict] = []
_words(eq["줄"], found)
_words(eq["풀이"], found)
assert found, code
for v in found:
assert not HAN.search(v.get("기호") or v.get("열쇠") or ""), (
code,
v["갈래"],
v["열쇠"],
)
def test_합판거푸집_표본_글():
eq = meq.export(loaded()[1], "GC000211")
head = {x["열쇠"]: x for x in eq["줄"]}
tok = lambda n: sym._text(n["기호"], n.get("아래", ""), n.get("위", "")) # noqa: E731
assert tok(head["합판율"]["결과"]) == "r_합판"
found: list[dict] = []
_words(head["합판율"]["이름식"], found)
assert {tok(v) for v in found} >= {"n_사용", "Q_합판^표"}
tokens = []
_words(eq["줄"], found := [])
tokens = {tok(v) for v in found}
assert {"Q_시공", "Q_시공^표", "w_형틀목공", "C_형틀목공", "P_합판", "S_폼타이"} <= tokens
def test_한_로직_안_다른_이름_같은_글이면_숫자():
out = {
"결과단위": "원/㎡",
"변수표": [],
"줄": [
{
"갈래": "호표",
"결과": {
"꼴": "변수",
"갈래": "합",
"열쇠": n,
"이름": n,
"기호": n,
"아래": "",
"위": "",
},
}
for n in ("공기압축기 21㎥/min", "공기압축기 10.5㎥/min", "공기압축기 21㎥/min")
],
}
sym.apply(out)
got = [meq.token(x["결과"]) for x in out["줄"]]
assert got == ["C_공기압축기", "C_공기압축기2", "C_공기압축기"]
def _print(whole, row) -> str:
try:
raw = mf.run(
whole,
str(row["키"]),
{
**ma.values(whole, row),
**{k: v for k, v in ma.defaults(whole, row).items() if v is not None},
},
)
text = json.dumps(raw, default=str, sort_keys=True, ensure_ascii=False)
text += json.dumps(
mcut.engine(whole, str(row["키"]), raw), default=str, sort_keys=True, ensure_ascii=False
)
except Exception as e: # 멈춤도 지문
text = f"ERR {type(e).__name__} {e}"
return hashlib.sha256(text.encode()).hexdigest()[:16]
def test_셈_전후_같음_변수표_걷어도():
now = loaded()[1]
files = copy.deepcopy(loaded()[0])
for data in files.values():
if data.get("그룹") == "로직":
for row in data["줄"]:
row.pop(meq.FIELD, None)
before = mf.Master(files)
for code in [r["키"] for r in logic_rows()][::6]:
assert _print(now, now.logic(code)) == _print(before, before.logic(code)), code
if __name__ == "__main__":
pytest.main([__file__, "-q"])