Skip to content

Commit 2f2e5e7

Browse files
authored
Merge pull request #866 from MiraGeoscience/GEOPY-2657
GEOPY-2657: Allow UIJson forms to accept geoh5py.Entity
2 parents a4d8267 + e962783 commit 2f2e5e7

14 files changed

Lines changed: 306 additions & 304 deletions

File tree

geoh5py/data/__init__.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,18 @@ def from_primitive_type(cls, primitive_type: PrimitiveTypeEnum) -> type:
8686
:return: The data type.
8787
"""
8888
return DataTypeEnum[primitive_type.name].value
89+
90+
@classmethod
91+
def _missing_(cls, value) -> DataTypeEnum:
92+
"""
93+
Allows for case-insensitive matching of enum members.
94+
95+
For example, "Integer" will match "INTEGER".
96+
97+
:param value: The value to match against the enum members.
98+
"""
99+
if isinstance(value, str):
100+
normalized = value.upper()
101+
if normalized in cls.__members__:
102+
return cls[normalized]
103+
return super()._missing_(value)

geoh5py/data/data_association_enum.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,18 @@ class DataAssociationEnum(Enum):
3737
FACE = 4
3838
GROUP = 5
3939
DEPTH = 6
40+
41+
@classmethod
42+
def _missing_(cls, value) -> DataAssociationEnum:
43+
"""
44+
Allows for case-insensitive matching of enum members.
45+
46+
For example, "Cell" will match "CELL".
47+
48+
:param value: The value to match against the enum members.
49+
"""
50+
if isinstance(value, str):
51+
normalized = value.upper()
52+
if normalized in cls.__members__:
53+
return cls[normalized]
54+
return super()._missing_(value)

geoh5py/shared/utils.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
from .exceptions import Geoh5FileClosedError
4040

4141

42+
UidOrNumeric = UUID | float | int | None
43+
StringOrNumeric = str | float | int
4244
# pylint: disable=too-many-lines
4345

4446
if TYPE_CHECKING:
@@ -1439,3 +1441,14 @@ def map_to_class(
14391441
class_map[identifier] = member
14401442

14411443
return class_map
1444+
1445+
1446+
def enum_name_to_str(value: Enum) -> str:
1447+
"""
1448+
Convert enum name to capitalized string.
1449+
1450+
:param value: Enum value to convert.
1451+
1452+
:return: Capitalized string.
1453+
"""
1454+
return value.name.capitalize()

geoh5py/shared/validators.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -145,13 +145,6 @@ def to_class(
145145
return out
146146

147147

148-
def none_to_empty_string(value):
149-
"""None transforms to empty string for serialization."""
150-
if value is None:
151-
return ""
152-
return value
153-
154-
155148
def types_to_string(types: list) -> list[str] | str:
156149
if len(types) > 1:
157150
return [f"{{{k.default_type_uid()!s}}}" for k in types]

geoh5py/ui_json/annotations.py

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,87 @@
1818
# ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
1919

2020
import logging
21+
from pathlib import Path
2122
from typing import Annotated, Any
2223
from uuid import UUID
2324

2425
from pydantic import BeforeValidator, Field, PlainSerializer
2526

26-
from geoh5py.ui_json.validations.form import empty_string_to_none, uuid_to_string
27+
from geoh5py.data import DataAssociationEnum, DataTypeEnum
28+
from geoh5py.groups import Group
29+
from geoh5py.objects import ObjectBase
30+
from geoh5py.shared.utils import enum_name_to_str, stringify
31+
from geoh5py.shared.validators import (
32+
to_class,
33+
to_list,
34+
to_path,
35+
to_type_uid_or_class,
36+
types_to_string,
37+
)
38+
from geoh5py.ui_json.utils import optional_uuid_mapper
2739

2840

2941
logger = logging.getLogger(__name__)
3042

31-
OptionalUUIDList = Annotated[
32-
list[UUID] | None, # pylint: disable=unsupported-binary-operation
33-
BeforeValidator(empty_string_to_none),
34-
PlainSerializer(uuid_to_string),
35-
]
36-
37-
OptionalValueList = Annotated[
38-
float | list[float] | None,
39-
BeforeValidator(empty_string_to_none),
40-
]
41-
4243

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

4849

50+
AssociationOptions = Annotated[
51+
DataAssociationEnum,
52+
PlainSerializer(enum_name_to_str),
53+
]
54+
55+
DataTypeOptions = Annotated[
56+
DataTypeEnum,
57+
PlainSerializer(enum_name_to_str),
58+
]
59+
60+
4961
Deprecated = Annotated[
5062
Any,
5163
Field(exclude=True),
5264
BeforeValidator(deprecate),
5365
]
66+
67+
GroupTypes = Annotated[
68+
list[type[Group]],
69+
BeforeValidator(to_class),
70+
BeforeValidator(to_type_uid_or_class),
71+
BeforeValidator(to_list),
72+
PlainSerializer(types_to_string, when_used="json"),
73+
]
74+
75+
MeshTypes = Annotated[
76+
list[type[ObjectBase]],
77+
BeforeValidator(to_class),
78+
BeforeValidator(to_type_uid_or_class),
79+
BeforeValidator(to_list),
80+
PlainSerializer(types_to_string, when_used="json"),
81+
]
82+
83+
OptionalUUID = Annotated[
84+
UUID | None,
85+
BeforeValidator(optional_uuid_mapper),
86+
PlainSerializer(stringify),
87+
]
88+
89+
OptionalUUIDList = Annotated[
90+
list[UUID] | None,
91+
BeforeValidator(optional_uuid_mapper),
92+
PlainSerializer(stringify),
93+
]
94+
95+
OptionalValueList = Annotated[
96+
float | list[float] | None,
97+
BeforeValidator(optional_uuid_mapper),
98+
]
99+
100+
PathList = Annotated[
101+
list[Path],
102+
BeforeValidator(to_path),
103+
BeforeValidator(to_list),
104+
]

geoh5py/ui_json/forms.py

Lines changed: 15 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -22,38 +22,31 @@
2222

2323
from enum import Enum
2424
from pathlib import Path
25-
from typing import Annotated, Any
25+
from typing import Any
2626
from uuid import UUID
2727

2828
import numpy as np
2929
from pydantic import (
3030
BaseModel,
3131
ConfigDict,
32-
PlainSerializer,
3332
TypeAdapter,
3433
ValidationError,
3534
field_serializer,
3635
field_validator,
3736
model_validator,
3837
)
3938
from pydantic.alias_generators import to_camel, to_snake
40-
from pydantic.functional_validators import BeforeValidator
41-
42-
from geoh5py.data import DataAssociationEnum, DataTypeEnum
43-
from geoh5py.groups import Group, GroupTypeEnum
44-
from geoh5py.objects import ObjectBase
45-
from geoh5py.shared.validators import (
46-
to_class,
47-
to_list,
48-
to_path,
49-
to_type_uid_or_class,
50-
types_to_string,
51-
)
52-
from geoh5py.ui_json.annotations import OptionalUUIDList, OptionalValueList
53-
from geoh5py.ui_json.validations.form import (
54-
empty_string_to_none,
55-
uuid_to_string,
56-
uuid_to_string_or_numeric,
39+
40+
from geoh5py.groups import GroupTypeEnum
41+
from geoh5py.ui_json.annotations import (
42+
AssociationOptions,
43+
DataTypeOptions,
44+
GroupTypes,
45+
MeshTypes,
46+
OptionalUUID,
47+
OptionalUUIDList,
48+
OptionalValueList,
49+
PathList,
5750
)
5851

5952

@@ -304,13 +297,6 @@ def valid_choice(self):
304297
return self
305298

306299

307-
PathList = Annotated[
308-
list[Path],
309-
BeforeValidator(to_path),
310-
BeforeValidator(to_list),
311-
]
312-
313-
314300
class FileForm(BaseForm):
315301
"""
316302
File path uijson form.
@@ -428,21 +414,6 @@ def force_file_description(cls, _):
428414
return ["Directory"]
429415

430416

431-
MeshTypes = Annotated[
432-
list[type[ObjectBase]],
433-
BeforeValidator(to_class),
434-
BeforeValidator(to_type_uid_or_class),
435-
BeforeValidator(to_list),
436-
PlainSerializer(types_to_string, when_used="json"),
437-
]
438-
439-
OptionalUUID = Annotated[
440-
UUID | None, # pylint: disable=unsupported-binary-operation
441-
BeforeValidator(empty_string_to_none),
442-
PlainSerializer(uuid_to_string),
443-
]
444-
445-
446417
class ObjectForm(BaseForm):
447418
"""
448419
Geoh5py object uijson form.
@@ -457,15 +428,6 @@ class ObjectForm(BaseForm):
457428
mesh_type: MeshTypes
458429

459430

460-
GroupTypes = Annotated[
461-
list[type[Group]],
462-
BeforeValidator(to_class),
463-
BeforeValidator(to_type_uid_or_class),
464-
BeforeValidator(to_list),
465-
PlainSerializer(types_to_string, when_used="json"),
466-
]
467-
468-
469431
class GroupForm(BaseForm):
470432
"""
471433
Geoh5py group uijson form.
@@ -480,23 +442,6 @@ class GroupForm(BaseForm):
480442
group_type: GroupTypes
481443

482444

483-
Association = Enum( # type: ignore
484-
"Association",
485-
[(k.name, k.name.capitalize()) for k in DataAssociationEnum],
486-
type=str,
487-
)
488-
489-
DataType = Enum( # type: ignore
490-
"DataType", [(k.name, k.name.capitalize()) for k in DataTypeEnum], type=str
491-
)
492-
493-
UUIDOrNumber = Annotated[
494-
UUID | float | int | None, # pylint: disable=unsupported-binary-operation
495-
BeforeValidator(empty_string_to_none),
496-
PlainSerializer(uuid_to_string_or_numeric),
497-
]
498-
499-
500445
class DataFormMixin(BaseModel):
501446
"""
502447
Mixin class to add common attributes a series of data classes.
@@ -513,8 +458,8 @@ class DataFormMixin(BaseModel):
513458
"""
514459

515460
parent: str
516-
association: Association | list[Association]
517-
data_type: DataType | list[DataType]
461+
association: AssociationOptions | list[AssociationOptions]
462+
data_type: DataTypeOptions | list[DataTypeOptions]
518463

519464

520465
class DataForm(DataFormMixin, BaseForm):
@@ -562,7 +507,7 @@ class GroupMultiDataForm(BaseForm):
562507
group_type: GroupTypes
563508
group_value: OptionalUUID
564509

565-
data_type: DataType | list[DataType]
510+
data_type: DataTypeOptions | list[DataTypeOptions]
566511
value: str | list[str]
567512
multi_select: bool = True
568513

geoh5py/ui_json/ui_json.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,23 @@
3838
from geoh5py import Workspace
3939
from geoh5py.groups import PropertyGroup, UIJsonGroup
4040
from geoh5py.shared import Entity
41-
from geoh5py.shared.utils import fetch_active_workspace, str2uuid, stringify
42-
from geoh5py.shared.validators import none_to_empty_string
41+
from geoh5py.shared.utils import (
42+
fetch_active_workspace,
43+
none2str,
44+
str2none,
45+
str2uuid,
46+
stringify,
47+
)
4348
from geoh5py.ui_json.forms import BaseForm
44-
from geoh5py.ui_json.validations import ErrorPool, UIJsonError, get_validations
45-
from geoh5py.ui_json.validations.form import empty_string_to_none
49+
from geoh5py.ui_json.validation import ErrorPool, UIJsonError, get_validations
4650

4751

4852
logger = logging.getLogger(__name__)
4953

5054
OptionalPath = Annotated[
51-
Path | None, # pylint: disable=unsupported-binary-operation
52-
BeforeValidator(empty_string_to_none),
53-
PlainSerializer(none_to_empty_string),
55+
Path | None,
56+
BeforeValidator(str2none),
57+
PlainSerializer(none2str),
5458
]
5559

5660

geoh5py/ui_json/utils.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@
3131
from geoh5py import Workspace
3232
from geoh5py.groups import ContainerGroup, Group
3333
from geoh5py.objects import ObjectBase
34-
from geoh5py.shared.utils import fetch_active_workspace
34+
from geoh5py.shared.utils import (
35+
dict_mapper,
36+
entity2uuid,
37+
fetch_active_workspace,
38+
str2none,
39+
)
3540

3641

3742
logger = getLogger(__name__)
@@ -531,3 +536,13 @@ def monitored_directory_copy(
531536
move(working_path / temp_geoh5, directory_path / temp_geoh5, copy)
532537

533538
return str(directory_path / temp_geoh5)
539+
540+
541+
def optional_uuid_mapper(value: Any):
542+
"""
543+
Take values and convert them into UUID or None (or list of).
544+
545+
:param value: Either a string of entity, or list of.
546+
:return: UUID, Nont or list of.
547+
"""
548+
return dict_mapper(value, [str2none, entity2uuid])

0 commit comments

Comments
 (0)