SPB Git

spb/autoscholar

Public
9.6 KB · 228 lines typescript
1import { createHash } from "node:crypto";2import { mkdir, writeFile } from "node:fs/promises";3import { dirname, join, resolve } from "node:path";4import { fileURLToPath } from "node:url";5import { eq } from "drizzle-orm";6import { closeDb, getDb } from "./client.js";7import { paperSequences, papers, paperTopics, paperVersions, series, topics } from "./schema/index.js";89const DISCIPLINES: Array<[string, string, string]> = [10  ["economics", "Economics", "Micro, macro, econometrics, finance-adjacent economics."],11  ["finance", "Finance", "Asset pricing, corporate finance, market microstructure, financial econometrics."],12  ["mathematics", "Mathematics", "Pure and applied mathematics, formal results and proofs."],13  ["statistics", "Statistics", "Statistical theory, methodology and computation."],14  ["computer-science", "Computer Science", "Algorithms, systems, machine learning and AI."],15  ["physics", "Physics", "Theoretical and computational physics."],16  ["chemistry", "Chemistry", "Computational and theoretical chemistry."],17  ["biology", "Biology", "Computational biology and public-data analyses (human review required)."],18  [19    "medicine",20    "Medicine & Health",21    "Literature synthesis and public-data health research (human review required).",22  ],23  ["engineering", "Engineering", "Methods, simulations and systems engineering."],24  [25    "earth-sciences",26    "Earth & Environmental Sciences",27    "Climate, geoscience and environmental data analyses.",28  ],29  ["social-sciences", "Social Sciences", "Sociology, political science, demography."],30  ["psychology", "Psychology", "Reviews and public-data analyses (human review required)."],31  ["humanities", "Humanities", "History, philosophy, textual and interpretive inquiry."],32  ["law", "Law", "Doctrinal and empirical legal studies (human review required)."],33  ["education", "Education", "Education research and policy."],34  ["interdisciplinary", "Interdisciplinary", "Studies spanning several disciplines."],35];36const SUBFIELDS: Array<[string, string, string]> = [37  ["economics", "econometrics", "Econometrics"],38  ["economics", "macroeconomics", "Macroeconomics"],39  ["economics", "real-estate-economics", "Real Estate Economics"],40  ["finance", "asset-pricing", "Asset Pricing"],41  ["finance", "market-microstructure", "Market Microstructure"],42  ["computer-science", "machine-learning", "Machine Learning"],43  ["computer-science", "large-language-models", "Large Language Models"],44  ["mathematics", "probability", "Probability"],45  ["mathematics", "optimization", "Optimization"],46  ["statistics", "bayesian-statistics", "Bayesian Statistics"],47];4849export async function seedTopicsAndSeries(db = getDb()): Promise<void> {50  for (const [i, [slug, name, description]] of DISCIPLINES.entries()) {51    await db52      .insert(topics)53      .values({ slug, name, description, sortOrder: i })54      .onConflictDoUpdate({ target: topics.slug, set: { name, description, sortOrder: i } });55  }56  for (const [parentSlug, slug, name] of SUBFIELDS) {57    const [parent] = await db58      .select({ id: topics.id })59      .from(topics)60      .where(eq(topics.slug, parentSlug))61      .limit(1);62    await db63      .insert(topics)64      .values({ slug, name, parentId: parent?.id ?? null, sortOrder: 100 })65      .onConflictDoUpdate({ target: topics.slug, set: { name, parentId: parent?.id ?? null } });66  }67  await db68    .insert(series)69    .values({70      slug: "as-wp",71      name: "AutoScholar Working Papers",72      description:73        "The main working-paper series. Numbers AS-WP-YEAR-NNNN are allocated at publication and never reused.",74    })75    .onConflictDoNothing();76}7778/** Staging only: one clearly labelled synthetic sample so the paper page and PDF viewer can be exercised. Never indexed, never shown in production. */79export async function seedStagingPaper(db = getDb()): Promise<string | null> {80  if (process.env.STAGING_MODE !== "1")81    throw new Error("seedStagingPaper refuses to run unless STAGING_MODE=1");82  const publicId = "AS-WP-2026-0000";83  const [exists] = await db84    .select({ id: papers.id, slug: papers.slug })85    .from(papers)86    .where(eq(papers.publicId, publicId))87    .limit(1);88  if (exists) return exists.slug;89  const { PDFDocument, StandardFonts } = await import("pdf-lib");90  const doc = await PDFDocument.create();91  doc.setTitle("[STAGING SAMPLE] A synthetic working paper for interface testing");92  const font = await doc.embedFont(StandardFonts.TimesRoman);93  const bold = await doc.embedFont(StandardFonts.TimesRomanBold);94  const lines = [95    ["AutoScholar Working Paper AS-WP-2026-0000 — STAGING SAMPLE", bold, 14],96    ["[STAGING SAMPLE] A synthetic working paper for interface testing", bold, 18],97    ["AutoScholar — AI-generated research. Research computed on MacLustr.", font, 11],98    ["AI-generated working paper; not formally peer reviewed.", font, 11],99    ["", font, 11],100    [101      "Abstract. This document contains no research. It exists only so the staging site can render a",102      font,103      11,104    ],105    [106      "paper page, a PDF viewer and download links. It is flagged synthetic and is excluded from production,",107      font,108      11,109    ],110    ["indexing and feeds.", font, 11],111  ] as const;112  for (let p = 0; p < 6; p++) {113    const page = doc.addPage([595, 842]);114    let y = 780;115    if (p === 0)116      for (const [text, f, size] of lines) {117        page.drawText(text, { x: 60, y, size, font: f });118        y -= size + 10;119      }120    page.drawText(`Page ${p + 1} of 6 — staging sample — AutoScholar Working Paper AS-WP-2026-0000`, {121      x: 60,122      y: 40,123      size: 9,124      font,125    });126    for (let i = 0; i < 30; i++) {127      page.drawText(`Placeholder line ${i + 1}. Synthetic content for layout testing only.`, {128        x: 60,129        y: (p === 0 ? 560 : 760) - i * 20,130        size: 10,131        font,132      });133    }134  }135  const pdf = Buffer.from(await doc.save());136  const sha256 = createHash("sha256").update(pdf).digest("hex");137  const root = resolve(process.env.ARTIFACT_ROOT ?? "./data/artifacts");138  const key = `public/${publicId}/v1/manuscript/main.pdf`;139  await mkdir(dirname(join(root, key)), { recursive: true });140  await writeFile(join(root, key), pdf);141  await writeFile(`${join(root, key)}.sha256`, `${sha256}  ${key}\n`);142  const abstractTxt =143    "This staging sample contains no research. It exists only so the staging site can render a paper page, a PDF viewer and download links. It is flagged synthetic, excluded from production, indexing and feeds, and must never be mistaken for an AutoScholar working paper. Everything on this page is placeholder content generated for interface testing.";144  const [s] = await db.select({ id: series.id }).from(series).where(eq(series.slug, "as-wp")).limit(1);145  const slug = "as-wp-2026-0000-staging-sample";146  const slugRow = await db.transaction(async (tx) => {147    await tx.insert(paperSequences).values({ year: 2026, last: 0 }).onConflictDoNothing();148    const [p] = await tx149      .insert(papers)150      .values({151        publicId,152        slug,153        title: "[STAGING SAMPLE] A synthetic working paper for interface testing",154        status: "published",155        discipline: "interdisciplinary",156        researchMode: "theoretical",157        seriesId: s?.id ?? null,158        synthetic: true,159      })160      .returning({ id: papers.id });161    const [v] = await tx162      .insert(paperVersions)163      .values({164        paperId: p!.id,165        versionNo: 1,166        runId: "00000000-0000-0000-0000-000000000000",167        title: "[STAGING SAMPLE] A synthetic working paper for interface testing",168        abstract: abstractTxt,169        keywords: ["staging", "synthetic sample", "interface test"],170        metadata: {171          contributions: ["None. This is a synthetic staging sample."],172          methods: "No methods: synthetic staging sample.",173          mainResults: "No results: synthetic staging sample.",174          limitations: "This document is a synthetic staging sample with no scientific content.",175          synthetic: true,176        },177        artifactSet: [178          {179            path: "manuscript/main.pdf",180            kind: "pdf",181            sha256,182            bytes: pdf.length,183            contentType: "application/pdf",184            publicPath: `/papers/${publicId}/v1/manuscript/main.pdf`,185          },186        ],187        pdfPublicPath: `/papers/${publicId}/v1/manuscript/main.pdf`,188        qaSummary: {189          publishable: false,190          warnings: ["synthetic staging sample — not evaluated"],191          policyVersion: "staging",192        },193        provenance: {194          model: "none (staging seed)",195          infrastructure: "none — synthetic staging sample",196          toolchain: { "pdf-lib": "seed" },197        },198        changelog: "Staging sample.",199      })200      .returning({ id: paperVersions.id });201    await tx.update(papers).set({ currentVersionId: v!.id }).where(eq(papers.id, p!.id));202    const [t] = await tx203      .select({ id: topics.id })204      .from(topics)205      .where(eq(topics.slug, "interdisciplinary"))206      .limit(1);207    if (t) await tx.insert(paperTopics).values({ paperId: p!.id, topicId: t.id }).onConflictDoNothing();208    return slug;209  });210  return slugRow;211}212213if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {214  (async () => {215    const db = getDb();216    await seedTopicsAndSeries(db);217    console.log("[seed] topics and series ensured");218    if (process.env.STAGING_MODE === "1")219      console.log("[seed] staging sample paper:", await seedStagingPaper(db));220    else console.log("[seed] STAGING_MODE!=1 → no sample paper");221    await closeDb();222  })().catch(async (e) => {223    console.error("[seed] failed:", e);224    await closeDb();225    process.exit(1);226  });227}228
Files · 87b4e55c3b9fbcc2cd191a1e469b33de0a24ccc8
Menu

Theme