← back to CelebritySignatures
scripts/filter-artists.mjs
40 lines
#!/usr/bin/env node
// Post-build occupation gate for the Artists category. The roster gap-fill
// (fetch-artists-rosters.mjs) unions ALL museum constituents that have a P109
// signature — which sweeps in portrait SUBJECTS / donors who are NOT artists
// (Shakespeare, Newton, Lincoln, Napoleon, Darwin, Voltaire…). This keeps only
// people with a genuine creative occupation, so the "Artists" category actually
// contains artists and doesn't double-list Politics figures.
//
// Age is deliberately NOT filtered: ancient Greek vase painters who signed their
// pots (Exekias, Euphronios, Douris) and Chinese painter-calligraphers (Zhao
// Mengfu, Emperor Huizong) are legit signed artists. Idempotent; famous-first.
import { readFile, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
const DATA = join(fileURLToPath(new URL('..', import.meta.url)), 'data');
const arts = JSON.parse(await readFile(join(DATA, 'artists.json'), 'utf8'));
const raw = JSON.parse(await readFile(join(DATA, 'artists-raw.json'), 'utf8'));
const ART_OCC = /(paint|sculpt|printmak|draughts|illustrat|photograph|architect|designer|engrav|\bartist\b|animat|ceramic|etcher|watercolo|muralist|lithograph|graphic|calligraph|craft|goldsmith|jewel|potter|weav|textile|cartoonist)/;
// qid → occupations from the raw harvest (final records don't carry occupations)
const occByQid = {};
for (const r of raw) occByQid[(r.wikidata || '').split('/').pop()] = (r.occupations || []).map(o => o.toLowerCase());
const before = arts.length;
const kept = arts.filter(r => {
const occs = occByQid[(r.wikidata || '').split('/').pop()] || [];
return occs.some(o => ART_OCC.test(o));
});
// keep famous-first order (already sorted), just re-number rank
kept.forEach((r, i) => { r.rank = i + 1; });
await writeFile(join(DATA, 'artists.json'), JSON.stringify(kept, null, 2));
console.log(`artist-occupation gate: ${before} → ${kept.length} (dropped ${before - kept.length} non-artist museum constituents)`);
console.log('famous-first top 10:');
kept.slice(0, 10).forEach(r => console.log(` ${r.rank}. ${r.full_name} (d.${r.death_date})`));