diff --git a/config/report-profiles/erediensten.ttl b/config/report-profiles/erediensten.ttl index 6ddcec3..f6aa697 100644 --- a/config/report-profiles/erediensten.ttl +++ b/config/report-profiles/erediensten.ttl @@ -29,7 +29,7 @@ @prefix person: . @prefix mandaat: . @prefix besluit: . -@prefix generiek: . +@prefix generiek: . @prefix lblodlg: . @prefix ch: . @prefix ere: . @@ -58,7 +58,7 @@ sh:declare [ sh:prefix "ext" ; sh:namespace "http://mu.semte.ch/vocabularies/ext/"^^xsd:anyURI ] ; sh:declare [ sh:prefix "regorg" ; sh:namespace "http://www.w3.org/ns/regorg#"^^xsd:anyURI ] ; sh:declare [ sh:prefix "geo" ; sh:namespace "http://www.opengis.net/ont/geosparql#"^^xsd:anyURI ] ; - sh:declare [ sh:prefix "generiek" ; sh:namespace "http://data.vlaanderen.be/ns/generiek#"^^xsd:anyURI ] ; + sh:declare [ sh:prefix "generiek" ; sh:namespace "https://data.vlaanderen.be/ns/generiek#"^^xsd:anyURI ] ; sh:declare [ sh:prefix "lblodlg" ; sh:namespace "http://data.lblod.info/vocabularies/leidinggevenden/"^^xsd:anyURI ] ; sh:declare [ sh:prefix "dct" ; sh:namespace "http://purl.org/dc/terms/"^^xsd:anyURI ] ; sh:declare [ sh:prefix "rdfs" ; sh:namespace "http://www.w3.org/2000/01/rdf-schema#"^^xsd:anyURI ] ; diff --git a/config/report-profiles/mandaten.ttl b/config/report-profiles/mandaten.ttl index 6834e1c..464313d 100644 --- a/config/report-profiles/mandaten.ttl +++ b/config/report-profiles/mandaten.ttl @@ -39,7 +39,7 @@ @prefix person: . @prefix mandaat: . @prefix besluit: . -@prefix generiek: . +@prefix generiek: . @prefix lblodlg: . @prefix ch: . @prefix code: . @@ -206,7 +206,7 @@ ent:LeidinggevendeBestuursfunctie sh:node ent:ContactPunt ] ; sh:property [ sh:path [ sh:inversePath lblodlg:heeftBestuursfunctie ] ; sh:name "in bestuursorgaan" ; sh:description "Het bestuursorgaan waar deze functie bij hoort" ; - sh:node ent:Bestuursorgaan ] . + sh:node ent:BestuursorgaanInTijd ] . ent:Functionaris a sh:NodeShape ; diff --git a/config/report-profiles/organisaties.ttl b/config/report-profiles/organisaties.ttl index a37d905..b33e557 100644 --- a/config/report-profiles/organisaties.ttl +++ b/config/report-profiles/organisaties.ttl @@ -37,7 +37,7 @@ @prefix prov: . @prefix schema: . @prefix organisatie: . -@prefix generiek: . +@prefix generiek: . @prefix lblodgeneriek: . @prefix code: . @prefix ext: . @@ -66,7 +66,7 @@ sh:declare [ sh:prefix "prov" ; sh:namespace "http://www.w3.org/ns/prov#"^^xsd:anyURI ] ; sh:declare [ sh:prefix "schema" ; sh:namespace "http://schema.org/"^^xsd:anyURI ] ; sh:declare [ sh:prefix "organisatie" ; sh:namespace "https://data.vlaanderen.be/ns/organisatie#"^^xsd:anyURI ] ; - sh:declare [ sh:prefix "generiek" ; sh:namespace "http://data.vlaanderen.be/ns/generiek#"^^xsd:anyURI ] ; + sh:declare [ sh:prefix "generiek" ; sh:namespace "https://data.vlaanderen.be/ns/generiek#"^^xsd:anyURI ] ; sh:declare [ sh:prefix "lblodgeneriek" ; sh:namespace "https://data.lblod.info/vocabularies/generiek/"^^xsd:anyURI ] ; sh:declare [ sh:prefix "code" ; sh:namespace "http://lblod.data.gift/vocabularies/organisatie/"^^xsd:anyURI ] ; sh:declare [ sh:prefix "ext" ; sh:namespace "http://mu.semte.ch/vocabularies/ext/"^^xsd:anyURI ] ; diff --git a/config/report-profiles/report-spec-readme.md b/config/report-profiles/report-spec-readme.md index 58b84ec..7d4837d 100644 --- a/config/report-profiles/report-spec-readme.md +++ b/config/report-profiles/report-spec-readme.md @@ -25,18 +25,55 @@ One `sh:NodeShape` per term: condition (`sh:hasValue`, `sh:in`, `sh:minCount`/`sh:maxCount`, `sh:min/maxInclusive`/`sh:min/maxExclusive`, `sh:pattern` + `sh:flags`, `rep:anyOf`). A path with no condition is refused by the validator. + Optional `rep:where` (see below). - **Column** — a list item: `sh:path` (hops, or `rep:self` for the row's own subject), `rdfs:label` (unique, required), optional `rep:collect` (`sh:groupConcat` default, `rep:row`, `sh:min`, `sh:max`), optional - `sh:separator`. No constraints on a column — `sh:hasValue` and friends - belong in filters. + `sh:separator`, optional `sh:nodeKind sh:IRI` (the cell shows the URI of + the node the path ends on; only then may the path end on a link). No + constraints on a column — `sh:hasValue` and friends belong in filters, or + in the column's `rep:where`. + +## Conditions on the way: rep:where + +A filter tests only the end of its own path, and two filters never share a +node. `rep:where` tests a node in the middle of a path. It is written like a +filter, with its path from the row. The steps it has in common with its +filter or column are the same nodes; its condition tests where its own path +ends. On a filter it drops rows; on a column it drops values from the cell, +never rows. Several `rep:where` on one filter or column all apply. + +```turtle +[ sh:path ( org:holds [ sh:inversePath lblodlg:heeftBestuursfunctie ] + generiek:isTijdspecialisatieVan besluit:bestuurt + [ sh:inversePath besluit:bestuurt ] [ sh:inversePath generiek:isTijdspecialisatieVan ] + org:hasPost [ sh:inversePath org:holds ] + mandaat:isBestuurlijkeAliasVan foaf:familyName ) ; + rdfs:label "Burgemeester" ; + rep:where [ sh:path ( org:holds [ sh:inversePath lblodlg:heeftBestuursfunctie ] + generiek:isTijdspecialisatieVan besluit:bestuurt + [ sh:inversePath besluit:bestuurt ] [ sh:inversePath generiek:isTijdspecialisatieVan ] + org:hasPost org:role ) ; + sh:hasValue ] ] +``` + +Per leidinggevende: the names of the mandatarissen whose mandate, in an +organ of the same bestuurseenheid, has the role Burgemeester. The condition +shares seven steps with the column, so it tests the mandate. `validate_spec` +says so: `rep:where 1 on column "Burgemeester" applies at "mandaat", after +step 7`. + +On a filter with `sh:maxCount 0`, the filter and its `rep:where` together +become "none such". ## What it cannot say (and the runner enforces anyway) -A shapes graph cannot express "a column must end on a value field" (that -needs the profile), "a path resolves hop by hop" (needs the profile walk), +A shapes graph cannot express "a column must end on a value field, or on a +link with `sh:nodeKind sh:IRI`" (that needs the profile), "a path resolves hop by hop" (needs the profile walk), "at most 24 anyOf terms" (cardinality over list contents), or "anyOf takes -words, not URIs" (term typing). Those live in the service's `src/runner/check.js`. +words, not URIs" (term typing), or "a rep:where shares at least one step with +its filter or column", or "a date or number condition ends on a value, not on +a link". Those live in the service's `src/runner/check.js`. ## Not yet @@ -62,6 +99,6 @@ graph must match). ## Validating a spec against these shapes -The shapes are self-describing documentation. `validate_spec` (the MCP tool) +The shapes are self-describing documentation. `validate_spec` (the conversion tool) does more: it walks every path through the mounted profile and refuses -what a shapes graph cannot see. Both together are the spec contract. \ No newline at end of file +what a shapes graph cannot see. Both together are the spec contract. diff --git a/config/report-profiles/report-spec-shapes.ttl b/config/report-profiles/report-spec-shapes.ttl index 5c2d395..6497d2b 100644 --- a/config/report-profiles/report-spec-shapes.ttl +++ b/config/report-profiles/report-spec-shapes.ttl @@ -2,7 +2,8 @@ # Reference definition of the spec format: a machine-readable # contract that doubles as documentation. validate_spec does more than this # graph can express: it walks every path through the mounted profile and -# refuses what shapes alone cannot see (columns ending on a link, unresolved +# refuses what shapes alone cannot see (columns ending on a link without +# sh:nodeKind sh:IRI, unresolved # hops, anyOf term limits, filters without a condition). # # Not yet: sh:count, sh:sum, sh:orderBy, sh:limit, sh:offset, rep:graph. @@ -139,6 +140,15 @@ rep:FilterShape sh:name "een van deze woorden" ; sh:maxCount 1 ; ] ; + # rep:where: a condition on a node this filter walks through, written as a + # filter from the row. The steps it shares with this filter are the same + # nodes. check.js refuses one that shares no step, and one nested in + # another (whereConditions). + sh:property [ + sh:path rep:where ; + sh:name "voorwaarde onderweg" ; + sh:node rep:FilterShape ; + ] ; sh:property [ sh:path rdfs:label ; sh:name "label" ; @@ -146,7 +156,11 @@ rep:FilterShape ] . # A column: path, label, collect mode, separator. No constraints here; -# check.js refuses a constraint inside rep:columns (columnPure). +# check.js refuses a constraint inside rep:columns (columnPure). The one +# exception is sh:nodeKind sh:IRI: the cell shows the URI of the node the +# path ends on, and only then may the path end on a link. A condition +# on a node the column walks through goes in rep:where; it drops values from +# the cell, never rows. rep:ColumnShape a sh:NodeShape ; sh:property [ @@ -173,6 +187,17 @@ rep:ColumnShape sh:name "scheidingsteken" ; sh:datatype xsd:string ; sh:maxCount 1 ; + ] ; + sh:property [ + sh:path sh:nodeKind ; + sh:name "toon de URI" ; + sh:in ( sh:IRI ) ; + sh:maxCount 1 ; + ] ; + sh:property [ + sh:path rep:where ; + sh:name "voorwaarde onderweg" ; + sh:node rep:FilterShape ; ] . # rep:collect takes exactly these values (SHACL Core groupConcat is the diff --git a/docker-compose.yml b/docker-compose.yml index 1c72c73..16a08bc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -688,7 +688,7 @@ services: # CHAT / NATURAL LANGUAGE REPORTS ################################################################################ natural-language-report: - image: lblod/natural-language-report-service:0.1.0 + image: lblod/natural-language-report-service:0.2.0 build: ../natural-language-report-service/ volumes: - ./data/files:/share @@ -698,10 +698,7 @@ services: CSV_SEPARATOR: ";" SUBJECT_CHUNK_SIZE: "100" ROW_LIMIT: "200000" - MAX_PATH_DEPTH: "8" - # The chat's turn answers in the background; full-organisation runs - # easily outgrow a minute. - RUN_TIMEOUT: "300" + MAX_PATH_DEPTH: "12" INLINE_VALUES_MAX: "50" VALUES_TTL: "3600" labels: