@lblod/ember-rdfa-editor-lblod-plugins 11.1.0 → 11.3.0
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.
- package/.woodpecker/.changelog.yml +9 -0
- package/.woodpecker/.test.yml +0 -8
- package/CHANGELOG.md +14 -1
- package/README.md +14 -12
- package/addon/components/citation-plugin/citation-card.ts +22 -18
- package/addon/components/citation-plugin/citation-insert.hbs +0 -1
- package/addon/components/citation-plugin/citation-insert.ts +5 -7
- package/addon/components/citation-plugin/citations/decision-detail.ts +4 -5
- package/addon/components/citation-plugin/citations/decision-preview.hbs +61 -15
- package/addon/components/citation-plugin/citations/decision-preview.ts +19 -0
- package/addon/components/citation-plugin/citations/search-modal.hbs +49 -47
- package/addon/components/citation-plugin/citations/search-modal.ts +28 -17
- package/addon/components/variable-plugin/codelist/edit.ts +6 -1
- package/addon/components/variable-plugin/codelist/insert.hbs +16 -6
- package/addon/components/variable-plugin/codelist/insert.ts +40 -9
- package/addon/plugins/citation-plugin/index.ts +8 -2
- package/addon/plugins/citation-plugin/utils/article.ts +193 -0
- package/addon/plugins/citation-plugin/utils/cache.ts +7 -0
- package/addon/plugins/citation-plugin/utils/legal-documents.ts +133 -0
- package/addon/plugins/citation-plugin/utils/process-match.ts +60 -44
- package/addon/plugins/citation-plugin/utils/public-decisions.ts +228 -0
- package/addon/plugins/citation-plugin/utils/{legislation-types.ts → types.ts} +24 -18
- package/addon/plugins/citation-plugin/utils/utils.ts +14 -0
- package/addon/plugins/citation-plugin/utils/vlaamse-codex.ts +29 -390
- package/addon/plugins/variable-plugin/utils/attribute-parsers.ts +4 -0
- package/addon/plugins/variable-plugin/variables/codelist.ts +15 -2
- package/components/citation-plugin/citation-card.d.ts +11 -14
- package/components/citation-plugin/citation-insert.d.ts +4 -3
- package/components/citation-plugin/citations/decision-detail.d.ts +3 -3
- package/components/citation-plugin/citations/decision-preview.d.ts +10 -0
- package/components/citation-plugin/citations/search-modal.d.ts +15 -16
- package/components/variable-plugin/codelist/insert.d.ts +18 -4
- package/package.json +4 -3
- package/plugins/citation-plugin/index.d.ts +2 -1
- package/plugins/citation-plugin/utils/article.d.ts +29 -0
- package/plugins/citation-plugin/utils/cache.d.ts +1 -0
- package/plugins/citation-plugin/utils/legal-documents.d.ts +48 -0
- package/plugins/citation-plugin/utils/public-decisions.d.ts +8 -0
- package/plugins/citation-plugin/utils/types.d.ts +28 -0
- package/plugins/citation-plugin/utils/utils.d.ts +1 -0
- package/plugins/citation-plugin/utils/vlaamse-codex.d.ts +7 -69
- package/plugins/variable-plugin/utils/attribute-parsers.d.ts +1 -0
- package/translations/en-US.yaml +7 -1
- package/translations/nl-BE.yaml +7 -1
- package/plugins/citation-plugin/utils/legislation-types.d.ts +0 -24
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
<PowerSelect
|
|
2
2
|
@allowClear={{false}}
|
|
3
3
|
@searchEnabled={{false}}
|
|
4
|
-
@options={{this.
|
|
5
|
-
@selected={{this.
|
|
6
|
-
@onChange={{this.
|
|
7
|
-
as |
|
|
4
|
+
@options={{this.codelistData.value}}
|
|
5
|
+
@selected={{this.selectedCodelist}}
|
|
6
|
+
@onChange={{this.selectCodelist}}
|
|
7
|
+
as |codelist|
|
|
8
8
|
>
|
|
9
|
-
{{
|
|
9
|
+
{{codelist.label}}
|
|
10
|
+
</PowerSelect>
|
|
11
|
+
<PowerSelect
|
|
12
|
+
@allowClear={{false}}
|
|
13
|
+
@searchEnabled={{false}}
|
|
14
|
+
@options={{this.selectionStyles}}
|
|
15
|
+
@selected={{this.selectedStyle}}
|
|
16
|
+
@onChange={{this.selectStyle}}
|
|
17
|
+
as |style|
|
|
18
|
+
>
|
|
19
|
+
{{style.label}}
|
|
10
20
|
</PowerSelect>
|
|
11
21
|
<AuFormRow>
|
|
12
22
|
<VariablePlugin::Utils::LabelInput
|
|
@@ -16,7 +26,7 @@
|
|
|
16
26
|
</AuFormRow>
|
|
17
27
|
<AuButton
|
|
18
28
|
{{on 'click' this.insert}}
|
|
19
|
-
@disabled={{not this.
|
|
29
|
+
@disabled={{not this.selectedCodelist}}
|
|
20
30
|
>
|
|
21
31
|
{{t 'variable-plugin.button'}}
|
|
22
32
|
</AuButton>
|
|
@@ -15,16 +15,22 @@ export type CodelistInsertOptions = {
|
|
|
15
15
|
publisher?: string;
|
|
16
16
|
endpoint: string;
|
|
17
17
|
};
|
|
18
|
+
|
|
18
19
|
type Args = {
|
|
19
20
|
controller: SayController;
|
|
20
21
|
options: CodelistInsertOptions;
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
interface SelectStyle {
|
|
25
|
+
label: string;
|
|
26
|
+
value: string;
|
|
27
|
+
}
|
|
26
28
|
|
|
29
|
+
export default class CodelistInsertComponent extends Component<Args> {
|
|
27
30
|
@service declare intl: IntlService;
|
|
31
|
+
@tracked selectedCodelist?: CodeList;
|
|
32
|
+
@tracked label?: string;
|
|
33
|
+
@tracked selectedStyleValue = 'single';
|
|
28
34
|
|
|
29
35
|
get controller() {
|
|
30
36
|
return this.args.controller;
|
|
@@ -42,9 +48,28 @@ export default class CodelistInsertComponent extends Component<Args> {
|
|
|
42
48
|
return this.args.options.endpoint;
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
get selectionStyles() {
|
|
52
|
+
const singleSelect = {
|
|
53
|
+
label: this.intl.t('variable.codelist.single-select'),
|
|
54
|
+
value: 'single',
|
|
55
|
+
};
|
|
56
|
+
const multiSelect = {
|
|
57
|
+
label: this.intl.t('variable.codelist.multi-select'),
|
|
58
|
+
value: 'multi',
|
|
59
|
+
};
|
|
60
|
+
return [singleSelect, multiSelect];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get selectedStyle() {
|
|
64
|
+
return this.selectionStyles.find(
|
|
65
|
+
(style) => style.value === this.selectedStyleValue,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
codelistData = trackedFunction(this, async () => {
|
|
46
70
|
return fetchCodeListsByPublisher(this.endpoint, this.publisher);
|
|
47
71
|
});
|
|
72
|
+
|
|
48
73
|
@action
|
|
49
74
|
updateLabel(event: InputEvent) {
|
|
50
75
|
this.label = (event.target as HTMLInputElement).value;
|
|
@@ -58,12 +83,13 @@ export default class CodelistInsertComponent extends Component<Args> {
|
|
|
58
83
|
{
|
|
59
84
|
mappingResource,
|
|
60
85
|
variableInstance,
|
|
61
|
-
codelistResource: this.
|
|
62
|
-
label: this.label ?? this.
|
|
86
|
+
codelistResource: this.selectedCodelist?.uri,
|
|
87
|
+
label: this.label ?? this.selectedCodelist?.label,
|
|
63
88
|
source: this.endpoint,
|
|
89
|
+
selectionStyle: this.selectedStyleValue,
|
|
64
90
|
},
|
|
65
91
|
this.schema.node('placeholder', {
|
|
66
|
-
placeholderText: this.
|
|
92
|
+
placeholderText: this.selectedCodelist?.label,
|
|
67
93
|
}),
|
|
68
94
|
);
|
|
69
95
|
|
|
@@ -78,7 +104,12 @@ export default class CodelistInsertComponent extends Component<Args> {
|
|
|
78
104
|
}
|
|
79
105
|
|
|
80
106
|
@action
|
|
81
|
-
|
|
82
|
-
this.
|
|
107
|
+
selectCodelist(codelist: CodeList) {
|
|
108
|
+
this.selectedCodelist = codelist;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@action
|
|
112
|
+
selectStyle(style: SelectStyle) {
|
|
113
|
+
this.selectedStyleValue = style.value;
|
|
83
114
|
}
|
|
84
115
|
}
|
|
@@ -81,12 +81,17 @@ const MINISTERIAL = `ministerieel${NNWS}?besluit`;
|
|
|
81
81
|
*/
|
|
82
82
|
const ENUM_ROYAL = `genummerd${NNWS}?${ROYAL}`;
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* match for "decision of the flemish government"
|
|
86
|
+
*/
|
|
87
|
+
const DECISION = 'besluit';
|
|
88
|
+
|
|
84
89
|
/**
|
|
85
90
|
* The type of citation that we need to search for
|
|
86
91
|
*/
|
|
87
|
-
const TYPE = `${DECREE}|${MEMO}|${TREATY}|${CONSTITUTION_CHANGE}|${COLLAB}|${BOOK}|${PROTOCOL}|${FLEMGOV}|${COORD}|${SPECIAL}|${LAW}|${ROYAL}|${MINISTERIAL}|${ENUM_ROYAL}`;
|
|
92
|
+
const TYPE = `${DECREE}|${MEMO}|${TREATY}|${CONSTITUTION_CHANGE}|${COLLAB}|${BOOK}|${PROTOCOL}|${FLEMGOV}|${COORD}|${SPECIAL}|${LAW}|${ROYAL}|${MINISTERIAL}|${ENUM_ROYAL}|${DECISION}`;
|
|
88
93
|
/**
|
|
89
|
-
* The monster regex that makes the citation plugin trigger
|
|
94
|
+
* The monster regex that makes the citation plugin trigger to show `CitationCard`.
|
|
90
95
|
* In restructuring, I've made sure that I didn't abstract away any of the capturing groups,
|
|
91
96
|
* only their content, so you can still see what's going on
|
|
92
97
|
*
|
|
@@ -134,6 +139,7 @@ export type CitationPluginConfig =
|
|
|
134
139
|
|
|
135
140
|
export type CitationPluginEmberComponentConfig = CitationPluginConfig & {
|
|
136
141
|
endpoint: string;
|
|
142
|
+
decisionsEndpoint?: string;
|
|
137
143
|
};
|
|
138
144
|
|
|
139
145
|
export function citationPlugin(config: CitationPluginConfig): CitationPlugin {
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { LegalDocumentsQueryConfig } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/legal-documents';
|
|
2
|
+
import {
|
|
3
|
+
executeCountQuery,
|
|
4
|
+
executeQuery,
|
|
5
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/sparql-helpers';
|
|
6
|
+
import {
|
|
7
|
+
dateValue,
|
|
8
|
+
escapeValue,
|
|
9
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/utils/strings';
|
|
10
|
+
import { Binding } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/types';
|
|
11
|
+
|
|
12
|
+
interface ArticleArgs {
|
|
13
|
+
uri: string;
|
|
14
|
+
number?: number;
|
|
15
|
+
content: string | null;
|
|
16
|
+
dateInForce: string | null;
|
|
17
|
+
dateNoLongerInForce: string | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export class Article {
|
|
21
|
+
uri: string;
|
|
22
|
+
number?: number;
|
|
23
|
+
content: string | null;
|
|
24
|
+
dateInForce: string | null;
|
|
25
|
+
dateNoLongerInForce: string | null;
|
|
26
|
+
|
|
27
|
+
constructor({
|
|
28
|
+
uri,
|
|
29
|
+
number,
|
|
30
|
+
content,
|
|
31
|
+
dateInForce,
|
|
32
|
+
dateNoLongerInForce,
|
|
33
|
+
}: ArticleArgs) {
|
|
34
|
+
this.uri = uri;
|
|
35
|
+
this.number = number;
|
|
36
|
+
this.content = content;
|
|
37
|
+
this.dateInForce = dateInForce;
|
|
38
|
+
this.dateNoLongerInForce = dateNoLongerInForce;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const fetchArticlesCache = new Map<string, ArticleCollection>();
|
|
43
|
+
|
|
44
|
+
async function fetchArticles({
|
|
45
|
+
legalExpression,
|
|
46
|
+
pageNumber,
|
|
47
|
+
pageSize,
|
|
48
|
+
articleFilter,
|
|
49
|
+
config,
|
|
50
|
+
}: {
|
|
51
|
+
legalExpression: string;
|
|
52
|
+
pageNumber: number;
|
|
53
|
+
pageSize: number;
|
|
54
|
+
articleFilter: string;
|
|
55
|
+
config: LegalDocumentsQueryConfig;
|
|
56
|
+
}): Promise<ArticleCollection> {
|
|
57
|
+
//Simpler here, only one way arguments are set up
|
|
58
|
+
const stringArguments = JSON.stringify({
|
|
59
|
+
legalExpression,
|
|
60
|
+
pageNumber,
|
|
61
|
+
pageSize,
|
|
62
|
+
articleFilter,
|
|
63
|
+
});
|
|
64
|
+
const results = fetchArticlesCache.get(stringArguments);
|
|
65
|
+
if (results) {
|
|
66
|
+
return results;
|
|
67
|
+
} else {
|
|
68
|
+
const newResults = await fetchArticlesMemo({
|
|
69
|
+
legalExpression,
|
|
70
|
+
pageNumber,
|
|
71
|
+
pageSize,
|
|
72
|
+
articleFilter,
|
|
73
|
+
config,
|
|
74
|
+
});
|
|
75
|
+
fetchArticlesCache.set(stringArguments, newResults);
|
|
76
|
+
return newResults;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface ArticleBinding {
|
|
81
|
+
count: Binding<string>;
|
|
82
|
+
content?: Binding<string>;
|
|
83
|
+
dateInForce?: Binding<string>;
|
|
84
|
+
dateNoLongerInForce?: Binding<string>;
|
|
85
|
+
article: Binding<string>;
|
|
86
|
+
number?: Binding<number>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface ArticleCollection {
|
|
90
|
+
totalCount: number;
|
|
91
|
+
articles: Article[];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function fetchArticlesMemo({
|
|
95
|
+
legalExpression,
|
|
96
|
+
pageNumber = 0,
|
|
97
|
+
pageSize = 10,
|
|
98
|
+
articleFilter,
|
|
99
|
+
config,
|
|
100
|
+
}: {
|
|
101
|
+
legalExpression: string;
|
|
102
|
+
pageNumber: number;
|
|
103
|
+
pageSize: number;
|
|
104
|
+
articleFilter: string;
|
|
105
|
+
config: LegalDocumentsQueryConfig;
|
|
106
|
+
}): Promise<ArticleCollection> {
|
|
107
|
+
const numberFilter = articleFilter
|
|
108
|
+
? `FILTER( !BOUND(?number) || CONTAINS(?number, "${articleFilter}"))`
|
|
109
|
+
: '';
|
|
110
|
+
const totalCount = await executeCountQuery({
|
|
111
|
+
query: `PREFIX eli: <http://data.europa.eu/eli/ontology#>
|
|
112
|
+
PREFIX dct: <http://purl.org/dc/terms/>
|
|
113
|
+
|
|
114
|
+
SELECT COUNT(DISTINCT(?article)) as ?count
|
|
115
|
+
WHERE {
|
|
116
|
+
?legalResource eli:is_realized_by <${legalExpression}> ;
|
|
117
|
+
eli:has_part ?articleResource .
|
|
118
|
+
?articleResource eli:is_realized_by ?article ;
|
|
119
|
+
dct:type <https://data.vlaanderen.be/id/concept/TypeRechtsbrononderdeel/Artikel>.
|
|
120
|
+
OPTIONAL {
|
|
121
|
+
?article eli:first_date_entry_in_force ?dateInForce .
|
|
122
|
+
FILTER (?dateInForce <= NOW() )
|
|
123
|
+
}
|
|
124
|
+
OPTIONAL { ?article eli:date_no_longer_in_force ?dateNoLongerInForce }
|
|
125
|
+
FILTER( !BOUND(?dateNoLongerInForce) || ?dateNoLongerInForce > NOW() )
|
|
126
|
+
OPTIONAL { ?article eli:number ?number . }
|
|
127
|
+
${numberFilter}
|
|
128
|
+
}`,
|
|
129
|
+
...config,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
if (totalCount > 0) {
|
|
133
|
+
// ?number has format like "Artikel 12." We parse the number from the string for ordering
|
|
134
|
+
// Second degree ordering on ?numberStr to make sure "Artikel 3/1." comes after "Artikel 3."
|
|
135
|
+
const response = await executeQuery<ArticleBinding>({
|
|
136
|
+
query: `PREFIX eli: <http://data.europa.eu/eli/ontology#>
|
|
137
|
+
PREFIX prov: <http://www.w3.org/ns/prov#>
|
|
138
|
+
PREFIX dct: <http://purl.org/dc/terms/>
|
|
139
|
+
|
|
140
|
+
SELECT DISTINCT ?article ?dateInForce ?dateNoLongerInForce ?number ?content WHERE {
|
|
141
|
+
?legalResource eli:is_realized_by <${legalExpression}> ;
|
|
142
|
+
eli:has_part ?articleResource .
|
|
143
|
+
?articleResource eli:is_realized_by ?article ;
|
|
144
|
+
dct:type <https://data.vlaanderen.be/id/concept/TypeRechtsbrononderdeel/Artikel>.
|
|
145
|
+
OPTIONAL {
|
|
146
|
+
?article eli:first_date_entry_in_force ?dateInForce .
|
|
147
|
+
FILTER (?dateInForce <= NOW() )
|
|
148
|
+
}
|
|
149
|
+
OPTIONAL { ?article eli:date_no_longer_in_force ?dateNoLongerInForce }
|
|
150
|
+
FILTER( !BOUND(?dateNoLongerInForce) || ?dateNoLongerInForce > NOW() )
|
|
151
|
+
OPTIONAL { ?article prov:value ?content . }
|
|
152
|
+
OPTIONAL { ?article eli:number ?number . }
|
|
153
|
+
${numberFilter}
|
|
154
|
+
BIND(REPLACE(?number, "Artikel ", "") as ?numberStr)
|
|
155
|
+
BIND(STRDT(?numberStr, xsd:integer) as ?numberInt)
|
|
156
|
+
} ORDER BY ?numberInt ?numberStr LIMIT ${pageSize} OFFSET ${
|
|
157
|
+
pageNumber * pageSize
|
|
158
|
+
}`,
|
|
159
|
+
...config,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const articles = response.results.bindings.map((binding) => {
|
|
163
|
+
const escapedContent = escapeValue(
|
|
164
|
+
binding.content && binding.content.value,
|
|
165
|
+
);
|
|
166
|
+
const dateInForce = dateValue(
|
|
167
|
+
binding.dateInForce && binding.dateInForce.value,
|
|
168
|
+
);
|
|
169
|
+
const dateNoLongerInForce = dateValue(
|
|
170
|
+
binding.dateNoLongerInForce && binding.dateNoLongerInForce.value,
|
|
171
|
+
);
|
|
172
|
+
return new Article({
|
|
173
|
+
uri: binding.article.value,
|
|
174
|
+
number: binding.number && binding.number.value,
|
|
175
|
+
content: escapedContent,
|
|
176
|
+
dateInForce,
|
|
177
|
+
dateNoLongerInForce,
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
totalCount,
|
|
183
|
+
articles,
|
|
184
|
+
};
|
|
185
|
+
} else {
|
|
186
|
+
return {
|
|
187
|
+
totalCount,
|
|
188
|
+
articles: [],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export { fetchArticles };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { fetchLegalDocumentsCache } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/legal-documents';
|
|
2
|
+
import { fetchArticlesCache } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/article';
|
|
3
|
+
|
|
4
|
+
export function cleanCaches() {
|
|
5
|
+
fetchLegalDocumentsCache.clear();
|
|
6
|
+
fetchArticlesCache.clear();
|
|
7
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isBesluitType,
|
|
3
|
+
LEGISLATION_TYPE_CONCEPTS,
|
|
4
|
+
} from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/types';
|
|
5
|
+
import { fetchVlaamseCodexLegalDocuments } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/vlaamse-codex';
|
|
6
|
+
import { Option } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
|
|
7
|
+
import { fetchPublicDecisions } from '@lblod/ember-rdfa-editor-lblod-plugins/plugins/citation-plugin/utils/public-decisions';
|
|
8
|
+
|
|
9
|
+
interface LegalDocumentArgs {
|
|
10
|
+
uri: string;
|
|
11
|
+
legislationTypeUri: Option<string>;
|
|
12
|
+
title: string | null;
|
|
13
|
+
publicationDate: string | null;
|
|
14
|
+
documentDate: string | null;
|
|
15
|
+
meta?: Record<string, unknown> | null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class LegalDocument {
|
|
19
|
+
uri: string;
|
|
20
|
+
legislationType: { label: string; value: string } | undefined;
|
|
21
|
+
title: string | null;
|
|
22
|
+
publicationDate: string | null;
|
|
23
|
+
documentDate: string | null;
|
|
24
|
+
meta: Record<string, unknown> | null;
|
|
25
|
+
|
|
26
|
+
constructor({
|
|
27
|
+
uri,
|
|
28
|
+
legislationTypeUri,
|
|
29
|
+
title,
|
|
30
|
+
publicationDate,
|
|
31
|
+
documentDate,
|
|
32
|
+
meta,
|
|
33
|
+
}: LegalDocumentArgs) {
|
|
34
|
+
this.uri = uri;
|
|
35
|
+
this.legislationType = LEGISLATION_TYPE_CONCEPTS.find(
|
|
36
|
+
(t) => t.value === legislationTypeUri,
|
|
37
|
+
);
|
|
38
|
+
this.title = title;
|
|
39
|
+
this.publicationDate = publicationDate;
|
|
40
|
+
this.documentDate = documentDate;
|
|
41
|
+
this.meta = meta ?? null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get fullTitle() {
|
|
45
|
+
return `${this.legislationType?.label ?? ''} ${this.title ?? ''}`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const isBesluitLegalDocument = (legalDocument: LegalDocument) =>
|
|
50
|
+
isBesluitType(legalDocument.legislationType?.value ?? '');
|
|
51
|
+
|
|
52
|
+
export const fetchLegalDocumentsCache = new Map<
|
|
53
|
+
string,
|
|
54
|
+
LegalDocumentsCollection
|
|
55
|
+
>();
|
|
56
|
+
|
|
57
|
+
export interface LegalDocumentsCollection {
|
|
58
|
+
totalCount: number;
|
|
59
|
+
legalDocuments: LegalDocument[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface LegalDocumentsQueryFilter {
|
|
63
|
+
type: string;
|
|
64
|
+
documentDateFrom?: Option<string>;
|
|
65
|
+
documentDateTo?: Option<string>;
|
|
66
|
+
publicationDateFrom?: Option<string>;
|
|
67
|
+
publicationDateTo?: Option<string>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export type LegalDocumentsQueryConfig = {
|
|
71
|
+
endpoint: string;
|
|
72
|
+
decisionsEndpoint?: string;
|
|
73
|
+
abortSignal?: AbortSignal;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export async function fetchLegalDocuments({
|
|
77
|
+
words,
|
|
78
|
+
filter,
|
|
79
|
+
pageNumber = 0,
|
|
80
|
+
pageSize = 5,
|
|
81
|
+
config,
|
|
82
|
+
}: {
|
|
83
|
+
words: string[];
|
|
84
|
+
filter: LegalDocumentsQueryFilter;
|
|
85
|
+
pageNumber: number;
|
|
86
|
+
pageSize: number;
|
|
87
|
+
config: LegalDocumentsQueryConfig;
|
|
88
|
+
}) {
|
|
89
|
+
//This is silly, but null != undefined, so we have to be careful and include the correct value here
|
|
90
|
+
//Also, reconstruct the whole filter object to always have the same ordering, hopefully.
|
|
91
|
+
filter = {
|
|
92
|
+
type: filter.type,
|
|
93
|
+
documentDateFrom: filter.documentDateFrom || null,
|
|
94
|
+
documentDateTo: filter.documentDateTo || null,
|
|
95
|
+
publicationDateFrom: filter.publicationDateFrom || null,
|
|
96
|
+
publicationDateTo: filter.publicationDateTo || null,
|
|
97
|
+
};
|
|
98
|
+
const stringArguments = JSON.stringify({
|
|
99
|
+
words,
|
|
100
|
+
filter,
|
|
101
|
+
pageNumber,
|
|
102
|
+
pageSize,
|
|
103
|
+
});
|
|
104
|
+
const results = fetchLegalDocumentsCache.get(stringArguments);
|
|
105
|
+
|
|
106
|
+
if (results) {
|
|
107
|
+
return results;
|
|
108
|
+
} else {
|
|
109
|
+
const shouldQueryPublicDecisions =
|
|
110
|
+
isBesluitType(filter.type) && config.decisionsEndpoint;
|
|
111
|
+
|
|
112
|
+
const fetchConfig = {
|
|
113
|
+
words,
|
|
114
|
+
filter,
|
|
115
|
+
pageNumber,
|
|
116
|
+
pageSize,
|
|
117
|
+
config: {
|
|
118
|
+
endpoint: shouldQueryPublicDecisions
|
|
119
|
+
? (config.decisionsEndpoint as string)
|
|
120
|
+
: config.endpoint,
|
|
121
|
+
abortSignal: config.abortSignal,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const newResults = shouldQueryPublicDecisions
|
|
126
|
+
? await fetchPublicDecisions(fetchConfig)
|
|
127
|
+
: await fetchVlaamseCodexLegalDocuments(fetchConfig);
|
|
128
|
+
|
|
129
|
+
fetchLegalDocumentsCache.set(stringArguments, newResults);
|
|
130
|
+
|
|
131
|
+
return newResults;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isBlank } from '@ember/utils';
|
|
2
|
-
import { LEGISLATION_TYPES } from './
|
|
2
|
+
import { isLegislationType, LEGISLATION_TYPES } from './types';
|
|
3
3
|
import { unwrap } from '@lblod/ember-rdfa-editor-lblod-plugins/utils/option';
|
|
4
4
|
|
|
5
5
|
const STOP_WORDS = ['het', 'de', 'van', 'tot', 'dat'];
|
|
@@ -28,6 +28,44 @@ interface ProcessedMatch {
|
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
const getTypeLabel = (
|
|
32
|
+
type: string,
|
|
33
|
+
): keyof typeof LEGISLATION_TYPES | string => {
|
|
34
|
+
switch (true) {
|
|
35
|
+
case /\w+decreet/i.test(type):
|
|
36
|
+
case /decreet/i.test(type):
|
|
37
|
+
return 'decreet';
|
|
38
|
+
case /\w+wetboek/i.test(type):
|
|
39
|
+
case /wetboek/i.test(type):
|
|
40
|
+
return 'wetboek';
|
|
41
|
+
case /geco[oö]rdineerde[^\S\n]wet(ten)?/i.test(type):
|
|
42
|
+
return 'gecoördineerde wetten';
|
|
43
|
+
case /grondwets?wijziging/i.test(type):
|
|
44
|
+
return 'grondwetswijziging';
|
|
45
|
+
case /grondwet/i.test(type):
|
|
46
|
+
return 'grondwet';
|
|
47
|
+
case /bijzondere[^\S\n]wet/i.test(type):
|
|
48
|
+
return 'bijzondere wet';
|
|
49
|
+
case /\w+wet/i.test(type):
|
|
50
|
+
case /wet/i.test(type):
|
|
51
|
+
return 'wet';
|
|
52
|
+
default:
|
|
53
|
+
return type.toLowerCase().trim();
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const getAugmentedSearchTerms = (type: string, searchTerms: string) => {
|
|
58
|
+
if (
|
|
59
|
+
/\w+decreet/i.test(type) ||
|
|
60
|
+
/\w+wetboek/i.test(type) ||
|
|
61
|
+
/\w+wet/i.test(type)
|
|
62
|
+
) {
|
|
63
|
+
return `${type} ${searchTerms}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return searchTerms;
|
|
67
|
+
};
|
|
68
|
+
|
|
31
69
|
export default function processMatch(
|
|
32
70
|
match: RegexpMatchArrayWithIndices,
|
|
33
71
|
): ProcessedMatch | null {
|
|
@@ -40,45 +78,23 @@ export default function processMatch(
|
|
|
40
78
|
if (!type || !searchTerms) {
|
|
41
79
|
return null;
|
|
42
80
|
}
|
|
43
|
-
|
|
81
|
+
|
|
82
|
+
const cleanedSearchTerms = cleanupText(searchTerms)
|
|
44
83
|
.split(SPACES_REGEX)
|
|
45
84
|
.filter(
|
|
46
85
|
(word) => !isBlank(word) && word.length > 3 && !STOP_WORDS.includes(word),
|
|
47
86
|
)
|
|
48
87
|
.join(' ');
|
|
88
|
+
|
|
49
89
|
if (type) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} else if (/\w+wetboek/i.test(type)) {
|
|
57
|
-
typeLabel = 'wetboek';
|
|
58
|
-
cleanedSearchTerms = `${type} ${cleanedSearchTerms}`;
|
|
59
|
-
} else if (/wetboek/i.test(type)) {
|
|
60
|
-
typeLabel = 'wetboek';
|
|
61
|
-
} else if (/geco[oö]rdineerde[^\S\n]wet(ten)?/i.test(type)) {
|
|
62
|
-
typeLabel = 'gecoördineerde wetten';
|
|
63
|
-
} else if (/grondwets?wijziging/i.test(type)) {
|
|
64
|
-
typeLabel = 'grondwetswijziging';
|
|
65
|
-
} else if (/grondwet/i.test(type)) {
|
|
66
|
-
typeLabel = 'grondwet';
|
|
67
|
-
} else if (/bijzondere[^\S\n]wet/i.test(type)) {
|
|
68
|
-
typeLabel = 'bijzondere wet';
|
|
69
|
-
} else if (/\w+wet/i.test(type)) {
|
|
70
|
-
typeLabel = 'wet';
|
|
71
|
-
cleanedSearchTerms = `${type} ${cleanedSearchTerms}`;
|
|
72
|
-
} else if (/wet/i.test(type)) {
|
|
73
|
-
typeLabel = 'wet';
|
|
74
|
-
} else {
|
|
75
|
-
typeLabel = type.toLowerCase().trim();
|
|
76
|
-
}
|
|
77
|
-
const typeUri =
|
|
78
|
-
(LEGISLATION_TYPES as Record<string, string>)[typeLabel] ||
|
|
79
|
-
LEGISLATION_TYPES['decreet'];
|
|
90
|
+
const typeLabel = getTypeLabel(type);
|
|
91
|
+
|
|
92
|
+
const typeUri = isLegislationType(typeLabel)
|
|
93
|
+
? LEGISLATION_TYPES[typeLabel]
|
|
94
|
+
: LEGISLATION_TYPES['decreet'];
|
|
95
|
+
|
|
80
96
|
return {
|
|
81
|
-
text: cleanedSearchTerms,
|
|
97
|
+
text: getAugmentedSearchTerms(type, cleanedSearchTerms),
|
|
82
98
|
legislationTypeUri: typeUri,
|
|
83
99
|
typeMatch: {
|
|
84
100
|
text: type,
|
|
@@ -91,18 +107,18 @@ export default function processMatch(
|
|
|
91
107
|
end: unwrap(match.indices.groups['searchTerms'])[1],
|
|
92
108
|
},
|
|
93
109
|
};
|
|
94
|
-
} else {
|
|
95
|
-
return {
|
|
96
|
-
text: cleanedSearchTerms,
|
|
97
|
-
legislationTypeUri: LEGISLATION_TYPES['decreet'],
|
|
98
|
-
typeMatch: null,
|
|
99
|
-
searchTextMatch: {
|
|
100
|
-
text: searchTerms,
|
|
101
|
-
start: unwrap(match.indices.groups['searchTerms'])[0],
|
|
102
|
-
end: unwrap(match.indices.groups['searchTerms'])[1],
|
|
103
|
-
},
|
|
104
|
-
};
|
|
105
110
|
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
text: cleanedSearchTerms,
|
|
114
|
+
legislationTypeUri: LEGISLATION_TYPES['decreet'],
|
|
115
|
+
typeMatch: null,
|
|
116
|
+
searchTextMatch: {
|
|
117
|
+
text: searchTerms,
|
|
118
|
+
start: unwrap(match.indices.groups['searchTerms'])[0],
|
|
119
|
+
end: unwrap(match.indices.groups['searchTerms'])[1],
|
|
120
|
+
},
|
|
121
|
+
};
|
|
106
122
|
}
|
|
107
123
|
|
|
108
124
|
function cleanupText(text?: string): string {
|