esgvoc 0.2.1__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of esgvoc might be problematic. Click here for more details.

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