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
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 typing import Any, Mapping, Iterable, Protocol, ClassVar
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 ParserIssueVisitor(Protocol):
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 parser issues visitor.
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
- def visit_blank_token_issue(self, issue: "BlankToken") -> Any:
25
- """Visit a blank token issue."""
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 ValidationIssueVisitor(Protocol):
66
+ class ComplianceIssueVisitor(Protocol):
30
67
  """
31
- Specifications for a validation issues visitor.
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
- def visit_invalid_token_issue(self, issue: "InvalidToken") -> Any:
38
- """Visit an invalid token issue."""
73
+
74
+ def visit_invalid_term_issue(self, issue: "InvalidTerm") -> Any:
75
+ """Visit an invalid term issue."""
39
76
  pass
40
- def visit_extra_token_issue(self, issue: "ExtraToken") -> Any:
41
- """Visit an extra token issue."""
77
+
78
+ def visit_extra_term_issue(self, issue: "ExtraTerm") -> Any:
79
+ """Visit an extra term issue."""
42
80
  pass
43
- def visit_missing_token_issue(self, issue: "MissingToken") -> Any:
44
- """Visit a missing token issue."""
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 GeneratorIssueVisitor(Protocol):
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
- def visit_invalid_token_issue(self, issue: "InvalidToken") -> Any:
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
- def visit_missing_token_issue(self, issue: "MissingToken") -> Any:
57
- """Visit a missing token issue."""
98
+
99
+ def visit_missing_term_issue(self, issue: "MissingTerm") -> Any:
100
+ """Visit a missing term issue."""
58
101
  pass
59
- def visit_too_many_tokens_collection_issue(self, issue: "TooManyTokensCollection") -> Any:
60
- """Visit a too many tokens collection issue."""
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
- def visit_assign_token_issue(self, issue: "AssignedToken") -> Any:
66
- """Visit an assign token issue."""
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 ParserIssue(DrsIssue):
135
+ class ParsingIssue(DrsIssue):
88
136
  """
89
- Generic class for the DRS parser issues.
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: ParserIssueVisitor) -> Any:
143
+ def accept(self, visitor: ParsingIssueVisitor) -> Any:
96
144
  """
97
- Accept an DRS parser issue visitor.
145
+ Accept an DRS parsing issue visitor.
98
146
 
99
- :param visitor: The DRS parser issue visitor.
100
- :type visitor: ParserIssueVisitor
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
- class Space(ParserIssue):
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
- def accept(self, visitor: ParserIssueVisitor) -> Any:
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
- class Unparsable(ParserIssue):
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
- def accept(self, visitor: ParserIssueVisitor) -> Any:
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
- class ExtraSeparator(ParserIssue):
190
+
191
+ class ExtraSeparator(ParsingIssue):
135
192
  """
136
193
  Represents a problem of multiple separator occurrences in the DRS expression.
137
194
  """
138
- def accept(self, visitor: ParserIssueVisitor) -> Any:
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
- class ExtraChar(ParserIssue):
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
- def accept(self, visitor: ParserIssueVisitor) -> Any:
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
- class BlankToken(ParserIssue):
222
+
223
+ class BlankTerm(ParsingIssue):
159
224
  """
160
- Represents a problem of blank token in the DRS expression (i.e., space[s] surrounded by separators).
225
+ Represents a problem of blank term in the DRS expression (i.e., space[s] surrounded by separators).
161
226
  """
162
- def accept(self, visitor: ParserIssueVisitor) -> Any:
163
- return visitor.visit_blank_token_issue(self)
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 token at column {self.column}"
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 ValidationIssue(DrsIssue):
239
+ class ComplianceIssue(DrsIssue):
171
240
  """
172
- Generic class for the validation issues.
241
+ Generic class for the compliance issues.
173
242
  """
174
243
  @abstractmethod
175
- def accept(self, visitor: ValidationIssueVisitor) -> Any:
244
+ def accept(self, visitor: ComplianceIssueVisitor) -> Any:
176
245
  """
177
- Accept an DRS validation issue visitor.
246
+ Accept an DRS compliance issue visitor.
178
247
 
179
- :param visitor: The DRS validation issue visitor.
180
- :type visitor: ValidationIssueVisitor
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(ValidationIssue):
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
- def accept(self, visitor: ValidationIssueVisitor) -> Any:
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
- class TokenIssue(ValidationIssue):
270
+
271
+ class TermIssue(ComplianceIssue):
200
272
  """
201
- Generic class for the DRS token issues.
273
+ Generic class for the DRS term issues.
202
274
  """
203
- token: str
204
- """The faulty token."""
205
- token_position: int
206
- """The position of the faulty token (the part position, not the column of the characters."""
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 GeneratorIssue(DrsIssue):
281
+ class GenerationIssue(DrsIssue):
210
282
  """
211
- Generic class for the DRS generator issues.
283
+ Generic class for the DRS generation issues.
212
284
  """
213
285
  @abstractmethod
214
- def accept(self, visitor: GeneratorIssueVisitor) -> Any:
286
+ def accept(self, visitor: GenerationIssueVisitor) -> Any:
215
287
  """
216
- Accept an DRS generator issue visitor.
288
+ Accept an DRS generation issue visitor.
217
289
 
218
- :param visitor: The DRS generator issue visitor.
219
- :type visitor: GeneratorIssueVisitor
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 InvalidToken(TokenIssue, GeneratorIssue):
298
+ class InvalidTerm(TermIssue, GenerationIssue):
227
299
  """
228
- Represents a problem of invalid token against a collection or a constant part of a DRS specification.
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
- def accept(self, visitor: ValidationIssueVisitor|GeneratorIssueVisitor) -> Any:
233
- return visitor.visit_invalid_token_issue(self)
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"token '{self.token}' not compliant with {self.collection_id_or_constant_value} at position {self.token_position}"
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
- class ExtraToken(TokenIssue):
316
+
317
+ class ExtraTerm(TermIssue):
241
318
  """
242
- Represents a problem of extra token at the end of the given DRS expression.
243
- All part of the DRS specification have been processed and this token is not necessary
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
- def accept(self, visitor: ValidationIssueVisitor) -> Any:
250
- return visitor.visit_extra_token_issue(self)
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 token {self.token}"
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.token_position}"
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
- class MissingToken(ValidationIssue, GeneratorIssue):
340
+
341
+ class MissingTerm(ComplianceIssue, GenerationIssue):
261
342
  """
262
- Represents a problem of missing token for a collection part of the DRS specification.
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
- def accept(self, visitor: ValidationIssueVisitor|GeneratorIssueVisitor) -> Any:
269
- return visitor.visit_missing_token_issue(self)
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 token for {self.collection_id} at position {self.collection_position}'
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
- class TooManyTokensCollection(GeneratorIssue):
360
+
361
+ class TooManyTermCollection(GenerationIssue):
277
362
  """
278
- Represents a problem while inferring a mapping collection - token in the generation
279
- of a DRS expression based on a bag of tokens. The problem is that more than one token
280
- is able to match this collection. The generator is unable to choose from these tokens
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
- tokens: list[str]
285
- """The faulty tokens."""
286
- def accept(self, visitor: GeneratorIssueVisitor) -> Any:
287
- return visitor.visit_too_many_tokens_collection_issue(self)
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
- tokens_str = ", ".join(token for token in self.tokens)
291
- result = f'collection {self.collection_id} has more than one token ({tokens_str})'
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(GeneratorIssue):
385
+ class ConflictingCollections(GenerationIssue):
298
386
  """
299
- Represents a problem while inferring a mapping collection - token in the generation
300
- of a DRS expression based on a bag of tokens. The problem is that these collections shares the
301
- very same tokens. The generator is unable to choose which token for which collection.
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
- tokens: list[str]
306
- """The shared tokens."""
307
- def accept(self, visitor: GeneratorIssueVisitor) -> Any:
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
- tokens_str = ", ".join(token for token in self.tokens)
312
- result = f"collections {collection_ids_str} are competing for the same token(s) {tokens_str}"
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 AssignedToken(GeneratorIssue):
410
+ class AssignedTerm(GenerationIssue):
319
411
  """
320
- Represents a decision of the Generator to assign this token to the collection, that may not be.
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
- token: str
326
- """The token."""
327
- def accept(self, visitor: GeneratorIssueVisitor) -> Any:
328
- return visitor.visit_assign_token_issue(self)
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 token {self.token} for collection {self.collection_id}"
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
- errors: list[DrsIssue]
452
+
453
+ errors: list
345
454
  """A list of DRS issues that are considered as errors."""
346
- warnings: list[DrsIssue]
455
+
456
+ warnings: list
347
457
  """A list of DRS issues that are considered as warnings."""
348
- @computed_field # type: ignore
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
- @computed_field # type: ignore
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
- @computed_field # type: ignore
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 DrsGeneratorReport(DrsReport):
506
+ class DrsGenerationReport(DrsReport):
383
507
  """
384
- The DRS generator report.
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
- given_mapping_or_bag_of_tokens: Mapping|Iterable
391
- """The mapping or the bag of tokens given."""
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 tokens (same mapping otherwise)."""
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 tokens"""
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
-