Files
Aislo/resources/tester/helper_302_3_print_quantity.mjs
T

153 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 302-3 인쇄 미리보기 수량산출 — 화면 코드를 node 로 묶어 모으기 결과와 B08 화면 표 짜기를 나란히 냄.
// node helper_302_3_print_quantity.mjs <저장소 뿌리>
// 표준입력 = { project, responses: {"/quantity/…": 응답}, image_b64, drafts: [[이름, 값]], screen }
// (screen = false → 화면 표 짜기 생략 · 응답을 일부러 뺀 실패 견본)
// 표준출력 끝 줄 = { sheets: [모으기 쪽 요약], screen: {B08 화면 표 짜기 요약}, posts: [hydro/calc 본문] }
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
const root = path.resolve(process.argv[2] ?? ".");
const { build } = await import(
pathToFileURL(path.join(root, "config/node_modules/vite/dist/node/index.js")).href
);
const input = JSON.parse(fs.readFileSync(0, "utf-8"));
// 화면 · 세션만 흉내 — 이 길은 DOM 을 안 그림
class Node {
constructor() {
this.children = [];
this.classList = { toggle() {}, add() {}, remove() {}, contains: () => false };
this.style = {};
this.dataset = {};
}
append(...kids) {
this.children.push(...kids);
}
appendChild(kid) {
this.children.push(kid);
return kid;
}
addEventListener() {}
setAttribute() {}
}
const session = new Map();
const storage = {
getItem: (k) => (session.has(k) ? session.get(k) : null),
setItem: (k, v) => session.set(k, String(v)),
removeItem: (k) => session.delete(k),
};
globalThis.document = {
createElement: () => new Node(),
createElementNS: () => new Node(),
createTextNode: () => new Node(),
head: new Node(),
body: new Node(),
addEventListener() {},
documentElement: new Node(),
};
globalThis.window = {
sessionStorage: storage,
localStorage: storage,
location: { origin: "http://localhost", hash: "" },
addEventListener() {},
setTimeout: (...a) => setTimeout(...a),
clearTimeout: (id) => clearTimeout(id),
};
globalThis.sessionStorage = storage;
globalThis.localStorage = storage;
const posts = [];
const png = Buffer.from(input.image_b64, "base64");
globalThis.fetch = async (url, init = {}) => {
const at = String(url)
.replace(/^.*\/projects\/[^/]+/, "")
.split("?")[0];
if (at === "/quantity/hydro/calc") posts.push(JSON.parse(init.body));
if (at === "/quantity/milestone/image")
return new Response(png, { status: 200, headers: { "Content-Type": "image/png" } });
const body = input.responses[at];
return body
? new Response(JSON.stringify(body), {
status: 200,
headers: { "Content-Type": "application/json" },
})
: new Response(JSON.stringify({ detail: `없음 ${at}` }), { status: 404 });
};
/** 표 요약 — 열 머리(묶음 › 이름) · 줄 · 합계 */
const shape = (t) => ({
heads: t.columns.map((c) => [...(c.group ?? []), c.label].join("›")),
rows: t.rows,
total: t.total ?? null,
mergeSame: t.mergeSame ?? null,
frozen: t.frozen ?? 0,
});
const outDir = fs.mkdtempSync(path.join(os.tmpdir(), "aislo-302-3-"));
try {
await build({
configFile: false,
logLevel: "silent",
root,
resolve: {
alias: {
"@ui": path.join(root, "ui_template"),
"@config": path.join(root, "config"),
"@util": path.join(root, "common_util"),
},
},
build: {
outDir,
minify: false,
target: "es2022",
lib: {
entry: path.join(root, "resources/tester/helper_302_3_print_quantity_entry.ts"),
formats: ["es"],
fileName: "entry",
},
},
});
const mod = await import(pathToFileURL(path.join(outDir, "entry.js")).href);
const p = input.project;
for (const [name, value] of input.drafts ?? []) mod.writeState(name, value, p);
const sheets = (await mod.collectQuantitySheets(p)).map((s) => ({
id: s.id,
name: s.name,
orientation: s.orientation ?? null,
images: s.images ?? [],
...shape(s.table),
}));
if (input.screen === false) {
console.log(JSON.stringify({ sheets, screen: null, posts }));
process.exit(0);
}
// B08 화면이 부르는 같은 함수 — 같은 응답 · 같은 캐시
const screen = {};
screen.summary = shape(mod.summaryTable(await mod.loadSummary(p)).options);
const m = await mod.loadMaterials(p);
const ms = m.calc(m.payload, m.overrides);
for (const id of ["material", "concrete", "quantity"])
screen[`material:${id}`] = shape(mod.materialOptions(mod.materialTable(id, ms, m.overrides)));
const { table, interval } = await mod.loadEarthwork(p);
screen["earthwork:summary"] = shape(mod.earthworkSummaryOptions(table));
screen["earthwork:sheet"] = shape(mod.earthworkSheetOptions(table, interval));
for (const id of ["haul", "free"]) {
const { groups, caption } = mod.HAUL_SHEETS[id](table);
const { options, averages } = mod.haulOptions(groups, caption);
screen[`earthwork:${id}`] = { ...shape(options), averages };
}
screen.earthworkNames = mod.EARTHWORK_SHEETS.map((s) => s.label);
const h = await mod.loadHydro(p);
screen.hydroInputs = h.sheet.stations.map((st) => mod.hydroInputs(h.sheet, st, h.drafts));
screen.milestone = shape(
mod.milestoneTable(mod.liveMilestone(p), mod.siteInfo()?.mid_default_m ?? null).options,
);
console.log(JSON.stringify({ sheets, screen, posts }));
} finally {
fs.rmSync(outDir, { recursive: true, force: true });
}