// Snapshots every published entry from the database back into // `src/data/problems.json` - the curated seed baseline and the repo's // disaster-recovery copy of the catalog. // // The database is the source of truth (community edits and imports live // there), so without periodic exports a fresh `db:seed` could only rebuild // the original launch set. Run `npm run db:export` after significant catalog // changes and commit the result. // // Round-trip safety: the seed upserts curated content by slug and never // touches votes, comments, status or submitter linkage, so exporting and // re-seeding a live database is a no-op for user state. On a FRESH database, // community-submitted entries are recreated as curator entries (the // submitter accounts don't exist there) - acceptable for recovery. import { writeFileSync } from "fs"; import { join } from "path"; import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); async function main() { const rows = await prisma.problem.findMany({ where: { status: "published" }, orderBy: { slug: "asc" }, include: { links: { select: { label: true, url: true }, orderBy: { position: "asc" } }, relationsFrom: { select: { kind: true, note: true, to: { select: { slug: true } } }, orderBy: { position: "asc" }, }, }, }); const out = rows.map((p) => { const entry: Record = { slug: p.slug, name: p.name, shortName: p.shortName, problemNumber: p.problemNumber, field: p.field, fieldGroup: p.fieldGroup, resolution: p.resolution, statement: p.statement, posedBy: p.posedBy, yearPosed: p.yearPosed, solveType: p.solveType, solveDate: p.solveDate, model: p.model, modelMaker: p.modelMaker, humanCollaborators: p.humanCollaborators, aiRole: p.aiRole, verification: p.verification, verificationNote: p.verificationNote, publication: p.publication, resolutionMethod: p.resolutionMethod, citations: p.citations, citationsPaper: p.citationsPaper, citationsSource: p.citationsSource, citationsUrl: p.citationsUrl, renownLangs: p.renownLangs, sourceUrl: p.sourceUrl, sourceName: p.sourceName, }; // Optional caveat fields are present only on entries that need them, // matching the documented JSON shape. if (p.renownNote !== null) entry.renownNote = p.renownNote; if (p.resultNote !== null) entry.resultNote = p.resultNote; if (p.ageNote !== null) entry.ageNote = p.ageNote; if (p.claimIssueNote !== null) entry.claimIssueNote = p.claimIssueNote; if (p.aiContribution !== null) entry.aiContribution = p.aiContribution; if (p.significance !== null) entry.significance = p.significance; if (p.significanceNote !== null) entry.significanceNote = p.significanceNote; if (Array.isArray(p.links) && p.links.length > 0) entry.links = p.links; if (p.relationsFrom.length > 0) { entry.relations = p.relationsFrom.map((r) => ({ to: r.to.slug, kind: r.kind, note: r.note })); } return entry; }); const path = join(__dirname, "..", "src", "data", "problems.json"); writeFileSync(path, JSON.stringify(out, null, 2) + "\n"); console.log(`Exported ${out.length} published entries to src/data/problems.json`); } main() .catch((error) => { console.error(error); process.exit(1); }) .finally(() => prisma.$disconnect());