The data model

How the journal is described as linked data - in DCAT for catalogues, in CIDOC CRM for cultural heritage, and how the two fit together. The diagrams are checked against the published graph on every build.

Colours in the diagrams

  • resource of the journal
  • thing in the world
  • class
  • vocabulary term
  • identifier elsewhere
  • value or property

1. From YAML to knowledge graph

Everything on this site comes from a few YAML files in the repository: the journal, one file per volume, the places and the vocabulary of entry types. A Python pipeline (main.py) enriches them with the Zenodo records and Wikidata places it has cached, and produces three things from the same merged data: the web pages, the citations and the knowledge graph. The graph is then validated, queried and mapped before the site is written.

flowchart LR
  yaml([content/*.yaml]):::inst --> merge[merge]:::prop
  zen([Zenodo cache]):::ext --> merge
  wd([Wikidata cache]):::ext --> merge
  merge --> json([entries.json]):::inst
  json --> cite[cite]:::prop --> bib([BibTeX · RIS · CSL-JSON]):::inst
  json --> rdf[rdf]:::prop --> dcat([DCAT graph]):::inst
  rdf --> crm([CIDOC CRM graph]):::inst
  dcat --> validate[validate · SHACL]:::prop
  crm --> validate
  dcat --> sparql[sparql]:::prop
  dcat --> map[map]:::prop
  validate --> site[site]:::prop
  sparql --> site
  map --> site
  bib --> site
  site --> pages([pages EN · DE]):::inst
classDef inst fill:#e2e8f0,stroke:#000000,color:#000000
classDef real fill:#166534,stroke:#000000,color:#ffffff
classDef cls fill:#9a3412,stroke:#000000,color:#ffffff
classDef term fill:#4c1d95,stroke:#000000,color:#ffffff
classDef ext fill:#ffffff,stroke:#000000,color:#000000
classDef prop fill:#fbbf24,stroke:#000000,color:#000000

2. Journal, volume, issue, entry

The journal is a catalogue of volumes, a volume a series of issues, an issue a series of entries. Every level has its own stable IRI under https://w3id.org/squirrelpapers/, and every entry its file on Zenodo, its people, and - for talks and posters - the event where it was presented and the place of that event. Types come from the journal's own SKOS vocabulary, mapped to COAR and FaBiO.

flowchart TB
  J([Journal · /]):::inst
  V([Volume 7 · /v7]):::inst
  I([Issue 4 · /v7/i4]):::inst
  E([Entry 5 · /v7/i4/e5]):::inst
  D([PDF · /v7/i4/e5#pdf]):::inst
  P([Person · /person/ORCID]):::real
  EV([Event · /event/…]):::real
  PL([Place · /place/…]):::real
  T([Type · /type/poster]):::term
  O([ORCID]):::ext
  W([Wikidata]):::ext
  Z([DOI · Zenodo]):::ext
  J -->|"dcat:dataset"| V
  V -->|"dct:hasPart"| I
  I -->|"dct:hasPart"| E
  E -->|"dcat:distribution"| D
  E -->|"dct:creator"| P
  E -->|"bibo:presentedAt"| EV
  EV -->|"schema:location"| PL
  E -->|"dct:type"| T
  P -->|"owl:sameAs"| O
  PL -->|"owl:sameAs"| W
  E -->|"owl:sameAs"| Z
classDef inst fill:#e2e8f0,stroke:#000000,color:#000000
classDef real fill:#166534,stroke:#000000,color:#ffffff
classDef cls fill:#9a3412,stroke:#000000,color:#ffffff
classDef term fill:#4c1d95,stroke:#000000,color:#ffffff
classDef ext fill:#ffffff,stroke:#000000,color:#000000
classDef prop fill:#fbbf24,stroke:#000000,color:#000000

3. The DCAT layer

The main graph describes the journal as a data catalogue in DCAT 3, validated against the DCAT-AP 3.0.1 profile of the European Union: the journal is a dcat:Catalog, volumes and issues are dcat:DatasetSeries, entries are dcat:Dataset with a dcat:Distribution for the PDF. Bibliographic vocabularies add what DCAT does not say: BIBO and FaBiO for the kind of publication, PRISM for volume and issue numbers, schema.org and FOAF for people, events and places, GeoSPARQL for coordinates, PROV for the Zenodo record an entry was derived from, SPDX for checksums. The small sqp: ontology holds only the journal's own numbering.

flowchart LR
  cat["dcat:Catalog<br>fabio:Journal"]:::cls
  ser["dcat:DatasetSeries<br>fabio:JournalVolume"]:::cls
  iss["dcat:DatasetSeries<br>fabio:JournalIssue"]:::cls
  ds["dcat:Dataset<br>bibo:Document · fabio:…"]:::cls
  dist["dcat:Distribution"]:::cls
  per["foaf:Person<br>schema:Person"]:::cls
  ev["event:Event<br>schema:Event"]:::cls
  pl["dct:Location<br>schema:Place · geo:Feature"]:::cls
  geom["geo:Geometry"]:::cls
  con["skos:Concept"]:::term
  cat -->|"dcat:dataset"| ser
  ser -->|"dct:isPartOf"| cat
  iss -->|"dcat:inSeries"| ser
  ds -->|"dcat:inSeries"| iss
  ds -->|"dcat:distribution"| dist
  ds -->|"dct:creator · dct:contributor"| per
  ds -->|"bibo:presentedAt"| ev
  ds -->|"dct:type"| con
  ev -->|"schema:location"| pl
  pl -->|"geo:hasGeometry"| geom
  ds --- lbl[/"sqp:citationLabel · sqp:entryNumber"/]:::prop
  iss --- sig[/"sqp:sigil"/]:::prop
  geom --- wkt[/"geo:asWKT"/]:::prop
classDef inst fill:#e2e8f0,stroke:#000000,color:#000000
classDef real fill:#166534,stroke:#000000,color:#ffffff
classDef cls fill:#9a3412,stroke:#000000,color:#ffffff
classDef term fill:#4c1d95,stroke:#000000,color:#ffffff
classDef ext fill:#ffffff,stroke:#000000,color:#000000
classDef prop fill:#fbbf24,stroke:#000000,color:#000000

4. The CIDOC CRM layer

A second graph describes the same resources in CIDOC CRM 7.1 with its extensions LRMoo (works and expressions) and CRMdig (digital objects). Where DCAT states facts as properties of the entry, CRM tells them as events: an entry was created (E65 / F28) by people (P14) within a time-span (P4); a conference is an activity (E7) that used the entry (P16) and took place at a place (P7) defined by coordinates (P168). Titles and identifiers are nodes of their own (E35, E42), each with its symbolic content (P190).

flowchart LR
  e73["crm:E73_Information_Object<br>lrmoo:F2_Expression"]:::cls
  e65["crm:E65_Creation<br>lrmoo:F28_Expression_Creation"]:::cls
  e21["crm:E21_Person"]:::cls
  e52["crm:E52_Time-Span"]:::cls
  e7["crm:E7_Activity"]:::cls
  e53["crm:E53_Place"]:::cls
  e35["crm:E35_Title"]:::cls
  e42["crm:E42_Identifier"]:::cls
  e41["crm:E41_Appellation"]:::cls
  d1["crmdig:D1_Digital_Object"]:::cls
  f18["lrmoo:F18_Serial_Work"]:::cls
  e65 -->|"crm:P94_has_created<br>lrmoo:R17_created"| e73
  e65 -->|"crm:P14_carried_out_by"| e21
  e65 -->|"crm:P4_has_time-span"| e52
  e7 -->|"crm:P16_used_specific_object"| e73
  e7 -->|"crm:P7_took_place_at"| e53
  e7 -->|"crm:P1_is_identified_by"| e41
  e7 -->|"crm:P4_has_time-span"| e52
  e73 -->|"crm:P102_has_title"| e35
  e73 -->|"crm:P1_is_identified_by"| e42
  e21 -->|"crm:P1_is_identified_by"| e42
  d1 -->|"crm:P165_incorporates"| e73
  e73b["crm:E73_Information_Object<br>issue · volume"]:::cls
  e73 -->|"crm:P106i_forms_part_of"| e73b
  e73b -->|"crm:P106i_forms_part_of"| f18
  e53 --- wkt[/"crm:P168_place_is_defined_by"/]:::prop
  e52 --- dates[/"crm:P82a_begin_of_the_begin<br>crm:P82b_end_of_the_end"/]:::prop
  e42 --- sym[/"crm:P190_has_symbolic_content"/]:::prop
classDef inst fill:#e2e8f0,stroke:#000000,color:#000000
classDef real fill:#166534,stroke:#000000,color:#ffffff
classDef cls fill:#9a3412,stroke:#000000,color:#ffffff
classDef term fill:#4c1d95,stroke:#000000,color:#ffffff
classDef ext fill:#ffffff,stroke:#000000,color:#000000
classDef prop fill:#fbbf24,stroke:#000000,color:#000000

5. How the two layers meet

Both graphs use the same IRIs. An entry, a person, an event or a place is one node, described once as catalogue record and once as a CRM entity; loading both files into one store joins them without any mapping. The table reads one fact in both ways. The DCAT graph stays readable on its own, and the CRM graph can be published to a CRM-based knowledge graph such as the one of NFDI4Objects without the catalogue vocabulary.

FactDCAT graphCIDOC CRM graph
who wrote it dct:creator crm:P94i_was_created_by / crm:P14_carried_out_by
when dct:issued … / crm:P4_has_time-span / crm:P82a_begin_of_the_begin
its title dct:title crm:P102_has_title / crm:P190_has_symbolic_content
its DOI bibo:doi crm:P1_is_identified_by / crm:P190_has_symbolic_content
its type dct:type crm:P2_has_type
where it was presented bibo:presentedAt / schema:location crm:P16i_was_used_for / crm:P7_took_place_at
its PDF dcat:distribution crm:P165i_is_incorporated_in
its issue dcat:inSeries crm:P106i_forms_part_of

6. One entry in both graphs: 7(4), λ5

Drawn by the build from the published graphs, not by hand: the entry Brückenbau zwischen LOD und RSE with its issue, person, event, place and file - first as DCAT, then as CIDOC CRM. Its complete description is in index.ttl next to the entry page.

DCAT
flowchart LR
  n0("/v7/i4/e5<br><small>bibo:Document · dcat:Dataset</small>"):::inst
  n1[/"7(4), λ5"/]:::prop
  n2[/"2025-09-30"/]:::prop
  n3("/v7/i4<br><small>dcat:DatasetSeries · fabio:JournalIssue</small>"):::inst
  n4("sqpt:conference-paper<br><small>skos:Concept</small>"):::term
  n5("Florian Thiery<br><small>foaf:Person · schema:Person</small>"):::real
  n6("NFDI4Objects Community Meeting 2025<br><small>event:Event · schema:Event</small>"):::real
  n7("/v7/i4/e5#pdf<br><small>dcat:Distribution</small>"):::inst
  n8["https://doi.org/10.5281/zenodo.16875678"]:::ext
  n9("German Mining Museum<br><small>dct:Location · geo:Feature</small>"):::real
  n10["wd:Q896952"]:::ext
  n11["iana:application/pdf<br><small>dct:MediaType</small>"]:::ext
  n12[/"3050344"/]:::prop
  n13["https://orcid.org/0000-0002-3246-3531"]:::ext
  n0 -->|"sqp:citationLabel"| n1
  n0 -->|"dct:issued"| n2
  n0 -->|"dcat:inSeries"| n3
  n0 -->|"dct:type"| n4
  n0 -->|"dct:creator"| n5
  n0 -->|"bibo:presentedAt"| n6
  n0 -->|"dcat:distribution"| n7
  n0 -->|"owl:sameAs"| n8
  n6 -->|"schema:location"| n9
  n9 -->|"owl:sameAs"| n10
  n7 -->|"dcat:mediaType"| n11
  n7 -->|"dcat:byteSize"| n12
  n5 -->|"owl:sameAs"| n13
classDef inst fill:#e2e8f0,stroke:#000000,color:#000000
classDef real fill:#166534,stroke:#000000,color:#ffffff
classDef cls fill:#9a3412,stroke:#000000,color:#ffffff
classDef term fill:#4c1d95,stroke:#000000,color:#ffffff
classDef ext fill:#ffffff,stroke:#000000,color:#000000
classDef prop fill:#fbbf24,stroke:#000000,color:#000000
CIDOC CRM
flowchart LR
  n0("/v7/i4/e5#creation<br><small>crm:E65_Creation · lrmoo:F28_Expression_Creation</small>"):::inst
  n1("/v7/i4/e5<br><small>crm:E73_Information_Object · lrmoo:F2_Expression</small>"):::inst
  n2("Florian Thiery<br><small>crm:E21_Person</small>"):::real
  n3("/v7/i4/e5#creation-time-span<br><small>crm:E52_Time-Span</small>"):::inst
  n4[/"2025-09-30T00:00:00"/]:::prop
  n5("/v7/i4/e5#title<br><small>crm:E35_Title</small>"):::inst
  n6[/"Brückenbau zwischen LOD und RSE: Von CIDOC CR…"/]:::prop
  n7("/v7/i4/e5#doi<br><small>crm:E42_Identifier</small>"):::inst
  n8[/"10.5281/zenodo.16875678"/]:::prop
  n9("sqpt:conference-paper"):::term
  n10("/v7/i4<br><small>crm:E73_Information_Object</small>"):::inst
  n11("NFDI4Objects Community Meeting 2025<br><small>crm:E7_Activity</small>"):::real
  n12("German Mining Museum<br><small>crm:E53_Place</small>"):::real
  n13[/"POINT(7.21722 51.4892)"/]:::prop
  n14("/v7/i4/e5#pdf<br><small>crmdig:D1_Digital_Object</small>"):::inst
  n0 -->|"crm:P94_has_created"| n1
  n0 -->|"crm:P14_carried_out_by"| n2
  n0 -->|"crm:P4_has_time-span"| n3
  n3 -->|"crm:P82a_begin_of_the_begin"| n4
  n1 -->|"crm:P102_has_title"| n5
  n5 -->|"crm:P190_has_symbolic_content"| n6
  n1 -->|"crm:P1_is_identified_by"| n7
  n7 -->|"crm:P190_has_symbolic_content"| n8
  n1 -->|"crm:P2_has_type"| n9
  n1 -->|"crm:P106i_forms_part_of"| n10
  n11 -->|"crm:P16_used_specific_object"| n1
  n11 -->|"crm:P7_took_place_at"| n12
  n12 -->|"crm:P168_place_is_defined_by"| n13
  n14 -->|"crm:P165_incorporates"| n1
classDef inst fill:#e2e8f0,stroke:#000000,color:#000000
classDef real fill:#166534,stroke:#000000,color:#ffffff
classDef cls fill:#9a3412,stroke:#000000,color:#ffffff
classDef term fill:#4c1d95,stroke:#000000,color:#ffffff
classDef ext fill:#ffffff,stroke:#000000,color:#000000
classDef prop fill:#fbbf24,stroke:#000000,color:#000000

7. The rules the graph must keep

Before the site is built, the graph is checked three ways with SHACL: against the official DCAT-AP 3.0.1 shapes, against the journal's own rules below, and against rules for the CRM view. A deliberately broken entry must be caught by every rule first, so the check cannot silently stop working. The rules are published as sqp-shapes.ttl and crm-shapes.ttl.

ShapeApplies toRules
EntryShape
shapes/sqp-shapes.ttl
bibo:Document
  • A DOI is written without resolver, '10.5281/zenodo.123'.
  • An entry belongs to exactly one issue.
  • An entry has at least one creator, a foaf:Person.
  • An entry has exactly one title.
  • An entry has one entry number.
  • An entry has one issue date, a full date or a year.
  • An entry keeps the order of its authors in one rdf:Seq.
  • An entry without distribution cannot be reached from the catalogue.
  • Every type of an entry is a concept of the Squirrel Papers type scheme.
  • The citation label reads like '7(4), λ5'.
  • The landing page is the entry's page on the site.
EventShape
shapes/sqp-shapes.ttl
event:Event
  • An event cannot end before it starts.
  • rdfs:label exactly 1
  • schema:endDate at most 1
  • schema:location at most 1
  • schema:startDate at most 1
IssueShape
shapes/sqp-shapes.ttl
fabio:JournalIssue
  • An issue belongs to exactly one volume.
  • An issue has a title in English and in German.
  • An issue has one sigil out of #, λ, §, 𝒬.
  • prism:number exactly 1
JournalShape
shapes/sqp-shapes.ttl
/
  • Entry {?value}: the IRI must agree with its issue's IRI and its entry number.
  • Entry {?value}: the citation label must agree with volume, issue and entry number.
  • Entry {?value}: when its language is known, the title carries the matching language tag.
  • bibo:issn at least 1
  • dcat:dataset at least 1
PersonShape
shapes/sqp-shapes.ttl
foaf:Person
  • A person is linked to at most one ORCID.
  • foaf:familyName exactly 1
  • foaf:name exactly 1
PlaceShape
shapes/sqp-shapes.ttl
schema:Place
  • A place has one point geometry.
  • rdfs:label exactly 1
VolumeShape
shapes/sqp-shapes.ttl
fabio:JournalVolume
  • A volume is part of the journal.
  • prism:volume exactly 1
CrmActivityShape
shapes/crm-shapes.ttl
crm:E7_Activity
  • crm:P16_used_specific_object at least 1
  • crm:P1_is_identified_by at least 1
  • crm:P7_took_place_at at most 1
CrmCreationShape
shapes/crm-shapes.ttl
crm:E65_Creation
  • crm:P14_carried_out_by at least 1
  • crm:P4_has_time-span at most 1
  • crm:P94_has_created exactly 1
CrmDigitalObjectShape
shapes/crm-shapes.ttl
crmdig:D1_Digital_Object
  • crm:P165_incorporates at least 1
CrmExpressionShape
shapes/crm-shapes.ttl
lrmoo:F2_Expression
  • Every expression has exactly one creation event.
  • crm:P102_has_title exactly 1
  • crm:P106i_forms_part_of exactly 1
  • crm:P2_has_type at least 1
CrmIdentifierShape
shapes/crm-shapes.ttl
crm:E42_Identifier
  • crm:P190_has_symbolic_content exactly 1
  • crm:P2_has_type exactly 1
CrmPlaceShape
shapes/crm-shapes.ttl
crm:E53_Place
  • crm:P168_place_is_defined_by at most 1
CrmTimeSpanShape
shapes/crm-shapes.ttl
crm:E52_Time-Span
  • A time-span cannot end before it begins.
  • crm:P82a_begin_of_the_begin exactly 1
  • crm:P82b_end_of_the_end exactly 1

8. Stable IRIs

Every resource has an IRI under https://w3id.org/squirrelpapers/ that stays when the site moves. It resolves to the page for a browser and to Turtle or JSON-LD for a machine; the same files lie next to every page.

IRIResourceClasses
/ the journal dcat:Catalog · fabio:Journal · lrmoo:F18_Serial_Work
/v7 volume 7 dcat:DatasetSeries · fabio:JournalVolume
/v7/i4 volume 7, issue 4 dcat:DatasetSeries · fabio:JournalIssue
/v7/i4/e5 entry 5 of that issue dcat:Dataset · bibo:Document · crm:E73_Information_Object
/v7/i4/e5#pdf its PDF on Zenodo dcat:Distribution · crmdig:D1_Digital_Object
/person/<ORCID> a person foaf:Person · crm:E21_Person
/event/<name-year> a conference or workshop event:Event · crm:E7_Activity
/place/<key> a place schema:Place · crm:E53_Place
/type/<slug> an entry type skos:Concept
/ontology the journal's ontology owl:Ontology