import fs from "fs/promises"; import path from "path"; import Database from "better-sqlite3"; const db = new Database("main.db"); db.pragma("journal_mode = WAL"); const omit = (data, keys = []) => Object.fromEntries( Object.entries(data).filter( ([key, value]) => !keys.includes(key) && (typeof value === "number" || typeof value === "boolean" || !!value), ), ); const exists = (table, primaryKey, value) => { const stmt = `SELECT COUNT(*) count FROM ${table} WHERE ${primaryKey}=${ typeof value === "string" ? `'${value}'` : value }`; return db.prepare(stmt).get().count > 0; }; const inserter = (table, data, primaryKey) => { console.log(); if (primaryKey && exists(table, primaryKey, data[primaryKey])) { console.log(`${table} ${data[primaryKey]} already exists`); return; } const entries = Object.entries(data); const stmt = `INSERT INTO ${table} (${entries .map(([key]) => key) .join(", ")}) VALUES (${entries .map(([, value]) => { if (Array.isArray(value)) { return `'${value.join(", ").replaceAll("'", "''")}'`; } if (typeof value === "string") { return `'${value.replaceAll("'", "''")}'`; } return value; }) .join(", ")});`; console.log(stmt); db.exec(stmt); }; const handleType = (type) => { if (type.issuer) { inserter("Issuer", omit(type.issuer), "code"); } if (type.value) { if (type.value.currency) { inserter("Currency", omit(type.value.currency), "id"); } inserter( "Value", omit( { ...type.value, currency_id: type.value.currency?.id, id: type.id, }, ["currency"], ), "id", ); } if (type.demonetization) { inserter( "Demonetization", omit({ ...type.demonetization, id: type.id }), "id", ); } if (type.composition) { inserter("Composition", omit({ ...type.composition, id: type.id }), "id"); } if (type.technique) { inserter("Technique", omit({ ...type.technique, id: type.id }), "id"); } if (type.obverse) { inserter("Obverse", omit({ ...type.obverse, id: type.id }), "id"); } if (type.reverse) { inserter("Reverse", omit({ ...type.reverse, id: type.id }), "id"); } if (type.edge) { inserter("Edge", omit({ ...type.edge, id: type.id }), "id"); } if (type.watermark) { inserter("Watermark", omit({ ...type.watermark, id: type.id }), "id"); } inserter( "NumistaType", omit( { ...type, issuer_code: type.issuer?.code, count: type.count || 1, ...(type.value && { value_id: type.id }), ...(type.demonetization && { demonetization_id: type.id }), ...(type.composition && { composition_id: type.id }), ...(type.technique && { technique_id: type.id }), ...(type.obverse && { obverse_id: type.id }), ...(type.reverse && { reverse_id: type.id }), ...(type.edge && { edge_id: type.id }), ...(type.watermark && { watermark_id: type.id }), }, [ "issuer", "value", "ruler", "obverse", "reverse", "edge", "watermark", "tags", "references", "mints", "printers", "demonetization", "composition", "technique", "related_types", ], ), "id", ); if (type.ruler?.length > 0) { for (const ruler of type.ruler) { if (ruler.group) { inserter("RulerGroup", omit(ruler.group), "id"); } inserter( "Ruler", omit( { ...ruler, group_id: ruler.group?.id, }, ["group"], ), "id", ); try { inserter("TypeRuler", { type_id: type.id, ruler_id: ruler.id, }); } catch { //ok } } } }; const handleRelated = (type) => { if (type.related_types?.length > 0) { for (const related_type of type.related_types) { if (exists("NumistaType", "id", related_type.id)) { inserter( "TypeRelated", omit({ type1_id: type.id, type2_id: related_type.id, }), ); } } } }; const forEachType = async (callback) => { for (const typeRoot of ["coins", "banknotes"]) { for (const country of await fs.readdir(path.join("../public", typeRoot))) { for (const typePath of ( await fs.readdir(path.join("../public", typeRoot, country)) ).filter((filepath) => filepath.endsWith(".json"))) { const type = JSON.parse( await fs.readFile( path.join("../public", typeRoot, country, typePath), "utf-8", ), ); callback(type); } } } }; await forEachType(handleType); await forEachType(handleRelated); db.close();