Merge remote-tracking branches 'origin/sub_laptop_1' and 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -1,11 +1,40 @@
|
||||
/* 778-5 D-5 마법사 단가산출 줄 고름 헬퍼 — `logic/Hopyo_SkPick`(순수) · `Hopyo_SkApi`(몸) 를 트랜스파일해 Node 로 돌림 ·
|
||||
* fetch 대역(부른 길 · 몸 모음) · stdin = {blocks} · stdout = JSON. */
|
||||
* fetch 대역(부른 길 · 몸 모음) · stdin = {blocks} · stdout = JSON.
|
||||
* 780-2 — stdin `{sheetSk: {...}}` = 엑셀 보기 오른쪽 판 바꾸기(`Hopyo_SheetSk`) · 단가산출 칸 · 시트 막대 · 판은 가짜. */
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { makeLoader } = require("./helper_ts_stub_loader.cjs");
|
||||
|
||||
const ROOT = path.join(__dirname, "..", "..");
|
||||
const STUBS = { "@config/config_frontend": { API_BASE_URL: "/api", ROUTES: {} } };
|
||||
const made = []; // 가짜 단가산출 칸 — 지은 인자 · 강조
|
||||
const fakeEl = () => ({
|
||||
hidden: false,
|
||||
attrs: {},
|
||||
kids: [],
|
||||
setAttribute(k, v) {
|
||||
this.attrs[k] = v;
|
||||
},
|
||||
append(x) {
|
||||
this.kids.push(x);
|
||||
},
|
||||
});
|
||||
const STUBS = {
|
||||
"@config/config_frontend": { API_BASE_URL: "/api", ROUTES: {} },
|
||||
"@ui/ui_template_elements": {
|
||||
el: (tag, o = {}) => ({ ...fakeEl(), tag, className: o.className }),
|
||||
},
|
||||
"./M02_MasterTemplete_Hopyo_WizardSk": {
|
||||
sangeunStep: (scope, key, file, onChange, o) => {
|
||||
const step = {
|
||||
args: { scope, key, file, o: { ...o, onRow: !!o.onRow } },
|
||||
marks: [],
|
||||
onRow: o.onRow,
|
||||
};
|
||||
made.push(step);
|
||||
return { root: fakeEl(), mark: (n) => step.marks.push(n) };
|
||||
},
|
||||
},
|
||||
};
|
||||
const stub = new Proxy({}, { get: () => () => {} });
|
||||
const { loadTs } = makeLoader({
|
||||
root: ROOT,
|
||||
@@ -19,7 +48,9 @@ const { loadTs } = makeLoader({
|
||||
global.sessionStorage = { getItem: () => null, setItem: () => {}, removeItem: () => {} };
|
||||
|
||||
(async () => {
|
||||
const { blocks } = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
const input = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
if (input.sheetSk) return process.stdout.write(JSON.stringify(sheetSk(input.sheetSk)));
|
||||
const { blocks } = input;
|
||||
const calls = [];
|
||||
global.fetch = async (url, init = {}) => {
|
||||
calls.push({
|
||||
@@ -77,8 +108,61 @@ global.sessionStorage = { getItem: () => null, setItem: () => {}, removeItem: ()
|
||||
await api.judgeSangeun({ layer: "personal", owner: null }, "HP-000001");
|
||||
await api.judgeSangeun({ layer: "personal", owner: null }, "HP-000001", out.body);
|
||||
out.calls = calls;
|
||||
// 780-2 일위대가 안 · 밖 · 줄 0 블록 — 거르기 · 판정 · 확인 막음 · 끝내기 글 재료
|
||||
const more = [
|
||||
{ ...a, 부름: [3, 7] },
|
||||
{ ...b, 부름: [] },
|
||||
{ ...b, 번: 9, 부름: [], 줄: [] },
|
||||
{ ...c, 부름: [12], 만든: "SK-000001" },
|
||||
];
|
||||
const e = more[2];
|
||||
let d2 = sk.setSure(sk.emptyDraft(), e, true);
|
||||
out.empty = {
|
||||
판정: sk.verdictOf(e, {}),
|
||||
켜짐: sk.canConfirm(e, {}),
|
||||
확인: d2.확인,
|
||||
몸: sk.sureBody({ 고름: {}, 확인: [9] }, more),
|
||||
};
|
||||
d2 = sk.emptyDraft();
|
||||
out.filter = Object.fromEntries(
|
||||
["all", "in", "out", "open"].map((f) => [f, sk.filterBlocks(more, d2, f).map((x) => x.번)]),
|
||||
);
|
||||
out.old = sk.filterBlocks(blocks, d2, "out").length; // 옛 서버(부름 없음) = 모두 안
|
||||
out.wait2 = sk.waiting(d2, more);
|
||||
process.stdout.write(JSON.stringify(out));
|
||||
})().catch((error) => {
|
||||
process.stderr.write(String(error?.stack ?? error));
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
/** 780-2 판 바꾸기 — 탭(역할) 차례로 고름 · 판 숨김 · 칸 지음 한 번 · 강조 · 줄 누름 뛰기 */
|
||||
function sheetSk({ same, canWrite, picks }) {
|
||||
const M02 = path.join(ROOT, "M02_MasterTemplete");
|
||||
const { createSheetSk } = loadTs(path.join(M02, "M02_MasterTemplete_Hopyo_SheetSk.ts"));
|
||||
const roles = { s1: "일위대가표", s2: "단가산출표", s3: "단가산출상세" };
|
||||
const jumps = [];
|
||||
const nav = {
|
||||
role: (sheet) => roles[sheet],
|
||||
blockAt: (sheet, row) =>
|
||||
sheet === "s2" ? row - 4 : sheet === "s3" ? Math.floor(row / 10) : null,
|
||||
spotOf: (no, role) => ({ 시트: role === "단가산출표" ? "s2" : "s3", 행: [no, no + 1] }),
|
||||
jumpTo: (sheet, rows) => jumps.push([sheet, rows]),
|
||||
};
|
||||
const toggled = [];
|
||||
const side = {
|
||||
hidden: false,
|
||||
after: () => {},
|
||||
parentElement: { classList: { toggle: (c, on) => toggled.push([c, on]) } },
|
||||
};
|
||||
const scope = { layer: "personal", owner: null };
|
||||
const target = same ? scope : { layer: "company", owner: null };
|
||||
const p = createSheetSk({ scope, key: "HP-000001", 파일: "a.xls", target, canWrite, nav, side });
|
||||
const seen = [];
|
||||
for (const [sheet, cell] of picks) {
|
||||
p.at({ 시트: sheet, 칸: cell });
|
||||
seen.push({ 호표판숨김: side.hidden, 단가판숨김: p.root.hidden, 지음: made.length });
|
||||
if (sheet === "s2" && made[0]) made[0].onRow(8); // 목록 줄 누름(단가산출표 탭)
|
||||
if (sheet === "s3" && made[0]) made[0].onRow(8); // 상세 탭
|
||||
}
|
||||
return { seen, toggled, made: made.map((m) => ({ args: m.args, marks: m.marks })), jumps };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
"""780-2 지킴 — 엑셀 보기 단가산출표 · 상세 탭 = 오른쪽 단가산출 목록(요구 627).
|
||||
|
||||
서버 없이 잡음: 탭(역할) 따라 판 바꿈(일위대가표 = 호표 목록 · 단가산출 탭 = 단가산출 목록 · 칸은 처음 한 번만 지음) ·
|
||||
시트 행 → 목록 강조 · 목록 줄 → 지금 탭의 블록 자리 · 만들 층 ≠ 파일 층 = 보기만 · 거르기(전체 · 안 · 밖 · 고를 줄 있음) ·
|
||||
줄 0 블록 = 「줄 없음」 확인 막음 · 마법사 칸과 같은 캐시 열쇠(두 벌 고름 맞춤) · [끝내기] = 막지 않고 물음창 글만 ·
|
||||
화면 = 부품 상자만 · 700 줄.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
HERE = os.path.dirname(__file__)
|
||||
M02 = Path(__file__).resolve().parents[2] / "M02_MasterTemplete"
|
||||
from test_778_5_sk_pick_screen import BLOCKS as sys_blocks # noqa: E402
|
||||
|
||||
|
||||
def _node(payload: dict) -> dict:
|
||||
proc = subprocess.run(
|
||||
["node", os.path.join(HERE, "helper_778_5_sk_pick.cjs")],
|
||||
input=json.dumps(payload, ensure_ascii=False),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
check=False,
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
return json.loads(proc.stdout)
|
||||
|
||||
|
||||
PICKS = [["s1", "B5"], ["s2", "B12"], ["s1", "C3"], ["s3", "A85"], ["s3", "A2"]]
|
||||
|
||||
|
||||
def test_판_바꿈_목록_지음_강조_뛰기():
|
||||
got = _node({"sheetSk": {"same": True, "canWrite": True, "picks": PICKS}})
|
||||
assert got["seen"] == [
|
||||
{"호표판숨김": False, "단가판숨김": True, "지음": 0}, # 일위대가표 = 호표 목록 · 칸 안 지음
|
||||
{"호표판숨김": True, "단가판숨김": False, "지음": 1}, # 단가산출표 = 단가산출 목록
|
||||
{"호표판숨김": False, "단가판숨김": True, "지음": 1},
|
||||
{"호표판숨김": True, "단가판숨김": False, "지음": 1}, # 다시 와도 한 번만 지음
|
||||
{"호표판숨김": True, "단가판숨김": False, "지음": 1},
|
||||
]
|
||||
assert [on for _, on in got["toggled"]] == [False, True, False, True, True]
|
||||
(step,) = got["made"]
|
||||
assert step["args"]["o"] == {"narrow": True, "onRow": True, "readOnly": False}
|
||||
assert step["args"]["key"] == "HP-000001"
|
||||
assert step["marks"] == [8, 8, 0] # 목록 행 12 → 단산8 · 상세 85 행 → 단산8 · 2 행 → 블록 0
|
||||
# 목록 줄 누름 = 지금 탭의 자리(단가산출표 = 목록 행 · 상세 = 블록 범위)
|
||||
assert got["jumps"] == [["s2", [8, 9]], ["s3", [8, 9]], ["s3", [8, 9]]]
|
||||
|
||||
|
||||
def test_다른_층_권한_없음_보기만():
|
||||
for same, can in ((False, True), (True, False)):
|
||||
got = _node({"sheetSk": {"same": same, "canWrite": can, "picks": [["s2", "B5"]]}})
|
||||
assert got["made"][0]["args"]["o"]["readOnly"] is True
|
||||
|
||||
|
||||
def test_거르기_줄0_막음_끝내기_글():
|
||||
got = _node({"blocks": sys_blocks})
|
||||
assert got["empty"] == {"판정": "줄 없음", "켜짐": False, "확인": [], "몸": []}
|
||||
assert got["filter"] == {"all": [2, 5, 9, 7], "in": [2, 7], "out": [5, 9], "open": [2, 9]}
|
||||
assert got["old"] == 0 # 옛 서버(부름 없음) = 모두 일위대가 안
|
||||
assert got["wait2"] == {"블록": 3, "밖": 2, "줄": 3} # 만든 블록 7 뺌 · 줄 0 블록 = 고를 줄 1
|
||||
|
||||
|
||||
def _src(name: str) -> str:
|
||||
return (M02 / f"M02_MasterTemplete_Hopyo_{name}.ts").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_캐시_공유_알림_칠_다시():
|
||||
wiz = _src("WizardSk")
|
||||
assert wiz.count("skDraftKey(scope, key)") == 1 # 두 벌 모두 같은 열쇠(층 · 주인 · 파일 키)
|
||||
assert "SK_SYNC" in wiz and "tell({ got: next })" in wiz # 옮김 = 받은 판정 그대로 다른 벌에
|
||||
assert wiz.count("tell();") == 2 # [확인] · 줄 고름 바뀜
|
||||
assert "new Event(HOPYO_BUILT)" in wiz # 옮김 뒤 열린 엑셀 보기 칠 다시
|
||||
sheet = _src("Sheet")
|
||||
assert "createSheetSk(" in sheet and "sk?.at(s);" in sheet
|
||||
side = _src("SheetSk")
|
||||
assert "sangeunStep(opt.scope, opt.key" in side # 마법사와 같은 파일 층 · 키
|
||||
nav = _src("SheetNav")
|
||||
assert "blockAt(sheet: string, row: number)" in nav and "spotOf(no: number, role: Role)" in nav
|
||||
wizard = _src("Wizard")
|
||||
assert 'L("M02_SkUnmoved")' in wizard and "finish.disabled = !!n?.줄;" in wizard # 밖 = 안 막음
|
||||
tone = (M02 / "logic" / "M02_MasterTemplete_Hopyo_SheetMap.ts").read_text(encoding="utf-8")
|
||||
assert '글: "일위대가 밖"' in tone
|
||||
|
||||
|
||||
def test_부품_상자만_700줄():
|
||||
for name in ("SheetSk", "Sheet", "SheetNav", "WizardSk", "Wizard"):
|
||||
src = _src(name)
|
||||
assert len(src.splitlines()) <= 700, name
|
||||
assert not re.search(r"#[0-9a-fA-F]{3,6}\b|rgb\(", src), name
|
||||
for name in ("SheetSk", "WizardSk"):
|
||||
assert not re.search(r'el\("(button|input|select)"|<button|<input', _src(name)), name
|
||||
css = (M02 / "M02_MasterTemplete_Hopyo.css").read_text(encoding="utf-8")
|
||||
block = css.split(".m02-hopyo-sheet.is-sk", 1)[1].split("/*", 1)[0]
|
||||
assert not re.search(r"#[0-9a-fA-F]{3,6}\b|rgb\(", block)
|
||||
@@ -1,6 +1,7 @@
|
||||
"""778-5 D-6 엑셀 셈 어긋남(`M02_Hopyo_Marks.misses` · `SheetMap.missesOf`) — 불리는 블록의 재현 못 한
|
||||
줄 · 덮음 실패 · 금액 차 · 까닭 · 상세 시트 행 차례. 북부청 임도 2 · 33 = 엑셀 합이 진동로울러 노무를 뺌 ·
|
||||
81 = 이제 맞음 · 칠은 안 바꿈(블록 칠 같음) · `hopyo-marks` 길에 실림(읽기만)."""
|
||||
81 = 이제 맞음 · 칠은 안 바꿈(블록 칠 같음) · `hopyo-marks` 길에 실림(읽기만) · 780-1 일위대가 밖 블록도
|
||||
(북부청 임도 4 · 44 · 48 · 77 · 78 · 읽기 흠은 보이는 대로)."""
|
||||
|
||||
import warnings
|
||||
|
||||
@@ -26,9 +27,9 @@ def north():
|
||||
return parsed, grid
|
||||
|
||||
|
||||
def _misses(parsed) -> dict[int, dict]:
|
||||
def _misses(parsed, outside: bool = False) -> dict[int, dict]:
|
||||
blocks, first = sg.calls(parsed)
|
||||
got = {n: mk.misses(blocks[n]) for n in first}
|
||||
got = {n: mk.misses(blocks[n]) for n in (blocks if outside else first)}
|
||||
return {n: m for n, m in got.items() if m}
|
||||
|
||||
|
||||
@@ -49,6 +50,8 @@ def test_북부청_임도_어긋남(north):
|
||||
assert all("못 읽음" in x["까닭"] for x in m["줄"])
|
||||
blocks, _ = sg.calls(north[0])
|
||||
assert mk.misses(blocks[81]) is None # 이제 맞음
|
||||
out = {n: m for n, m in _misses(north[0], True).items() if n not in got}
|
||||
assert sorted(out) == [4, 44, 48, 77, 78] and all(m["까닭"] for m in out.values())
|
||||
|
||||
|
||||
def test_다른_파일_어긋남_수():
|
||||
|
||||
@@ -120,8 +120,9 @@ def test_확인한_블록만_단위_SK_고름_재사용(client):
|
||||
fit.run_job(folder, key)
|
||||
got = client.post(url, json={}).json()
|
||||
assert got["만듦"] == [] and not _sks(folder) # 확인 안 한 블록 = 안 만듦
|
||||
assert len(got["판정"]) == got["블록"] == 49 # 불리는 블록
|
||||
rows = {r["번"]: r for r in got["판정"]}
|
||||
assert len(got["판정"]) == got["블록"] == 95 # 모든 블록(780-1 · 밖 포함)
|
||||
rows = {r["번"]: r for r in got["판정"] if r["부름"]} # 일위대가 안 49
|
||||
assert len(rows) == 49
|
||||
good = [n for n, r in rows.items() if r["판정"] == skunit.GOOD and r["근거"]]
|
||||
pick = next(n for n, r in rows.items() if r["고를줄"])
|
||||
assert rows[pick]["판정"] == f"고를 줄 {rows[pick]['고를줄']}"
|
||||
|
||||
@@ -92,7 +92,11 @@ def test_몸_꼴_이음_줄_자동_확인_없음():
|
||||
assert first["method"] == "POST" and first["body"] == {"확인": []} # 판정만
|
||||
assert first["url"].endswith("/layers/personal/hopyo/HP-000001/sangeun")
|
||||
assert second["body"] == {"확인": got["body"]}
|
||||
assert got["wait"] == {"블록": 3, "줄": 1} # 안 옮긴 블록 셋 · 블록 7 하위 줄
|
||||
assert got["wait"] == {
|
||||
"블록": 3,
|
||||
"밖": 0,
|
||||
"줄": 1,
|
||||
} # 안 옮긴 블록 셋 · 블록 7 하위 줄 · 옛 서버 = 밖 없음
|
||||
assert got["tidy"] == [5] # 고름 지우면 그 블록 확인 빠짐
|
||||
assert got["round"]["확인"] == [2, 5] and got["bad"] == {"고름": {}, "확인": []}
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ def test_marks_읽기만_판정_캐시_같음_SK(client):
|
||||
assert judged and all(r["판정"] in ("맞음", "다른 점", "후보", "없음") for r in judged)
|
||||
assert {r["판정"] for r in judged} <= {e["판정"]["상태"] for e in cache.values()}
|
||||
assert all(r["SK"] is None and r["갈래"] is None and r["못옮김"] == [] for r in rows)
|
||||
# 778-5 D-6 — 엑셀 셈 어긋남 덧칸(불리는 블록) · 2 · 33 = 엑셀 합이 줄 뺌
|
||||
assert {r["번"] for r in rows if r["어긋남"]} == {2, 33, 64, 95}
|
||||
# 778-5 D-6 — 엑셀 셈 어긋남 덧칸(780-1 모든 블록) · 2 · 33 = 엑셀 합이 줄 뺌 · 4 … 78 = 일위대가 밖
|
||||
assert {r["번"] for r in rows if r["어긋남"]} == {2, 33, 64, 95, 4, 44, 48, 77, 78}
|
||||
# [단가산출 만들기] 뒤 = SK · 갈래 · 못옮김 실림(778-5 — 확인한 블록만)
|
||||
rows = client.post(f"{base}/{KEY}/sangeun").json()["판정"]
|
||||
sure = [{"번": r["번"]} for r in rows if not r["고를줄"]]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""779-5 블록 안 줄 칠(`logic/M02_MasterTemplete_Hopyo_SheetMap.ts` · Node) — 블록 `줄 [[행, 상태, 차]]` →
|
||||
출처 행만 칠(이어짐 초록 · 차 있으면 주황 · 후보 파랑 · 못 이음 빨강 · 밖 흐림) · 머리 행 = 가장 나쁜 칠 ·
|
||||
목록 행 = 머리와 같음 · 줄 재료 없으면 옛 블록 칠(범위 통째) · 실물(북부청 임도 · 영월) 줄 칠 수 = 단위 줄 수 ·
|
||||
목록 행 = 머리와 같음 · 줄 재료 없으면 옛 블록 칠(범위 통째) · 실물(북부청 임도 · 영월) 줄 칠 수 = 단위 줄 수
|
||||
(780-1 모든 블록 · 일위대가 밖 포함) ·
|
||||
상태 = 줄 잇기(D-2) · 참고 차 = STmate 옛 노임만."""
|
||||
|
||||
import warnings
|
||||
@@ -63,21 +64,22 @@ def test_실물_줄_칠_수_단위_줄_수(tag, units):
|
||||
grid = hg.read(path.name, data, parsed)
|
||||
grid.pop("책")
|
||||
blocks, first = sg.calls(parsed)
|
||||
assert len(first) < len(blocks) # 일위대가 밖 블록 있음
|
||||
marks, want = [], 0
|
||||
for no in sorted(blocks):
|
||||
lines = mk.lines(blocks[no], units) if no in first else []
|
||||
lines = mk.lines(blocks[no], units)
|
||||
rows = [u["행"] for u in blocks[no]["항목"] if u.get("행") is not None]
|
||||
want += len(rows) if no in first else 0
|
||||
assert [x[0] for x in lines] == (rows if no in first else [])
|
||||
want += len(rows)
|
||||
assert [x[0] for x in lines] == rows
|
||||
assert all(s in ("이어짐", "고를 것", "못 이음", "밖") for _, s, _ in lines)
|
||||
assert all(not d or s == "이어짐" for _, s, d in lines) # 차 = 이어진 줄만
|
||||
marks.append(
|
||||
{"번": no, "판정": "맞음", "SK": None, "갈래": None, "못옮김": [], "줄": lines}
|
||||
)
|
||||
detail = _node({"grid": grid, "blocks": marks})["paint"][grid["보기"]["단가산출상세"]]
|
||||
heads = {b["행"][0] for b in grid["단산"] if b["번"] in first}
|
||||
heads = {b["행"][0] for b in grid["단산"]}
|
||||
unit_rows = {x[0] for m in marks for x in m["줄"]}
|
||||
spans = [b["행"] for b in grid["단산"] if b["번"] in first]
|
||||
spans = [b["행"] for b in grid["단산"] if any(x["번"] == b["번"] and x["줄"] for x in marks)]
|
||||
painted = {int(r) for r in detail if any(x <= int(r) <= y for x, y in spans)} - heads
|
||||
assert unit_rows - heads == painted and want == sum(len(m["줄"]) for m in marks)
|
||||
diff = sum(d for m in marks for _, _, d in m["줄"])
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
"""780-1 일위대가 밖 단가산출도 잇기(요구 627) — 판정 · 확인 = 모든 블록 · `부름 [호표 번]`([] = 일위대가 밖) ·
|
||||
밖 블록 확인 = 단위 SK(이름 · 단위 = 블록 것 · 근거 없음) · 빈 블록(줄 0) = 「줄 없음」 확인해도 못만듦 ·
|
||||
하위 밖 블록(울진소광 492 → 83) = 확인 목록에 함께 넣으면 하위 먼저 만듦 · 칠 재료 `줄` · `어긋남` 모든 블록 ·
|
||||
여덟 파일 블록 459 판정 오류 0(빈 블록은 모두 밖 = 일위대가 안 판정 그대로). 층 = 임시 폴더 · 마스터 읽기만.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from M02_MasterTemplete import M02_Hopyo_Fit as fit
|
||||
from M02_MasterTemplete import M02_Hopyo_Sangeun as sangeun
|
||||
from M02_MasterTemplete import M02_Hopyo_SkUnit as skunit
|
||||
from M02_MasterTemplete import M02_Hopyo_Units as units
|
||||
from M02_MasterTemplete import M02_Template_Layers as layers
|
||||
from resources.tester import helper_m02_hopyo_golden as golden
|
||||
from resources.tester.test_m02_hopyo_excel import URL, _post, client, master_hash # noqa: F401
|
||||
|
||||
BLOCKS = {"북부청임도": (95, 49), "북부청사방": (67, 45), "거창": (61, 13), "봉화": (65, 31)}
|
||||
BLOCKS |= {"동부청영월": (69, 35), "울진대흥": (47, 14), "영덕계류": (15, 2), "울진소광": (40, 17)}
|
||||
|
||||
|
||||
def _sks(folder) -> dict[str, dict]:
|
||||
return {p.stem: json.loads(p.read_bytes()) for p in (folder / "sangeun").glob("SK-*.json")}
|
||||
|
||||
|
||||
def _ready(client, tag="북부청임도"):
|
||||
path = golden.FILES[tag]
|
||||
key = _post(client, data=path.read_bytes(), name=path.name).json()["키"]
|
||||
folder = layers.personal_dir(7, 42)
|
||||
fit.run_job(folder, key)
|
||||
return folder, key, f"{URL.format('personal')}/{key}/sangeun"
|
||||
|
||||
|
||||
def _first_picks(row: dict) -> dict:
|
||||
"""고를 줄마다 첫 후보(없으면 뺌)."""
|
||||
picks = {}
|
||||
for x in row["줄"]:
|
||||
if x["상태"] in (units.PICK, units.NONE):
|
||||
c = (x["후보"] or [{}])[0]
|
||||
k = c.get("키") or c.get("자재")
|
||||
field = {"L": "노임", "X": "기계", "M": "자재"}.get(x["묶음"])
|
||||
picks[str(x["차례"])] = {field: k} if k and field else c or {"뺌": "현장"}
|
||||
return picks
|
||||
|
||||
|
||||
def test_북부청_임도_밖_블록_판정_확인_빈_블록_칠(client):
|
||||
before = master_hash()
|
||||
folder, key, url = _ready(client)
|
||||
got = client.post(url, json={}).json()
|
||||
rows = {r["번"]: r for r in got["판정"]}
|
||||
assert got["블록"] == len(rows) == 95 and sum(bool(r["부름"]) for r in rows.values()) == 49
|
||||
assert all(r["근거"] is None for r in rows.values() if not r["부름"]) # 밖 = 맞춤셈 없음
|
||||
inside = next(r for r in rows.values() if r["부름"])
|
||||
assert all(isinstance(n, int) for n in inside["부름"])
|
||||
eight, empty = rows[8], rows[1]
|
||||
assert eight["부름"] == [] and eight["판정"] == skunit.GOOD and eight["줄"]
|
||||
assert (empty["판정"], empty["고를줄"], empty["줄"]) == (skunit.EMPTY, 1, [])
|
||||
# 밖 블록 확인 = 단위 SK(이름 · 단위 = 블록) · 빈 블록 = 못만듦(빈 SK 없음)
|
||||
made = client.post(url, json={"확인": [{"번": 8}, {"번": 1}]}).json()
|
||||
assert [x["번"] for x in made["만듦"]] == [8]
|
||||
assert made["못만듦"] == [{"번": 1, "이름": empty["이름"], "까닭": skunit.EMPTY}]
|
||||
docs = _sks(folder)
|
||||
assert list(docs) == [made["만듦"][0]["키"]]
|
||||
doc = docs[made["만듦"][0]["키"]]
|
||||
assert (doc["이름"], doc["단위"]) == (eight["이름"], eight["단위"])
|
||||
assert doc["바탕"] is None and doc["더한줄"] and "근거" not in doc["본뜬"]
|
||||
assert client.post(url, json={"확인": [{"번": 9999}]}).status_code == 404
|
||||
# 칠 재료 — 밖 블록도 줄 · 어긋남 · 부름 · 만든 SK
|
||||
marks = {r["번"]: r for r in client.get(f"{URL.format('personal')}-marks/{key}").json()["블록"]}
|
||||
assert len(marks) == 95 and marks[8]["SK"] == doc["키"] and marks[8]["부름"] == []
|
||||
assert marks[8]["판정"] is None and len(marks[8]["줄"]) == len(eight["줄"])
|
||||
assert all(s == units.LINKED for _, s, _ in marks[8]["줄"])
|
||||
assert {n for n, r in marks.items() if r["어긋남"]} == {2, 33, 64, 95, 4, 44, 48, 77, 78}
|
||||
assert master_hash() == before
|
||||
|
||||
|
||||
def test_울진소광_하위_밖_블록_함께(client):
|
||||
folder, key, url = _ready(client, "울진소광")
|
||||
rows = {r["번"]: r for r in client.post(url, json={}).json()["판정"]}
|
||||
assert rows[83]["부름"] == [] and rows[492]["부름"] and rows[918]["부름"]
|
||||
sure = [{"번": n, "고름": _first_picks(rows[n])} for n in (492, 83)]
|
||||
made = client.post(url, json={"확인": sure}).json()
|
||||
assert [x["번"] for x in made["만듦"]] == [83, 492] and made["못만듦"] == []
|
||||
sub = made["만듦"][0]["키"]
|
||||
top = _sks(folder)[made["만듦"][1]["키"]]
|
||||
assert any((x.get("본뜬") or {}).get("키") == sub for x in top["더한줄"]) # 하위 SK 줄 펼침
|
||||
|
||||
|
||||
def test_여덟_파일_모든_블록_판정():
|
||||
us = units.Units()
|
||||
for tag, (n, called) in BLOCKS.items():
|
||||
hp = golden.read(tag)
|
||||
blocks, first = sangeun.calls(hp)
|
||||
call = sangeun.callers(hp)
|
||||
assert (len(blocks), len(first)) == (n, called), tag
|
||||
assert {b for b in blocks if call.get(b)} == set(first), tag # 부름 = 첫 줄과 같은 블록
|
||||
for no, block in blocks.items():
|
||||
got = skunit.judge(block, us, lambda sub: None, None, str(block.get("단위") or ""))
|
||||
if not block.get("항목"): # 빈 블록 = 모두 밖
|
||||
assert no not in first and got["판정"] == skunit.EMPTY, (tag, no)
|
||||
Reference in New Issue
Block a user