stats: history and culture collector (step 4): events, offices, people, holidays, UNESCO, religion, long-run series
Some checks failed
Deploy production / deploy (push) Failing after 1m0s

This commit is contained in:
2026-10-25 23:25:46 +00:00
parent fa3aa25f0b
commit 0088e5202e
8 changed files with 39743 additions and 2 deletions

View File

@@ -744,3 +744,108 @@ export const statsGdeltThemeVol = statsSchema.table(
},
(t) => [primaryKey({ columns: [t.date, t.country, t.theme] })]
);
// ── Step 4: history and culture per place (§9) ───────────────────────────────────────────
// Places are region codes: NLD, GM0513 (Gouda), CUW, USA, KOR. Every row carries its source link.
/** Curated key events (scripts/stats/history-events.json), each with a source link. */
export const statsHistEvent = statsSchema.table(
'hist_event',
{
place: text('place').notNull(),
/** Stable slug within the place, e.g. 'dutch-revolt'. */
slug: text('slug').notNull(),
year: integer('year').notNull(),
yearEnd: integer('year_end'),
/** Optional exact or partial date label ("1 July 1863"). */
dateLabel: text('date_label'),
title: text('title').notNull(),
summary: text('summary').notNull(),
/** politics | war | economy | society | culture | science */
theme: text('theme').notNull(),
sourceUrl: text('source_url').notNull()
},
(t) => [primaryKey({ columns: [t.place, t.slug] })]
);
/** Curated short culture notes: cuisine and cultural exports. */
export const statsHistFact = statsSchema.table(
'hist_fact',
{
place: text('place').notNull(),
slug: text('slug').notNull(),
/** cuisine | export */
topic: text('topic').notNull(),
title: text('title').notNull(),
body: text('body').notNull(),
sourceUrl: text('source_url').notNull(),
sort: integer('sort').notNull()
},
(t) => [primaryKey({ columns: [t.place, t.slug] })]
);
/** Office holders from Wikidata (P39 with start/end qualifiers). */
export const statsHistOffice = statsSchema.table(
'hist_office',
{
place: text('place').notNull(),
/** head_of_government | head_of_state | mayor | governor */
kind: text('kind').notNull(),
officeQid: text('office_qid').notNull(),
officeLabel: text('office_label').notNull(),
personQid: text('person_qid').notNull(),
personLabel: text('person_label').notNull(),
party: text('party'),
startDate: date('start_date', { mode: 'string' }).notNull(),
endDate: date('end_date', { mode: 'string' })
},
(t) => [primaryKey({ columns: [t.officeQid, t.personQid, t.startDate] })]
);
/** Well-known people per place from Wikidata, ranked by number of Wikipedia sitelinks. */
export const statsHistPerson = statsSchema.table(
'hist_person',
{
place: text('place').notNull(),
personQid: text('person_qid').notNull(),
label: text('label').notNull(),
description: text('description'),
birthYear: integer('birth_year'),
deathYear: integer('death_year'),
sitelinks: integer('sitelinks').notNull(),
/** Wikipedia article (en, else nl) or the Wikidata page. */
url: text('url').notNull(),
rank: integer('rank').notNull()
},
(t) => [primaryKey({ columns: [t.place, t.personQid] })]
);
/** Public holidays from the Nager.Date API. */
export const statsHistHoliday = statsSchema.table(
'hist_holiday',
{
place: text('place').notNull(),
date: date('date', { mode: 'string' }).notNull(),
name: text('name').notNull(),
localName: text('local_name'),
/** Nationwide (false = regional only). */
global: boolean('global').notNull(),
types: jsonb('types')
},
(t) => [primaryKey({ columns: [t.place, t.date, t.name] })]
);
/** UNESCO World Heritage sites (UNESCO WHC open data, dataset whc001). */
export const statsUnescoSite = statsSchema.table('unesco_site', {
idNo: integer('id_no').primaryKey(),
name: text('name').notNull(),
/** Region codes of the places this site lies in (NLD, USA, KOR, CUW). */
places: jsonb('places').notNull(),
category: text('category').notNull(),
yearInscribed: integer('year_inscribed'),
danger: boolean('danger').notNull(),
description: text('description'),
lat: doublePrecision('lat'),
lon: doublePrecision('lon'),
imageUrl: text('image_url')
});