159 lines
6.0 KiB
JavaScript
159 lines
6.0 KiB
JavaScript
#!/usr/bin/env node
|
|
// Fügt Orte aus einem KI-Import-JSON (Format wie in anforderung.md
|
|
// "Vorgabe für das KI-JSON" beschrieben, z.B. Export aus dem
|
|
// Prompt-Generator) dauerhaft zu data/seed.js hinzu.
|
|
//
|
|
// Nutzung:
|
|
// node scripts/add-seed-places.js <pfad-zur-ki-datei.json> [--dry-run]
|
|
//
|
|
// Lädt die echten App-Module (normalizePlace, Kategorie-Auflösung per
|
|
// Label, generateId) per vm-Sandbox aus js/, statt deren Logik hier
|
|
// erneut nachzubauen - so bleibt das Skript automatisch konsistent mit
|
|
// der App, auch wenn sich Kategorien oder Default-Felder dort ändern.
|
|
// Neue Einträge werden als Text vor dem abschließenden "];" eingefügt;
|
|
// bestehende Einträge in seed.js bleiben dabei byteidentisch erhalten.
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const vm = require('vm');
|
|
const crypto = require('crypto');
|
|
|
|
const ROOT = path.join(__dirname, '..');
|
|
const SEED_PATH = path.join(ROOT, 'data', 'seed.js');
|
|
|
|
function loadAppSandbox() {
|
|
// window muss der Kontext-Global selbst sein (nicht nur eine Property
|
|
// darauf), damit die App-Dateien - die im Browser als Sloppy-Mode-Scripts
|
|
// laufen und "EventMap" bar (ohne "window.") referenzieren - dieselbe
|
|
// globale Bindung sehen wie "window.EventMap".
|
|
const sandbox = { crypto: { randomUUID: crypto.randomUUID } };
|
|
sandbox.window = sandbox;
|
|
vm.createContext(sandbox);
|
|
|
|
// Reihenfolge muss der Abhängigkeitskette aus index.html entsprechen.
|
|
const files = [
|
|
'js/utils/dom.js',
|
|
'js/utils/id.js',
|
|
'js/config/categories.js',
|
|
'js/models/schema.js',
|
|
'js/models/place.js',
|
|
];
|
|
for (const file of files) {
|
|
const code = fs.readFileSync(path.join(ROOT, file), 'utf8');
|
|
vm.runInContext(code, sandbox, { filename: file });
|
|
}
|
|
|
|
const seedCode = fs.readFileSync(SEED_PATH, 'utf8');
|
|
vm.runInContext(seedCode, sandbox, { filename: 'data/seed.js' });
|
|
|
|
return sandbox.EventMap;
|
|
}
|
|
|
|
function inlineTags(tags) {
|
|
return '[' + tags.map((t) => JSON.stringify(t)).join(', ') + ']';
|
|
}
|
|
|
|
/** Formatiert einen normalisierten Place exakt im Stil der bestehenden seed.js-Einträge. */
|
|
function formatPlace(p) {
|
|
const lines = [
|
|
` "id": ${JSON.stringify(p.id)},`,
|
|
` "name": ${JSON.stringify(p.name)},`,
|
|
` "category": ${JSON.stringify(p.category)},`,
|
|
` "shortDescription": ${JSON.stringify(p.shortDescription)},`,
|
|
` "description": ${JSON.stringify(p.description)},`,
|
|
` "latitude": ${JSON.stringify(p.latitude)},`,
|
|
` "longitude": ${JSON.stringify(p.longitude)},`,
|
|
` "address": ${JSON.stringify(p.address)},`,
|
|
` "city": ${JSON.stringify(p.city)},`,
|
|
` "region": ${JSON.stringify(p.region)},`,
|
|
` "ageRecommendation": { "min": ${JSON.stringify(p.ageRecommendation.min)}, "max": ${JSON.stringify(p.ageRecommendation.max)} },`,
|
|
` "cost": { "type": ${JSON.stringify(p.cost.type)}, "description": ${JSON.stringify(p.cost.description)} },`,
|
|
` "openingHours": ${JSON.stringify(p.openingHours)},`,
|
|
` "durationMinutes": ${JSON.stringify(p.durationMinutes)},`,
|
|
` "facilities": { "strollerAccessible": ${p.facilities.strollerAccessible}, "wheelchairAccessible": ${p.facilities.wheelchairAccessible}, "toilets": ${p.facilities.toilets}, "parking": ${p.facilities.parking}, "restaurant": ${p.facilities.restaurant} },`,
|
|
` "environment": ${JSON.stringify(p.environment)},`,
|
|
` "website": ${JSON.stringify(p.website)},`,
|
|
` "image": ${JSON.stringify(p.image)},`,
|
|
` "rating": ${JSON.stringify(p.rating)},`,
|
|
` "tags": ${inlineTags(p.tags)},`,
|
|
` "source": ${JSON.stringify(p.source)},`,
|
|
` "sourceVerified": ${p.sourceVerified},`,
|
|
` "createdAt": ${JSON.stringify(p.createdAt)},`,
|
|
` "updatedAt": ${JSON.stringify(p.updatedAt)}`,
|
|
];
|
|
return ' {\n' + lines.join('\n') + '\n }';
|
|
}
|
|
|
|
function main() {
|
|
const args = process.argv.slice(2);
|
|
const dryRun = args.includes('--dry-run');
|
|
const inputPath = args.find((a) => !a.startsWith('--'));
|
|
|
|
if (!inputPath) {
|
|
console.error('Nutzung: node scripts/add-seed-places.js <pfad-zur-ki-datei.json> [--dry-run]');
|
|
process.exit(1);
|
|
}
|
|
|
|
const EventMap = loadAppSandbox();
|
|
const existingPlaces = EventMap.data.SEED_PLACES;
|
|
const existingNames = new Set(existingPlaces.map((p) => p.name.trim().toLowerCase()));
|
|
|
|
const inputRaw = fs.readFileSync(path.resolve(inputPath), 'utf8');
|
|
const input = JSON.parse(inputRaw);
|
|
const candidates = Array.isArray(input.places) ? input.places : [];
|
|
|
|
if (candidates.length === 0) {
|
|
console.error('Keine "places" im angegebenen JSON gefunden.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const toAdd = [];
|
|
const skipped = [];
|
|
|
|
for (const raw of candidates) {
|
|
const key = String(raw.name || '').trim().toLowerCase();
|
|
if (!key || existingNames.has(key)) {
|
|
skipped.push(raw.name || '(ohne Namen)');
|
|
continue;
|
|
}
|
|
const normalized = EventMap.models.normalizePlace(raw);
|
|
toAdd.push(normalized);
|
|
existingNames.add(key); // schützt auch vor Duplikaten innerhalb derselben Eingabedatei
|
|
}
|
|
|
|
console.log(`Gefunden: ${candidates.length}, neu: ${toAdd.length}, übersprungen (Duplikat/Name fehlt): ${skipped.length}`);
|
|
if (skipped.length > 0) {
|
|
console.log('Übersprungen:', skipped.join(', '));
|
|
}
|
|
|
|
if (toAdd.length === 0) {
|
|
console.log('Nichts zu tun.');
|
|
return;
|
|
}
|
|
|
|
const seedText = fs.readFileSync(SEED_PATH, 'utf8');
|
|
const closingIndex = seedText.lastIndexOf('\n];');
|
|
if (closingIndex === -1) {
|
|
console.error('Konnte das Ende von EventMap.data.SEED_PLACES in seed.js nicht finden.');
|
|
process.exit(1);
|
|
}
|
|
|
|
const before = seedText.slice(0, closingIndex);
|
|
const after = seedText.slice(closingIndex); // beginnt mit "\n];"
|
|
|
|
const newBlocks = toAdd.map(formatPlace).join(',\n');
|
|
const updated = `${before},\n${newBlocks}${after}`;
|
|
|
|
if (dryRun) {
|
|
console.log('--dry-run: seed.js wird nicht verändert. Neue Blöcke:\n');
|
|
console.log(newBlocks);
|
|
return;
|
|
}
|
|
|
|
fs.writeFileSync(SEED_PATH, updated, 'utf8');
|
|
console.log(`data/seed.js aktualisiert: ${toAdd.length} neue Einträge hinzugefügt.`);
|
|
console.log(toAdd.map((p) => ` - ${p.name}`).join('\n'));
|
|
}
|
|
|
|
main();
|