[object Object]

← back to CelebritySignatures

harvest infra: fix WDQS 504 timeout on the 25-museum query — now batched (3 museums/query) with retry+backoff, robust for every future harvest; register 5 Amsterdam/Dutch museums (Van Gogh Museum, Stedelijk, Rembrandt House, Amsterdam Museum, Mauritshuis) for when their Wikidata P195 coverage improves. NOTE: P195 itemization for these is currently sparse (+6 net artists, no marquee retags) — a real Amsterdam haul needs a Rijksmuseum-roster source (TK-10181)

e4eec832566987912042699cf75301b9865e0236 · 2026-08-03 20:44:41 -0700 · Steve Abrams

Files touched

Diff

commit e4eec832566987912042699cf75301b9865e0236
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Mon Aug 3 20:44:41 2026 -0700

    harvest infra: fix WDQS 504 timeout on the 25-museum query — now batched (3 museums/query) with retry+backoff, robust for every future harvest; register 5 Amsterdam/Dutch museums (Van Gogh Museum, Stedelijk, Rembrandt House, Amsterdam Museum, Mauritshuis) for when their Wikidata P195 coverage improves. NOTE: P195 itemization for these is currently sparse (+6 net artists, no marquee retags) — a real Amsterdam haul needs a Rijksmuseum-roster source (TK-10181)
---
 scripts/fetch-artists.mjs | 62 ++++++++++++++++++++++++++++++++++-------------
 1 file changed, 45 insertions(+), 17 deletions(-)

diff --git a/scripts/fetch-artists.mjs b/scripts/fetch-artists.mjs
index fee9e35..5dca7bf 100644
--- a/scripts/fetch-artists.mjs
+++ b/scripts/fetch-artists.mjs
@@ -46,15 +46,25 @@ const MUSEUMS = {
   Q657415: 'Cleveland Museum of Art',
   Q49133: 'Museum of Fine Arts Boston',
   Q510324: 'Philadelphia Museum of Art',
+  // Amsterdam / Dutch expansion (Steve 2026-08-03: "more from Amsterdam
+  // museums like the Rijks"). Rijksmuseum already above. QIDs verified same day.
+  Q224124: 'Van Gogh Museum',
+  Q924335: 'Stedelijk Museum Amsterdam',
+  Q277316: 'Rembrandt House Museum',
+  Q1820897: 'Amsterdam Museum',
+  Q221092: 'Mauritshuis',
 };
-const VALUES = Object.keys(MUSEUMS).map(q => `wd:${q}`).join(' ');
-
-const QUERY = `
+// Per-museum-batch query. The all-museums-in-one-VALUES query 504s on WDQS now
+// (too heavy across 25 museums), so we query in small batches with retry/backoff
+// and union the rows (dedup happens in the union step below).
+function queryFor(museumQids) {
+  const vals = museumQids.map(q => `wd:${q}`).join(' ');
+  return `
 SELECT ?person ?personLabel ?sig ?dod ?links
        (GROUP_CONCAT(DISTINCT ?museumLabel; separator="|") AS ?museums)
        (GROUP_CONCAT(DISTINCT ?occLabel; separator="|") AS ?occs)
 WHERE {
-  VALUES ?museum { ${VALUES} }
+  VALUES ?museum { ${vals} }
   ?work wdt:P170 ?person ; wdt:P195 ?museum .
   ?person wdt:P109 ?sig .
   ?person wdt:P570 ?dod .
@@ -66,22 +76,40 @@ WHERE {
   SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
 }
 GROUP BY ?person ?personLabel ?sig ?dod ?links
-ORDER BY DESC(?links)
-`;
-
-async function sparql(q) {
+ORDER BY DESC(?links)`;
+}
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+async function sparql(q, tries = 5) {
   const url = `${ENDPOINT}?format=json&query=${encodeURIComponent(q)}`;
-  const res = await fetch(url, {
-    headers: {
-      'Accept': 'application/sparql-results+json',
-      'User-Agent': 'CelebritySignatures/1.0 (designerwallcoverings.com; PD-signature artist catalog build)',
-    },
-  });
-  if (!res.ok) throw new Error(`SPARQL ${res.status}: ${(await res.text()).slice(0, 300)}`);
-  return (await res.json()).results.bindings;
+  for (let i = 1; ; i++) {
+    let res;
+    try {
+      res = await fetch(url, {
+        headers: {
+          'Accept': 'application/sparql-results+json',
+          'User-Agent': 'CelebritySignatures/1.0 (designerwallcoverings.com; PD-signature artist catalog build)',
+        },
+      });
+    } catch (e) { if (i > tries) throw e; await sleep(8000 * i); continue; }
+    if (res.ok) return (await res.json()).results.bindings;
+    if ((res.status === 504 || res.status === 429 || res.status >= 500) && i <= tries) {
+      process.stdout.write(`(WDQS ${res.status}; backoff ${8 * i}s, try ${i}/${tries}) `);
+      await sleep(8000 * i); continue;
+    }
+    throw new Error(`SPARQL ${res.status}: ${(await res.text()).slice(0, 200)}`);
+  }
 }
 
-const rows = await sparql(QUERY);
+const ALL_QIDS = Object.keys(MUSEUMS);
+const rows = [];
+for (let i = 0; i < ALL_QIDS.length; i += 3) {
+  const batch = ALL_QIDS.slice(i, i + 3);
+  process.stdout.write(`[museums ${i + 1}-${i + batch.length}/${ALL_QIDS.length}] ${batch.map(q => MUSEUMS[q]).join(', ')} … `);
+  const r = await sparql(queryFor(batch));
+  rows.push(...r);
+  console.log(`${r.length} rows (total ${rows.length})`);
+  await sleep(1500);
+}
 console.log(`raw rows: ${rows.length}`);
 
 // One record per person; Special:FilePath URL is directly usable as an <img> src.

← 36e1cbe yoloforever C3 fast-follow (Cody SHIP-IT verified real): can  ·  back to CelebritySignatures  ·  Amsterdam haul via P6379: +110 net Artists (1262→1372), site 4e31653 →