Source code for LOGS.Auxiliary.CustomFieldValueTypeChecker

from datetime import date, datetime, time, timedelta
from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Union

from LOGS.Auxiliary.MinimalModelGenerator import MinimalModelGenerator
from LOGS.Auxiliary.Tools import Tools
from LOGS.Converter.DateTimeRange import DateTimeRange
from LOGS.Entities.CustomFieldModels import CustomFieldDataType

if TYPE_CHECKING:
    from LOGS.LOGSConnection import LOGSConnection


[docs] class CustomFieldValueTypeChecker: _customFieldTypeMap: Dict[CustomFieldDataType, Union[type, str, Callable]] = { CustomFieldDataType.String: str, CustomFieldDataType.StringArray: str, CustomFieldDataType.Integer: int, CustomFieldDataType.IntegerArray: int, CustomFieldDataType.Float: float, CustomFieldDataType.FloatArray: float, CustomFieldDataType.Boolean: bool, CustomFieldDataType.Date: date, CustomFieldDataType.DateArray: date, CustomFieldDataType.DateTime: datetime, CustomFieldDataType.DateTimeArray: datetime, CustomFieldDataType.Time: time, CustomFieldDataType.TimeArray: time, CustomFieldDataType.DateTimeRange: DateTimeRange, CustomFieldDataType.TimeRange: lambda value: ( timedelta(seconds=value) if isinstance(value, (int, float)) else value ), CustomFieldDataType.Dataset: "DatasetMinimal", CustomFieldDataType.DatasetArray: "DatasetMinimal", CustomFieldDataType.Sample: "SampleMinimal", CustomFieldDataType.SampleArray: "SampleMinimal", CustomFieldDataType.Project: "ProjectMinimal", CustomFieldDataType.ProjectArray: "ProjectMinimal", CustomFieldDataType.Person: "PersonMinimal", CustomFieldDataType.PersonArray: "PersonMinimal", CustomFieldDataType.Method: "MethodMinimal", CustomFieldDataType.MethodArray: "MethodMinimal", CustomFieldDataType.SharedContent: "SharedContentMinimal", CustomFieldDataType.SharedContentArray: "SharedContentMinimal", CustomFieldDataType.LabNotebook: "LabNotebookMinimal", CustomFieldDataType.LabNotebookArray: "LabNotebookMinimal", CustomFieldDataType.LabNotebookExperiment: "LabNotebookExperimentMinimal", CustomFieldDataType.LabNotebookExperimentArray: "LabNotebookExperimentMinimal", CustomFieldDataType.LabNotebookEntry: "LabNotebookEntryMinimal", CustomFieldDataType.LabNotebookEntryArray: "LabNotebookEntryMinimal", CustomFieldDataType.Attachment: "AttachmentMinimal", CustomFieldDataType.InventoryItem: "InventoryItemMinimal", CustomFieldDataType.InventoryItemArray: "InventoryItemMinimal", CustomFieldDataType.Barcode: str, CustomFieldDataType.Url: str, CustomFieldDataType.UrlArray: str, } _converter: Callable[[Any, str], Any] _isArrayType: bool _enumOptions: Any = None @classmethod def _arrayTypeCheck(cls, data_type: CustomFieldDataType) -> bool: return data_type.name.endswith("Array") @classmethod def _generateConverter( cls, dataType: CustomFieldDataType, isArrayType: bool, connection: Optional["LOGSConnection"], ) -> Callable[[Any, str], Any]: dataClass = cls._customFieldTypeMap.get(dataType, None) if dataClass is None: raise ValueError(f"Unknown custom field type: '{dataType}'") if isinstance(dataClass, str): if isArrayType: return lambda value, fieldName: MinimalModelGenerator.MinimalFromList( value, dataClass, fieldName, connection ) else: return lambda value, fieldName: MinimalModelGenerator.MinimalFromSingle( value, dataClass, fieldName, connection ) elif isinstance(dataClass, type): if isArrayType: return lambda value, fieldName: Tools.checkListAndConvert( value, dataClass, fieldName, allowNone=True ) else: return lambda value, fieldName: Tools.checkAndConvert( value, dataClass, f"{fieldName} <{dataType.name}>", allowNone=True ) elif callable(dataClass): if isArrayType: return lambda value, fieldName: Tools.checkListAndConvert( value, dataType.name, fieldName, dataClass, allowNone=True ) else: return lambda value, fieldName: Tools.checkAndConvert( value, dataType.name, fieldName, dataClass, allowNone=True ) else: raise ValueError(f"Cannot create converter for type '{dataType}'") def __str__(self): s = " array" if self._isArrayType else "" return f"<{type(self).__name__} [{self.dataType.name}]{s}>" def __init__( self, dataType: CustomFieldDataType, connection: Optional["LOGSConnection"], enumOptions: Any = None, ): Tools.checkAndConvert( dataType, CustomFieldDataType, "dataType", allowNone=False ) self.dataType = dataType self._isArrayType = self._arrayTypeCheck(dataType) self._converter = self._generateConverter( dataType, self._isArrayType, connection ) self.enumOptions = enumOptions
[docs] def checkAndConvertIgnoreEnumOptions(self, value: Any, fieldName: str = "value"): if not self._isArrayType and isinstance(value, list): return [ self._converter(v, f"{fieldName}[{i}]") for i, v in enumerate(value) ] return self._converter(value, fieldName)
[docs] def checkAndConvert(self, value: Any, fieldName: str = "value", allowNone=True): if allowNone and value is None: return None result = self._converter(value, fieldName) if self._isArrayType: if self._enumOptions: for i, v in enumerate(result): if v not in self._enumOptions: raise ValueError( f"Value of field '{fieldName}[{i}]' must be out of enumOptions. (Value '{Tools.truncString(str(v))}' is not accepted)" ) else: if self._enumOptions and result not in self._enumOptions: raise ValueError( f"Value of field '{fieldName}' must be out of enumOptions. (Value '{Tools.truncString(str(result))}' is not accepted)" ) return result
@property def enumOptions(self) -> Any: return self._enumOptions @enumOptions.setter def enumOptions(self, value: Any): if not value: self._enumOptions = None return self._enumOptions = Tools.checkAndConvert( value, list, "enumOptions", allowNone=False )