@lblod/ember-rdfa-editor-lblod-plugins 31.1.1 → 32.1.0-dev.2e9fd650499765c0030b9262f90aa975fdfd7a2f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.changeset/empty-donkeys-change.md +5 -0
  2. package/.changeset/tidy-dancers-grow.md +5 -0
  3. package/.changeset/wicked-tools-serve.md +5 -0
  4. package/CHANGELOG.md +21 -0
  5. package/addon/components/citation-plugin/citations/search-modal.hbs +2 -2
  6. package/addon/components/document-validation-plugin/card.gts +131 -0
  7. package/addon/components/roadsign-regulation-plugin/roadsigns-table.gts +10 -3
  8. package/addon/components/snippet-plugin/nodes/snippet.gts +1 -1
  9. package/addon/components/structure-plugin/_private/structure.gts +1 -3
  10. package/addon/components/template-comments-plugin/template-comment.hbs +1 -3
  11. package/addon/components/worship-plugin/administrative-unit-picker.hbs +3 -3
  12. package/addon/components/worship-plugin/search-modal.hbs +4 -4
  13. package/addon/plugins/document-validation-plugin/index.ts +150 -0
  14. package/addon/plugins/roadsign-regulation-plugin/actions/insert-measure.ts +22 -2
  15. package/addon/plugins/roadsign-regulation-plugin/constants.ts +22 -6
  16. package/addon/plugins/roadsign-regulation-plugin/queries/mobility-measure-concept.ts +1 -1
  17. package/addon/plugins/roadsign-regulation-plugin/queries/mobility-template.ts +1 -1
  18. package/addon/plugins/roadsign-regulation-plugin/queries/road-sign-category.ts +6 -1
  19. package/addon/plugins/roadsign-regulation-plugin/queries/sign-concept.ts +19 -13
  20. package/addon/plugins/roadsign-regulation-plugin/schemas/sign-concept.ts +22 -8
  21. package/addon/plugins/snippet-plugin/nodes/snippet.ts +1 -0
  22. package/addon/plugins/structure-plugin/node.ts +1 -0
  23. package/addon/plugins/template-comments-plugin/node.ts +1 -0
  24. package/addon/plugins/variable-plugin/variables/date.ts +1 -0
  25. package/addon/utils/remove-quotes.ts +7 -0
  26. package/app/components/document-validation-plugin/card.js +1 -0
  27. package/app/styles/document-validation.scss +24 -0
  28. package/app/styles/snippet-plugin.scss +7 -1
  29. package/app/styles/structure-plugin.scss +2 -1
  30. package/app/styles/template-comments-plugin.scss +1 -1
  31. package/declarations/addon/components/document-validation-plugin/card.d.ts +18 -0
  32. package/declarations/addon/components/roadsign-regulation-plugin/roadsigns-modal.d.ts +20 -8
  33. package/declarations/addon/plugins/document-validation-plugin/index.d.ts +14 -0
  34. package/declarations/addon/plugins/roadsign-regulation-plugin/constants.d.ts +11 -5
  35. package/declarations/addon/plugins/roadsign-regulation-plugin/queries/mobility-measure-concept.d.ts +10 -4
  36. package/declarations/addon/plugins/roadsign-regulation-plugin/queries/road-sign-category.d.ts +1 -0
  37. package/declarations/addon/plugins/roadsign-regulation-plugin/queries/sign-concept.d.ts +14 -4
  38. package/declarations/addon/plugins/roadsign-regulation-plugin/schemas/mobility-measure-concept.d.ts +52 -21
  39. package/declarations/addon/plugins/roadsign-regulation-plugin/schemas/sign-concept.d.ts +32 -13
  40. package/declarations/addon/utils/remove-quotes.d.ts +1 -0
  41. package/package.json +8 -4
  42. package/pnpm-lock.yaml +1530 -1166
  43. package/translations/en-US.yaml +15 -7
  44. package/translations/nl-BE.yaml +17 -9
  45. package/types/global.d.ts +4 -0
@@ -0,0 +1,5 @@
1
+ ---
2
+ '@lblod/ember-rdfa-editor-lblod-plugins': minor
3
+ ---
4
+
5
+ Add new plugin for document validation with SHACL
@@ -0,0 +1,5 @@
1
+ ---
2
+ '@lblod/ember-rdfa-editor-lblod-plugins': patch
3
+ ---
4
+
5
+ Fix missing translations in citation-plugin
@@ -0,0 +1,5 @@
1
+ ---
2
+ '@lblod/ember-rdfa-editor-lblod-plugins': patch
3
+ ---
4
+
5
+ Improve translations of worship-plugin (capitalization, add missing translations etc.)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @lblod/ember-rdfa-editor-lblod-plugins
2
2
 
3
+ ## 32.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#573](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/573) [`be9e2f4`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/be9e2f476266037926f43f2729aae7e969c55652) Thanks [@elpoelma](https://github.com/elpoelma)! - Roadsign-regulation-plugin: differentiate between 'Verkeersbord' and 'Onderbord' signs
8
+
9
+ ### Patch Changes
10
+
11
+ - [#576](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/576) [`c68bef3`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/c68bef3a3e215e0d721894b72f8e0754b9988ca9) Thanks [@elpoelma](https://github.com/elpoelma)! - Slightly readjust styling of `snippet` and `structure` nodes, to ensure better cursor behaviour in firefox.
12
+ Note: for the better cursor behaviour in firefox to work as expected, `@lblod/ember-rdfa-editor` version [12.8.0](https://github.com/lblod/ember-rdfa-editor/releases/tag/%40lblod%2Fember-rdfa-editor%4012.8.0) or higher needs to be installed.
13
+
14
+ ## 32.0.0
15
+
16
+ ### Major Changes
17
+
18
+ - [#575](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/575) [`30abd63`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/30abd63cf2552a124c4d9168f5e040d4e847cc3d) Thanks [@lagartoverde](https://github.com/lagartoverde)! - BREAKING CHANGE: Adapt roadsign plugin to the new MOW model
19
+
20
+ ### Patch Changes
21
+
22
+ - [#572](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/pull/572) [`da42a7a`](https://github.com/lblod/ember-rdfa-editor-lblod-plugins/commit/da42a7afe59b58c12039bbbb7feb714ebd9a35c2) Thanks [@elpoelma](https://github.com/elpoelma)! - IRGN: increase font size of measure preview"
23
+
3
24
  ## 31.1.1
4
25
 
5
26
  ### Patch Changes
@@ -38,8 +38,8 @@
38
38
  @allowClear={{false}}
39
39
  @disabled={{false}}
40
40
  @searchEnabled={{true}}
41
- @loadingMessage={{t 'citaten-plugin.alert.loading'}}
42
- @noMatchesMessage={{t 'citaten-plugin.alert.no-results'}}
41
+ @loadingMessage={{t 'common.loading'}}
42
+ @noMatchesMessage={{t 'common.search.no-results'}}
43
43
  @searchMessage={{t 'citaten-plugin.search.placeholder'}}
44
44
  @options={{this.legislationTypes}}
45
45
  @selected={{this.legislationSelected}}
@@ -0,0 +1,131 @@
1
+ import Component from '@glimmer/component';
2
+ import { SayController } from '@lblod/ember-rdfa-editor';
3
+ import AuCard from '@appuniversum/ember-appuniversum/components/au-card';
4
+ import { documentValidationPluginKey } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/document-validation-plugin';
5
+ import AuIcon from '@appuniversum/ember-appuniversum/components/au-icon';
6
+ import { CloseFilledIcon } from '@appuniversum/ember-appuniversum/components/icons/close-filled';
7
+ import { CheckFilledIcon } from '@appuniversum/ember-appuniversum/components/icons/check-filled';
8
+ import { selectNodeBySubject } from '@lblod/ember-rdfa-editor/commands/_private/rdfa-commands/select-node-by-subject';
9
+ import { on } from '@ember/modifier';
10
+ import { fn } from '@ember/helper';
11
+ import AuButton from '@appuniversum/ember-appuniversum/components/au-button';
12
+ import { ExternalLinkIcon } from '@appuniversum/ember-appuniversum/components/icons/external-link';
13
+ import removeQuotes from '@lblod/ember-rdfa-editor-lblod-plugins/utils/remove-quotes';
14
+ import t from 'ember-intl/helpers/t';
15
+ import type { ShaclValidationReport } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/document-validation-plugin';
16
+ import { SayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
17
+
18
+ interface Sig {
19
+ Args: {
20
+ controller: SayController;
21
+ };
22
+ }
23
+
24
+ export default class DocumentValidationPluginCard extends Component<Sig> {
25
+ get documentValidationErrors() {
26
+ const { report } = documentValidationPluginKey.getState(
27
+ this.controller.mainEditorView.state,
28
+ );
29
+ if (!report) return undefined;
30
+ return shaclReportToErrorArray(report);
31
+ }
32
+ get propertiesWithoutErrors() {
33
+ const { propertiesWithoutErrors } = documentValidationPluginKey.getState(
34
+ this.controller.mainEditorView.state,
35
+ );
36
+ return propertiesWithoutErrors;
37
+ }
38
+ get controller() {
39
+ return this.args.controller;
40
+ }
41
+ goToSubject = (subject: string) => {
42
+ this.controller.doCommand(selectNodeBySubject({ subject }), {
43
+ view: this.controller.mainEditorView,
44
+ });
45
+ this.controller.focus();
46
+ };
47
+ get isValidDocument() {
48
+ return this.documentValidationErrors?.length === 0;
49
+ }
50
+ <template>
51
+ {{#if this.documentValidationErrors}}
52
+ <AuCard
53
+ @flex={{true}}
54
+ @divided={{true}}
55
+ @isOpenInitially={{true}}
56
+ @expandable={{true}}
57
+ @shadow={{true}}
58
+ @size='small'
59
+ class={{if
60
+ this.isValidDocument
61
+ 'say-document-validation__card-valid'
62
+ 'say-document-validation__card-invalid'
63
+ }}
64
+ as |c|
65
+ >
66
+ <c.header>
67
+ <p class='au-u-medium au-u-h6'>
68
+ {{#if this.isValidDocument}}
69
+ {{t 'document-validation-plugin.valid-document-title'}}
70
+ {{else}}
71
+ {{t 'document-validation-plugin.invalid-document-title'}}
72
+ {{/if}}
73
+ </p>
74
+ </c.header>
75
+ <c.content>
76
+ <p class='au-u-medium au-u-para-small'>{{t
77
+ 'document-validation-plugin.description'
78
+ }}</p>
79
+ {{#each this.propertiesWithoutErrors as |property|}}
80
+ <div class='say-document-validation__error-container'>
81
+ <AuIcon
82
+ @icon={{CheckFilledIcon}}
83
+ @size='large'
84
+ @ariaHidden={{true}}
85
+ class='say-document-validation__icon-success au-u-margin-right-small'
86
+ />
87
+ {{property.message}}
88
+ </div>
89
+ {{/each}}
90
+ {{#each this.documentValidationErrors as |error|}}
91
+ <div class='say-document-validation__error-container'>
92
+ <AuIcon
93
+ @icon={{CloseFilledIcon}}
94
+ @size='large'
95
+ @ariaHidden={{true}}
96
+ class='say-document-validation__icon-error au-u-margin-right-small'
97
+ />
98
+ {{error.message}}
99
+ <AuButton
100
+ class='au-u-padding-left-none au-u-padding-right-none'
101
+ @icon={{ExternalLinkIcon}}
102
+ @skin='link'
103
+ title={{error.subject}}
104
+ {{on 'click' (fn this.goToSubject error.subject)}}
105
+ >{{t 'document-validation-plugin.see-related-node'}}</AuButton>
106
+ </div>
107
+ {{/each}}
108
+ </c.content>
109
+
110
+ </AuCard>
111
+ {{/if}}
112
+ </template>
113
+ }
114
+
115
+ function shaclReportToErrorArray(report: ShaclValidationReport) {
116
+ let errorArray = [];
117
+ const factory = new SayDataFactory();
118
+ for (const r of report.results) {
119
+ const match = [
120
+ ...r.dataset.match(
121
+ r.sourceShape,
122
+ factory.namedNode('http://www.w3.org/ns/shacl#resultMessage'),
123
+ ),
124
+ ][0];
125
+ errorArray.push({
126
+ message: removeQuotes(match.object.value),
127
+ subject: r.focusNode?.value,
128
+ });
129
+ }
130
+ return errorArray;
131
+ }
@@ -19,6 +19,7 @@ import { eq } from 'ember-truth-helpers';
19
19
  import t from 'ember-intl/helpers/t';
20
20
  import { on } from '@ember/modifier';
21
21
  import { fn } from '@ember/helper';
22
+ import { SIGN_CONCEPT_TYPES } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/roadsign-regulation-plugin/constants';
22
23
 
23
24
  type Signature = {
24
25
  Args: {
@@ -44,8 +45,10 @@ export default class RoadSignsTable extends Component<Signature> {
44
45
  categories = (measureConcept: MobilityMeasureConcept) => {
45
46
  const categorySet: Set<string> = new Set();
46
47
  for (const signConcept of measureConcept.signConcepts) {
47
- const categories = signConcept.classifications;
48
- addAll(categorySet, ...categories);
48
+ if (signConcept.type === SIGN_CONCEPT_TYPES.ROAD_SIGN) {
49
+ const categoryLabels = signConcept.categories.map((cat) => cat.label);
50
+ addAll(categorySet, ...categoryLabels);
51
+ }
49
52
  }
50
53
  return [...categorySet].sort();
51
54
  };
@@ -106,7 +109,11 @@ export default class RoadSignsTable extends Component<Signature> {
106
109
  </div>
107
110
  </td>
108
111
  <td>
109
- <AuHelpText skin='secondary' class='au-u-margin-none'>
112
+ <AuHelpText
113
+ @size='large'
114
+ skin='secondary'
115
+ class='au-u-margin-none'
116
+ >
110
117
  <MeasurePreview
111
118
  @concept={{measureConcept}}
112
119
  @limitText={{true}}
@@ -226,7 +226,7 @@ export default class SnippetNode extends Component<Signature> {
226
226
  </span>
227
227
  {{this.node.attrs.title}}
228
228
  </div>
229
- <div class='say-snippet-content'>
229
+ <div class='say-snippet-body'>
230
230
  {{yield}}
231
231
  <div class='say-snippet-icons' contenteditable='false'>
232
232
  <SnippetButton
@@ -226,9 +226,7 @@ export default class Structure extends Component<Sig> {
226
226
  </Tag>
227
227
  {{/let}}
228
228
  </div>
229
- <div class='say-structure__content'>
230
- {{yield}}
231
- </div>
229
+ {{yield}}
232
230
  </div>
233
231
  </template>
234
232
  }
@@ -8,7 +8,5 @@
8
8
  {{if this.selectionInside "ProseMirror-selectednode"}}
9
9
  say-default-cursor'
10
10
  >
11
- <div class='say-text-cursor'>
12
- {{yield}}
13
- </div>
11
+ {{yield}}
14
12
  </AuAlert>
@@ -1,9 +1,9 @@
1
1
  {{! @glint-nocheck: not typesafe yet }}
2
2
  <PowerSelect
3
3
  @loadingMessage={{t 'common.search.loading'}}
4
- @searchMessage={{t 'worship-plugin.modal.search.search'}}
5
- @noMatchesMessage={{t 'worship-plugin.modal.search.no-results'}}
6
- @placeholderMessage={{t 'worship-plugin.modal.fields.admin-unit'}}
4
+ @searchMessage={{t 'worship-plugin.modal.fields.admin-unit.search'}}
5
+ @noMatchesMessage={{t 'worship-plugin.modal.fields.admin-unit.no-results'}}
6
+ @placeholder={{t 'worship-plugin.modal.fields.admin-unit.placeholder'}}
7
7
  @allowClear={{true}}
8
8
  @renderInPlace={{true}}
9
9
  @searchEnabled={{true}}
@@ -18,21 +18,21 @@
18
18
  <div class='au-c-sidebar'>
19
19
  <div class='au-c-sidebar__content au-u-padding'>
20
20
  <AuHeading @level='3' @skin='4' class='au-u-padding-bottom-small'>
21
- {{t 'worship-plugin.modal.search.title'}}
21
+ {{t 'worship-plugin.modal.filter-on'}}
22
22
  </AuHeading>
23
23
  <AuLabel class='au-margin-bottom-small' for='searchTerm'>
24
- {{t 'worship-plugin.modal.fields.name'}}
24
+ {{t 'worship-plugin.modal.fields.name.label'}}
25
25
  </AuLabel>
26
26
  <AuNativeInput
27
27
  @type='text'
28
28
  @width='block'
29
29
  id='searchTerm'
30
30
  value={{this.searchText}}
31
- placeholder={{t 'worship-plugin.modal.search.placeholder'}}
31
+ placeholder={{t 'worship-plugin.modal.fields.name.placeholder'}}
32
32
  {{on 'input' this.setInputSearchText}}
33
33
  />
34
34
  <AuLabel class='au-margin-bottom-small' for='admin-unit-select'>
35
- {{t 'worship-plugin.modal.fields.admin-unit'}}
35
+ {{t 'worship-plugin.modal.fields.admin-unit.label'}}
36
36
  </AuLabel>
37
37
  <WorshipPlugin::AdministrativeUnitPicker
38
38
  id='admin-unit-select'
@@ -0,0 +1,150 @@
1
+ import factory from '@rdfjs/dataset';
2
+ import SHACLValidator from 'rdf-validate-shacl';
3
+ import { Parser as ParserN3 } from 'n3';
4
+ import { RdfaParser } from 'rdfa-streaming-parser';
5
+ import { ProsePlugin, PluginKey, EditorView } from '@lblod/ember-rdfa-editor';
6
+ import removeQuotes from '@lblod/ember-rdfa-editor-lblod-plugins/utils/remove-quotes';
7
+ import {
8
+ DataFactory,
9
+ DatasetCore,
10
+ DatasetCoreFactory,
11
+ Quad,
12
+ } from '@rdfjs/types';
13
+ import ValidationReport from 'rdf-validate-shacl/src/validation-report';
14
+ import { SayDataFactory } from '@lblod/ember-rdfa-editor/core/say-data-factory';
15
+
16
+ export const documentValidationPluginKey = new PluginKey('DOCUMENT_VALIDATION');
17
+
18
+ interface DocumentValidationPluginArgs {
19
+ documentShape: string;
20
+ }
21
+
22
+ export type ShaclValidationReport = ValidationReport.ValidationReport<
23
+ DataFactory<Quad, Quad> &
24
+ DatasetCoreFactory<Quad, Quad, DatasetCore<Quad, Quad>>
25
+ >;
26
+
27
+ interface documentValidationTransactionMeta {
28
+ type: string;
29
+ report: ValidationReport;
30
+ propertiesWithoutErrors: string[];
31
+ }
32
+
33
+ export const documentValidationPlugin = (
34
+ options: DocumentValidationPluginArgs,
35
+ ) =>
36
+ new ProsePlugin({
37
+ key: documentValidationPluginKey,
38
+ state: {
39
+ init() {
40
+ return {
41
+ validationCallback: validationCallback,
42
+ documentShape: options.documentShape,
43
+ };
44
+ },
45
+ apply(tr, state) {
46
+ const pluginTransaction = tr.getMeta(documentValidationPluginKey) as
47
+ | documentValidationTransactionMeta
48
+ | undefined;
49
+ if (pluginTransaction) {
50
+ if (pluginTransaction.type === 'setNewReport') {
51
+ return {
52
+ ...state,
53
+ report: pluginTransaction.report,
54
+ propertiesWithoutErrors:
55
+ pluginTransaction.propertiesWithoutErrors,
56
+ };
57
+ }
58
+ }
59
+
60
+ return state;
61
+ },
62
+ },
63
+ });
64
+
65
+ async function validationCallback(view: EditorView, documentHtml: string) {
66
+ const { documentShape } = documentValidationPluginKey.getState(view.state);
67
+ const rdf = await htmlToRdf(documentHtml);
68
+ const shacl = await parse(documentShape);
69
+
70
+ const validator = new SHACLValidator(shacl, {
71
+ // @ts-expect-error ts doesn't recognize the configuration parameter not sure why
72
+ allowNamedNodeInList: true,
73
+ });
74
+ const report = validator.validate(rdf);
75
+ const sayFactory = new SayDataFactory();
76
+ const propertyPred = sayFactory.namedNode(
77
+ 'http://www.w3.org/ns/shacl#property',
78
+ );
79
+ const propertyNodes = [
80
+ ...shacl.match(undefined, propertyPred, undefined),
81
+ ].map((quad: Quad) => quad.object);
82
+
83
+ const propertiesWithErrors: string[] = [];
84
+ for (const r of report.results) {
85
+ const shapeId = r.sourceShape?.value;
86
+ if (shapeId) propertiesWithErrors.push(shapeId);
87
+ }
88
+ const propertiesWithoutErrorsArray = propertyNodes.filter(
89
+ (term) => !propertiesWithErrors.includes(term.value),
90
+ );
91
+
92
+ const successMessagePred = sayFactory.namedNode(
93
+ 'http://mu.semte.ch/vocabularies/ext/successMessage',
94
+ );
95
+ const propertiesWithoutErrors = propertiesWithoutErrorsArray
96
+ .map((propertyNode) => {
97
+ const match = shacl.match(propertyNode, successMessagePred, undefined);
98
+ const message = [...match][0]?.object.value;
99
+ return message ? { message: removeQuotes(message) } : undefined;
100
+ })
101
+ .filter((message) => message);
102
+ const transaction = view.state.tr;
103
+ transaction.setMeta(documentValidationPluginKey, {
104
+ type: 'setNewReport',
105
+ report,
106
+ propertiesWithoutErrors,
107
+ });
108
+ transaction.setMeta('addToHistory', false);
109
+ view.dispatch(transaction);
110
+ }
111
+
112
+ interface N3Parser {
113
+ parse: (
114
+ triples: string,
115
+ callback: (error: string, quad: Quad) => void,
116
+ ) => void;
117
+ }
118
+
119
+ async function parse(triples: string): Promise<DatasetCore<Quad>> {
120
+ return new Promise((resolve, reject) => {
121
+ // @ts-expect-error we have to use a custom type to make the quads compatible or else ts will complain
122
+ const parser: N3Parser = new ParserN3();
123
+ const dataset = factory.dataset();
124
+ parser.parse(triples, (error, quad) => {
125
+ if (error) {
126
+ console.warn(error);
127
+ reject(error);
128
+ } else if (quad) {
129
+ dataset.add(quad);
130
+ } else {
131
+ resolve(dataset);
132
+ }
133
+ });
134
+ });
135
+ }
136
+
137
+ function htmlToRdf(html: string): Promise<DatasetCore<Quad>> {
138
+ return new Promise((res, rej) => {
139
+ const myParser = new RdfaParser({ contentType: 'text/html' });
140
+ const dataset = factory.dataset();
141
+ myParser
142
+ .on('data', (data) => {
143
+ dataset.add(data);
144
+ })
145
+ .on('error', rej)
146
+ .on('end', () => res(dataset));
147
+ myParser.write(html);
148
+ myParser.end();
149
+ });
150
+ }
@@ -25,7 +25,8 @@ import { buildArticleStructure } from '../../decision-plugin/utils/build-article
25
25
  import { insertArticle } from '../../decision-plugin/actions/insert-article';
26
26
  import { SignConcept } from '../schemas/sign-concept';
27
27
  import {
28
- SIGN_CONCEPT_TYPE_LABELS,
28
+ ROAD_SIGN_CATEGORIES,
29
+ SIGN_CONCEPT_TYPES,
29
30
  SIGN_TYPE_MAPPING,
30
31
  SIGN_TYPES,
31
32
  ZONALITY_OPTIONS,
@@ -190,9 +191,28 @@ function constructMeasureBody(
190
191
  return schema.nodes.paragraph.create({}, nodes);
191
192
  }
192
193
 
194
+ function determineSignLabel(signConcept: SignConcept) {
195
+ switch (signConcept.type) {
196
+ case SIGN_CONCEPT_TYPES.TRAFFIC_LIGHT:
197
+ return 'Verkeerslicht';
198
+ case SIGN_CONCEPT_TYPES.ROAD_MARKING:
199
+ return 'Wegmarkering';
200
+ case SIGN_CONCEPT_TYPES.ROAD_SIGN:
201
+ if (
202
+ signConcept.categories
203
+ .map((cat) => cat.uri)
204
+ .includes(ROAD_SIGN_CATEGORIES.ONDERBORD)
205
+ ) {
206
+ return 'Onderbord';
207
+ } else {
208
+ return 'Verkeersbord';
209
+ }
210
+ }
211
+ }
212
+
193
213
  function constructSignNode(signConcept: SignConcept, schema: Schema) {
194
214
  const signUri = `http://data.lblod.info/verkeerstekens/${uuid()}`;
195
- const prefix = SIGN_CONCEPT_TYPE_LABELS[signConcept.type];
215
+ const prefix = determineSignLabel(signConcept);
196
216
  const node = schema.nodes.inline_rdfa.create(
197
217
  {
198
218
  rdfaNodeType: 'resource',
@@ -29,9 +29,25 @@ export const SIGN_TYPE_MAPPING = {
29
29
  [SIGN_CONCEPT_TYPES.ROAD_MARKING]: SIGN_TYPES.ROAD_MARKING,
30
30
  } as const;
31
31
 
32
- export const SIGN_CONCEPT_TYPE_LABELS = {
33
- [SIGN_CONCEPT_TYPES.TRAFFIC_SIGN]: 'Verkeersteken',
34
- [SIGN_CONCEPT_TYPES.ROAD_SIGN]: 'Verkeersbord',
35
- [SIGN_CONCEPT_TYPES.TRAFFIC_LIGHT]: 'Verkeerslicht',
36
- [SIGN_CONCEPT_TYPES.ROAD_MARKING]: 'Wegmarkering',
37
- } as const;
32
+ export const ROAD_SIGN_CATEGORIES = {
33
+ XXBORD:
34
+ 'https://data.vlaanderen.be/id/concept/Verkeersbordcategorie/ae1b7231-1f31-492d-947a-25fc5d114492',
35
+ 'XX-AWVBORD':
36
+ 'https://data.vlaanderen.be/id/concept/Verkeersbordcategorie/8e302648-0eca-478b-8b48-67c3b0e39c0a',
37
+ GEVAARSBORD:
38
+ 'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/2982567006d9e19f04063df73123f56f40e3a28941031a7ba6e6667f64740fa9',
39
+ STILSTAANPARKEERBORD:
40
+ 'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/29ea3335e357e414d07229242607b352941c0c21e78760600cc0f5270f18c38b',
41
+ VOORRANGSBORD:
42
+ 'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/737da5751bc7f311398a834f34df310dd95255a0b62afa2db2882c72d54b47d2',
43
+ ZONEBORD:
44
+ 'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/86a67f3cba6512ae10c4b9b09ba35d8c80109189b44d37e848858af9efb37019',
45
+ VERBODSBORD:
46
+ 'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/955a9adc73d076a2a424754cd540b73da8d15fb002ab6c9f115d080edddb57e8',
47
+ ONDERBORD:
48
+ 'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/991b04b477b77bc7cf1414fb5d255cc4435dd9c1681e8de66f770710c1c83ad0',
49
+ GEBODSBORD:
50
+ 'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/9d84069e70f192b7a474d02f07687bc3343ee324207ad9e093c0b2f5def647f8',
51
+ AANWIJSBORD:
52
+ 'http://data.vlaanderen.be/id/concept/Verkeersbordcategorie/9ea8f8b421343370d20a8bd45d6226aadc48125bda8ddbbeeb53d99f181ee05a',
53
+ };
@@ -95,7 +95,7 @@ async function _queryMobilityMeasures<Count extends boolean>(
95
95
  a mobiliteit:Mobiliteitmaatregelconcept;
96
96
  skos:prefLabel ?label;
97
97
  ext:zonality ?zonality;
98
- mobiliteit:template ?templateUri.
98
+ mobiliteit:Mobiliteitsmaatregelconcept.template ?templateUri.
99
99
 
100
100
  ?templateUri ext:preview ?preview.
101
101
 
@@ -39,7 +39,7 @@ export async function queryMobilityTemplates(
39
39
  }
40
40
  ${
41
41
  measureConceptUri
42
- ? `${sparqlEscapeUri(measureConceptUri)} mobiliteit:template ?uri`
42
+ ? `${sparqlEscapeUri(measureConceptUri)} mobiliteit:Mobiliteitsmaatregelconcept.template ?uri`
43
43
  : ''
44
44
  }
45
45
  }
@@ -2,6 +2,7 @@ import {
2
2
  BindingObject,
3
3
  executeQuery,
4
4
  objectify,
5
+ sparqlEscapeUri,
5
6
  } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/sparql-helpers';
6
7
  import {
7
8
  RoadSignCategory,
@@ -10,16 +11,18 @@ import {
10
11
 
11
12
  type QueryOptions = {
12
13
  abortSignal?: AbortSignal;
14
+ roadSignConceptUri?: string;
13
15
  };
14
16
 
15
17
  export default async function queryRoadSignCategories(
16
18
  endpoint: string,
17
19
  options: QueryOptions = {},
18
20
  ) {
19
- const { abortSignal } = options;
21
+ const { abortSignal, roadSignConceptUri } = options;
20
22
  const query = /* sparql */ `
21
23
  PREFIX mobiliteit: <https://data.vlaanderen.be/ns/mobiliteit#>
22
24
  PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
25
+ PREFIX dct: <http://purl.org/dc/terms/>
23
26
 
24
27
  SELECT DISTINCT
25
28
  ?uri
@@ -27,6 +30,8 @@ export default async function queryRoadSignCategories(
27
30
  WHERE {
28
31
  ?uri a mobiliteit:Verkeersbordcategorie;
29
32
  skos:prefLabel ?label.
33
+
34
+ ${roadSignConceptUri ? `${sparqlEscapeUri(roadSignConceptUri)} dct:type ?uri` : ''}
30
35
  }
31
36
  `;
32
37
  const queryResult = await executeQuery<BindingObject<RoadSignCategory>>({
@@ -6,6 +6,8 @@ import {
6
6
  sparqlEscapeUri,
7
7
  } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/sparql-helpers';
8
8
  import { SignConcept, SignConceptSchema } from '../schemas/sign-concept';
9
+ import queryRoadSignCategories from './road-sign-category';
10
+ import { SIGN_CONCEPT_TYPES } from '../constants';
9
11
 
10
12
  type QueryOptions = {
11
13
  imageBaseUrl?: string;
@@ -31,7 +33,6 @@ export async function querySignConcepts(
31
33
  ?code
32
34
  ?zonality
33
35
  (CONCAT(${sparqlEscapeString(imageBaseUrl ?? '')}, "/files/", ?imageId, "/download") AS ?image)
34
- (GROUP_CONCAT(?classification; SEPARATOR="|") AS ?classifications)
35
36
  WHERE {
36
37
  ?uri
37
38
  a mobiliteit:Verkeerstekenconcept;
@@ -44,9 +45,6 @@ export async function querySignConcepts(
44
45
  OPTIONAL {
45
46
  ?uri ext:zonality ?zonality.
46
47
  }
47
- OPTIONAL {
48
- ?uri dct:type/skos:prefLabel ?classification.
49
- }
50
48
 
51
49
  VALUES ?type {
52
50
  <https://data.vlaanderen.be/ns/mobiliteit#Verkeersbordconcept>
@@ -63,13 +61,21 @@ export async function querySignConcepts(
63
61
  abortSignal,
64
62
  });
65
63
  const bindings = queryResult.results.bindings;
66
- const processed = bindings.map(objectify).map((binding) => {
67
- return {
68
- ...binding,
69
- classifications: binding.classifications
70
- ? binding.classifications.split('|')
71
- : [],
72
- };
73
- });
74
- return SignConceptSchema.array().parse(processed);
64
+ const concepts = SignConceptSchema.array().parse(bindings.map(objectify));
65
+ const conceptsWithCategories = await Promise.all(
66
+ concepts.map(async (concept) => {
67
+ if (concept.type === SIGN_CONCEPT_TYPES.ROAD_SIGN) {
68
+ const categories = await queryRoadSignCategories(endpoint, {
69
+ roadSignConceptUri: concept.uri,
70
+ });
71
+ return {
72
+ ...concept,
73
+ categories,
74
+ };
75
+ } else {
76
+ return concept;
77
+ }
78
+ }),
79
+ );
80
+ return conceptsWithCategories;
75
81
  }