Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions geoh5py/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,18 @@ def from_primitive_type(cls, primitive_type: PrimitiveTypeEnum) -> type:
:return: The data type.
"""
return DataTypeEnum[primitive_type.name].value

@classmethod
def _missing_(cls, value) -> DataTypeEnum:
"""
Allows for case-insensitive matching of enum members.

For example, "Integer" will match "INTEGER".

:param value: The value to match against the enum members.
"""
if isinstance(value, str):
normalized = value.upper()
if normalized in cls.__members__:
return cls[normalized]
return super()._missing_(value)
15 changes: 15 additions & 0 deletions geoh5py/data/data_association_enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,18 @@ class DataAssociationEnum(Enum):
FACE = 4
GROUP = 5
DEPTH = 6

@classmethod
def _missing_(cls, value) -> DataAssociationEnum:
"""
Allows for case-insensitive matching of enum members.

For example, "Cell" will match "CELL".

:param value: The value to match against the enum members.
"""
if isinstance(value, str):
normalized = value.upper()
if normalized in cls.__members__:
return cls[normalized]
return super()._missing_(value)
13 changes: 13 additions & 0 deletions geoh5py/shared/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
from .exceptions import Geoh5FileClosedError


UidOrNumeric = UUID | float | int | None
StringOrNumeric = str | float | int
# pylint: disable=too-many-lines

if TYPE_CHECKING:
Expand Down Expand Up @@ -1439,3 +1441,14 @@ def map_to_class(
class_map[identifier] = member

return class_map


def enum_name_to_str(value: Enum) -> str:
"""
Convert enum name to capitalized string.

:param value: Enum value to convert.

:return: Capitalized string.
"""
return value.name.capitalize()
7 changes: 0 additions & 7 deletions geoh5py/shared/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,6 @@ def to_class(
return out


def none_to_empty_string(value):
"""None transforms to empty string for serialization."""
if value is None:
return ""
return value


def types_to_string(types: list) -> list[str] | str:
if len(types) > 1:
return [f"{{{k.default_type_uid()!s}}}" for k in types]
Expand Down
75 changes: 63 additions & 12 deletions geoh5py/ui_json/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,87 @@
# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

import logging
from pathlib import Path
from typing import Annotated, Any
from uuid import UUID

from pydantic import BeforeValidator, Field, PlainSerializer

from geoh5py.ui_json.validations.form import empty_string_to_none, uuid_to_string
from geoh5py.data import DataAssociationEnum, DataTypeEnum
from geoh5py.groups import Group
from geoh5py.objects import ObjectBase
from geoh5py.shared.utils import enum_name_to_str, stringify
from geoh5py.shared.validators import (
to_class,
to_list,
to_path,
to_type_uid_or_class,
types_to_string,
)
from geoh5py.ui_json.utils import optional_uuid_mapper


logger = logging.getLogger(__name__)

OptionalUUIDList = Annotated[
list[UUID] | None, # pylint: disable=unsupported-binary-operation
BeforeValidator(empty_string_to_none),
PlainSerializer(uuid_to_string),
]

OptionalValueList = Annotated[
float | list[float] | None,
BeforeValidator(empty_string_to_none),
]


def deprecate(value, info):
"""Issue deprecation warning."""
logger.warning("Skipping deprecated field: %s.", info.field_name)
return value


AssociationOptions = Annotated[
DataAssociationEnum,
PlainSerializer(enum_name_to_str),
]

DataTypeOptions = Annotated[
DataTypeEnum,
PlainSerializer(enum_name_to_str),
]


Deprecated = Annotated[
Any,
Field(exclude=True),
BeforeValidator(deprecate),
]

GroupTypes = Annotated[
list[type[Group]],
BeforeValidator(to_class),
BeforeValidator(to_type_uid_or_class),
BeforeValidator(to_list),
PlainSerializer(types_to_string, when_used="json"),
]

MeshTypes = Annotated[
list[type[ObjectBase]],
BeforeValidator(to_class),
BeforeValidator(to_type_uid_or_class),
BeforeValidator(to_list),
PlainSerializer(types_to_string, when_used="json"),
]

OptionalUUID = Annotated[
UUID | None,
BeforeValidator(optional_uuid_mapper),
PlainSerializer(stringify),
]

OptionalUUIDList = Annotated[
list[UUID] | None,
BeforeValidator(optional_uuid_mapper),
PlainSerializer(stringify),
]

OptionalValueList = Annotated[
float | list[float] | None,
BeforeValidator(optional_uuid_mapper),
]

PathList = Annotated[
list[Path],
BeforeValidator(to_path),
BeforeValidator(to_list),
]
85 changes: 15 additions & 70 deletions geoh5py/ui_json/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,31 @@

from enum import Enum
from pathlib import Path
from typing import Annotated, Any
from typing import Any
from uuid import UUID

import numpy as np
from pydantic import (
BaseModel,
ConfigDict,
PlainSerializer,
TypeAdapter,
ValidationError,
field_serializer,
field_validator,
model_validator,
)
from pydantic.alias_generators import to_camel, to_snake
from pydantic.functional_validators import BeforeValidator

from geoh5py.data import DataAssociationEnum, DataTypeEnum
from geoh5py.groups import Group, GroupTypeEnum
from geoh5py.objects import ObjectBase
from geoh5py.shared.validators import (
to_class,
to_list,
to_path,
to_type_uid_or_class,
types_to_string,
)
from geoh5py.ui_json.annotations import OptionalUUIDList, OptionalValueList
from geoh5py.ui_json.validations.form import (
empty_string_to_none,
uuid_to_string,
uuid_to_string_or_numeric,

from geoh5py.groups import GroupTypeEnum
from geoh5py.ui_json.annotations import (
AssociationOptions,
DataTypeOptions,
GroupTypes,
MeshTypes,
OptionalUUID,
OptionalUUIDList,
OptionalValueList,
PathList,
)


Expand Down Expand Up @@ -304,13 +297,6 @@ def valid_choice(self):
return self


PathList = Annotated[
list[Path],
BeforeValidator(to_path),
BeforeValidator(to_list),
]


class FileForm(BaseForm):
"""
File path uijson form.
Expand Down Expand Up @@ -428,21 +414,6 @@ def force_file_description(cls, _):
return ["Directory"]


MeshTypes = Annotated[
list[type[ObjectBase]],
BeforeValidator(to_class),
BeforeValidator(to_type_uid_or_class),
BeforeValidator(to_list),
PlainSerializer(types_to_string, when_used="json"),
]

OptionalUUID = Annotated[
UUID | None, # pylint: disable=unsupported-binary-operation
BeforeValidator(empty_string_to_none),
PlainSerializer(uuid_to_string),
]


class ObjectForm(BaseForm):
"""
Geoh5py object uijson form.
Expand All @@ -457,15 +428,6 @@ class ObjectForm(BaseForm):
mesh_type: MeshTypes


GroupTypes = Annotated[
list[type[Group]],
BeforeValidator(to_class),
BeforeValidator(to_type_uid_or_class),
BeforeValidator(to_list),
PlainSerializer(types_to_string, when_used="json"),
]


class GroupForm(BaseForm):
"""
Geoh5py group uijson form.
Expand All @@ -480,23 +442,6 @@ class GroupForm(BaseForm):
group_type: GroupTypes


Association = Enum( # type: ignore
"Association",
[(k.name, k.name.capitalize()) for k in DataAssociationEnum],
type=str,
)

DataType = Enum( # type: ignore
"DataType", [(k.name, k.name.capitalize()) for k in DataTypeEnum], type=str
)

UUIDOrNumber = Annotated[
UUID | float | int | None, # pylint: disable=unsupported-binary-operation
BeforeValidator(empty_string_to_none),
PlainSerializer(uuid_to_string_or_numeric),
]


class DataFormMixin(BaseModel):
"""
Mixin class to add common attributes a series of data classes.
Expand All @@ -513,8 +458,8 @@ class DataFormMixin(BaseModel):
"""

parent: str
association: Association | list[Association]
data_type: DataType | list[DataType]
association: AssociationOptions | list[AssociationOptions]
data_type: DataTypeOptions | list[DataTypeOptions]


class DataForm(DataFormMixin, BaseForm):
Expand Down Expand Up @@ -562,7 +507,7 @@ class GroupMultiDataForm(BaseForm):
group_type: GroupTypes
group_value: OptionalUUID

data_type: DataType | list[DataType]
data_type: DataTypeOptions | list[DataTypeOptions]
value: str | list[str]
multi_select: bool = True

Expand Down
18 changes: 11 additions & 7 deletions geoh5py/ui_json/ui_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,23 @@
from geoh5py import Workspace
from geoh5py.groups import PropertyGroup, UIJsonGroup
from geoh5py.shared import Entity
from geoh5py.shared.utils import fetch_active_workspace, str2uuid, stringify
from geoh5py.shared.validators import none_to_empty_string
from geoh5py.shared.utils import (
fetch_active_workspace,
none2str,
str2none,
str2uuid,
stringify,
)
from geoh5py.ui_json.forms import BaseForm
from geoh5py.ui_json.validations import ErrorPool, UIJsonError, get_validations
from geoh5py.ui_json.validations.form import empty_string_to_none
from geoh5py.ui_json.validation import ErrorPool, UIJsonError, get_validations


logger = logging.getLogger(__name__)

OptionalPath = Annotated[
Path | None, # pylint: disable=unsupported-binary-operation
BeforeValidator(empty_string_to_none),
PlainSerializer(none_to_empty_string),
Path | None,
BeforeValidator(str2none),
PlainSerializer(none2str),
]


Expand Down
17 changes: 16 additions & 1 deletion geoh5py/ui_json/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,12 @@
from geoh5py import Workspace
from geoh5py.groups import ContainerGroup, Group
from geoh5py.objects import ObjectBase
from geoh5py.shared.utils import fetch_active_workspace
from geoh5py.shared.utils import (
dict_mapper,
entity2uuid,
fetch_active_workspace,
str2none,
)


logger = getLogger(__name__)
Expand Down Expand Up @@ -531,3 +536,13 @@ def monitored_directory_copy(
move(working_path / temp_geoh5, directory_path / temp_geoh5, copy)

return str(directory_path / temp_geoh5)


def optional_uuid_mapper(value: Any):
"""
Take values and convert them into UUID or None (or list of).

:param value: Either a string of entity, or list of.
:return: UUID, Nont or list of.
"""
return dict_mapper(value, [str2none, entity2uuid])
Loading
Loading