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