- 줄 표: 갈래 · 수량 · 단위 · 엑셀 단가 · 맞춤 · 입력값 · 환산 · 단추 열 줄바꿈 없음(글 폭) · 입력값 길면 줄임표 · 칸 여백 좁힘 · 품명 · 규격 · 대상이 남는 폭 - 고르기 창 머리: [닫기] 단추 줄바꿈 없음 · 안 줄어듦 - 후보: 정리 이름 뜻 낱말(괄호 밖 두 글자 이상 덩어리 · 낱글자 붙임)이 하나도 안 겹치면 뺌(잔토처리 · 임도표지석 → 후보 없음) · 다 걸러지면 「가까운 것 없음」 + [다른 로직 · 자재 찾기] - 알림: 있던 사전조합 = 「다시 씀 — 빠진 줄 N」 · [새로 만들기] 뒤 = 「새로 만들어 덮음」(토스트 · 판 글) - 시험 hopyo 넷 42 통과 · typecheck 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
148 lines
6.2 KiB
Python
148 lines
6.2 KiB
Python
"""일위대가 실무자료 화면(PLAN 324-4) — 좌측 판 · 본 화면 · [로직 추가] 「실무 호표」 줄.
|
||
|
||
- 고르기 창 덧줄(Node · fetch 대역): 이 층 · 위 층 호표 줄(맞춤 n/m) · 종류 고르기 ·
|
||
고르면 문서 층에 만들기 길 → `lib.pick` 참조 줄(별칭 = 호표 품명) · 프로젝트 층엔 없음 ·
|
||
본 화면 글 도우미
|
||
- 소스: 좌측 판 자리(사전조합 아래 · 구조물 조합 층 · 쓰기 권한 없으면 올리기 꺼짐 ·
|
||
숨은 file 입력) · Labor_Detail 줄 수 그대로 · 새 파일 이름 · 700 줄 · 색 = 공용 토큰
|
||
"""
|
||
|
||
import json
|
||
import os
|
||
import re
|
||
import subprocess
|
||
from pathlib import Path
|
||
|
||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||
M02 = Path(HERE).parents[1] / "M02_MasterTemplete"
|
||
NEW = ("Hopyo_Api", "Hopyo_Pick", "Hopyo_Side", "Hopyo_View")
|
||
|
||
|
||
def _text(name: str) -> str:
|
||
return (M02 / name).read_text(encoding="utf-8")
|
||
|
||
|
||
def _run() -> dict:
|
||
row = {
|
||
"층": "system",
|
||
"주인": None,
|
||
"키": "HP-000001",
|
||
"파일": "북부청.xls",
|
||
"번": 3,
|
||
"이름": "임도표지석",
|
||
"규격": "910×600×150",
|
||
"단위": "개소",
|
||
"맞춤": [2, 4],
|
||
}
|
||
built = {
|
||
"참조": {"층": "company", "주인": "7", "키": "SC-000009"},
|
||
"이름": "임도표지석",
|
||
"다시씀": False,
|
||
"빠진줄": [{"차례": 0}],
|
||
"비교": {},
|
||
}
|
||
proc = subprocess.run(
|
||
["node", os.path.join(HERE, "helper_m02_hopyo_pick.cjs")],
|
||
input=json.dumps({"rows": [row], "built": built}, ensure_ascii=False),
|
||
capture_output=True,
|
||
text=True,
|
||
encoding="utf-8",
|
||
check=True,
|
||
)
|
||
return json.loads(proc.stdout)
|
||
|
||
|
||
def test_고르기_창_실무_호표_줄과_고름():
|
||
got = _run()
|
||
assert got["calls"][0] == {
|
||
"url": "/api/m02/layers/company/hopyo-picks?owner=7",
|
||
"method": "GET",
|
||
"body": None,
|
||
}
|
||
assert got["kinds"] == ["logic", "precombo", "hopyo"]
|
||
assert got["rows"] == [
|
||
{
|
||
"kind": "hopyo",
|
||
"key": "system||HP-000001|3",
|
||
"tag": "실무 호표",
|
||
"name": "임도표지석 910×600×150",
|
||
"sub": "북부청.xls · 제3호표 · 마스터",
|
||
"unit": "개소 · 맞춤 2/4",
|
||
}
|
||
]
|
||
build = got["calls"][1]
|
||
assert build["url"] == "/api/m02/layers/company/hopyo-build?owner=7" # 만드는 층 = 문서 층
|
||
assert build["body"] == {
|
||
"층": "system",
|
||
"주인": None,
|
||
"키": "HP-000001",
|
||
"번": 3,
|
||
"새로": False,
|
||
}
|
||
assert got["picked"] == [
|
||
{
|
||
"ref": {"층": "company", "주인": "7", "키": "SC-000009"},
|
||
"name": "임도표지석",
|
||
"alias": "임도표지석",
|
||
}
|
||
]
|
||
assert got["project"] is True # 프로젝트 층 = 덧줄 없음
|
||
|
||
|
||
def test_본_화면_글_도우미():
|
||
view = _run()["view"]
|
||
assert view["target"] == ["구조물터파기 [㎥]", "레미콘 25-21-120 [㎥]", "제46호표 [㎡]"]
|
||
assert view["inputs"] == "횟수=4회사용시, 지역=서울 · ⚠ 못 읽음: 보정작업"
|
||
assert view["memory"] == [
|
||
{"로직": "GF000101", "입력값": {"지역": "서울"}, "환산": 1},
|
||
{"자재": "MT1", "입력값": {}, "환산": 0.5},
|
||
None, # 환산 0 = 확정 못 함
|
||
None, # 대상 없음
|
||
]
|
||
assert view["word"] == ["만듦", "다시 씀", "새로 만들어 덮음"] # 알림 · 판 글
|
||
|
||
|
||
def test_줄_표_줄바꿈_없음과_고르기_창_머리():
|
||
css = _text("M02_MasterTemplete_Hopyo.css")
|
||
rule = css.split(".m02-hopyo__table.m02-hopyo__lines\n :is(th, td):is(")[1].split("}")[0]
|
||
cols = [int(n) for n in re.findall(r":nth-child\((\d+)\)", rule)]
|
||
assert cols == [1, 4, 5, 6, 7, 9, 10, 11] and "white-space: nowrap;" in rule
|
||
heads = ["갈래", "품명", "규격", "수량", "단위", "엑셀 단가", "맞춤", "대상", "입력값", "환산"]
|
||
view = _text("M02_MasterTemplete_Hopyo_View.ts")
|
||
flat = re.sub(r"\s+", "", view)
|
||
assert re.sub(r"\s+", "", ",".join(f'"{h}"' for h in heads)) in flat # 열 차례 그대로
|
||
top = css.split(".m02-hopyo__choose .ui-modal__top > .ui-btn {")[1].split("}")[0]
|
||
assert "flex-shrink: 0;" in top and "white-space: nowrap;" in top
|
||
assert '"가까운 것 없음"' in view and "builtWord(got, fresh)" in view
|
||
|
||
|
||
def test_좌측_판_자리와_권한():
|
||
side = _text("M02_MasterTemplete_UI_Side.ts")
|
||
assert 'import { buildHopyoSection } from "./M02_MasterTemplete_Hopyo_Side";' in side
|
||
host = side.split('className: "m02-side__lists ui-sidebar-panel"')[1]
|
||
assert host.index("precomboSec,") < host.index("section(hopyo.title, hopyo.body),")
|
||
assert host.index("section(hopyo.title, hopyo.body),") < host.index("extras ?")
|
||
assert "canWrite: canEdit(layer, opt.isAdmin)," in side # 구조물 조합 층 쓰기 권한
|
||
assert "hopyo.paint();" in side.split("function paint(): void {")[1]
|
||
names = set(re.findall(r"\b\w*[Pp]ractice\w*\b", side))
|
||
assert names <= {"buildPracticeSection", "practice", "M02_MasterTemplete_Practice"} # 옛 이름만
|
||
assert "LaborSection" not in side and "labor?." not in side and "picked" not in side
|
||
hopyo_side = _text("M02_MasterTemplete_Hopyo_Side.ts")
|
||
assert 'accept: ".xls,.xlsx"' in hopyo_side and "input.hidden = true;" in hopyo_side
|
||
assert "disabled: !opt.canWrite," in hopyo_side and "pageScope()" in hopyo_side
|
||
|
||
|
||
def test_로직_추가_한_곳과_파일_규칙():
|
||
detail = _text("M02_MasterTemplete_Labor_Detail.ts")
|
||
assert len(detail.splitlines()) == 693 # 줄 수 그대로(제자리 고침)
|
||
assert "withHopyo(library)," in detail
|
||
assert 'cur.담은로직.push({ 로직: "", 참조: ref, 별칭, 메모: null' in detail
|
||
pick = _text("M02_MasterTemplete_Labor_Pick.ts")
|
||
assert "more?: () => Promise<PickExtra[]>;" in pick and "...later" in pick
|
||
for name in NEW:
|
||
text = _text(f"M02_MasterTemplete_{name}.ts")
|
||
assert len(text.splitlines()) <= 700, name
|
||
css = _text("M02_MasterTemplete_Hopyo.css")
|
||
assert "var(--color-" in css and not re.search(r"#[0-9a-fA-F]{3,6}\b", css)
|
||
assert '"./M02_MasterTemplete_Hopyo.css"' in _text("M02_MasterTemplete_Hopyo_View.ts")
|