mp_tpu_TESTES / scripts /import_legacy_data.mjs
Guilherme Silberfarb Costa
Rebuild app with React, FastAPI and persistent JSON storage
ce583c4
Raw History Blame Contribute Delete
10.8 kB
import fs from "node:fs";
import path from "node:path";
import vm from "node:vm";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const html = fs.readFileSync(path.join(root, "index.html"), "utf8");
function expression(name, pattern) {
const match = html.match(pattern);
if (!match) throw new Error(`Não foi possível extrair ${name}.`);
return vm.runInNewContext(`(${match[1]})`);
}
const savedDefault = expression("savedDefault", /const savedDefault=(\[[^\n]+\]);/);
const areaById = expression("areaById", /const areaById=({[^\n]+});/);
const savedPinPositions = expression("savedPinPositions", /const savedPinPositions=({[^\n]+});/);
const upperDefault = expression("upperDefault", /const upperDefault=(\(\(\)=>\{[\s\S]*?\}\)\(\));/);
const accessoryLinks = expression("TPU_ACCESSORY_LINKS", /const TPU_ACCESSORY_LINKS=({[^\n]+});/);
const baseLinks = expression("TPU_BASE_LINKS", /const TPU_BASE_LINKS=({[^\n]+});/);
const requiredStores = expression("REQUIRED_ACCESSORY_STORES", /const REQUIRED_ACCESSORY_STORES=(\[[\s\S]*?\n\]);/);
const requiredTpus = expression("REQUIRED_TPUS", /const REQUIRED_TPUS=(\[[^\n]+\]);/);
const context = { window: {} };
vm.createContext(context);
vm.runInContext(fs.readFileSync(path.join(root, "tpu_mpc_v4_data.js"), "utf8"), context);
vm.runInContext(fs.readFileSync(path.join(root, "tpu_area_details.js"), "utf8"), context);
const imported = context.window.MP_IMPORTED_TPUS;
const details = context.window.MP_TPU_DETAILS.byId;
const quadrantNames = {
74: "DEP 6 (I)", 103: "BOX 1 (IV)", 120: "DEP (II)",
135: "DEP 2 (III)", 136: "DEP 3 (III)", 137: "DEP 4 (III)",
138: "DEP 5 (III)", 139: "DEP 1 (III)",
};
function spaceType(name) {
const normalized = String(name).normalize("NFD").replace(/[\u0300-\u036f]/g, "").toUpperCase();
const number = normalized.match(/^(?:LOJ|LOJA|LJ)\s*0*(\d+)/);
if (number && ["16", "18", "20"].includes(String(Number(number[1])))) return "SUBSTACAO";
if (normalized.startsWith("LOJ")) return "LOJ";
if (normalized.startsWith("BC")) return "BC";
if (normalized.startsWith("BP")) return "BP";
if (normalized.startsWith("AM") || normalized.startsWith("MI")) return "AM";
if (normalized.startsWith("PATIO")) return "PATIO";
if (normalized.startsWith("BOX")) return "BOX";
if (normalized.startsWith("DEP")) return "DEP";
if (normalized.startsWith("LED")) return "LED";
if (normalized.startsWith("REST")) return "REST";
if (normalized.startsWith("CON")) return "CON";
return "OUTRO";
}
const types = [
["LOJ", "Loja", "#F6D5C5", 1], ["BC", "Banca", "#CDE8D6", 1],
["BP", "Banca do peixe", "#DCD5F2", 1], ["AM", "Área de mesa", "#F7E7B8", .85],
["PATIO", "Pátio", "#CFE3F4", 1], ["BOX", "Box", "#F4C7C3", 1],
["DEP", "Depósito", "#D7E1EA", .75], ["LED", "Painel LED", "#FFE08A", 1],
["REST", "Restaurante", "#F3D3E0", 1], ["CON", "Concessão", "#CDEBE7", 1],
["SUBSTACAO", "Subestação", "#D9D2E9", 1], ["OUTRO", "Outro", "#FFFFFF", 1],
].map(([code, label, color, coefficient]) => ({ code, label, color, coefficient }));
function normalizeSpace(raw, floorId) {
const corrected = floorId === "ground"
? (quadrantNames[Number(raw.id)] || (Number(raw.id) === 145 && raw.name.trim().toUpperCase() === "AM PIII-1" ? "AM PIII-2" : raw.name))
: raw.name;
const pin = floorId === "ground" ? savedPinPositions[raw.id] : null;
const x = Number.isFinite(Number(raw.pinX)) ? Number(raw.pinX) : pin?.[0] ?? ((raw.col - 1 + raw.width / 2) / 42);
const y = Number.isFinite(Number(raw.pinY)) ? Number(raw.pinY) : pin?.[1] ?? ((raw.row - 1 + raw.height / 2) / 43);
return {
id: `${floorId}:${raw.id}`,
legacy_id: Number(raw.id),
floor_id: floorId,
name: corrected,
type: spaceType(corrected),
base_area_m2: Number((floorId === "ground" ? areaById[raw.id] : raw.area) || 0),
position: { x: Number(x.toFixed(7)), y: Number(y.toFixed(7)) },
grid: { col: Number(raw.col), row: Number(raw.row), width: Number(raw.width), height: Number(raw.height) },
components: { mezzanine: 0, deposit: 0, deck: 0, table: 0 },
};
}
const groundById = new Map(savedDefault.map(item => [Number(item.id), item]));
for (const item of requiredStores) if (!groundById.has(Number(item.id))) groundById.set(Number(item.id), item);
const spaces = [
...[...groundById.values()].map(item => normalizeSpace(item, "ground")),
...upperDefault.map(item => normalizeSpace(item, "upper")),
];
function splitBoxes(items) {
const combined = items.find(t => Number(t.id) === 400115);
if (!combined) return items;
const boxes = [103, 147, 148, 149, 150, 151, 152, 153];
const deposits = [154, 155, 156, 157, 158, 159, 160, 161];
const tables = [162, 163, 164, 165, 166, 167, 168, 169];
const ids = [400115, 400129, 400130, 400131, 400132, 400133, 400134, 400135];
const replacements = ids.map((id, index) => ({
...combined,
id,
name: `TPU_BOX_${index + 1}_IV`,
storeIds: [boxes[index], deposits[index], tables[index]],
baseStoreIds: [boxes[index]],
accessoryStoreIds: [deposits[index], tables[index]],
linkedFloor: null,
linkedStoreIds: [],
allocationRefs: [{ floor: "ground", id: boxes[index] }],
sourceOrder: String(112 + index), sourceGrossArea: 19.62,
sourceUnitAreas: [9.52], sourceMezzanine: 0, sourceDeposit: 7.4,
sourceDeck: 0, sourceTableArea: 2.7, sourceWeightedArea: 17.365,
sourceArea: 17.365,
}));
return items.filter(t => Number(t.id) !== 400115).concat(replacements);
}
const rawByFloor = {
ground: splitBoxes(imported.ground).concat(requiredTpus),
upper: imported.upper,
};
const tpuMap = new Map();
for (const [floorId, items] of Object.entries(rawByFloor)) {
for (const raw of items) {
const merged = { ...(details[String(raw.id)] || {}), ...raw };
let target = tpuMap.get(String(raw.id));
if (!target) {
const source = {};
for (const [key, value] of Object.entries(merged)) {
if (key.startsWith("source")) source[key.slice(6, 7).toLowerCase() + key.slice(7)] = value;
}
source.allocationRefs = (merged.allocationRefs || []).map(ref => `${ref.floor}:${ref.id}`);
target = {
id: String(raw.id), legacy_id: Number(raw.id), name: raw.name,
color: raw.color || "#5B8DB8", members: [], source,
visual_links: [],
};
tpuMap.set(String(raw.id), target);
}
const accessories = new Set(merged.accessoryStoreIds || (floorId === "ground" ? accessoryLinks[raw.id] || [] : []));
const bases = new Set(merged.baseStoreIds || [
...(raw.storeIds || []).filter(id => !accessories.has(id)),
...(floorId === "ground" ? baseLinks[raw.id] || [] : []),
]);
const localIds = new Set([...bases, ...accessories, ...(raw.storeIds || [])]);
for (const id of localIds) {
const member = { space_id: `${floorId}:${id}`, role: accessories.has(id) ? "accessory" : "base" };
if (!target.members.some(item => item.space_id === member.space_id)) target.members.push(member);
}
if (raw.linkedFloor) {
for (const id of raw.linkedStoreIds || []) {
const spaceId = `${raw.linkedFloor}:${id}`;
if (!target.members.some(item => item.space_id === spaceId)) target.members.push({ space_id: spaceId, role: "base" });
}
}
}
}
const document = {
schema_version: 1,
revision: 0,
updated_at: new Date().toISOString(),
floors: [
{ id: "ground", name: "Térreo", image: "/plants/planta_terreo_mercado_publico.png", width: 1200, height: 1189, order: 1 },
{ id: "upper", name: "2º pavimento", image: "/plants/planta_segundo_pavimento_mercado_publico.png", width: 1200, height: 1184, order: 2 },
],
space_types: types,
component_types: [
{ code: "mezzanine", label: "Mezanino", coefficient: .75 },
{ code: "deposit", label: "Depósito", coefficient: .75 },
{ code: "deck", label: "Deck", coefficient: .30 },
{ code: "table", label: "Área de mesa", coefficient: .85 },
],
spaces,
tpus: [...tpuMap.values()],
settings: { active_floor: "ground", show_tpu_links: true },
};
const spacesById = new Map(document.spaces.map(space => [space.id, space]));
for (const tpu of document.tpus) {
const refs = tpu.source.allocationRefs || [];
if (!refs.length) continue;
const units = (tpu.source.unitAreas || []).map(Number).filter(value => value > 0);
let bases;
if (units.length === refs.length) {
bases = units;
} else if (refs.length === 1) {
bases = [units.reduce((sum, value) => sum + value, 0) || spacesById.get(refs[0])?.base_area_m2 || 0];
} else {
const mapped = refs.map(ref => spacesById.get(ref)?.base_area_m2 || 0);
const mappedTotal = mapped.reduce((sum, value) => sum + value, 0);
const unitTotal = units.reduce((sum, value) => sum + value, 0);
bases = mapped.map(value => mappedTotal ? unitTotal * value / mappedTotal : unitTotal / refs.length);
}
const memberSpaces = tpu.members.map(member => ({ ...member, space: spacesById.get(member.space_id) })).filter(item => item.space);
const depositAccessories = memberSpaces.filter(item => item.role === "accessory" && item.space.type === "DEP");
const tableAccessories = memberSpaces.filter(item => item.role === "accessory" && item.space.type === "AM");
const hasDepositAccessory = depositAccessories.length > 0;
const hasTableAccessory = tableAccessories.length > 0;
for (const item of depositAccessories) {
item.space.base_area_m2 = Number((Number(tpu.source.deposit || 0) / depositAccessories.length).toFixed(4));
item.space.components = { mezzanine: 0, deposit: 0, deck: 0, table: 0 };
}
for (const item of tableAccessories) {
item.space.base_area_m2 = Number((Number(tpu.source.tableArea || 0) / tableAccessories.length).toFixed(4));
item.space.components = { mezzanine: 0, deposit: 0, deck: 0, table: 0 };
}
const baseTotal = bases.reduce((sum, value) => sum + value, 0) || refs.length;
refs.forEach((ref, index) => {
const space = spacesById.get(ref);
if (!space) return;
const share = baseTotal ? bases[index] / baseTotal : 1 / refs.length;
space.base_area_m2 = Number((bases[index] || 0).toFixed(4));
space.components = {
mezzanine: Number((Number(tpu.source.mezzanine || 0) * share).toFixed(4)),
deposit: hasDepositAccessory ? 0 : Number((Number(tpu.source.deposit || 0) * share).toFixed(4)),
deck: Number((Number(tpu.source.deck || 0) * share).toFixed(4)),
table: hasTableAccessory ? 0 : Number((Number(tpu.source.tableArea || 0) * share).toFixed(4)),
};
});
}
const output = path.join(root, "seed", "default.json");
fs.writeFileSync(output, `${JSON.stringify(document, null, 2)}\n`);
console.log(`Seed criado: ${spaces.length} espaços, ${document.tpus.length} TPUs.`);