Skip to content

Commit

Permalink
Added _validate_collection and validate_custom_attributes methods
Browse files Browse the repository at this point in the history
  • Loading branch information
imatiushin committed Oct 17, 2023
1 parent 5d429e8 commit 1ac56db
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 29 deletions.
31 changes: 9 additions & 22 deletions deker/ABC/base_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@
from deker.dimensions import Dimension, TimeDimension
from deker.errors import DekerMetaDataError, DekerValidationError
from deker.log import SelfLoggerMixin
from deker.schemas import ArraySchema, TimeDimensionSchema, VArraySchema
from deker.schemas import ArraySchema, VArraySchema
from deker.subset import Subset, VSubset
from deker.tools.array import check_memory, get_id
from deker.validators import is_valid_uuid
from deker.validators import is_valid_uuid, validate_custom_attributes
from deker.tools.schema import create_dimensions
from deker.types.private.classes import ArrayMeta, Serializer
from deker.types.private.typings import FancySlice, Numeric, Slice
Expand Down Expand Up @@ -381,26 +381,13 @@ def update_custom_attributes(self, attributes: dict) -> None:
:param attributes: attributes for updating
"""
if not attributes:
raise DekerValidationError("No attributes passed for update")
for s in self.schema.dimensions:
if (
isinstance(s, TimeDimensionSchema)
and isinstance(s.start_value, str)
and s.start_value.startswith("$")
):
if s.start_value[1:] in self.primary_attributes:
continue
if s.start_value[1:] not in attributes:
for d in self.dimensions:
if d.name == s.name:
attributes[s.start_value[1:]] = d.start_value # type: ignore[attr-defined]
else:
for attr in self.schema.attributes:
if not attr.primary and attr.name not in attributes:
attributes[attr.name] = self.custom_attributes[attr.name]

process_attributes(self.schema, self.primary_attributes, attributes)
attributes = validate_custom_attributes(
self.schema,
self.dimensions,
self.primary_attributes,
self.custom_attributes,
attributes
)
self._adapter.update_meta_custom_attributes(self, attributes)
self.custom_attributes = attributes
self.logger.info(f"{self!s} custom attributes updated: {attributes}")
Expand Down
14 changes: 11 additions & 3 deletions deker/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,9 @@ def get_collection(
self.logger.info(f"Collection {name} not found")
return None

def collection_from_dict(self, collection_data: dict) -> Collection:
"""Create a new ``Collection`` in the database from collection metadata dictionary.
def _validate_collection(self, collection_data: dict) -> Collection:
"""Validate ``Collection`` object and return it without creation.
Not recommended to use except for validation.
:param collection_data: Dictionary with collection metadata
"""
Expand Down Expand Up @@ -432,9 +433,16 @@ def collection_from_dict(self, collection_data: dict) -> Collection:

elif k not in collection_data[key]:
collection_data[key][k] = default_fields[key][k]
collection = self.__adapter.create_collection_from_meta( # type: ignore[return-value]
return self.__adapter.create_collection_from_meta( # type: ignore[return-value]
collection_data, self.__factory
)

def collection_from_dict(self, collection_data: dict) -> Collection:
"""Create a new ``Collection`` in the database from collection metadata dictionary.
:param collection_data: Dictionary with collection metadata
"""
collection = self._validate_collection(collection_data)
self.__adapter.create(collection)
self.logger.debug(f"Collection {collection.name} created from dict")
return collection # type: ignore[return-value]
Expand Down
10 changes: 7 additions & 3 deletions deker/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,10 @@ def arrays(self) -> ArrayManager:

@not_deleted
def create(
self, primary_attributes: Optional[dict] = None, custom_attributes: Optional[dict] = None
self,
primary_attributes: Optional[dict] = None,
custom_attributes: Optional[dict] = None,
id_: Optional[str] = None
) -> Union[Array, VArray]:
"""Create ``Array`` or ``VArray`` according to collection main schema.
Expand All @@ -272,12 +275,13 @@ def create(
Otherwise, only ``Arrays`` will be created.
:param primary_attributes: ``Array`` or ``VArray`` primary attribute
:param custom_attributes: ``VArray`` or ``VArray`` custom attributes
:param custom_attributes: ``Array`` or ``VArray`` custom attributes
:param id_: ``Array`` or ``VArray`` unique UUID string
"""
schema = self.array_schema
shape = schema.arrays_shape if hasattr(schema, "arrays_shape") else schema.shape
check_memory(shape, schema.dtype, self.__adapter.ctx.config.memory_limit)
array = self.__manager.create(primary_attributes, custom_attributes)
array = self.__manager.create(primary_attributes, custom_attributes, id_)
self.logger.debug(
f"{array.__class__.__name__} id={array.id} {primary_attributes=}, {custom_attributes=} created"
)
Expand Down
41 changes: 40 additions & 1 deletion deker/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@

from deker_tools.time import get_utc

from deker import Dimension, TimeDimension
from deker.errors import DekerValidationError


if TYPE_CHECKING:
from deker.schemas import ArraySchema, AttributeSchema, VArraySchema
from deker.schemas import ArraySchema, AttributeSchema, VArraySchema, TimeDimensionSchema


def process_time_dimension_attrs(attributes: dict, attr_name: str) -> datetime.datetime:
Expand Down Expand Up @@ -134,6 +135,44 @@ def process_attributes(
return primary_attributes, custom_attributes


def validate_custom_attributes(
schema: Union["ArraySchema", "VArraySchema"],
dimensions: Tuple[Union[Dimension, TimeDimension], ...],
primary_attributes: Optional[dict],
custom_attributes: Optional[dict],
attributes: Optional[dict],
) -> dict:
"""Validate custom attributes over schema.
:param schema: ArraySchema or VArraySchema instance
:param dimensions: tuple of (V)Array dimensions
:param primary_attributes: (V)Array primary attributes
:param custom_attributes: old custom attributes
:param attributes: new custom attributes to validate
"""
if not attributes:
raise DekerValidationError("No attributes passed for update")
for s in schema.dimensions:
if (
isinstance(s, TimeDimensionSchema)
and isinstance(s.start_value, str)
and s.start_value.startswith("$")
):
if s.start_value[1:] in primary_attributes:
continue
if s.start_value[1:] not in attributes:
for d in dimensions:
if d.name == s.name:
attributes[s.start_value[1:]] = d.start_value # type: ignore[attr-defined]
else:
for attr in schema.attributes:
if not attr.primary and attr.name not in attributes:
attributes[attr.name] = custom_attributes[attr.name]

process_attributes(schema, primary_attributes, attributes)
return attributes


def is_valid_uuid(id_: str) -> bool:
"""Validate if id is in uuid format.
Expand Down

0 comments on commit 1ac56db

Please sign in to comment.