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/validator.py
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
from typing import cast
|
|
2
|
-
|
|
3
|
-
DrsType,
|
|
4
|
-
DrsPart,
|
|
5
|
-
DrsSpecification,
|
|
6
|
-
DrsPartKind,
|
|
7
|
-
DrsCollection,
|
|
8
|
-
DrsConstant)
|
|
2
|
+
|
|
9
3
|
import esgvoc.api.projects as projects
|
|
10
4
|
import esgvoc.apps.drs.constants as constants
|
|
11
|
-
from esgvoc.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
5
|
+
from esgvoc.api.project_specs import (
|
|
6
|
+
DrsCollection,
|
|
7
|
+
DrsConstant,
|
|
8
|
+
DrsPart,
|
|
9
|
+
DrsPartKind,
|
|
10
|
+
DrsSpecification,
|
|
11
|
+
DrsType,
|
|
12
|
+
ProjectSpecs,
|
|
13
|
+
)
|
|
14
|
+
from esgvoc.apps.drs.report import (
|
|
15
|
+
BlankTerm,
|
|
16
|
+
ComplianceIssue,
|
|
17
|
+
DrsIssue,
|
|
18
|
+
DrsValidationReport,
|
|
19
|
+
ExtraChar,
|
|
20
|
+
ExtraSeparator,
|
|
21
|
+
ExtraTerm,
|
|
22
|
+
FileNameExtensionIssue,
|
|
23
|
+
InvalidTerm,
|
|
24
|
+
MissingTerm,
|
|
25
|
+
ParsingIssue,
|
|
26
|
+
Space,
|
|
27
|
+
Unparsable,
|
|
28
|
+
ValidationError,
|
|
29
|
+
ValidationWarning,
|
|
30
|
+
)
|
|
31
|
+
from esgvoc.core.exceptions import EsgvocDbError, EsgvocNotFoundError
|
|
24
32
|
|
|
25
33
|
|
|
26
34
|
class DrsApplication:
|
|
@@ -33,7 +41,9 @@ class DrsApplication:
|
|
|
33
41
|
"""The project id."""
|
|
34
42
|
self.pedantic: bool = pedantic
|
|
35
43
|
"""Same as the option of GCC: turn warnings into errors. Default False."""
|
|
36
|
-
project_specs: ProjectSpecs = projects.
|
|
44
|
+
project_specs: ProjectSpecs | None = projects.get_project(project_id)
|
|
45
|
+
if not project_specs:
|
|
46
|
+
raise EsgvocNotFoundError(f"unable to find project '{project_id}'")
|
|
37
47
|
for specs in project_specs.drs_specs:
|
|
38
48
|
match specs.type:
|
|
39
49
|
case DrsType.DIRECTORY:
|
|
@@ -46,7 +56,7 @@ class DrsApplication:
|
|
|
46
56
|
self.dataset_id_specs: DrsSpecification = specs
|
|
47
57
|
"""The DRS dataset id specs of the project."""
|
|
48
58
|
case _:
|
|
49
|
-
raise
|
|
59
|
+
raise EsgvocDbError(f"unsupported DRS specs type '{specs.type}'")
|
|
50
60
|
|
|
51
61
|
def _get_full_file_name_extension(self) -> str:
|
|
52
62
|
"""
|
|
@@ -61,38 +71,33 @@ class DrsApplication:
|
|
|
61
71
|
full_extension = specs.properties[constants.FILE_NAME_EXTENSION_SEPARATOR_KEY] + \
|
|
62
72
|
specs.properties[constants.FILE_NAME_EXTENSION_KEY]
|
|
63
73
|
else:
|
|
64
|
-
raise
|
|
65
|
-
|
|
74
|
+
raise EsgvocDbError('missing properties in the DRS file name specifications of the ' +
|
|
75
|
+
f"project '{self.project_id}'")
|
|
66
76
|
return full_extension
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
match drs_type:
|
|
70
|
-
case DrsType.DIRECTORY:
|
|
71
|
-
specs = self.directory_specs
|
|
72
|
-
case DrsType.FILE_NAME:
|
|
73
|
-
specs = self.file_name_specs
|
|
74
|
-
case DrsType.DATASET_ID:
|
|
75
|
-
specs = self.dataset_id_specs
|
|
76
|
-
case _:
|
|
77
|
-
raise ValueError(f'unsupported DRS type {drs_type}')
|
|
78
|
-
return specs
|
|
79
|
-
|
|
77
|
+
|
|
78
|
+
|
|
80
79
|
class DrsValidator(DrsApplication):
|
|
81
80
|
"""
|
|
82
81
|
Valid a DRS directory, dataset id and file name expression against a project.
|
|
83
82
|
"""
|
|
84
|
-
|
|
85
|
-
def validate_directory(self, drs_expression: str
|
|
83
|
+
|
|
84
|
+
def validate_directory(self, drs_expression: str,
|
|
85
|
+
prefix: str | None = None) -> DrsValidationReport:
|
|
86
86
|
"""
|
|
87
87
|
Validate a DRS directory expression.
|
|
88
88
|
|
|
89
89
|
:param drs_expression: A DRS directory expression.
|
|
90
90
|
:type drs_expression: str
|
|
91
|
+
:param prefix: A directory prefix to be removed from the directory expression.
|
|
92
|
+
:type prefix: str|None
|
|
91
93
|
:returns: A validation report.
|
|
92
94
|
:rtype: DrsValidationReport
|
|
93
95
|
"""
|
|
96
|
+
if prefix:
|
|
97
|
+
# Remove prefix if present. Always returns a copy.
|
|
98
|
+
drs_expression = drs_expression.removeprefix(prefix)
|
|
94
99
|
return self._validate(drs_expression, self.directory_specs)
|
|
95
|
-
|
|
100
|
+
|
|
96
101
|
def validate_dataset_id(self, drs_expression: str) -> DrsValidationReport:
|
|
97
102
|
"""
|
|
98
103
|
Validate a DRS dataset id expression.
|
|
@@ -123,7 +128,7 @@ class DrsValidator(DrsApplication):
|
|
|
123
128
|
[issue], [])
|
|
124
129
|
return result
|
|
125
130
|
|
|
126
|
-
def validate(self, drs_expression: str, drs_type: DrsType|str) -> DrsValidationReport:
|
|
131
|
+
def validate(self, drs_expression: str, drs_type: DrsType | str) -> DrsValidationReport:
|
|
127
132
|
"""
|
|
128
133
|
Validate a DRS expression.
|
|
129
134
|
|
|
@@ -134,15 +139,22 @@ class DrsValidator(DrsApplication):
|
|
|
134
139
|
:returns: A validation report.
|
|
135
140
|
:rtype: DrsValidationReport
|
|
136
141
|
"""
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
142
|
+
match drs_type:
|
|
143
|
+
case DrsType.DIRECTORY:
|
|
144
|
+
return self.validate_directory(drs_expression=drs_expression)
|
|
145
|
+
case DrsType.FILE_NAME:
|
|
146
|
+
return self.validate_file_name(drs_expression=drs_expression)
|
|
147
|
+
case DrsType.DATASET_ID:
|
|
148
|
+
return self.validate_dataset_id(drs_expression=drs_expression)
|
|
149
|
+
case _:
|
|
150
|
+
raise EsgvocDbError(f"unsupported drs type '{drs_type}'")
|
|
151
|
+
|
|
140
152
|
def _parse(self,
|
|
141
153
|
drs_expression: str,
|
|
142
154
|
separator: str,
|
|
143
|
-
drs_type: DrsType) -> tuple[list[str]|None, #
|
|
155
|
+
drs_type: DrsType) -> tuple[list[str] | None, # terms
|
|
144
156
|
list[DrsIssue], # Errors
|
|
145
|
-
list[DrsIssue]]:
|
|
157
|
+
list[DrsIssue]]: # Warnings
|
|
146
158
|
errors: list[DrsIssue] = list()
|
|
147
159
|
warnings: list[DrsIssue] = list()
|
|
148
160
|
cursor_offset = 0
|
|
@@ -150,7 +162,7 @@ class DrsValidator(DrsApplication):
|
|
|
150
162
|
start_with_space = drs_expression[0].isspace()
|
|
151
163
|
end_with_space = drs_expression[-1].isspace()
|
|
152
164
|
if start_with_space or end_with_space:
|
|
153
|
-
issue:
|
|
165
|
+
issue: ParsingIssue = Space()
|
|
154
166
|
if self.pedantic:
|
|
155
167
|
errors.append(issue)
|
|
156
168
|
else:
|
|
@@ -161,78 +173,77 @@ class DrsValidator(DrsApplication):
|
|
|
161
173
|
cursor_offset = previous_len - len(drs_expression)
|
|
162
174
|
if end_with_space:
|
|
163
175
|
drs_expression = drs_expression.rstrip()
|
|
164
|
-
|
|
165
|
-
if len(
|
|
176
|
+
terms = drs_expression.split(separator)
|
|
177
|
+
if len(terms) < 2:
|
|
166
178
|
errors.append(Unparsable(expected_drs_type=drs_type))
|
|
167
|
-
return None, errors, warnings
|
|
168
|
-
|
|
179
|
+
return None, errors, warnings # Early exit
|
|
180
|
+
max_term_index = len(terms)
|
|
169
181
|
cursor_position = initial_cursor_position = len(drs_expression) + 1
|
|
170
|
-
|
|
171
|
-
for index in range(
|
|
172
|
-
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
cursor_position -= len(
|
|
176
|
-
del
|
|
182
|
+
has_white_term = False
|
|
183
|
+
for index in range(max_term_index-1, -1, -1):
|
|
184
|
+
term = terms[index]
|
|
185
|
+
if (is_white_term := term.isspace()) or (not term):
|
|
186
|
+
has_white_term = has_white_term or is_white_term
|
|
187
|
+
cursor_position -= len(term) + 1
|
|
188
|
+
del terms[index]
|
|
177
189
|
continue
|
|
178
190
|
else:
|
|
179
191
|
break
|
|
180
192
|
if cursor_position != initial_cursor_position:
|
|
181
|
-
|
|
193
|
+
max_term_index = len(terms)
|
|
182
194
|
column = cursor_position+cursor_offset
|
|
183
|
-
if (drs_type == DrsType.DIRECTORY) and (not
|
|
195
|
+
if (drs_type == DrsType.DIRECTORY) and (not has_white_term):
|
|
184
196
|
issue = ExtraSeparator(column=column)
|
|
185
|
-
|
|
197
|
+
if self.pedantic:
|
|
198
|
+
errors.append(issue)
|
|
199
|
+
else:
|
|
200
|
+
warnings.append(issue)
|
|
186
201
|
else:
|
|
187
202
|
issue = ExtraChar(column=column)
|
|
188
203
|
errors.append(issue)
|
|
189
|
-
for index in range(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if not
|
|
204
|
+
for index in range(max_term_index-1, -1, -1):
|
|
205
|
+
term = terms[index]
|
|
206
|
+
len_term = len(term)
|
|
207
|
+
if not term:
|
|
193
208
|
column = cursor_position + cursor_offset
|
|
194
209
|
issue = ExtraSeparator(column=column)
|
|
195
|
-
if
|
|
210
|
+
if self.pedantic or drs_type != DrsType.DIRECTORY or index == 0:
|
|
196
211
|
errors.append(issue)
|
|
197
212
|
else:
|
|
198
213
|
warnings.append(issue)
|
|
199
|
-
del
|
|
200
|
-
if
|
|
201
|
-
column = cursor_position + cursor_offset -
|
|
202
|
-
issue =
|
|
214
|
+
del terms[index]
|
|
215
|
+
if term.isspace():
|
|
216
|
+
column = cursor_position + cursor_offset - len_term
|
|
217
|
+
issue = BlankTerm(column=column)
|
|
203
218
|
errors.append(issue)
|
|
204
|
-
del
|
|
205
|
-
cursor_position -=
|
|
206
|
-
|
|
207
|
-
#
|
|
208
|
-
sorted_errors = DrsValidator._sort_parser_issues(errors)
|
|
209
|
-
sorted_warnings = DrsValidator._sort_parser_issues(warnings)
|
|
210
|
-
return
|
|
211
|
-
|
|
219
|
+
del terms[index]
|
|
220
|
+
cursor_position -= len_term + 1
|
|
221
|
+
|
|
222
|
+
# Mypy doesn't understand that ParsingIssues are DrsIssues...
|
|
223
|
+
sorted_errors = DrsValidator._sort_parser_issues(errors) # type: ignore
|
|
224
|
+
sorted_warnings = DrsValidator._sort_parser_issues(warnings) # type: ignore
|
|
225
|
+
return terms, sorted_errors, sorted_warnings # type: ignore
|
|
226
|
+
|
|
212
227
|
@staticmethod
|
|
213
|
-
def _sort_parser_issues(issues: list[
|
|
228
|
+
def _sort_parser_issues(issues: list[ParsingIssue]) -> list[ParsingIssue]:
|
|
214
229
|
return sorted(issues, key=lambda issue: issue.column if issue.column else 0)
|
|
215
230
|
|
|
216
|
-
def
|
|
231
|
+
def _validate_term(self, term: str, part: DrsPart) -> bool:
|
|
217
232
|
match part.kind:
|
|
218
233
|
case DrsPartKind.COLLECTION:
|
|
219
234
|
casted_part: DrsCollection = cast(DrsCollection, part)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
casted_part.collection_id)
|
|
224
|
-
except Exception as e:
|
|
225
|
-
msg = f'problem while validating token: {e}.Abort.'
|
|
226
|
-
raise ValueError(msg) from e
|
|
235
|
+
matching_terms = projects.valid_term_in_collection(term,
|
|
236
|
+
self.project_id,
|
|
237
|
+
casted_part.collection_id)
|
|
227
238
|
if len(matching_terms) > 0:
|
|
228
239
|
return True
|
|
229
240
|
else:
|
|
230
241
|
return False
|
|
231
242
|
case DrsPartKind.CONSTANT:
|
|
232
243
|
part_casted: DrsConstant = cast(DrsConstant, part)
|
|
233
|
-
return part_casted.value !=
|
|
244
|
+
return part_casted.value != term
|
|
234
245
|
case _:
|
|
235
|
-
raise
|
|
246
|
+
raise EsgvocDbError(f"unsupported DRS specs part type '{part.kind}'")
|
|
236
247
|
|
|
237
248
|
def _create_report(self,
|
|
238
249
|
type: DrsType,
|
|
@@ -240,93 +251,69 @@ class DrsValidator(DrsApplication):
|
|
|
240
251
|
errors: list[DrsIssue],
|
|
241
252
|
warnings: list[DrsIssue]) -> DrsValidationReport:
|
|
242
253
|
return DrsValidationReport(project_id=self.project_id, type=type,
|
|
243
|
-
expression=drs_expression,
|
|
254
|
+
expression=drs_expression,
|
|
255
|
+
errors=cast(list[ValidationError], errors),
|
|
256
|
+
warnings=cast(list[ValidationWarning], warnings))
|
|
244
257
|
|
|
245
258
|
def _validate(self,
|
|
246
259
|
drs_expression: str,
|
|
247
260
|
specs: DrsSpecification) -> DrsValidationReport:
|
|
248
|
-
|
|
249
|
-
if not
|
|
250
|
-
return self._create_report(specs.type, drs_expression, errors, warnings)
|
|
251
|
-
|
|
252
|
-
|
|
261
|
+
terms, errors, warnings = self._parse(drs_expression, specs.separator, specs.type)
|
|
262
|
+
if not terms:
|
|
263
|
+
return self._create_report(specs.type, drs_expression, errors, warnings) # Early exit.
|
|
264
|
+
term_index = 0
|
|
265
|
+
term_max_index = len(terms)
|
|
253
266
|
part_index = 0
|
|
254
267
|
part_max_index = len(specs.parts)
|
|
255
268
|
matching_code_mapping = dict()
|
|
256
269
|
while part_index < part_max_index:
|
|
257
|
-
|
|
270
|
+
term = terms[term_index]
|
|
258
271
|
part = specs.parts[part_index]
|
|
259
|
-
if self.
|
|
260
|
-
|
|
272
|
+
if self._validate_term(term, part):
|
|
273
|
+
term_index += 1
|
|
261
274
|
part_index += 1
|
|
262
275
|
matching_code_mapping[part.__str__()] = 0
|
|
263
276
|
elif part.kind == DrsPartKind.CONSTANT or \
|
|
264
|
-
cast(DrsCollection, part).is_required:
|
|
265
|
-
issue:
|
|
266
|
-
|
|
267
|
-
|
|
277
|
+
cast(DrsCollection, part).is_required: # noqa E127
|
|
278
|
+
issue: ComplianceIssue = InvalidTerm(term=term,
|
|
279
|
+
term_position=term_index+1,
|
|
280
|
+
collection_id_or_constant_value=str(part))
|
|
268
281
|
errors.append(issue)
|
|
269
282
|
matching_code_mapping[part.__str__()] = 1
|
|
270
|
-
|
|
283
|
+
term_index += 1
|
|
271
284
|
part_index += 1
|
|
272
|
-
else:
|
|
285
|
+
else: # The part is not required so try to match the term with the next part.
|
|
273
286
|
part_index += 1
|
|
274
287
|
matching_code_mapping[part.__str__()] = -1
|
|
275
|
-
if
|
|
288
|
+
if term_index == term_max_index:
|
|
276
289
|
break
|
|
277
290
|
# Cases:
|
|
278
|
-
# - All
|
|
279
|
-
#
|
|
280
|
-
# - Extra
|
|
281
|
-
# + The last collections are required => report extra
|
|
282
|
-
# + The last collections are not required and these
|
|
291
|
+
# - All terms and collections have been processed.
|
|
292
|
+
# - Not enough term to process all collections.
|
|
293
|
+
# - Extra terms left whereas all collections have been processed:
|
|
294
|
+
# + The last collections are required => report extra terms.
|
|
295
|
+
# + The last collections are not required and these terms were not validated by them.
|
|
283
296
|
# => Should report error even if the collections are not required.
|
|
284
|
-
if part_index < part_max_index:
|
|
297
|
+
if part_index < part_max_index: # Missing terms.
|
|
285
298
|
for index in range(part_index, part_max_index):
|
|
286
299
|
part = specs.parts[index]
|
|
287
|
-
issue =
|
|
300
|
+
issue = MissingTerm(collection_id=str(part), collection_position=index+1)
|
|
288
301
|
if part.kind == DrsPartKind.CONSTANT or \
|
|
289
302
|
cast(DrsCollection, part).is_required:
|
|
290
303
|
errors.append(issue)
|
|
291
304
|
else:
|
|
292
305
|
warnings.append(issue)
|
|
293
|
-
elif
|
|
294
|
-
part_index -=
|
|
295
|
-
for index in range(
|
|
296
|
-
|
|
306
|
+
elif term_index < term_max_index: # Extra terms.
|
|
307
|
+
part_index -= term_max_index - term_index
|
|
308
|
+
for index in range(term_index, term_max_index):
|
|
309
|
+
term = terms[index]
|
|
297
310
|
part = specs.parts[part_index]
|
|
298
311
|
if part.kind != DrsPartKind.CONSTANT and \
|
|
299
312
|
(not cast(DrsCollection, part).is_required) and \
|
|
300
|
-
matching_code_mapping[part.__str__()] < 0:
|
|
301
|
-
issue =
|
|
313
|
+
matching_code_mapping[part.__str__()] < 0: # noqa E125
|
|
314
|
+
issue = ExtraTerm(term=term, term_position=index, collection_id=str(part))
|
|
302
315
|
else:
|
|
303
|
-
issue =
|
|
316
|
+
issue = ExtraTerm(term=term, term_position=index, collection_id=None)
|
|
304
317
|
errors.append(issue)
|
|
305
318
|
part_index += 1
|
|
306
319
|
return self._create_report(specs.type, drs_expression, errors, warnings)
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if __name__ == "__main__":
|
|
310
|
-
project_id = 'cmip6plus'
|
|
311
|
-
validator = DrsValidator(project_id)
|
|
312
|
-
drs_expressions = [
|
|
313
|
-
".CMIP6Plus.CMIP.IPSL. .MIROC6.amip..r2i2p1f2.ACmon.od550aer. ..gn",
|
|
314
|
-
]
|
|
315
|
-
import time
|
|
316
|
-
for drs_expression in drs_expressions:
|
|
317
|
-
start_time = time.perf_counter_ns()
|
|
318
|
-
report = validator.validate_dataset_id(drs_expression)
|
|
319
|
-
stop_time = time.perf_counter_ns()
|
|
320
|
-
print(f'elapsed time: {(stop_time-start_time)/1000000} ms')
|
|
321
|
-
if report.nb_errors > 0:
|
|
322
|
-
print(f'error(s): {report.nb_errors}')
|
|
323
|
-
for error in report.errors:
|
|
324
|
-
print(error)
|
|
325
|
-
else:
|
|
326
|
-
print('error(s): 0')
|
|
327
|
-
if report.nb_warnings > 0:
|
|
328
|
-
print(f'warning(s): {report.nb_warnings}')
|
|
329
|
-
for warning in report.warnings:
|
|
330
|
-
print(warning)
|
|
331
|
-
else:
|
|
332
|
-
print('warning(s): 0')
|
esgvoc/apps/py.typed
ADDED
|
File without changes
|
esgvoc/cli/drs.py
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
from esgvoc.apps.drs.report import DrsValidationReport, DrsGeneratorReport
|
|
3
|
-
from esgvoc.apps.drs.validator import DrsValidator
|
|
1
|
+
import shlex
|
|
4
2
|
import sys
|
|
3
|
+
from typing import List, Optional
|
|
4
|
+
|
|
5
5
|
import typer
|
|
6
6
|
from rich.console import Console
|
|
7
7
|
from rich.table import Table
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
import esgvoc.api as ev
|
|
10
|
-
import
|
|
10
|
+
from esgvoc.apps.drs.generator import DrsGenerator
|
|
11
|
+
from esgvoc.apps.drs.report import DrsGenerationReport, DrsValidationReport
|
|
12
|
+
from esgvoc.apps.drs.validator import DrsValidator
|
|
13
|
+
from esgvoc.core.exceptions import EsgvocValueError
|
|
11
14
|
|
|
12
15
|
app = typer.Typer()
|
|
13
16
|
console = Console()
|
|
@@ -35,6 +38,10 @@ def drsvalid(
|
|
|
35
38
|
file: Optional[typer.FileText] = typer.Option(None, "--file", "-f", help="File containing DRS validation inputs, one per line in the form <project> <drstype> <string>"),
|
|
36
39
|
verbose: bool = typer.Option(False, "-v", "--verbose", help="Provide detailed validation results"),
|
|
37
40
|
output: Optional[str] = typer.Option(None, "-o", "--output", help="File to save the DRS entries validation"),
|
|
41
|
+
rm_prefix: Optional[str] = typer.Option(None,"-p","--prefix", help="Remove given prefix from all checked directory"),
|
|
42
|
+
pedantic: Optional[bool] = typer.Option(False,"-e","--enforce", help="Enable pedantic mode, enforcing strict compliance, mean that warnings are now errors.")
|
|
43
|
+
|
|
44
|
+
|
|
38
45
|
|
|
39
46
|
) -> List[DrsValidationReport]:
|
|
40
47
|
"""
|
|
@@ -88,17 +95,21 @@ def drsvalid(
|
|
|
88
95
|
|
|
89
96
|
string = entries[i]
|
|
90
97
|
i += 1
|
|
91
|
-
validator = DrsValidator(current_project)
|
|
98
|
+
validator = DrsValidator(current_project, pedantic=pedantic)
|
|
92
99
|
report = None
|
|
93
100
|
match current_drs_type:
|
|
94
101
|
case "filename":
|
|
95
102
|
report = validator.validate_file_name(string)
|
|
96
103
|
case "directory":
|
|
97
|
-
|
|
104
|
+
if rm_prefix:
|
|
105
|
+
prefix = rm_prefix+"/" if rm_prefix[-1]!="/" else ""
|
|
106
|
+
else:
|
|
107
|
+
prefix=None
|
|
108
|
+
report = validator.validate_directory(string, prefix)
|
|
98
109
|
case "dataset":
|
|
99
110
|
report = validator.validate_dataset_id(string)
|
|
100
111
|
case _:
|
|
101
|
-
raise
|
|
112
|
+
raise EsgvocValueError(f"unsupported drs type '{current_drs_type}'")
|
|
102
113
|
reports.append(report)
|
|
103
114
|
|
|
104
115
|
if verbose:
|
|
@@ -111,7 +122,7 @@ def drsvalid(
|
|
|
111
122
|
|
|
112
123
|
for report in reports:
|
|
113
124
|
entry = str(report.expression)
|
|
114
|
-
proj_and_type = str(report.project_id) + " " + report.type + " "
|
|
125
|
+
proj_and_type = str(report.project_id) + " " + report.type + " "
|
|
115
126
|
warnings = "\n".join(["⚠️ " + str(warning) for warning in report.warnings])
|
|
116
127
|
errors = "\n".join(["⚠️ " + str(error) for error in report.errors])
|
|
117
128
|
valid = "✅ Valid" if report else "❌ Invalid"
|
|
@@ -136,16 +147,16 @@ def drsvalid(
|
|
|
136
147
|
|
|
137
148
|
@app.command()
|
|
138
149
|
def drsgen(
|
|
139
|
-
drs_entries: Optional[List[str]] = typer.Argument(None, help="List of inputs to generate DRS in the form <project> <drstype> <
|
|
140
|
-
file: Optional[typer.FileText] = typer.Option(None, "--file", "-f", help="File containing DRS generation inputs, one per line in the form <project> <drstype> <
|
|
150
|
+
drs_entries: Optional[List[str]] = typer.Argument(None, help="List of inputs to generate DRS in the form <project> <drstype> <bag_of_terms>"),
|
|
151
|
+
file: Optional[typer.FileText] = typer.Option(None, "--file", "-f", help="File containing DRS generation inputs, one per line in the form <project> <drstype> <bag_of_terms>"),
|
|
141
152
|
verbose: bool = typer.Option(False, "-v", "--verbose", help="Provide detailed generation results"),
|
|
142
153
|
output: Optional[str] = typer.Option(None, "-o", "--output", help="File to save the generated DRS entries"),
|
|
143
|
-
) -> List[
|
|
154
|
+
) -> List[DrsGenerationReport]:
|
|
144
155
|
"""
|
|
145
|
-
Generates DRS strings for a specific project and type based on input bag of
|
|
156
|
+
Generates DRS strings for a specific project and type based on input bag of terms.
|
|
146
157
|
|
|
147
158
|
Args:
|
|
148
|
-
drs_entries (Optional[List[str]]): A list of inputs in the form <project> <drstype> <
|
|
159
|
+
drs_entries (Optional[List[str]]): A list of inputs in the form <project> <drstype> <bag_of_terms>.
|
|
149
160
|
file (Optional[typer.FileText]): File containing DRS generation inputs, one per line.
|
|
150
161
|
verbose (bool): If true, prints detailed generation results.
|
|
151
162
|
output (Optional[str]): File path to save the generated DRS entries.
|
|
@@ -189,21 +200,21 @@ def drsgen(
|
|
|
189
200
|
if current_drs_type is None:
|
|
190
201
|
raise typer.BadParameter(f"Invalid drs_type: {entries[i]}")
|
|
191
202
|
|
|
192
|
-
|
|
193
|
-
|
|
203
|
+
bag_of_terms = entries[i]
|
|
204
|
+
bag_of_terms = set(entries[i].split(" "))
|
|
194
205
|
i += 1
|
|
195
206
|
|
|
196
207
|
generator = DrsGenerator(current_project)
|
|
197
208
|
report = None
|
|
198
209
|
match current_drs_type:
|
|
199
210
|
case "filename":
|
|
200
|
-
report = generator.
|
|
211
|
+
report = generator.generate_file_name_from_bag_of_terms(bag_of_terms)
|
|
201
212
|
case "directory":
|
|
202
|
-
report = generator.
|
|
213
|
+
report = generator.generate_directory_from_bag_of_terms(bag_of_terms)
|
|
203
214
|
case "dataset":
|
|
204
|
-
report = generator.
|
|
215
|
+
report = generator.generate_dataset_id_from_bag_of_terms(bag_of_terms)
|
|
205
216
|
case _:
|
|
206
|
-
raise
|
|
217
|
+
raise EsgvocValueError(f"unsupported drs type '{current_drs_type}'")
|
|
207
218
|
generated_reports.append(report)
|
|
208
219
|
|
|
209
220
|
if verbose:
|
|
@@ -216,7 +227,7 @@ def drsgen(
|
|
|
216
227
|
entry = str(report.mapping_used)
|
|
217
228
|
warnings = "\n".join(["⚠️ " + str(warning) for warning in report.warnings])
|
|
218
229
|
errors = "\n".join([f"🔍 {error}" for error in report.errors])
|
|
219
|
-
result = report.
|
|
230
|
+
result = report.generated_drs_expression
|
|
220
231
|
table.add_row(entry, warnings, errors, result)
|
|
221
232
|
table.add_row("----", "----", "----", "----")
|
|
222
233
|
if table.columns[3].width is not None and len(result) > table.columns[3].width:
|