-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #82 - Making validation errors more type-specific & adding some…
… unit tests (#122)
- Loading branch information
Showing
11 changed files
with
617 additions
and
66 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
""" | ||
Qb-Cube Validation Errors | ||
------------------------- | ||
""" | ||
|
||
from dataclasses import dataclass | ||
from typing import Optional, Type, Union | ||
|
||
from csvqb.models.cube.csvqb.components import ( | ||
QbObservationValue, | ||
QbMultiUnits, | ||
QbDataStructureDefinition, | ||
) | ||
from csvqb.models.validationerror import SpecificValidationError | ||
|
||
ComponentTypeDescription = Union[str, Type[QbDataStructureDefinition]] | ||
|
||
|
||
def _get_description_for_component(t: ComponentTypeDescription) -> str: | ||
if isinstance(t, str): | ||
return t | ||
|
||
return t.__name__ | ||
|
||
|
||
@dataclass | ||
class OutputUriTemplateMissingError(SpecificValidationError): | ||
""" | ||
Represents an error where the user has defined a component which cannot infer its own output_uri_template without | ||
manually specifying an output_uri_template. | ||
""" | ||
|
||
csv_column_name: str | ||
component_type: ComponentTypeDescription | ||
|
||
def __post_init__(self): | ||
self.message = ( | ||
f"'{self.csv_column_name}' - a {_get_description_for_component(self.component_type)} must have an " | ||
+ "output_uri_template defined." | ||
) | ||
|
||
|
||
@dataclass | ||
class MaxNumComponentsExceededError(SpecificValidationError): | ||
""" | ||
Represents an error where the user can define a maximum number of components of a given type, but has defined | ||
too many. | ||
""" | ||
|
||
component_type: ComponentTypeDescription | ||
maximum_number: int | ||
actual_number: int | ||
additional_explanation: Optional[str] = None | ||
|
||
def __post_init__(self): | ||
self.message = ( | ||
f"Found {self.actual_number} of {_get_description_for_component(self.component_type)}s. " | ||
+ f"Expected a maximum of {self.maximum_number}." | ||
) | ||
if self.additional_explanation is not None: | ||
self.message += " " + self.additional_explanation | ||
|
||
|
||
@dataclass | ||
class MinNumComponentsNotSatisfiedError(SpecificValidationError): | ||
""" | ||
Represents an error where the user must define a minimum number of components of a given type, but has not done so. | ||
""" | ||
|
||
component_type: ComponentTypeDescription | ||
minimum_number: int | ||
actual_number: int | ||
additional_explanation: Optional[str] = None | ||
|
||
def __post_init__(self): | ||
self.message = ( | ||
f"At least {self.minimum_number} {_get_description_for_component(self.component_type)}s must be defined." | ||
+ f" Found {self.actual_number}." | ||
) | ||
if self.additional_explanation is not None: | ||
self.message += " " + self.additional_explanation | ||
|
||
|
||
@dataclass | ||
class WrongNumberComponentsError(SpecificValidationError): | ||
""" | ||
Represents an error where the user must include a specific number of components, but has not done so. | ||
""" | ||
|
||
component_type: ComponentTypeDescription | ||
expected_number: int | ||
actual_number: int | ||
additional_explanation: Optional[str] = None | ||
|
||
def __post_init__(self): | ||
self.message = ( | ||
f"Found {self.actual_number} {_get_description_for_component(self.component_type)}s." | ||
+ f" Expected exactly {self.expected_number}." | ||
) | ||
if self.additional_explanation is not None: | ||
self.message += " " + self.additional_explanation | ||
|
||
|
||
@dataclass | ||
class NeitherDefinedError(SpecificValidationError): | ||
""" | ||
An error for when the user must define one of two different kinds of component, but has defined neither. | ||
""" | ||
|
||
component_one: ComponentTypeDescription | ||
component_two: ComponentTypeDescription | ||
additional_explanation: Optional[str] = None | ||
|
||
def __post_init__(self): | ||
self.message = ( | ||
f"Found neither {_get_description_for_component(self.component_one)} " | ||
+ f"nor {_get_description_for_component(self.component_two)} defined. " | ||
+ "One of these must be defined." | ||
) | ||
if self.additional_explanation is not None: | ||
self.message += " " + self.additional_explanation | ||
|
||
|
||
@dataclass | ||
class UnitsNotDefinedError(NeitherDefinedError): | ||
""" | ||
An error for when the user has not defined any units for the dataset. | ||
""" | ||
|
||
component_one: ComponentTypeDescription = f"{QbObservationValue.__name__}.unit" | ||
component_two: ComponentTypeDescription = QbMultiUnits | ||
additional_explanation: Optional[str] = None | ||
|
||
|
||
@dataclass | ||
class IncompatibleComponentsError(SpecificValidationError): | ||
""" | ||
An error for when the user has defined components which are incompatible with each-other. | ||
""" | ||
|
||
component_one: ComponentTypeDescription | ||
component_two: ComponentTypeDescription | ||
additional_explanation: Optional[str] = None | ||
|
||
def __post_init__(self): | ||
self.message = ( | ||
f"Both {_get_description_for_component(self.component_one)} " | ||
+ f"and {_get_description_for_component(self.component_two)} have been defined. " | ||
+ f"These components cannot be used together." | ||
) | ||
if self.additional_explanation is not None: | ||
self.message += " " + self.additional_explanation | ||
|
||
|
||
@dataclass | ||
class BothUnitTypesDefinedError(IncompatibleComponentsError): | ||
""" | ||
An error for when the user has both a units column as well as setting `QbObservationValue.unit`. | ||
""" | ||
|
||
component_one: ComponentTypeDescription = f"{QbObservationValue.__name__}.unit" | ||
component_two: ComponentTypeDescription = QbMultiUnits | ||
additional_explanation: Optional[str] = None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
""" | ||
Cube Validation Errors | ||
---------------------- | ||
""" | ||
from dataclasses import dataclass | ||
|
||
from csvqb.models.validationerror import SpecificValidationError | ||
|
||
|
||
@dataclass | ||
class DuplicateColumnTitleError(SpecificValidationError): | ||
""" | ||
An error to inform the user that they have defined two instances of the same column. | ||
""" | ||
|
||
csv_column_title: str | ||
|
||
def __post_init__(self): | ||
self.message = f"Duplicate column title '{self.csv_column_title}'" | ||
|
||
|
||
@dataclass | ||
class ColumnNotFoundInDataError(SpecificValidationError): | ||
""" | ||
An error to inform the user that they have defined a column which cannot be found in the provided data. | ||
""" | ||
|
||
csv_column_title: str | ||
|
||
def __post_init__(self): | ||
self.message = f"Column '{self.csv_column_title}' not found in data provided." | ||
|
||
|
||
@dataclass | ||
class MissingColumnDefinitionError(SpecificValidationError): | ||
""" | ||
An error to inform the user that there is a column in their data that does not have a mapping specified. | ||
""" | ||
|
||
csv_column_title: str | ||
|
||
def __post_init__(self): | ||
self.message = ( | ||
f"Column '{self.csv_column_title}' does not have a mapping defined." | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
""" | ||
Contains Models for mapping data to RDF which are unique to the `csvqb` package. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.