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.

Files changed (79) hide show
  1. esgvoc/__init__.py +3 -1
  2. esgvoc/api/__init__.py +96 -72
  3. esgvoc/api/data_descriptors/__init__.py +18 -12
  4. esgvoc/api/data_descriptors/activity.py +8 -45
  5. esgvoc/api/data_descriptors/area_label.py +6 -0
  6. esgvoc/api/data_descriptors/branded_suffix.py +5 -0
  7. esgvoc/api/data_descriptors/branded_variable.py +5 -0
  8. esgvoc/api/data_descriptors/consortium.py +16 -56
  9. esgvoc/api/data_descriptors/data_descriptor.py +106 -0
  10. esgvoc/api/data_descriptors/date.py +3 -46
  11. esgvoc/api/data_descriptors/directory_date.py +3 -46
  12. esgvoc/api/data_descriptors/experiment.py +19 -54
  13. esgvoc/api/data_descriptors/forcing_index.py +3 -45
  14. esgvoc/api/data_descriptors/frequency.py +6 -43
  15. esgvoc/api/data_descriptors/grid_label.py +6 -44
  16. esgvoc/api/data_descriptors/horizontal_label.py +6 -0
  17. esgvoc/api/data_descriptors/initialisation_index.py +3 -44
  18. esgvoc/api/data_descriptors/institution.py +11 -54
  19. esgvoc/api/data_descriptors/license.py +4 -44
  20. esgvoc/api/data_descriptors/mip_era.py +6 -44
  21. esgvoc/api/data_descriptors/model_component.py +7 -45
  22. esgvoc/api/data_descriptors/organisation.py +3 -40
  23. esgvoc/api/data_descriptors/physic_index.py +3 -45
  24. esgvoc/api/data_descriptors/product.py +4 -43
  25. esgvoc/api/data_descriptors/realisation_index.py +3 -44
  26. esgvoc/api/data_descriptors/realm.py +4 -42
  27. esgvoc/api/data_descriptors/resolution.py +6 -44
  28. esgvoc/api/data_descriptors/source.py +18 -53
  29. esgvoc/api/data_descriptors/source_type.py +3 -41
  30. esgvoc/api/data_descriptors/sub_experiment.py +3 -41
  31. esgvoc/api/data_descriptors/table.py +6 -48
  32. esgvoc/api/data_descriptors/temporal_label.py +6 -0
  33. esgvoc/api/data_descriptors/time_range.py +3 -27
  34. esgvoc/api/data_descriptors/variable.py +13 -71
  35. esgvoc/api/data_descriptors/variant_label.py +3 -47
  36. esgvoc/api/data_descriptors/vertical_label.py +5 -0
  37. esgvoc/api/project_specs.py +3 -2
  38. esgvoc/api/projects.py +727 -446
  39. esgvoc/api/py.typed +0 -0
  40. esgvoc/api/report.py +29 -16
  41. esgvoc/api/search.py +140 -95
  42. esgvoc/api/universe.py +362 -156
  43. esgvoc/apps/__init__.py +3 -4
  44. esgvoc/apps/drs/constants.py +1 -1
  45. esgvoc/apps/drs/generator.py +185 -198
  46. esgvoc/apps/drs/report.py +272 -136
  47. esgvoc/apps/drs/validator.py +132 -145
  48. esgvoc/apps/py.typed +0 -0
  49. esgvoc/cli/drs.py +32 -21
  50. esgvoc/cli/get.py +35 -31
  51. esgvoc/cli/install.py +11 -8
  52. esgvoc/cli/main.py +0 -2
  53. esgvoc/cli/status.py +5 -5
  54. esgvoc/cli/valid.py +40 -40
  55. esgvoc/core/constants.py +1 -1
  56. esgvoc/core/db/__init__.py +2 -4
  57. esgvoc/core/db/connection.py +5 -3
  58. esgvoc/core/db/models/project.py +50 -8
  59. esgvoc/core/db/models/universe.py +51 -12
  60. esgvoc/core/db/project_ingestion.py +60 -46
  61. esgvoc/core/db/universe_ingestion.py +58 -29
  62. esgvoc/core/exceptions.py +33 -0
  63. esgvoc/core/logging_handler.py +1 -1
  64. esgvoc/core/repo_fetcher.py +4 -3
  65. esgvoc/core/service/__init__.py +37 -5
  66. esgvoc/core/service/configuration/config_manager.py +188 -0
  67. esgvoc/core/service/configuration/setting.py +88 -0
  68. esgvoc/core/service/state.py +49 -32
  69. {esgvoc-0.2.1.dist-info → esgvoc-0.4.0.dist-info}/METADATA +34 -3
  70. esgvoc-0.4.0.dist-info/RECORD +80 -0
  71. esgvoc/api/_utils.py +0 -39
  72. esgvoc/cli/config.py +0 -82
  73. esgvoc/core/service/settings.py +0 -73
  74. esgvoc/core/service/settings.toml +0 -17
  75. esgvoc/core/service/settings_default.toml +0 -17
  76. esgvoc-0.2.1.dist-info/RECORD +0 -73
  77. {esgvoc-0.2.1.dist-info → esgvoc-0.4.0.dist-info}/WHEEL +0 -0
  78. {esgvoc-0.2.1.dist-info → esgvoc-0.4.0.dist-info}/entry_points.txt +0 -0
  79. {esgvoc-0.2.1.dist-info → esgvoc-0.4.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,26 +1,34 @@
1
1
  from typing import cast
2
- from esgvoc.api.project_specs import (ProjectSpecs,
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.apps.drs.report import (DrsValidationReport,
12
- DrsIssue,
13
- ParserIssue,
14
- ValidationIssue,
15
- Space,
16
- Unparsable,
17
- ExtraSeparator,
18
- ExtraChar,
19
- BlankToken,
20
- InvalidToken,
21
- ExtraToken,
22
- MissingToken,
23
- FileNameExtensionIssue)
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.get_project_specs(project_id)
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 ValueError(f'unsupported DRS specs type {specs.type}')
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 ValueError('missing properties in the DRS file name specifications of the ' +
65
- f'project {self.project_id}')
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
- def _get_specs(self, drs_type: DrsType|str) -> DrsSpecification:
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) -> DrsValidationReport:
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
- specs = self._get_specs(drs_type)
138
- return self._validate(drs_expression, specs)
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, # Tokens
155
+ drs_type: DrsType) -> tuple[list[str] | None, # terms
144
156
  list[DrsIssue], # Errors
145
- list[DrsIssue]]: # Warnings
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: ParserIssue = Space()
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
- tokens = drs_expression.split(separator)
165
- if len(tokens) < 2:
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 # Early exit
168
- max_token_index = len(tokens)
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
- has_white_token = False
171
- for index in range(max_token_index-1, -1, -1):
172
- token = tokens[index]
173
- if (is_white_token := token.isspace()) or (not token):
174
- has_white_token = has_white_token or is_white_token
175
- cursor_position -= len(token) + 1
176
- del tokens[index]
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
- max_token_index = len(tokens)
193
+ max_term_index = len(terms)
182
194
  column = cursor_position+cursor_offset
183
- if (drs_type == DrsType.DIRECTORY) and (not has_white_token):
195
+ if (drs_type == DrsType.DIRECTORY) and (not has_white_term):
184
196
  issue = ExtraSeparator(column=column)
185
- warnings.append(issue)
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(max_token_index-1, -1, -1):
190
- token = tokens[index]
191
- len_token = len(token)
192
- if not token:
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 (drs_type != DrsType.DIRECTORY) or self.pedantic or (index == 0):
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 tokens[index]
200
- if token.isspace():
201
- column = cursor_position + cursor_offset - len_token
202
- issue = BlankToken(column=column)
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 tokens[index]
205
- cursor_position -= len_token + 1
206
-
207
- # Mypy doesn't understand that ParserIssues are DrsIssues...
208
- sorted_errors = DrsValidator._sort_parser_issues(errors) # type: ignore
209
- sorted_warnings = DrsValidator._sort_parser_issues(warnings) # type: ignore
210
- return tokens, sorted_errors, sorted_warnings # type: ignore
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[ParserIssue]) -> list[ParserIssue]:
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 _validate_token(self, token: str, part: DrsPart) -> bool:
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
- try:
221
- matching_terms = projects.valid_term_in_collection(token,
222
- self.project_id,
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 != token
244
+ return part_casted.value != term
234
245
  case _:
235
- raise ValueError(f'unsupported DRS specs part type {part.kind}')
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, errors=errors, warnings=warnings)
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
- tokens, errors, warnings = self._parse(drs_expression, specs.separator, specs.type)
249
- if not tokens:
250
- return self._create_report(specs.type, drs_expression, errors, warnings) # Early exit.
251
- token_index = 0
252
- token_max_index = len(tokens)
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
- token = tokens[token_index]
270
+ term = terms[term_index]
258
271
  part = specs.parts[part_index]
259
- if self._validate_token(token, part):
260
- token_index += 1
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: ValidationIssue = InvalidToken(token=token,
266
- token_position=token_index+1,
267
- collection_id_or_constant_value=str(part))
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
- token_index += 1
283
+ term_index += 1
271
284
  part_index += 1
272
- else: # The part is not required so try to match the token with the next part.
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 token_index == token_max_index:
288
+ if term_index == term_max_index:
276
289
  break
277
290
  # Cases:
278
- # - All tokens and collections have been processed.
279
- # - Not enough token to process all collections.
280
- # - Extra tokens left whereas all collections have been processed:
281
- # + The last collections are required => report extra tokens.
282
- # + The last collections are not required and these tokens were not validated by them.
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: # Missing tokens.
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 = MissingToken(collection_id=str(part), collection_position=index+1)
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 token_index < token_max_index: # Extra tokens.
294
- part_index -= token_max_index - token_index
295
- for index in range(token_index, token_max_index):
296
- token = tokens[index]
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 = ExtraToken(token=token, token_position=index, collection_id=str(part))
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 = ExtraToken(token=token, token_position=index, collection_id=None)
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
- from esgvoc.apps.drs.generator import DrsGenerator
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
- from typing import List, Optional
8
+
9
9
  import esgvoc.api as ev
10
- import shlex
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
- report = validator.validate_directory(string)
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 RuntimeError("drstype is not known")
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> <bag_of_tokens>"),
140
- 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_tokens>"),
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[DrsGeneratorReport]:
154
+ ) -> List[DrsGenerationReport]:
144
155
  """
145
- Generates DRS strings for a specific project and type based on input bag of tokens.
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> <bag_of_tokens>.
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
- bag_of_tokens = entries[i]
193
- bag_of_tokens = set(entries[i].split(" "))
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.generate_file_name_from_bag_of_tokens(bag_of_tokens)
211
+ report = generator.generate_file_name_from_bag_of_terms(bag_of_terms)
201
212
  case "directory":
202
- report = generator.generate_directory_from_bag_of_tokens(bag_of_tokens)
213
+ report = generator.generate_directory_from_bag_of_terms(bag_of_terms)
203
214
  case "dataset":
204
- report = generator.generate_dataset_id_from_bag_of_tokens(bag_of_tokens)
215
+ report = generator.generate_dataset_id_from_bag_of_terms(bag_of_terms)
205
216
  case _:
206
- raise RuntimeError("drstype is not known")
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.computed_drs_expression
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: