From 254000f46da781e4eaaa8e3c946fe46ebfa2751a Mon Sep 17 00:00:00 2001 From: frehburg Date: Wed, 18 Sep 2024 13:03:07 +0200 Subject: [PATCH 1/6] import warnings --- src/phenopacket_mapper/data_standards/data_model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/phenopacket_mapper/data_standards/data_model.py b/src/phenopacket_mapper/data_standards/data_model.py index 9da997b0..291370d6 100644 --- a/src/phenopacket_mapper/data_standards/data_model.py +++ b/src/phenopacket_mapper/data_standards/data_model.py @@ -12,6 +12,7 @@ from pathlib import Path from types import MappingProxyType from typing import Union, List, Literal, Dict +import warnings from phenopacket_mapper.data_standards import CodeSystem from phenopacket_mapper.data_standards.date import Date From 5ed0414eb85f366f531fdda9a3788f26e7bad357 Mon Sep 17 00:00:00 2001 From: frehburg Date: Wed, 18 Sep 2024 13:03:29 +0200 Subject: [PATCH 2/6] add warnings in fieldvalue.validate --- src/phenopacket_mapper/data_standards/data_model.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/phenopacket_mapper/data_standards/data_model.py b/src/phenopacket_mapper/data_standards/data_model.py index 291370d6..beb35150 100644 --- a/src/phenopacket_mapper/data_standards/data_model.py +++ b/src/phenopacket_mapper/data_standards/data_model.py @@ -73,10 +73,12 @@ def validate(self) -> bool: :return: True if the instance is valid, False otherwise """ if self.field.required and self.value is None: + warnings.warn(f"Field {self.field.name} is required but has no value") return False if self.value is not None and self.field.value_set: if self.value in self.field.value_set: return True + warnings.warn(f"Value {self.value} is not in the value set of field {self.field.name}") return False From 19baaeb34959f1da48be77d107072e958d5277da Mon Sep 17 00:00:00 2001 From: frehburg Date: Wed, 18 Sep 2024 13:03:47 +0200 Subject: [PATCH 3/6] added postinit to datamodel instance --- src/phenopacket_mapper/data_standards/data_model.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/phenopacket_mapper/data_standards/data_model.py b/src/phenopacket_mapper/data_standards/data_model.py index beb35150..87f8884c 100644 --- a/src/phenopacket_mapper/data_standards/data_model.py +++ b/src/phenopacket_mapper/data_standards/data_model.py @@ -196,6 +196,9 @@ class DataModelInstance: data_model: DataModel values: List[DataFieldValue] + def __post_init__(self): + self.validate() + def validate(self) -> bool: """Validates the data model instance based on data model definition From f0e3420a25a0c188fcdcae99c8783189e959cc62 Mon Sep 17 00:00:00 2001 From: frehburg Date: Wed, 18 Sep 2024 13:04:03 +0200 Subject: [PATCH 4/6] added compliance to datamodelinstance --- src/phenopacket_mapper/data_standards/data_model.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/phenopacket_mapper/data_standards/data_model.py b/src/phenopacket_mapper/data_standards/data_model.py index 87f8884c..2f248c41 100644 --- a/src/phenopacket_mapper/data_standards/data_model.py +++ b/src/phenopacket_mapper/data_standards/data_model.py @@ -192,9 +192,13 @@ class DataModelInstance: :ivar data_model: The `DataModel` object that defines the data model for this instance :ivar values: A list of `DataFieldValue` objects, each adhering to the `DataField` definition in the `DataModel` + :ivar compliance: Compliance level to enforce when validating the instance. If 'soft', the instance can have extra + fields that are not in the DataModel. If 'hard', the instance must have all fields in the + DataModel. """ data_model: DataModel values: List[DataFieldValue] + compliance: Literal['soft', 'hard'] = 'soft' def __post_init__(self): self.validate() From 8115649fe2c05e77f58dbe01f679cbef41048a07 Mon Sep 17 00:00:00 2001 From: frehburg Date: Wed, 18 Sep 2024 13:04:30 +0200 Subject: [PATCH 5/6] added warnings, compliance and valueerror to validate datamodelinstance --- src/phenopacket_mapper/data_standards/data_model.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/phenopacket_mapper/data_standards/data_model.py b/src/phenopacket_mapper/data_standards/data_model.py index 2f248c41..f6f60186 100644 --- a/src/phenopacket_mapper/data_standards/data_model.py +++ b/src/phenopacket_mapper/data_standards/data_model.py @@ -211,7 +211,14 @@ def validate(self) -> bool: :return: True if the instance is valid, False otherwise """ + error_msg = f"Instance values do not comply with their respective fields' valuesets. {self}" for v in self.values: if not v.validate(): - return False + if self.compliance == 'hard': + raise ValueError(error_msg) + elif self.compliance == 'soft': + warnings.warn(error_msg) + return False + else: + raise ValueError(f"Compliance level {self.compliance} is not valid") return True From 325d23c1b7ebedc2f430a83d0f1d21e77cab1d2e Mon Sep 17 00:00:00 2001 From: frehburg Date: Wed, 18 Sep 2024 13:10:23 +0200 Subject: [PATCH 6/6] changed date to use post init --- src/phenopacket_mapper/data_standards/date.py | 61 +++++++------------ 1 file changed, 22 insertions(+), 39 deletions(-) diff --git a/src/phenopacket_mapper/data_standards/date.py b/src/phenopacket_mapper/data_standards/date.py index 69a406e4..90490b9f 100644 --- a/src/phenopacket_mapper/data_standards/date.py +++ b/src/phenopacket_mapper/data_standards/date.py @@ -1,4 +1,4 @@ -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import datetime from typing import Tuple, Union, Literal @@ -21,7 +21,7 @@ def _check_invalid_padd_zeros(value: int, places: int = 2, valid_range: Tuple[in return f'{value:0{places}d}' -@dataclass +@dataclass(init=True, eq=True, order=True, repr=False, slots=True) class Date: """Data class for Date @@ -35,44 +35,27 @@ class Date: :ivar minute: the minute of the date :ivar second: the second of the date """ - year: int - month: int - day: int - hour: int - minute: int - second: int - - def __init__( - self, - year: int = 0, month: int = 0, day: int = 0, - hour: int = 0, minute: int = 0, second: int = 0 - ): - """ - Constructor for Date class - - Initializes the fields and their string representations, padding them with zeros if necessary. If no values are - provided, the fields are initialized to 0. - - :param year: year 0-9999 - :param month: 1 - 12 - :param day: 1 - 31 - :param hour: - :param minute: - :param second: - """ - self.year = year - self.month = month - self.day = day - self.hour = hour - self.minute = minute - self.second = second - self.year_str = _check_invalid_padd_zeros(year) - self.month_str = _check_invalid_padd_zeros(month, valid_range=(0, 12)) - self.day_str = _check_invalid_padd_zeros(day, valid_range=(0, 31)) + year: int = field(default=0) + month: int = field(default=0) + day: int = field(default=0) + hour: int = field(default=0) + minute: int = field(default=0) + second: int = field(default=0) + year_str: str = field(init=False) + month_str: str = field(init=False) + day_str: str = field(init=False) + hour_str: str = field(init=False) + minute_str: str = field(init=False) + second_str: str = field(init=False) + + def __post_init__(self): + self.year_str = _check_invalid_padd_zeros(self.year) + self.month_str = _check_invalid_padd_zeros(self.month, valid_range=(0, 12)) + self.day_str = _check_invalid_padd_zeros(self.day, valid_range=(0, 31)) self._check_invalid_day_month_combinations() - self.hour_str = _check_invalid_padd_zeros(hour, valid_range=(0, 23)) - self.minute_str = _check_invalid_padd_zeros(minute, valid_range=(0, 59)) - self.second_str = _check_invalid_padd_zeros(second, valid_range=(0, 59)) + self.hour_str = _check_invalid_padd_zeros(self.hour, valid_range=(0, 23)) + self.minute_str = _check_invalid_padd_zeros(self.minute, valid_range=(0, 59)) + self.second_str = _check_invalid_padd_zeros(self.second, valid_range=(0, 59)) def _check_invalid_day_month_combinations(self): # check month specific day month combinations