- Created a new script `migrate-to-sqlite.js` to migrate data from JSON files into a SQLite database. - Implemented functions to read JSON files safely and handle errors. - Established database schema with tables for armour, weapons, bestiary, and rules. - Added logic to insert data from JSON files into the corresponding database tables. - Included backup functionality for the existing database before migration. - Logged import totals and sample data from each table for verification. - Added a new script `check-shop-stats.js` to fetch and log item statistics from the database.
167 lines
5.6 KiB
JavaScript
167 lines
5.6 KiB
JavaScript
#!/usr/bin/env node
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const Database = require('better-sqlite3');
|
|
|
|
const dbPath = path.join(__dirname, '../database/sqlite/deathwatch.db');
|
|
const backupPath = dbPath + '.backup.' + Date.now();
|
|
|
|
function safeReadJSON(p) {
|
|
if (!fs.existsSync(p)) return null;
|
|
try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch (e) { console.error('JSON parse error', p, e.message); return null; }
|
|
}
|
|
|
|
console.log('=== MIGRATE JSON DATA INTO SQLITE ===');
|
|
if (!fs.existsSync(dbPath)) {
|
|
console.error('DB not found at', dbPath);
|
|
process.exit(1);
|
|
}
|
|
|
|
fs.copyFileSync(dbPath, backupPath);
|
|
console.log('Backup created:', backupPath);
|
|
|
|
const db = new Database(dbPath);
|
|
|
|
// Create consolidated tables
|
|
db.exec(`
|
|
CREATE TABLE IF NOT EXISTS armour (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT UNIQUE NOT NULL,
|
|
req INTEGER DEFAULT 0,
|
|
renown TEXT DEFAULT 'None',
|
|
category TEXT,
|
|
stats TEXT,
|
|
source TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS weapons (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT UNIQUE NOT NULL,
|
|
req INTEGER DEFAULT 0,
|
|
renown TEXT DEFAULT 'None',
|
|
category TEXT,
|
|
stats TEXT,
|
|
source TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS bestiary (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT NOT NULL,
|
|
book TEXT,
|
|
page TEXT,
|
|
pdf TEXT,
|
|
stats TEXT,
|
|
profile TEXT,
|
|
snippet TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS rules (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
rule_id TEXT UNIQUE,
|
|
title TEXT,
|
|
content TEXT,
|
|
page INTEGER,
|
|
source TEXT,
|
|
source_abbr TEXT,
|
|
category TEXT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
`);
|
|
|
|
const insertArmour = db.prepare(`INSERT OR IGNORE INTO armour (name, req, renown, category, stats, source) VALUES (?, ?, ?, ?, ?, ?)`);
|
|
const insertWeapon = db.prepare(`INSERT OR IGNORE INTO weapons (name, req, renown, category, stats, source) VALUES (?, ?, ?, ?, ?, ?)`);
|
|
const insertBestiary = db.prepare(`INSERT INTO bestiary (name, book, page, pdf, stats, profile, snippet) VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
const insertRule = db.prepare(`INSERT OR IGNORE INTO rules (rule_id, title, content, page, source, source_abbr, category) VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
|
|
let totals = { armour:0, weapons:0, bestiary:0, rules:0 };
|
|
|
|
// Import armour
|
|
const armourFile = path.join(__dirname, '../database/public/deathwatch-armor.json');
|
|
const armourData = safeReadJSON(armourFile);
|
|
if (armourData) {
|
|
const categories = Object.keys(armourData);
|
|
categories.forEach(cat => {
|
|
const arr = armourData[cat];
|
|
if (!Array.isArray(arr)) return;
|
|
const insert = insertArmour;
|
|
db.transaction(() => {
|
|
for (const item of arr) {
|
|
const stats = JSON.stringify(item.stats || {});
|
|
const src = (item.stats && item.stats.source) || '';
|
|
insert.run(item.name || '(unnamed)', item.req || 0, item.renown || 'None', item.category || cat, stats, src);
|
|
totals.armour++;
|
|
}
|
|
})();
|
|
});
|
|
}
|
|
|
|
// Import weapons
|
|
const weaponsFile = path.join(__dirname, '../database/public/deathwatch-weapons-comprehensive.json');
|
|
const weaponsData = safeReadJSON(weaponsFile);
|
|
if (weaponsData) {
|
|
// many weapon files use keys like rangedWeapons, meleeWeapons
|
|
Object.keys(weaponsData).forEach(k => {
|
|
const arr = weaponsData[k];
|
|
if (!Array.isArray(arr)) return;
|
|
db.transaction(() => {
|
|
for (const w of arr) {
|
|
const stats = JSON.stringify(w.stats || {});
|
|
const src = (w.stats && w.stats.source) || '';
|
|
insertWeapon.run(w.name || '(unnamed)', w.req || 0, w.renown || 'Any', w.category || k, stats, src);
|
|
totals.weapons++;
|
|
}
|
|
})();
|
|
});
|
|
}
|
|
|
|
// Import bestiary
|
|
const bestiaryFile = path.join(__dirname, '../database/deathwatch-bestiary-extracted.json');
|
|
const bestiaryData = safeReadJSON(bestiaryFile);
|
|
if (bestiaryData && Array.isArray(bestiaryData.results)) {
|
|
db.transaction(() => {
|
|
for (const e of bestiaryData.results) {
|
|
const stats = JSON.stringify(e.stats || {});
|
|
const profile = JSON.stringify(e.profile || {});
|
|
const name = e.bestiaryName || e.name || '(unnamed)';
|
|
insertBestiary.run(name, e.book || '', e.page || '', e.pdf || '', stats, profile, e.stats && e.stats.snippet ? e.stats.snippet : '');
|
|
totals.bestiary++;
|
|
}
|
|
})();
|
|
}
|
|
|
|
// Import rules
|
|
const rulesFile = path.join(__dirname, '../database/rules/rules-database.json');
|
|
const rulesData = safeReadJSON(rulesFile);
|
|
if (rulesData && Array.isArray(rulesData.rules)) {
|
|
db.transaction(() => {
|
|
for (const r of rulesData.rules) {
|
|
insertRule.run(r.id || null, r.title || '', r.content || '', r.page || null, r.source || '', r.sourceAbbr || '', r.category || 'general');
|
|
totals.rules++;
|
|
}
|
|
})();
|
|
}
|
|
|
|
console.log('Import totals:', totals);
|
|
|
|
// Show row counts from DB for verification
|
|
const counts = {
|
|
armour: db.prepare('SELECT COUNT(*) as c FROM armour').get().c,
|
|
weapons: db.prepare('SELECT COUNT(*) as c FROM weapons').get().c,
|
|
bestiary: db.prepare('SELECT COUNT(*) as c FROM bestiary').get().c,
|
|
rules: db.prepare('SELECT COUNT(*) as c FROM rules').get().c
|
|
};
|
|
|
|
console.log('DB row counts:', counts);
|
|
|
|
// Print a small sample from each table
|
|
console.log('\nSample armour:', db.prepare('SELECT name, category, stats FROM armour LIMIT 3').all());
|
|
console.log('\nSample weapons:', db.prepare('SELECT name, category, stats FROM weapons LIMIT 3').all());
|
|
console.log('\nSample bestiary:', db.prepare('SELECT name, book, snippet FROM bestiary LIMIT 3').all());
|
|
console.log('\nSample rules:', db.prepare('SELECT rule_id, title FROM rules LIMIT 3').all());
|
|
|
|
db.close();
|
|
console.log('\nMigration complete. DB backed up at', backupPath);
|