Skip to content

Commit

Permalink
json serialization/deserialization for OSDU official version
Browse files Browse the repository at this point in the history
  • Loading branch information
valentin-gauthier-geosiris committed Jun 14, 2024
1 parent 6376fe4 commit a1e06c5
Show file tree
Hide file tree
Showing 13 changed files with 544 additions and 239 deletions.
7 changes: 5 additions & 2 deletions energyml-utils/example/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from dataclasses import fields

from energyml.eml.v2_3.commonv2 import *
from energyml.resqml.v2_0_1.resqmlv2 import DoubleHdf5Array
from energyml.resqml.v2_2.resqmlv2 import (
TriangulatedSetRepresentation,
FaultInterpretation,
Expand Down Expand Up @@ -172,6 +173,8 @@ def tests_content_type():

print(get_content_type_from_class(tr))
print(get_qualified_type_from_class(tr))
print(get_qualified_type_from_class(DoubleHdf5Array()), get_class_from_qualified_type(get_qualified_type_from_class(DoubleHdf5Array())))
print(get_qualified_type_from_class(dor_correct), get_class_from_qualified_type(get_qualified_type_from_class(dor_correct)))

print(gen_energyml_object_path(tr, EpcExportVersion.EXPANDED))
print(gen_energyml_object_path(tr))
Expand Down Expand Up @@ -407,7 +410,7 @@ def test_obj_attribs():

if __name__ == "__main__":
# tests_0()
# tests_content_type()
tests_content_type()

# tests_epc()
# tests_dor()
Expand All @@ -418,4 +421,4 @@ def test_obj_attribs():
# tests_hdf()
# test_local_depth_crs()

test_obj_attribs()
# test_obj_attribs()
33 changes: 30 additions & 3 deletions energyml-utils/example/mainjson.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,38 @@
from src.energyml.utils.serialization import read_energyml_xml_bytes, read_energyml_json_bytes, read_energyml_json_str
import json

from src.energyml.utils.introspection import get_obj_identifier
from src.energyml.utils.serialization import read_energyml_json_str, \
JSON_VERSION
from src.energyml.utils.serialization import to_json_dict


def read_json0():
filePath = "../rc/resqml20.obj_Grid2dRepresentation_7194be4d-169d-420c-98a5-d3ec4671f0cc.json"
with open(filePath, "r") as f:
print(read_energyml_json_str(f.read()))
f_content = f.read()
for o in read_energyml_json_str(f_content, JSON_VERSION.OSDU_OFFICIAL):
print("> ", o)


# import energyml.resqml.v2_0_1.resqmlv2


def write_json0():
filePath = "../rc/resqml20.obj_Grid2dRepresentation_7194be4d-169d-420c-98a5-d3ec4671f0cc.json"
with open(filePath, "r") as f:
f_content = f.read()
objs = read_energyml_json_str(f_content, JSON_VERSION.OSDU_OFFICIAL)
result = json.dumps(to_json_dict(objs[0], {get_obj_identifier(o): o for o in objs}), indent=4)

print(json.dumps(json.loads(f_content), sort_keys=True))
print(json.dumps(json.loads(result), sort_keys=True))

assert json.dumps(json.loads(f_content), sort_keys=True) == json.dumps(json.loads(result), sort_keys=True)


if __name__ == "__main__":
read_json0()
# read_json0()
write_json0()
# print(sys.modules["energyml.resqml.v2_0_1.resqmlv2"].__dict__)
# for name, obj in inspect.getmembers(sys.modules["energyml.resqml.v2_0_1.resqmlv2"], inspect.isclass):
# print(obj)
80 changes: 79 additions & 1 deletion energyml-utils/src/energyml/utils/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import datetime
import re
import uuid as uuid_mod
from typing import List

ENERGYML_NAMESPACES = {
"eml": "http://www.energistics.org/energyml/data/commonv2",
Expand Down Expand Up @@ -118,4 +122,78 @@
RELS_CONTENT_TYPE = "application/vnd.openxmlformats-package.core-properties+xml"
RELS_FOLDER_NAME = "_rels"

primitives = (bool, str, int, float, type(None))
primitives = (bool, str, int, float, type(None))


# ______ __ _
# / ____/_ ______ _____/ /_(_)___ ____ _____
# / /_ / / / / __ \/ ___/ __/ / __ \/ __ \/ ___/
# / __/ / /_/ / / / / /__/ /_/ / /_/ / / / (__ )
# /_/ \__,_/_/ /_/\___/\__/_/\____/_/ /_/____/


def snake_case(s: str) -> str:
""" Transform a str into snake case. """
s = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', s)
s = re.sub('__([A-Z])', r'_\1', s)
s = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s)
return s.lower()


def pascal_case(s: str) -> str:
""" Transform a str into pascal case. """
return snake_case(s).replace("_", " ").title().replace(" ", "")


def flatten_concatenation(matrix) -> List:
"""
Flatten a matrix.
Example :
[ [a,b,c], [d,e,f], [ [x,y,z], [0] ] ]
will be translated in: [a, b, c, d, e, f, [x,y,z], [0]]
:param matrix:
:return:
"""
flat_list = []
for row in matrix:
flat_list += row
return flat_list


def parse_content_type(ct: str):
return re.search(RGX_CONTENT_TYPE, ct)


def parse_qualified_type(ct: str):
return re.search(RGX_QUALIFIED_TYPE, ct)


def now(time_zone=datetime.timezone(datetime.timedelta(hours=1), "UTC")) -> float:
""" Return an epoch value """
return datetime.datetime.timestamp(datetime.datetime.now(time_zone))


def epoch(time_zone=datetime.timezone(datetime.timedelta(hours=1), "UTC")) -> int:
return int(now(time_zone))


def date_to_epoch(date: str) -> int:
"""
Transform a energyml date into an epoch datetime
:return: int
"""
return int(datetime.datetime.fromisoformat(date).timestamp())


def epoch_to_date(epoch_value: int, time_zone=datetime.timezone(datetime.timedelta(hours=1), "UTC")) -> str:
date = datetime.datetime.fromtimestamp(epoch_value, time_zone)
return date.strftime("%Y-%m-%dT%H:%M:%S%z")


def gen_uuid() -> str:
"""
Generate a new uuid.
:return:
"""
return str(uuid_mod.uuid4())
96 changes: 3 additions & 93 deletions energyml-utils/src/energyml/utils/epc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,18 @@
Identifier, Keywords1
from xsdata.formats.dataclass.models.generics import DerivedElement

from .constants import RELS_CONTENT_TYPE, RELS_FOLDER_NAME
from .introspection import (
get_class_from_content_type,
get_obj_type, search_attribute_matching_type, get_obj_version, get_obj_uuid,
get_object_type_for_file_path_from_class, get_content_type_from_class, get_direct_dor_list, epoch_to_date, epoch,
gen_uuid, get_object_attribute_no_verif
gen_uuid, get_obj_identifier
)
from .manager import get_class_pkg, get_class_pkg_version
from .serialization import (
serialize_xml, read_energyml_xml_str, read_energyml_xml_bytes
)
from .uri import Uri
from .xml import is_energyml_content_type, parse_content_type, parse_qualified_type

from .constants import RELS_CONTENT_TYPE, RELS_FOLDER_NAME
from .xml import is_energyml_content_type


class EpcExportVersion(Enum):
Expand Down Expand Up @@ -471,94 +469,6 @@ def read_stream(cls, epc_file_io: BytesIO): # returns an Epc instance
# /____//____/


def get_obj_pkg_pkgv_type_uuid_version(obj: Any) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]]:
"""
return from an energyml object or a DOR a tuple :
- package : e.g. resqml|eml|witsml|prodml
- package version : e.g. 20
- type : e.g. obj_TriangulatedSetRepresentation
- uuid
- object version
:param obj:
:return:
"""
pkg: Optional[str] = get_class_pkg(obj)
pkg_v: Optional[str] = get_class_pkg_version(obj)
obj_type: Optional[str] = get_obj_type(obj)
obj_uuid = get_obj_uuid(obj)
obj_version = get_obj_version(obj)

ct = None
try:
ct = get_object_attribute_no_verif(
obj, "content_type"
)
except:
pass

if ct is not None:
ct_match = parse_content_type(ct)
print("ct : ", ct_match)
if ct_match is not None:
pkg = ct_match.group("domain")
pkg_v = ct_match.group("domainVersion")
obj_type = ct_match.group("type")
else:
try:
qt = get_object_attribute_no_verif(
obj, "qualified_type"
)
qt_match = parse_qualified_type(qt)
print("qt : ", qt, obj.__dict__, qt_match)
if qt_match is not None:
pkg = qt_match.group("domain")
pkg_v = qt_match.group("domainVersion")
obj_type = qt_match.group("type")
except:
pass


# flattening version
if pkg_v is not None:
pkg_v = pkg_v.replace(".", "")

return pkg, pkg_v, obj_type, obj_uuid, obj_version


def get_obj_identifier(obj: Any) -> str:
"""
Generates an objet identifier as : 'OBJ_UUID.OBJ_VERSION'
If the object version is None, the result is 'OBJ_UUID.'
:param obj:
:return: str
"""
obj_obj_version = get_obj_version(obj)
if obj_obj_version is None:
obj_obj_version = ""
obj_uuid = get_obj_uuid(obj)
return f"{obj_uuid}.{obj_obj_version}"


def get_obj_uri(obj: Any, dataspace: Optional[str] = None) -> Uri:
"""
Generates an objet etp Uri from an objet or a DOR
:param obj:
:param dataspace: the etp dataspace
:return: str
"""
domain, domain_version, object_type, obj_uuid, obj_version = get_obj_pkg_pkgv_type_uuid_version(obj)

return Uri(
dataspace=dataspace,
domain=domain,
domain_version=domain_version,
object_type=object_type,
uuid=obj_uuid,
version=obj_version,
)



def get_reverse_dor_list(obj_list: List[Any], key_func: Callable = get_obj_identifier) -> Dict[str, List[Any]]:
"""
Compute a dict with 'OBJ_UUID.OBJ_VERSION' as Key, and list of DOR that reference it.
Expand Down
15 changes: 14 additions & 1 deletion energyml-utils/src/energyml/utils/exception.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Copyright (c) 2023-2024 Geosiris.
# SPDX-License-Identifier: Apache-2.0
from typing import Optional


class NotImplementedError(Exception):
"""Exception for not implemented functions"""
Expand All @@ -11,6 +13,17 @@ def __init__(self, msg):
class NoCrsError(Exception):
pass


class ObjectNotFoundNotError(Exception):
def __init__(self, obj_id):
super().__init__(f"Object id: {obj_id}")
super().__init__(f"Object id: {obj_id}")


class UnknownTypeFromQualifiedType(Exception):
def __init__(self, qt: Optional[str] = None):
super().__init__(f"not matchable qualified type: {qt}")


class NotParsableType(Exception):
def __init__(self, t: Optional[str] = None):
super().__init__(f"type: {t}")
Loading

0 comments on commit a1e06c5

Please sign in to comment.