- 원본 처리 근거대로 기초단가 가나다 정렬, 위치정리, 호표 번호정렬 - 원본 서식 근거(메모) 글자 보기와 삭제 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
3.6 KiB
Python
91 lines
3.6 KiB
Python
"""EST 테스트 앱 정렬·데이타 위치정리·호표 정리(원본 BarSort·TFRMTABLESORT·popTitleSort)."""
|
|
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
FILES = [
|
|
"native-math.js",
|
|
"native-formula.js",
|
|
"native-machine.js",
|
|
"native-rows.js",
|
|
"native-text.js",
|
|
"native-quantity.cjs",
|
|
"native-wonga.js",
|
|
"native-engine.js",
|
|
"native-row-match.js",
|
|
"native-escalation.js",
|
|
"native-progress.js",
|
|
"native-business.js",
|
|
"native-review.js",
|
|
"native-resources.js",
|
|
"native-schedule.js",
|
|
"native-model.js",
|
|
"native-verify.js",
|
|
"native-tidy.js",
|
|
]
|
|
|
|
|
|
def run_js(code: str):
|
|
runner = f"""
|
|
const fs = require('fs'), vm = require('vm');
|
|
const ctx = vm.createContext({{ structuredClone, console }});
|
|
for (const f of {json.dumps(FILES)})
|
|
vm.runInContext(fs.readFileSync('tools/est-test/' + f, 'utf8'), ctx, {{ filename: f }});
|
|
ctx.source = JSON.parse(fs.readFileSync('tools/est-test/sample.json', 'utf8'));
|
|
process.stdout.write(JSON.stringify(vm.runInContext({json.dumps(code)}, ctx)));
|
|
"""
|
|
return json.loads(subprocess.check_output(["node", "-e", runner], cwd=ROOT).decode("utf-8"))
|
|
|
|
|
|
def test_sort_base_groups_by_gangub_and_drops_blank():
|
|
result = run_js(
|
|
"""(() => {
|
|
const p = { tables: { StuffcDat: [
|
|
{ Index: 1, Code: 'M3', Name: '철근', Gangub: 0 }, { Index: 2, Code: 'M1', Name: '레미콘', Gangub: 1 },
|
|
{ Index: 3, Code: 'M2', Name: '거푸집', Gangub: 0 }, { Index: 0, Code: 'M9', Name: '번호없음' }, { Index: 4 } ] } };
|
|
const dropped = EstTidy.sortBase(p, 'StuffcDat');
|
|
const s = { tables: { SilJukDat: [{ Index: 1, Code: 'S2', Name: '가' }, { Index: 2, Code: 'S1', Name: '나' }] } };
|
|
EstTidy.sortBase(s, 'SilJukDat', true);
|
|
let bad = ''; try { EstTidy.sortBase(p, 'HeadMainDat'); } catch (e) { bad = e.message.slice(0, 4); }
|
|
return { order: p.tables.StuffcDat.map((r) => [r.Code, r.Index]), dropped, code: s.tables.SilJukDat.map((r) => r.Code), bad };
|
|
})()"""
|
|
)
|
|
assert result == {
|
|
"order": [["M2", 1], ["M3", 2], ["M1", 3]],
|
|
"dropped": 2,
|
|
"code": ["S1", "S2"],
|
|
"bad": "자재·노",
|
|
}
|
|
|
|
|
|
def test_arrange_groups_and_number_heads_keep_totals():
|
|
result = run_js(
|
|
"""(() => {
|
|
const calc = (x) => EstNative.calculate(EstModel.validate(x));
|
|
const p = calc(structuredClone(source));
|
|
const total = p.calculation.total;
|
|
const n = EstTidy.arrange(p, 'HeadMainDat', false, 10);
|
|
const heads = [...EstNative.groups(p.tables.HeadMainDat).values()].map((g) => g.rows[0]);
|
|
const sorted = heads.every((h, i) => i === 0 || String(heads[i - 1].Name) <= String(h.Name));
|
|
const numbers = heads.map((h) => h.Index);
|
|
const titles = p.tables.HeadTitleDat.filter((t) => heads.some((h) => h.Code === t.Code)).every((t) => t.Index === heads.find((h) => h.Code === t.Code).Index);
|
|
EstTidy.numberHeads(p, 'HeadMainDat', 1);
|
|
const after = calc(p);
|
|
return { n: n === heads.length, sorted, first: numbers[0], step: numbers[1] - numbers[0], titles,
|
|
renum: [...EstNative.groups(p.tables.HeadMainDat).values()].map((g) => g.rows[0].Index).join() === heads.map((_, i) => i + 1).join(),
|
|
total: after.calculation.total === total, errors: after.calculation.errors.length };
|
|
})()"""
|
|
)
|
|
assert result == {
|
|
"n": True,
|
|
"sorted": True,
|
|
"first": 10,
|
|
"step": 1,
|
|
"titles": True,
|
|
"renum": True,
|
|
"total": True,
|
|
"errors": 0,
|
|
}
|