Skip to content

Usage Guide: Adding a New Subject

This is the repeatable process for turning a new biographical source (a paper, an oral history, an archive) into a new subjects/<slug>/ folder. Follow it in order — each step depends on the one before. The live version of this checklist lives in extraction/EXTRACTION_GUIDE.md.

Read the data model first

Before extracting anything, read Data model reference — in particular what counts as an "event" and the fuzzy-date chronology mechanism. Extraction is only consistent if it follows that model exactly: don't improvise a new event type or relation type mid-extraction. If nothing in the controlled vocabulary fits, use "other" for events, or see Extending & Contributing for how to propose a new vocabulary value.

0. Read the whole source once, straight through

Before extracting anything, read the paper end to end to get the arc of the story. Note in passing: who are the recurring people, places, organizations, and named things (inventions, patents, products, books)? This becomes the seed list for the next step.

1. Draft entities.json

One entry per distinct person, place, organization, or artifact that will be referenced by at least one event or relation. Don't create an entity for something mentioned exactly once with no dateable connection to anything else, unless it will anchor a relation or event.

{
  "id": "v_b_aleskovskii",
  "entity_type": "person",
  "name": "Valentin Borisovich Aleskovskii",
  "summary": "Soviet chemist who independently discovered molecular layering, the Soviet-side counterpart to Suntola's Atomic Layer Epitaxy.",
  "subtype": "researcher"
}
  • id: snake_case, derived from the name (v_b_aleskovskii, not person_1).
  • summary: static facts only — no dates, no "in 1974...". If a sentence has a "when," it belongs in an event instead.
  • Watch for id collisions between a place and an organization that share a name (e.g. a company town) — suffix one (_city / _oy) if needed, the way subjects/suntola/entities.json distinguishes lohja_city (place) from lohja_oy (organization).

2. Draft sources.json

At minimum, one entry for the source paper itself:

{
  "id": "malygin2015",
  "title": "Aleskovskii and the molecular layering method",
  "authors": ["A. A. Malygin"],
  "year": 2015,
  "publication": "Some Journal",
  "file": "data/malygin2015.pdf"
}

Add more entries only if the paper itself cites another primary source you're drawing directly from (an autobiography, an interview transcript).

3. Draft events.json

Walk the source again, this time pulling out every dateable occurrence that meets the four requirements from Data model reference (a date, however fuzzy; a type; at least one participant; at least one source citation).

{
  "id": "aleskovskii_molecular_layering",
  "event_type": "invention",
  "label": "Aleskovskii formulates molecular layering",
  "description": "Aleskovskii and Kol'tsov proposed the molecular layering method for surface synthesis of solid compounds.",
  "date": { "display": "1965", "precision": "year", "sort_start": "1965-01-01", "sort_end": "1965-12-31" },
  "location": "leningrad",
  "participants": [
    { "entity_id": "v_b_aleskovskii", "role": "inventor" },
    { "entity_id": "molecular_layering", "role": "invention" }
  ],
  "sources": [
    { "source_id": "malygin2015", "page": 4, "quote": "the molecular layering method was first proposed in 1965" }
  ]
}

Practical rules:

  • Every event date must trace to something the text actually says. If the text says "in the early 1970s," date.display is "early 1970s" with precision: "decade" — not a guessed exact year.
  • Quote sparingly, but quote the load-bearing sentence for pivotal events (the invention moment, the first public disclosure) so a reader can verify the claim without re-opening the source.
  • Page numbers are the PDF's printed page number, not the PDF viewer's page index, so citations match what a human reader sees.
  • Don't split one sentence into five events. A paragraph describing one coherent episode is one event with a fuller description.
  • Capture connective-tissue events too — an organization founded, sold, or renamed; a collaborator's milestone that later matters to the subject — not just the subject's own life events. These are what make the graph interesting to explore, and what a future cross-subject bridge connects to.

4. Draft relations.json

For each event that implies a durable fact about the connection between two entities (an employment, an invention, a founding, a visit), add a corresponding relation with event_id pointing back to it:

{
  "id": "aleskovskii_invented_molecular_layering",
  "source": "v_b_aleskovskii",
  "type": "invented",
  "target": "molecular_layering",
  "event_id": "aleskovskii_molecular_layering",
  "sources": [{ "source_id": "malygin2015", "page": 4 }]
}

Then add any relations the source states as a fact without a specific dateable event behind it (e.g. "they remained close collaborators for the rest of their lives") — these stand alone, with no event_id.

Keep relations directed per the vocabulary's defined reading direction — e.g. worked_at always reads person → organization, never the reverse. See Data model reference for the full list.

5. Write subject.json

{
  "slug": "aleskovskii",
  "name": "V. B. Aleskovskii",
  "summary": "Discoverer of molecular layering — from Malygin (2015)"
}

6. Validate and build

pip install jsonschema --break-system-packages   # once
python3 scripts/build_site.py aleskovskii

The build validates every file against the schemas and checks referential integrity before writing dist/aleskovskii.html. Fix every reported error — see Usage Guide for a worked example of diagnosing one.

7. Open the built page and sanity-check it

Click through a handful of entities and events. Things to look for: nodes with no connections at all (probably a relation is missing), events clustered at implausible dates (probably a sort_start/sort_end typo), and any name that renders as undefined (a broken id reference).

8. Once both subjects exist: cross-subject bridges

Create subjects/_bridges/<a>-<b>.json — an array of relation objects using the same shape as relation.schema.json, except source/target are qualified as "<slug>:<entity_id>":

[
  {
    "id": "suntola_met_aleskovskii_leningrad",
    "source": "suntola:tuomo_suntola",
    "target": "aleskovskii:v_b_aleskovskii",
    "type": "met",
    "event_id": "suntola:suntola_visits_leningrad",
    "sources": [{ "source_id": "suntola:puurunen2014", "page": 341 }]
  }
]

This keeps each subject file self-contained while still letting a later combined view draw the edges between them. The Puurunen paper already documents one such link — Suntola's 1990 visit to Leningrad to meet Aleskovskii (subjects/suntola/events.json#suntola_visits_leningrad) — so aleskovskii-suntola.json is the first bridge file this project will need once subjects/aleskovskii/ exists.