esgvoc 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl
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.
Potentially problematic release.
This version of esgvoc might be problematic. Click here for more details.
- esgvoc/__init__.py +3 -1
- esgvoc/api/__init__.py +23 -34
- esgvoc/api/_utils.py +28 -14
- esgvoc/api/data_descriptors/__init__.py +18 -12
- esgvoc/api/data_descriptors/activity.py +8 -45
- esgvoc/api/data_descriptors/area_label.py +6 -0
- esgvoc/api/data_descriptors/branded_suffix.py +5 -0
- esgvoc/api/data_descriptors/branded_variable.py +5 -0
- esgvoc/api/data_descriptors/consortium.py +16 -56
- esgvoc/api/data_descriptors/data_descriptor.py +106 -0
- esgvoc/api/data_descriptors/date.py +3 -46
- esgvoc/api/data_descriptors/directory_date.py +3 -46
- esgvoc/api/data_descriptors/experiment.py +19 -54
- esgvoc/api/data_descriptors/forcing_index.py +3 -45
- esgvoc/api/data_descriptors/frequency.py +6 -43
- esgvoc/api/data_descriptors/grid_label.py +6 -44
- esgvoc/api/data_descriptors/horizontal_label.py +6 -0
- esgvoc/api/data_descriptors/initialisation_index.py +3 -44
- esgvoc/api/data_descriptors/institution.py +11 -54
- esgvoc/api/data_descriptors/license.py +4 -44
- esgvoc/api/data_descriptors/mip_era.py +6 -44
- esgvoc/api/data_descriptors/model_component.py +7 -45
- esgvoc/api/data_descriptors/organisation.py +3 -40
- esgvoc/api/data_descriptors/physic_index.py +3 -45
- esgvoc/api/data_descriptors/product.py +4 -43
- esgvoc/api/data_descriptors/realisation_index.py +3 -44
- esgvoc/api/data_descriptors/realm.py +4 -42
- esgvoc/api/data_descriptors/resolution.py +6 -44
- esgvoc/api/data_descriptors/source.py +18 -53
- esgvoc/api/data_descriptors/source_type.py +3 -41
- esgvoc/api/data_descriptors/sub_experiment.py +3 -41
- esgvoc/api/data_descriptors/table.py +6 -48
- esgvoc/api/data_descriptors/temporal_label.py +6 -0
- esgvoc/api/data_descriptors/time_range.py +3 -27
- esgvoc/api/data_descriptors/variable.py +13 -71
- esgvoc/api/data_descriptors/variant_label.py +3 -47
- esgvoc/api/data_descriptors/vertical_label.py +5 -0
- esgvoc/api/projects.py +187 -171
- esgvoc/api/report.py +21 -12
- esgvoc/api/search.py +3 -1
- esgvoc/api/universe.py +44 -34
- esgvoc/apps/__init__.py +3 -4
- esgvoc/apps/drs/generator.py +166 -161
- esgvoc/apps/drs/report.py +222 -131
- esgvoc/apps/drs/validator.py +103 -105
- esgvoc/cli/drs.py +29 -19
- esgvoc/cli/get.py +26 -25
- esgvoc/cli/install.py +11 -8
- esgvoc/cli/main.py +0 -2
- esgvoc/cli/status.py +5 -5
- esgvoc/cli/valid.py +40 -40
- esgvoc/core/db/models/universe.py +3 -3
- esgvoc/core/db/project_ingestion.py +1 -1
- esgvoc/core/db/universe_ingestion.py +6 -5
- esgvoc/core/logging_handler.py +1 -1
- esgvoc/core/repo_fetcher.py +4 -3
- esgvoc/core/service/__init__.py +37 -5
- esgvoc/core/service/configuration/config_manager.py +188 -0
- esgvoc/core/service/configuration/setting.py +88 -0
- esgvoc/core/service/state.py +49 -32
- {esgvoc-0.2.1.dist-info → esgvoc-0.3.0.dist-info}/METADATA +34 -3
- esgvoc-0.3.0.dist-info/RECORD +78 -0
- esgvoc/cli/config.py +0 -82
- esgvoc/core/service/settings.py +0 -73
- esgvoc/core/service/settings.toml +0 -17
- esgvoc/core/service/settings_default.toml +0 -17
- esgvoc-0.2.1.dist-info/RECORD +0 -73
- {esgvoc-0.2.1.dist-info → esgvoc-0.3.0.dist-info}/WHEEL +0 -0
- {esgvoc-0.2.1.dist-info → esgvoc-0.3.0.dist-info}/entry_points.txt +0 -0
- {esgvoc-0.2.1.dist-info → esgvoc-0.3.0.dist-info}/licenses/LICENSE.txt +0 -0
esgvoc/apps/drs/report.py
CHANGED
|
@@ -1,14 +1,48 @@
|
|
|
1
|
-
from pydantic import BaseModel, computed_field
|
|
2
1
|
from abc import ABC, abstractmethod
|
|
3
|
-
from
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import (Annotated, Any, ClassVar, Iterable, Literal, Mapping,
|
|
4
|
+
Protocol)
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field, computed_field
|
|
7
|
+
|
|
4
8
|
from esgvoc.api.project_specs import DrsType
|
|
5
9
|
|
|
6
10
|
|
|
7
|
-
class
|
|
11
|
+
class IssueKind(str, Enum):
|
|
12
|
+
"""
|
|
13
|
+
The kinds of validation and generation issues.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
SPACE = 'Space'
|
|
17
|
+
"""Represents a problem of unnecessary space[s] at the beginning or end of the DRS expression."""
|
|
18
|
+
UNPARSABLE = 'Unparsable'
|
|
19
|
+
"""Represents a problem of non-compliance of the DRS expression."""
|
|
20
|
+
EXTRA_SEPARATOR = 'ExtraSeparator'
|
|
21
|
+
"""Represents a problem of multiple separator occurrences in the DRS expression."""
|
|
22
|
+
EXTRA_CHAR = 'ExtraChar'
|
|
23
|
+
"""Represents a problem of extra characters at the end of the DRS expression."""
|
|
24
|
+
BLANK_TERM = 'BlankTerm'
|
|
25
|
+
"""Represents a problem of blank term in the DRS expression (i.e., space[s] surrounded by separators)."""
|
|
26
|
+
FILE_NAME = 'FileNameExtensionIssue'
|
|
27
|
+
"""Represents a problem on the given file name extension (missing or not compliant)."""
|
|
28
|
+
INVALID_TERM = 'InvalidTerm'
|
|
29
|
+
"""Represents a problem of invalid term against a collection or a constant part of a DRS specification."""
|
|
30
|
+
EXTRA_TERM = 'ExtraTerm'
|
|
31
|
+
"""Represents a problem of extra term at the end of the given DRS expression."""
|
|
32
|
+
MISSING_TERM = 'MissingTerm'
|
|
33
|
+
"""Represents a problem of missing term for a collection part of the DRS specification."""
|
|
34
|
+
TOO_MANY = 'TooManyTermsCollection'
|
|
35
|
+
"""Represents a problem while inferring a mapping: one term is able to match a collection"""
|
|
36
|
+
CONFLICT = 'ConflictingCollections'
|
|
37
|
+
"""Represents a problem while inferring a mapping: collections shares the very same terms"""
|
|
38
|
+
ASSIGNED = 'AssignedTerm'
|
|
39
|
+
"""Represents a decision of the Generator to assign a term to a collection, that may not be."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ParsingIssueVisitor(Protocol):
|
|
8
43
|
"""
|
|
9
|
-
Specifications for a
|
|
44
|
+
Specifications for a parsing issues visitor.
|
|
10
45
|
"""
|
|
11
|
-
|
|
12
46
|
def visit_space_issue(self, issue: "Space") -> Any:
|
|
13
47
|
"""Visit a space issue."""
|
|
14
48
|
pass
|
|
@@ -21,57 +55,61 @@ class ParserIssueVisitor(Protocol):
|
|
|
21
55
|
def visit_extra_char_issue(self, issue: "ExtraChar") -> Any:
|
|
22
56
|
"""Visit an extra char issue."""
|
|
23
57
|
pass
|
|
24
|
-
def
|
|
25
|
-
"""Visit a blank
|
|
58
|
+
def visit_blank_term_issue(self, issue: "BlankTerm") -> Any:
|
|
59
|
+
"""Visit a blank term issue."""
|
|
26
60
|
pass
|
|
27
61
|
|
|
28
62
|
|
|
29
|
-
class
|
|
63
|
+
class ComplianceIssueVisitor(Protocol):
|
|
30
64
|
"""
|
|
31
|
-
Specifications for a
|
|
65
|
+
Specifications for a compliance issues visitor.
|
|
32
66
|
"""
|
|
33
|
-
|
|
34
67
|
def visit_filename_extension_issue(self, issue: "FileNameExtensionIssue") -> Any:
|
|
35
68
|
"""Visit a file name extension issue."""
|
|
36
69
|
pass
|
|
37
|
-
def
|
|
38
|
-
"""Visit an invalid
|
|
70
|
+
def visit_invalid_term_issue(self, issue: "InvalidTerm") -> Any:
|
|
71
|
+
"""Visit an invalid term issue."""
|
|
39
72
|
pass
|
|
40
|
-
def
|
|
41
|
-
"""Visit an extra
|
|
73
|
+
def visit_extra_term_issue(self, issue: "ExtraTerm") -> Any:
|
|
74
|
+
"""Visit an extra term issue."""
|
|
42
75
|
pass
|
|
43
|
-
def
|
|
44
|
-
"""Visit a missing
|
|
76
|
+
def visit_missing_term_issue(self, issue: "MissingTerm") -> Any:
|
|
77
|
+
"""Visit a missing term issue."""
|
|
45
78
|
pass
|
|
46
79
|
|
|
47
80
|
|
|
48
|
-
class
|
|
81
|
+
class ValidationIssueVisitor(ParsingIssueVisitor, ComplianceIssueVisitor):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class GenerationIssueVisitor(Protocol):
|
|
49
86
|
"""
|
|
50
87
|
Specifications for a generator issues visitor.
|
|
51
88
|
"""
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"""Visit an invalid token issue."""
|
|
89
|
+
def visit_invalid_term_issue(self, issue: "InvalidTerm") -> Any:
|
|
90
|
+
"""Visit an invalid term issue."""
|
|
55
91
|
pass
|
|
56
|
-
def
|
|
57
|
-
"""Visit a missing
|
|
92
|
+
def visit_missing_term_issue(self, issue: "MissingTerm") -> Any:
|
|
93
|
+
"""Visit a missing term issue."""
|
|
58
94
|
pass
|
|
59
|
-
def
|
|
60
|
-
"""Visit a too many
|
|
95
|
+
def visit_too_many_terms_collection_issue(self, issue: "TooManyTermCollection") -> Any:
|
|
96
|
+
"""Visit a too many terms collection issue."""
|
|
61
97
|
pass
|
|
62
98
|
def visit_conflicting_collections_issue(self, issue: "ConflictingCollections") -> Any:
|
|
63
99
|
"""Visit a conflicting collections issue."""
|
|
64
100
|
pass
|
|
65
|
-
def
|
|
66
|
-
"""Visit an assign
|
|
101
|
+
def visit_assign_term_issue(self, issue: "AssignedTerm") -> Any:
|
|
102
|
+
"""Visit an assign term issue."""
|
|
67
103
|
pass
|
|
68
104
|
|
|
69
105
|
|
|
70
106
|
class DrsIssue(BaseModel, ABC):
|
|
107
|
+
kind: str
|
|
108
|
+
"""The class name of the issue for JSON serialization/deserialization."""
|
|
109
|
+
|
|
71
110
|
"""
|
|
72
111
|
Generic class for all the DRS issues.
|
|
73
112
|
"""
|
|
74
|
-
|
|
75
113
|
@abstractmethod
|
|
76
114
|
def accept(self, visitor) -> Any:
|
|
77
115
|
"""
|
|
@@ -84,284 +122,316 @@ class DrsIssue(BaseModel, ABC):
|
|
|
84
122
|
pass
|
|
85
123
|
|
|
86
124
|
|
|
87
|
-
class
|
|
125
|
+
class ParsingIssue(DrsIssue):
|
|
88
126
|
"""
|
|
89
|
-
Generic class for the DRS
|
|
127
|
+
Generic class for the DRS parsing issues.
|
|
90
128
|
"""
|
|
91
129
|
column: int|None = None
|
|
92
|
-
"""the column of faulty characters"""
|
|
130
|
+
"""the column of faulty characters."""
|
|
93
131
|
|
|
94
132
|
@abstractmethod
|
|
95
|
-
def accept(self, visitor:
|
|
133
|
+
def accept(self, visitor: ParsingIssueVisitor) -> Any:
|
|
96
134
|
"""
|
|
97
|
-
Accept an DRS
|
|
135
|
+
Accept an DRS parsing issue visitor.
|
|
98
136
|
|
|
99
|
-
:param visitor: The DRS
|
|
100
|
-
:type visitor:
|
|
137
|
+
:param visitor: The DRS parsing issue visitor.
|
|
138
|
+
:type visitor: ParsingIssueVisitor
|
|
101
139
|
:return: Depending on the visitor.
|
|
102
140
|
:rtype: Any
|
|
103
141
|
"""
|
|
104
142
|
pass
|
|
105
143
|
|
|
106
|
-
|
|
144
|
+
|
|
145
|
+
class Space(ParsingIssue):
|
|
107
146
|
"""
|
|
108
147
|
Represents a problem of unnecessary space[s] at the beginning or end of the DRS expression.
|
|
109
148
|
Note: `column` is `None`.
|
|
110
149
|
"""
|
|
111
|
-
|
|
150
|
+
kind: Literal[IssueKind.SPACE] = IssueKind.SPACE
|
|
151
|
+
def accept(self, visitor: ParsingIssueVisitor) -> Any:
|
|
112
152
|
return visitor.visit_space_issue(self)
|
|
113
153
|
def __str__(self):
|
|
114
154
|
return "expression is surrounded by white space[s]"
|
|
115
155
|
def __repr__(self) -> str:
|
|
116
156
|
return self.__str__()
|
|
117
|
-
|
|
118
157
|
|
|
119
|
-
|
|
158
|
+
|
|
159
|
+
class Unparsable(ParsingIssue):
|
|
120
160
|
"""
|
|
121
161
|
Represents a problem of non-compliance of the DRS expression.
|
|
122
162
|
Note: `column` is `None`.
|
|
123
163
|
"""
|
|
124
164
|
expected_drs_type: DrsType
|
|
125
165
|
"""The expected DRS type of the expression (directory, file name or dataset id)."""
|
|
126
|
-
|
|
166
|
+
kind: Literal[IssueKind.UNPARSABLE] = IssueKind.UNPARSABLE
|
|
167
|
+
def accept(self, visitor: ParsingIssueVisitor) -> Any:
|
|
127
168
|
return visitor.visit_unparsable_issue(self)
|
|
128
169
|
def __str__(self):
|
|
129
170
|
return "unable to parse this expression"
|
|
130
171
|
def __repr__(self) -> str:
|
|
131
172
|
return self.__str__()
|
|
132
|
-
|
|
133
173
|
|
|
134
|
-
|
|
174
|
+
|
|
175
|
+
class ExtraSeparator(ParsingIssue):
|
|
135
176
|
"""
|
|
136
177
|
Represents a problem of multiple separator occurrences in the DRS expression.
|
|
137
178
|
"""
|
|
138
|
-
|
|
179
|
+
kind: Literal[IssueKind.EXTRA_SEPARATOR] = IssueKind.EXTRA_SEPARATOR
|
|
180
|
+
def accept(self, visitor: ParsingIssueVisitor) -> Any:
|
|
139
181
|
return visitor.visit_extra_separator_issue(self)
|
|
140
182
|
def __str__(self):
|
|
141
183
|
return f"extra separator(s) at column {self.column}"
|
|
142
184
|
def __repr__(self) -> str:
|
|
143
185
|
return self.__str__()
|
|
144
|
-
|
|
145
186
|
|
|
146
|
-
|
|
187
|
+
|
|
188
|
+
class ExtraChar(ParsingIssue):
|
|
147
189
|
"""
|
|
148
190
|
Represents a problem of extra characters at the end of the DRS expression.
|
|
149
191
|
"""
|
|
150
|
-
|
|
192
|
+
kind: Literal[IssueKind.EXTRA_CHAR] = IssueKind.EXTRA_CHAR
|
|
193
|
+
def accept(self, visitor: ParsingIssueVisitor) -> Any:
|
|
151
194
|
return visitor.visit_extra_char_issue(self)
|
|
152
195
|
def __str__(self):
|
|
153
196
|
return f"extra character(s) at column {self.column}"
|
|
154
197
|
def __repr__(self) -> str:
|
|
155
198
|
return self.__str__()
|
|
156
|
-
|
|
157
199
|
|
|
158
|
-
|
|
200
|
+
|
|
201
|
+
class BlankTerm(ParsingIssue):
|
|
159
202
|
"""
|
|
160
|
-
Represents a problem of blank
|
|
203
|
+
Represents a problem of blank term in the DRS expression (i.e., space[s] surrounded by separators).
|
|
161
204
|
"""
|
|
162
|
-
|
|
163
|
-
|
|
205
|
+
kind: Literal[IssueKind.BLANK_TERM] = IssueKind.BLANK_TERM
|
|
206
|
+
def accept(self, visitor: ParsingIssueVisitor) -> Any:
|
|
207
|
+
return visitor.visit_blank_term_issue(self)
|
|
164
208
|
def __str__(self):
|
|
165
|
-
return f"blank
|
|
209
|
+
return f"blank term at column {self.column}"
|
|
166
210
|
def __repr__(self) -> str:
|
|
167
211
|
return self.__str__()
|
|
168
212
|
|
|
169
213
|
|
|
170
|
-
class
|
|
214
|
+
class ComplianceIssue(DrsIssue):
|
|
171
215
|
"""
|
|
172
|
-
Generic class for the
|
|
216
|
+
Generic class for the compliance issues.
|
|
173
217
|
"""
|
|
174
218
|
@abstractmethod
|
|
175
|
-
def accept(self, visitor:
|
|
219
|
+
def accept(self, visitor: ComplianceIssueVisitor) -> Any:
|
|
176
220
|
"""
|
|
177
|
-
Accept an DRS
|
|
221
|
+
Accept an DRS compliance issue visitor.
|
|
178
222
|
|
|
179
|
-
:param visitor: The DRS
|
|
180
|
-
:type visitor:
|
|
223
|
+
:param visitor: The DRS compliance issue visitor.
|
|
224
|
+
:type visitor: ComplianceIssueVisitor
|
|
181
225
|
:return: Depending on the visitor.
|
|
182
226
|
:rtype: Any
|
|
183
227
|
"""
|
|
184
228
|
pass
|
|
185
229
|
|
|
186
230
|
|
|
187
|
-
class FileNameExtensionIssue(
|
|
231
|
+
class FileNameExtensionIssue(ComplianceIssue):
|
|
188
232
|
"""
|
|
189
233
|
Represents a problem on the given file name extension (missing or not compliant).
|
|
190
234
|
"""
|
|
191
235
|
expected_extension: str
|
|
192
236
|
"""The expected file name extension."""
|
|
193
|
-
|
|
237
|
+
kind: Literal[IssueKind.FILE_NAME] = IssueKind.FILE_NAME
|
|
238
|
+
def accept(self, visitor: ComplianceIssueVisitor) -> Any:
|
|
194
239
|
return visitor.visit_filename_extension_issue(self)
|
|
195
240
|
def __str__(self):
|
|
196
241
|
return f"filename extension missing or not compliant with '{self.expected_extension}'"
|
|
197
|
-
|
|
198
242
|
|
|
199
|
-
|
|
243
|
+
|
|
244
|
+
class TermIssue(ComplianceIssue):
|
|
200
245
|
"""
|
|
201
|
-
Generic class for the DRS
|
|
246
|
+
Generic class for the DRS term issues.
|
|
202
247
|
"""
|
|
203
|
-
|
|
204
|
-
"""The faulty
|
|
205
|
-
|
|
206
|
-
"""The position of the faulty
|
|
248
|
+
term: str
|
|
249
|
+
"""The faulty term."""
|
|
250
|
+
term_position: int
|
|
251
|
+
"""The position of the faulty term (the part position, not the column of the characters."""
|
|
207
252
|
|
|
208
253
|
|
|
209
|
-
class
|
|
254
|
+
class GenerationIssue(DrsIssue):
|
|
210
255
|
"""
|
|
211
|
-
Generic class for the DRS
|
|
256
|
+
Generic class for the DRS generation issues.
|
|
212
257
|
"""
|
|
213
258
|
@abstractmethod
|
|
214
|
-
def accept(self, visitor:
|
|
259
|
+
def accept(self, visitor: GenerationIssueVisitor) -> Any:
|
|
215
260
|
"""
|
|
216
|
-
Accept an DRS
|
|
261
|
+
Accept an DRS generation issue visitor.
|
|
217
262
|
|
|
218
|
-
:param visitor: The DRS
|
|
219
|
-
:type visitor:
|
|
263
|
+
:param visitor: The DRS generation issue visitor.
|
|
264
|
+
:type visitor: GenerationIssueVisitor
|
|
220
265
|
:return: Depending on the visitor.
|
|
221
266
|
:rtype: Any
|
|
222
267
|
"""
|
|
223
268
|
pass
|
|
224
269
|
|
|
225
270
|
|
|
226
|
-
class
|
|
271
|
+
class InvalidTerm(TermIssue, GenerationIssue):
|
|
227
272
|
"""
|
|
228
|
-
Represents a problem of invalid
|
|
273
|
+
Represents a problem of invalid term against a collection or a constant part of a DRS specification.
|
|
229
274
|
"""
|
|
230
275
|
collection_id_or_constant_value: str
|
|
231
276
|
"""The collection id or the constant part of a DRS specification."""
|
|
232
|
-
|
|
233
|
-
|
|
277
|
+
kind: Literal[IssueKind.INVALID_TERM] = IssueKind.INVALID_TERM
|
|
278
|
+
def accept(self, visitor: ComplianceIssueVisitor|GenerationIssueVisitor) -> Any:
|
|
279
|
+
return visitor.visit_invalid_term_issue(self)
|
|
234
280
|
def __str__(self):
|
|
235
|
-
return f"
|
|
281
|
+
return f"term '{self.term}' not compliant with {self.collection_id_or_constant_value} at position {self.term_position}"
|
|
236
282
|
def __repr__(self) -> str:
|
|
237
283
|
return self.__str__()
|
|
238
|
-
|
|
239
284
|
|
|
240
|
-
|
|
285
|
+
|
|
286
|
+
class ExtraTerm(TermIssue):
|
|
241
287
|
"""
|
|
242
|
-
Represents a problem of extra
|
|
243
|
-
All part of the DRS specification have been processed and this
|
|
244
|
-
(`collection_id` is `None`) or it has been invalidated by an optional collection part
|
|
288
|
+
Represents a problem of extra term at the end of the given DRS expression.
|
|
289
|
+
All part of the DRS specification have been processed and this term is not necessary
|
|
290
|
+
(`collection_id` is `None`) or it has been invalidated by an optional collection part
|
|
245
291
|
of the DRS specification (`collection_id` is set).
|
|
246
292
|
"""
|
|
247
293
|
collection_id: str|None
|
|
248
|
-
"""The optional collection id or `None
|
|
249
|
-
|
|
250
|
-
|
|
294
|
+
"""The optional collection id or `None`."""
|
|
295
|
+
kind: Literal[IssueKind.EXTRA_TERM] = IssueKind.EXTRA_TERM
|
|
296
|
+
def accept(self, visitor: ComplianceIssueVisitor) -> Any:
|
|
297
|
+
return visitor.visit_extra_term_issue(self)
|
|
251
298
|
def __str__(self):
|
|
252
|
-
repr = f"extra
|
|
299
|
+
repr = f"extra term {self.term}"
|
|
253
300
|
if self.collection_id:
|
|
254
301
|
repr += f" invalidated by the optional collection {self.collection_id}"
|
|
255
|
-
return repr + f" at position {self.
|
|
302
|
+
return repr + f" at position {self.term_position}"
|
|
256
303
|
def __repr__(self) -> str:
|
|
257
304
|
return self.__str__()
|
|
258
|
-
|
|
259
305
|
|
|
260
|
-
|
|
306
|
+
|
|
307
|
+
class MissingTerm(ComplianceIssue, GenerationIssue):
|
|
261
308
|
"""
|
|
262
|
-
Represents a problem of missing
|
|
309
|
+
Represents a problem of missing term for a collection part of the DRS specification.
|
|
263
310
|
"""
|
|
264
311
|
collection_id: str
|
|
265
312
|
"""The collection id."""
|
|
266
313
|
collection_position: int
|
|
267
314
|
"""The collection part position (not the column of the characters)."""
|
|
268
|
-
|
|
269
|
-
|
|
315
|
+
kind: Literal[IssueKind.MISSING_TERM] = IssueKind.MISSING_TERM
|
|
316
|
+
def accept(self, visitor: ComplianceIssueVisitor|GenerationIssueVisitor) -> Any:
|
|
317
|
+
return visitor.visit_missing_term_issue(self)
|
|
270
318
|
def __str__(self):
|
|
271
|
-
return f'missing
|
|
319
|
+
return f'missing term for {self.collection_id} at position {self.collection_position}'
|
|
272
320
|
def __repr__(self) -> str:
|
|
273
321
|
return self.__str__()
|
|
274
|
-
|
|
275
322
|
|
|
276
|
-
|
|
323
|
+
|
|
324
|
+
class TooManyTermCollection(GenerationIssue):
|
|
277
325
|
"""
|
|
278
|
-
Represents a problem while inferring a mapping collection -
|
|
279
|
-
of a DRS expression based on a bag of
|
|
280
|
-
is able to match this collection. The generator is unable to choose from these
|
|
326
|
+
Represents a problem while inferring a mapping collection - term in the generation
|
|
327
|
+
of a DRS expression based on a bag of terms. The problem is that more than one term
|
|
328
|
+
is able to match this collection. The generator is unable to choose from these terms
|
|
281
329
|
"""
|
|
282
330
|
collection_id: str
|
|
283
331
|
"""The collection id."""
|
|
284
|
-
|
|
285
|
-
"""The faulty
|
|
286
|
-
|
|
287
|
-
|
|
332
|
+
terms: list[str]
|
|
333
|
+
"""The faulty terms."""
|
|
334
|
+
kind: Literal[IssueKind.TOO_MANY] = IssueKind.TOO_MANY
|
|
335
|
+
def accept(self, visitor: GenerationIssueVisitor) -> Any:
|
|
336
|
+
return visitor.visit_too_many_terms_collection_issue(self)
|
|
288
337
|
|
|
289
338
|
def __str__(self):
|
|
290
|
-
|
|
291
|
-
result = f'collection {self.collection_id} has more than one
|
|
339
|
+
terms_str = ", ".join(term for term in self.terms)
|
|
340
|
+
result = f'collection {self.collection_id} has more than one term ({terms_str})'
|
|
292
341
|
return result
|
|
293
342
|
def __repr__(self) -> str:
|
|
294
343
|
return self.__str__()
|
|
295
344
|
|
|
296
345
|
|
|
297
|
-
class ConflictingCollections(
|
|
346
|
+
class ConflictingCollections(GenerationIssue):
|
|
298
347
|
"""
|
|
299
|
-
Represents a problem while inferring a mapping collection -
|
|
300
|
-
of a DRS expression based on a bag of
|
|
301
|
-
very same
|
|
348
|
+
Represents a problem while inferring a mapping collection - term in the generation
|
|
349
|
+
of a DRS expression based on a bag of terms. The problem is that these collections shares the
|
|
350
|
+
very same terms. The generator is unable to choose which term for which collection.
|
|
302
351
|
"""
|
|
303
352
|
collection_ids: list[str]
|
|
304
353
|
"""The ids of the collections."""
|
|
305
|
-
|
|
306
|
-
"""The shared
|
|
307
|
-
|
|
354
|
+
terms: list[str]
|
|
355
|
+
"""The shared terms."""
|
|
356
|
+
kind: Literal[IssueKind.CONFLICT] = IssueKind.CONFLICT
|
|
357
|
+
def accept(self, visitor: GenerationIssueVisitor) -> Any:
|
|
308
358
|
return visitor.visit_conflicting_collections_issue(self)
|
|
309
359
|
def __str__(self):
|
|
310
360
|
collection_ids_str = ", ".join(collection_id for collection_id in self.collection_ids)
|
|
311
|
-
|
|
312
|
-
result = f"collections {collection_ids_str} are competing for the same
|
|
361
|
+
terms_str = ", ".join(term for term in self.terms)
|
|
362
|
+
result = f"collections {collection_ids_str} are competing for the same term(s) {terms_str}"
|
|
313
363
|
return result
|
|
314
364
|
def __repr__(self) -> str:
|
|
315
365
|
return self.__str__()
|
|
316
366
|
|
|
317
367
|
|
|
318
|
-
class
|
|
368
|
+
class AssignedTerm(GenerationIssue):
|
|
319
369
|
"""
|
|
320
|
-
Represents a decision of the Generator to assign this
|
|
370
|
+
Represents a decision of the Generator to assign this term to the collection, that may not be.
|
|
321
371
|
relevant.
|
|
322
372
|
"""
|
|
323
373
|
collection_id: str
|
|
324
374
|
"""The collection id."""
|
|
325
|
-
|
|
326
|
-
"""The
|
|
327
|
-
|
|
328
|
-
|
|
375
|
+
term: str
|
|
376
|
+
"""The term."""
|
|
377
|
+
kind: Literal[IssueKind.ASSIGNED] = IssueKind.ASSIGNED
|
|
378
|
+
def accept(self, visitor: GenerationIssueVisitor) -> Any:
|
|
379
|
+
return visitor.visit_assign_term_issue(self)
|
|
329
380
|
def __str__(self):
|
|
330
|
-
result = f"assign
|
|
381
|
+
result = f"assign term {self.term} for collection {self.collection_id}"
|
|
331
382
|
return result
|
|
332
383
|
def __repr__(self) -> str:
|
|
333
384
|
return self.__str__()
|
|
334
385
|
|
|
335
386
|
|
|
387
|
+
GenerationError = Annotated[AssignedTerm | ConflictingCollections | InvalidTerm | MissingTerm | \
|
|
388
|
+
TooManyTermCollection, Field(discriminator='kind')]
|
|
389
|
+
GenerationWarning = Annotated[AssignedTerm | MissingTerm, Field(discriminator='kind')]
|
|
390
|
+
|
|
391
|
+
ValidationError = Annotated[BlankTerm | ExtraChar | ExtraSeparator | ExtraTerm | \
|
|
392
|
+
FileNameExtensionIssue | InvalidTerm | MissingTerm | Space | Unparsable,
|
|
393
|
+
Field(discriminator='kind')]
|
|
394
|
+
ValidationWarning = Annotated[ExtraSeparator | MissingTerm | Space, Field(discriminator='kind')]
|
|
395
|
+
|
|
396
|
+
|
|
336
397
|
class DrsReport(BaseModel):
|
|
337
398
|
"""
|
|
338
399
|
Generic DRS application report class.
|
|
339
400
|
"""
|
|
401
|
+
|
|
340
402
|
project_id: str
|
|
341
|
-
"""The project id associated to the result of the DRS application"""
|
|
403
|
+
"""The project id associated to the result of the DRS application."""
|
|
404
|
+
|
|
342
405
|
type: DrsType
|
|
343
406
|
"""The type of the DRS"""
|
|
344
|
-
|
|
407
|
+
|
|
408
|
+
errors: list
|
|
345
409
|
"""A list of DRS issues that are considered as errors."""
|
|
346
|
-
|
|
410
|
+
|
|
411
|
+
warnings: list
|
|
347
412
|
"""A list of DRS issues that are considered as warnings."""
|
|
413
|
+
|
|
348
414
|
@computed_field # type: ignore
|
|
349
415
|
@property
|
|
350
416
|
def nb_errors(self) -> int:
|
|
351
417
|
"""The number of errors."""
|
|
352
418
|
return len(self.errors) if self.errors else 0
|
|
419
|
+
|
|
353
420
|
@computed_field # type: ignore
|
|
354
421
|
@property
|
|
355
422
|
def nb_warnings(self) -> int:
|
|
356
423
|
"""The number of warnings."""
|
|
357
424
|
return len(self.warnings) if self.warnings else 0
|
|
425
|
+
|
|
358
426
|
@computed_field # type: ignore
|
|
359
427
|
@property
|
|
360
428
|
def validated(self) -> bool:
|
|
361
429
|
"""The correctness of the result of the DRS application."""
|
|
362
430
|
return False if self.errors else True
|
|
431
|
+
|
|
363
432
|
def __len__(self) -> int:
|
|
364
433
|
return self.nb_errors
|
|
434
|
+
|
|
365
435
|
def __bool__(self) -> bool:
|
|
366
436
|
return self.validated
|
|
367
437
|
|
|
@@ -370,32 +440,53 @@ class DrsValidationReport(DrsReport):
|
|
|
370
440
|
"""
|
|
371
441
|
The DRS validation report class.
|
|
372
442
|
"""
|
|
443
|
+
|
|
373
444
|
expression: str
|
|
374
|
-
"""The DRS expression been checked"""
|
|
445
|
+
"""The DRS expression been checked."""
|
|
446
|
+
|
|
447
|
+
errors: list[ValidationError]
|
|
448
|
+
"""A list of DRS parsing and compliance issues that are considered as errors."""
|
|
449
|
+
|
|
450
|
+
warnings: list[ValidationWarning]
|
|
451
|
+
"""A list of DRS parsing and compliance issues that are considered as warnings."""
|
|
452
|
+
|
|
375
453
|
def __str__(self) -> str:
|
|
376
454
|
return f"'{self.expression}' has {self.nb_errors} error(s) and " + \
|
|
377
455
|
f"{self.nb_warnings} warning(s)"
|
|
456
|
+
|
|
378
457
|
def __repr__(self) -> str:
|
|
379
458
|
return self.__str__()
|
|
380
459
|
|
|
381
460
|
|
|
382
|
-
class
|
|
461
|
+
class DrsGenerationReport(DrsReport):
|
|
383
462
|
"""
|
|
384
|
-
The DRS
|
|
463
|
+
The DRS generation report.
|
|
385
464
|
"""
|
|
465
|
+
|
|
386
466
|
MISSING_TAG: ClassVar[str] = '[MISSING]'
|
|
387
467
|
"""Tag used in the DRS generated expression to replace a missing term."""
|
|
468
|
+
|
|
388
469
|
INVALID_TAG: ClassVar[str] = '[INVALID]'
|
|
389
470
|
"""Tag used in the DRS generated expression to replace a invalid term."""
|
|
390
|
-
|
|
391
|
-
|
|
471
|
+
|
|
472
|
+
given_mapping_or_bag_of_terms: Mapping|Iterable
|
|
473
|
+
"""The mapping or the bag of terms given."""
|
|
474
|
+
|
|
392
475
|
mapping_used: Mapping
|
|
393
|
-
"""The mapping inferred from the given bag of
|
|
476
|
+
"""The mapping inferred from the given bag of terms (same mapping otherwise)."""
|
|
477
|
+
|
|
394
478
|
generated_drs_expression: str
|
|
395
|
-
"""The generated DRS expression with possible tags to replace missing or invalid
|
|
479
|
+
"""The generated DRS expression with possible tags to replace missing or invalid terms."""
|
|
480
|
+
|
|
481
|
+
errors: list[GenerationError]
|
|
482
|
+
"""A list of DRS generation issues that are considered as errors."""
|
|
483
|
+
|
|
484
|
+
warnings: list[GenerationWarning]
|
|
485
|
+
"""A list of DRS generation issues that are considered as warnings."""
|
|
486
|
+
|
|
396
487
|
def __str__(self) -> str:
|
|
397
488
|
return f"'{self.generated_drs_expression}' has {self.nb_errors} error(s) and " + \
|
|
398
489
|
f"{self.nb_warnings} warning(s)"
|
|
490
|
+
|
|
399
491
|
def __repr__(self) -> str:
|
|
400
492
|
return self.__str__()
|
|
401
|
-
|