compressedfhir 1.0.3__tar.gz → 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of compressedfhir might be problematic. Click here for more details.
- {compressedfhir-1.0.3/compressedfhir.egg-info → compressedfhir-1.0.4}/PKG-INFO +1 -1
- compressedfhir-1.0.4/VERSION +1 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/compressed_dict/v1/compressed_dict.py +2 -6
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py +107 -0
- compressedfhir-1.0.4/compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py +150 -0
- compressedfhir-1.0.4/compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py +171 -0
- compressedfhir-1.0.4/compressedfhir/utilities/json_serializers/type_preservation_decoder.py +110 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/json_serializers/type_preservation_serializer.py +3 -1
- {compressedfhir-1.0.3 → compressedfhir-1.0.4/compressedfhir.egg-info}/PKG-INFO +1 -1
- compressedfhir-1.0.3/VERSION +0 -1
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py +0 -82
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py +0 -60
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/type_preservation_decoder.py +0 -63
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/LICENSE +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/MANIFEST.in +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/Makefile +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/README.md +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/base_resource_list.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle_entry.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle_entry_list.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle_entry_request.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle_entry_response.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle_entry_search.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_identifier.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_link.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_meta.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_resource.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_resource_list.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_resource_map.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_bundle_entry.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_bundle_entry_list.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_bundle_entry_request.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_bundle_entry_response.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_fhir_bundle.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_fhir_resource.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_fhir_resource_list.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_fhir_resource_map.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/py.typed +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/compressed_dict/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/compressed_dict/v1/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/compressed_dict/v1/compressed_dict_access_error.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/compressed_dict/v1/compressed_dict_storage_mode.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/compressed_dict/v1/test/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/fhir_json_encoder.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/json_helpers.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/json_serializers/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/json_serializers/test/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/json_serializers/test/test_type_preservation_encoder.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/json_serializers/type_preservation_encoder.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/ordered_dict_to_dict_converter/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/ordered_dict_to_dict_converter/ordered_dict_to_dict_converter.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/string_compressor/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/string_compressor/v1/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/string_compressor/v1/string_compressor.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/string_compressor/v1/test/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/string_compressor/v1/test/test_string_compressor.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/test/__init__.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/test/test_fhir_json_encoder.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/test/test_json_helpers.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir.egg-info/SOURCES.txt +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir.egg-info/dependency_links.txt +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir.egg-info/not-zip-safe +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir.egg-info/requires.txt +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir.egg-info/top_level.txt +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/setup.cfg +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/setup.py +0 -0
- {compressedfhir-1.0.3 → compressedfhir-1.0.4}/tests/__init__.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.0.4
|
|
@@ -176,9 +176,7 @@ class CompressedDict[K, V](MutableMapping[K, V]):
|
|
|
176
176
|
assert isinstance(dictionary, OrderedDict)
|
|
177
177
|
if storage_type == "compressed":
|
|
178
178
|
# Serialize to JSON and compress with zlib
|
|
179
|
-
json_str = TypePreservationSerializer.serialize(
|
|
180
|
-
dictionary, separators=(",", ":")
|
|
181
|
-
)
|
|
179
|
+
json_str = TypePreservationSerializer.serialize(dictionary)
|
|
182
180
|
return zlib.compress(
|
|
183
181
|
json_str.encode("utf-8"), level=zlib.Z_BEST_COMPRESSION
|
|
184
182
|
)
|
|
@@ -219,9 +217,7 @@ class CompressedDict[K, V](MutableMapping[K, V]):
|
|
|
219
217
|
decompressed_bytes: bytes = zlib.decompress(serialized_dict_bytes)
|
|
220
218
|
decoded_text: str = decompressed_bytes.decode("utf-8")
|
|
221
219
|
# noinspection PyTypeChecker
|
|
222
|
-
decompressed_dict = TypePreservationSerializer.deserialize(
|
|
223
|
-
decoded_text, object_pairs_hook=OrderedDict
|
|
224
|
-
)
|
|
220
|
+
decompressed_dict = TypePreservationSerializer.deserialize(decoded_text)
|
|
225
221
|
assert isinstance(decompressed_dict, OrderedDict)
|
|
226
222
|
return cast(OrderedDict[K, V], decompressed_dict)
|
|
227
223
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
4
|
from typing import Any, cast
|
|
3
5
|
|
|
@@ -358,3 +360,108 @@ def test_transaction_error_handling() -> None:
|
|
|
358
360
|
# Verify the dictionary state remains unchanged
|
|
359
361
|
with compressed_dict.transaction() as d:
|
|
360
362
|
assert d.dict() == {"key1": "value1", "key2": "value2"}
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def test_nested_dict_with_datetime() -> None:
|
|
366
|
+
nested_dict = {
|
|
367
|
+
"beneficiary": {"reference": "Patient/1234567890123456703", "type": "Patient"},
|
|
368
|
+
"class": [
|
|
369
|
+
{
|
|
370
|
+
"name": "Aetna Plan",
|
|
371
|
+
"type": {
|
|
372
|
+
"coding": [
|
|
373
|
+
{
|
|
374
|
+
"code": "plan",
|
|
375
|
+
"display": "Plan",
|
|
376
|
+
"system": "http://terminology.hl7.org/CodeSystem/coverage-class",
|
|
377
|
+
}
|
|
378
|
+
]
|
|
379
|
+
},
|
|
380
|
+
"value": "AE303",
|
|
381
|
+
}
|
|
382
|
+
],
|
|
383
|
+
"costToBeneficiary": [
|
|
384
|
+
{
|
|
385
|
+
"type": {"text": "Annual Physical Exams NMC - In Network"},
|
|
386
|
+
"valueQuantity": {
|
|
387
|
+
"system": "http://aetna.com/Medicare/CostToBeneficiary/ValueQuantity/code",
|
|
388
|
+
"unit": "$",
|
|
389
|
+
"value": 50.0,
|
|
390
|
+
},
|
|
391
|
+
}
|
|
392
|
+
],
|
|
393
|
+
"id": "3456789012345670304",
|
|
394
|
+
"identifier": [
|
|
395
|
+
{
|
|
396
|
+
"system": "https://sources.aetna.com/coverage/identifier/membershipid/59",
|
|
397
|
+
"type": {
|
|
398
|
+
"coding": [
|
|
399
|
+
{
|
|
400
|
+
"code": "SN",
|
|
401
|
+
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
|
402
|
+
}
|
|
403
|
+
]
|
|
404
|
+
},
|
|
405
|
+
"value": "435679010300+AE303+2021-01-01",
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
"id": "uuid",
|
|
409
|
+
"system": "https://www.icanbwell.com/uuid",
|
|
410
|
+
"value": "92266603-aa8b-58c6-99bd-326fd1da1896",
|
|
411
|
+
},
|
|
412
|
+
],
|
|
413
|
+
"meta": {
|
|
414
|
+
"security": [
|
|
415
|
+
{"code": "aetna", "system": "https://www.icanbwell.com/owner"},
|
|
416
|
+
{"code": "aetna", "system": "https://www.icanbwell.com/access"},
|
|
417
|
+
{"code": "aetna", "system": "https://www.icanbwell.com/vendor"},
|
|
418
|
+
{"code": "proa", "system": "https://www.icanbwell.com/connectionType"},
|
|
419
|
+
],
|
|
420
|
+
"source": "http://mock-server:1080/test_patient_access_transformer/source/4_0_0/Coverage/3456789012345670304",
|
|
421
|
+
},
|
|
422
|
+
"network": "Medicare - MA/NY/NJ - Full Reciprocity",
|
|
423
|
+
"payor": [
|
|
424
|
+
{
|
|
425
|
+
"display": "Aetna",
|
|
426
|
+
"reference": "Organization/6667778889990000015",
|
|
427
|
+
"type": "Organization",
|
|
428
|
+
}
|
|
429
|
+
],
|
|
430
|
+
"period": {
|
|
431
|
+
"end": datetime.fromisoformat("2021-12-31").date(),
|
|
432
|
+
"start": datetime.fromisoformat("2021-01-01").date(),
|
|
433
|
+
},
|
|
434
|
+
"policyHolder": {"reference": "Patient/1234567890123456703", "type": "Patient"},
|
|
435
|
+
"relationship": {
|
|
436
|
+
"coding": [
|
|
437
|
+
{
|
|
438
|
+
"code": "self",
|
|
439
|
+
"system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship",
|
|
440
|
+
}
|
|
441
|
+
]
|
|
442
|
+
},
|
|
443
|
+
"resourceType": "Coverage",
|
|
444
|
+
"status": "active",
|
|
445
|
+
"subscriber": {"reference": "Patient/1234567890123456703", "type": "Patient"},
|
|
446
|
+
"subscriberId": "435679010300",
|
|
447
|
+
"type": {
|
|
448
|
+
"coding": [
|
|
449
|
+
{
|
|
450
|
+
"code": "PPO",
|
|
451
|
+
"display": "preferred provider organization policy",
|
|
452
|
+
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
|
|
453
|
+
}
|
|
454
|
+
]
|
|
455
|
+
},
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
compressed_dict = CompressedDict(
|
|
459
|
+
initial_dict=nested_dict,
|
|
460
|
+
storage_mode=CompressedDictStorageMode.compressed(),
|
|
461
|
+
properties_to_cache=[],
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
plain_dict = compressed_dict.to_plain_dict()
|
|
465
|
+
|
|
466
|
+
assert plain_dict["period"]["start"] == nested_dict["period"]["start"] # type: ignore[index]
|
|
467
|
+
assert plain_dict == nested_dict
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from datetime import datetime, date
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from typing import Type, Any, Dict, Optional
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from compressedfhir.utilities.json_serializers.type_preservation_decoder import (
|
|
7
|
+
TypePreservationDecoder,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestCustomObject:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
name: str,
|
|
15
|
+
value: int,
|
|
16
|
+
created_at: Optional[datetime] = None,
|
|
17
|
+
nested_data: Optional[Dict[str, Any]] = None,
|
|
18
|
+
):
|
|
19
|
+
self.name: str = name
|
|
20
|
+
self.value: int = value
|
|
21
|
+
self.created_at: Optional[datetime] = created_at
|
|
22
|
+
self.nested_data: Optional[Dict[str, Any]] = nested_data
|
|
23
|
+
|
|
24
|
+
def __eq__(self, other: Any) -> bool:
|
|
25
|
+
if not isinstance(other, TestCustomObject):
|
|
26
|
+
return False
|
|
27
|
+
return (
|
|
28
|
+
self.name == other.name
|
|
29
|
+
and self.value == other.value
|
|
30
|
+
and self.created_at == other.created_at
|
|
31
|
+
and self.nested_data == other.nested_data
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.mark.parametrize(
|
|
36
|
+
"input_type, input_dict, expected_type",
|
|
37
|
+
[
|
|
38
|
+
(
|
|
39
|
+
"datetime",
|
|
40
|
+
{"__type__": "datetime", "iso": "2023-01-01T00:00:00+00:00"},
|
|
41
|
+
datetime,
|
|
42
|
+
),
|
|
43
|
+
("date", {"__type__": "date", "iso": "2023-01-01"}, date),
|
|
44
|
+
("decimal", {"__type__": "decimal", "value": "3.14"}, Decimal),
|
|
45
|
+
("complex", {"__type__": "complex", "real": 3, "imag": 4}, complex),
|
|
46
|
+
("bytes", {"__type__": "bytes", "value": "test"}, bytes),
|
|
47
|
+
("set", {"__type__": "set", "values": [1, 2, 3]}, set),
|
|
48
|
+
],
|
|
49
|
+
)
|
|
50
|
+
def test_complex_type_decoding(
|
|
51
|
+
input_type: str, input_dict: Dict[str, Any], expected_type: Type[Any]
|
|
52
|
+
) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Test decoding of various complex types
|
|
55
|
+
"""
|
|
56
|
+
decoded = TypePreservationDecoder.decode(input_dict)
|
|
57
|
+
assert isinstance(decoded, expected_type)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_custom_object_decoding() -> None:
|
|
61
|
+
"""
|
|
62
|
+
Test decoding of custom objects
|
|
63
|
+
"""
|
|
64
|
+
custom_obj_dict = {
|
|
65
|
+
"__type__": "TestCustomObject",
|
|
66
|
+
"__module__": __name__,
|
|
67
|
+
"attributes": {"name": "test", "value": 42},
|
|
68
|
+
}
|
|
69
|
+
decoded = TypePreservationDecoder.decode(custom_obj_dict)
|
|
70
|
+
assert isinstance(decoded, TestCustomObject)
|
|
71
|
+
assert decoded.name == "test"
|
|
72
|
+
assert decoded.value == 42
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_custom_decoder() -> None:
|
|
76
|
+
"""
|
|
77
|
+
Test custom decoder functionality
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def custom_decoder(data: Dict[str, Any]) -> Any:
|
|
81
|
+
if data.get("__type__") == "special_type":
|
|
82
|
+
return f"Decoded: {data['value']}"
|
|
83
|
+
return data
|
|
84
|
+
|
|
85
|
+
special_dict = {"__type__": "special_type", "value": "test"}
|
|
86
|
+
decoded = TypePreservationDecoder.decode(
|
|
87
|
+
special_dict, custom_decoders={"special_type": custom_decoder}
|
|
88
|
+
)
|
|
89
|
+
assert decoded == "Decoded: test"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_nested_datetime_decoding() -> None:
|
|
93
|
+
"""
|
|
94
|
+
Test decoding of nested datetime fields
|
|
95
|
+
"""
|
|
96
|
+
nested_datetime_dict = {
|
|
97
|
+
"__type__": "TestCustomObject",
|
|
98
|
+
"__module__": __name__,
|
|
99
|
+
"attributes": {
|
|
100
|
+
"name": "test",
|
|
101
|
+
"value": 42,
|
|
102
|
+
"created_at": {"__type__": "datetime", "iso": "2023-06-15T10:30:00"},
|
|
103
|
+
"nested_data": {
|
|
104
|
+
"timestamp": {"__type__": "datetime", "iso": "2023-06-16T15:45:00"}
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
decoded: TestCustomObject = TypePreservationDecoder.decode(nested_datetime_dict)
|
|
110
|
+
|
|
111
|
+
assert isinstance(decoded, TestCustomObject)
|
|
112
|
+
assert decoded.name == "test"
|
|
113
|
+
assert decoded.value == 42
|
|
114
|
+
|
|
115
|
+
# Check nested datetime fields
|
|
116
|
+
assert hasattr(decoded, "created_at")
|
|
117
|
+
assert isinstance(decoded.created_at, datetime)
|
|
118
|
+
assert decoded.created_at.year == 2023
|
|
119
|
+
assert decoded.created_at.month == 6
|
|
120
|
+
assert decoded.created_at.day == 15
|
|
121
|
+
|
|
122
|
+
assert hasattr(decoded, "nested_data")
|
|
123
|
+
assert isinstance(decoded.nested_data, dict)
|
|
124
|
+
assert "timestamp" in decoded.nested_data
|
|
125
|
+
assert isinstance(decoded.nested_data["timestamp"], datetime)
|
|
126
|
+
assert decoded.nested_data["timestamp"].year == 2023
|
|
127
|
+
assert decoded.nested_data["timestamp"].month == 6
|
|
128
|
+
assert decoded.nested_data["timestamp"].day == 16
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def test_direct_value_decoding() -> None:
|
|
132
|
+
"""
|
|
133
|
+
Test decoding of direct values without type markers
|
|
134
|
+
"""
|
|
135
|
+
# Test datetime direct string
|
|
136
|
+
datetime_str = "2023-01-01T00:00:00"
|
|
137
|
+
decoded_datetime = TypePreservationDecoder.decode(datetime_str)
|
|
138
|
+
assert decoded_datetime == datetime_str
|
|
139
|
+
|
|
140
|
+
# Test list with mixed types
|
|
141
|
+
mixed_list = [
|
|
142
|
+
{"__type__": "datetime", "iso": "2023-06-15T10:30:00"},
|
|
143
|
+
42,
|
|
144
|
+
"plain string",
|
|
145
|
+
]
|
|
146
|
+
decoded_list = TypePreservationDecoder.decode(mixed_list)
|
|
147
|
+
assert len(decoded_list) == 3
|
|
148
|
+
assert isinstance(decoded_list[0], datetime)
|
|
149
|
+
assert decoded_list[1] == 42
|
|
150
|
+
assert decoded_list[2] == "plain string"
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from datetime import datetime, timezone, date
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
from logging import Logger
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from compressedfhir.utilities.json_serializers.type_preservation_serializer import (
|
|
8
|
+
TypePreservationSerializer,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestCustomObject:
|
|
13
|
+
def __init__(self, name: str, value: int):
|
|
14
|
+
self.name: str = name
|
|
15
|
+
self.value: int = value
|
|
16
|
+
|
|
17
|
+
def __eq__(self, other: Any) -> bool:
|
|
18
|
+
if not isinstance(other, TestCustomObject):
|
|
19
|
+
return False
|
|
20
|
+
return self.name == other.name and self.value == other.value
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_complex_data_serialization() -> None:
|
|
24
|
+
"""
|
|
25
|
+
Test serialization and deserialization of complex data
|
|
26
|
+
"""
|
|
27
|
+
complex_data = {
|
|
28
|
+
"timestamp": datetime.now(timezone.utc),
|
|
29
|
+
"today": date.today(),
|
|
30
|
+
"precise_value": Decimal("3.14159"),
|
|
31
|
+
"complex_number": 3 + 4j,
|
|
32
|
+
"byte_data": b"Hello",
|
|
33
|
+
"unique_items": {1, 2, 3},
|
|
34
|
+
"custom_obj": TestCustomObject("test", 42),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Serialize
|
|
38
|
+
serialized = TypePreservationSerializer.serialize(complex_data)
|
|
39
|
+
|
|
40
|
+
# Deserialize
|
|
41
|
+
deserialized = TypePreservationSerializer.deserialize(serialized)
|
|
42
|
+
|
|
43
|
+
# Verify types
|
|
44
|
+
assert isinstance(deserialized["timestamp"], datetime)
|
|
45
|
+
assert isinstance(deserialized["today"], date)
|
|
46
|
+
assert isinstance(deserialized["precise_value"], Decimal)
|
|
47
|
+
assert isinstance(deserialized["complex_number"], complex)
|
|
48
|
+
assert isinstance(deserialized["byte_data"], bytes)
|
|
49
|
+
assert isinstance(deserialized["unique_items"], set)
|
|
50
|
+
assert isinstance(deserialized["custom_obj"], TestCustomObject)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_nested_complex_data() -> None:
|
|
54
|
+
"""
|
|
55
|
+
Test serialization of nested complex data
|
|
56
|
+
"""
|
|
57
|
+
nested_data = {"level1": {"level2": {"timestamp": datetime.now(timezone.utc)}}}
|
|
58
|
+
|
|
59
|
+
serialized = TypePreservationSerializer.serialize(nested_data)
|
|
60
|
+
deserialized = TypePreservationSerializer.deserialize(serialized)
|
|
61
|
+
|
|
62
|
+
assert isinstance(deserialized["level1"]["level2"]["timestamp"], datetime)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_nested_dict() -> None:
|
|
66
|
+
"""
|
|
67
|
+
Test serialization of nested dictionaries
|
|
68
|
+
"""
|
|
69
|
+
logger: Logger = logging.getLogger(__name__)
|
|
70
|
+
nested_dict = {
|
|
71
|
+
"beneficiary": {"reference": "Patient/1234567890123456703", "type": "Patient"},
|
|
72
|
+
"class": [
|
|
73
|
+
{
|
|
74
|
+
"name": "Aetna Plan",
|
|
75
|
+
"type": {
|
|
76
|
+
"coding": [
|
|
77
|
+
{
|
|
78
|
+
"code": "plan",
|
|
79
|
+
"display": "Plan",
|
|
80
|
+
"system": "http://terminology.hl7.org/CodeSystem/coverage-class",
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
"value": "AE303",
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
"costToBeneficiary": [
|
|
88
|
+
{
|
|
89
|
+
"type": {"text": "Annual Physical Exams NMC - In Network"},
|
|
90
|
+
"valueQuantity": {
|
|
91
|
+
"system": "http://aetna.com/Medicare/CostToBeneficiary/ValueQuantity/code",
|
|
92
|
+
"unit": "$",
|
|
93
|
+
"value": 50.0,
|
|
94
|
+
},
|
|
95
|
+
}
|
|
96
|
+
],
|
|
97
|
+
"id": "3456789012345670304",
|
|
98
|
+
"identifier": [
|
|
99
|
+
{
|
|
100
|
+
"system": "https://sources.aetna.com/coverage/identifier/membershipid/59",
|
|
101
|
+
"type": {
|
|
102
|
+
"coding": [
|
|
103
|
+
{
|
|
104
|
+
"code": "SN",
|
|
105
|
+
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
"value": "435679010300+AE303+2021-01-01",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"id": "uuid",
|
|
113
|
+
"system": "https://www.icanbwell.com/uuid",
|
|
114
|
+
"value": "92266603-aa8b-58c6-99bd-326fd1da1896",
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
"meta": {
|
|
118
|
+
"security": [
|
|
119
|
+
{"code": "aetna", "system": "https://www.icanbwell.com/owner"},
|
|
120
|
+
{"code": "aetna", "system": "https://www.icanbwell.com/access"},
|
|
121
|
+
{"code": "aetna", "system": "https://www.icanbwell.com/vendor"},
|
|
122
|
+
{"code": "proa", "system": "https://www.icanbwell.com/connectionType"},
|
|
123
|
+
],
|
|
124
|
+
"source": "http://mock-server:1080/test_patient_access_transformer/source/4_0_0/Coverage/3456789012345670304",
|
|
125
|
+
},
|
|
126
|
+
"network": "Medicare - MA/NY/NJ - Full Reciprocity",
|
|
127
|
+
"payor": [
|
|
128
|
+
{
|
|
129
|
+
"display": "Aetna",
|
|
130
|
+
"reference": "Organization/6667778889990000015",
|
|
131
|
+
"type": "Organization",
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
"period": {
|
|
135
|
+
"end": datetime.fromisoformat("2021-12-31").date(),
|
|
136
|
+
"start": datetime.fromisoformat("2021-01-01").date(),
|
|
137
|
+
},
|
|
138
|
+
"policyHolder": {"reference": "Patient/1234567890123456703", "type": "Patient"},
|
|
139
|
+
"relationship": {
|
|
140
|
+
"coding": [
|
|
141
|
+
{
|
|
142
|
+
"code": "self",
|
|
143
|
+
"system": "http://terminology.hl7.org/CodeSystem/subscriber-relationship",
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
"resourceType": "Coverage",
|
|
148
|
+
"status": "active",
|
|
149
|
+
"subscriber": {"reference": "Patient/1234567890123456703", "type": "Patient"},
|
|
150
|
+
"subscriberId": "435679010300",
|
|
151
|
+
"type": {
|
|
152
|
+
"coding": [
|
|
153
|
+
{
|
|
154
|
+
"code": "PPO",
|
|
155
|
+
"display": "preferred provider organization policy",
|
|
156
|
+
"system": "http://terminology.hl7.org/CodeSystem/v3-ActCode",
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
logger.info("-------- Serialized --------")
|
|
163
|
+
serialized = TypePreservationSerializer.serialize(nested_dict)
|
|
164
|
+
logger.info(serialized)
|
|
165
|
+
logger.info("-------- Deserialized --------")
|
|
166
|
+
deserialized = TypePreservationSerializer.deserialize(serialized)
|
|
167
|
+
logger.info(deserialized)
|
|
168
|
+
|
|
169
|
+
assert isinstance(deserialized["period"]["start"], date)
|
|
170
|
+
assert isinstance(deserialized["period"]["end"], date)
|
|
171
|
+
assert nested_dict == deserialized
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections import OrderedDict
|
|
3
|
+
from datetime import datetime, date
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
from logging import Logger
|
|
6
|
+
from typing import Any, Dict, Callable, Optional, Union, cast, List
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TypePreservationDecoder:
|
|
10
|
+
"""
|
|
11
|
+
Advanced JSON decoder for complex type reconstruction with nested type support
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def decode(
|
|
16
|
+
cls,
|
|
17
|
+
dct: Union[str, Dict[str, Any], List[Any]],
|
|
18
|
+
custom_decoders: Optional[Dict[str, Callable[[Any], Any]]] = None,
|
|
19
|
+
use_ordered_dict: bool = True,
|
|
20
|
+
) -> Any:
|
|
21
|
+
"""
|
|
22
|
+
Decode complex types, including nested datetime fields
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
dct: Dictionary to decode
|
|
26
|
+
custom_decoders: Optional additional custom decoders
|
|
27
|
+
use_ordered_dict: Flag to control whether to use OrderedDict or not
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Reconstructed object or original dictionary
|
|
31
|
+
"""
|
|
32
|
+
logger: Logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
# Default decoders for built-in types with nested support
|
|
35
|
+
def datetime_decoder(d: Union[str, Dict[str, Any]]) -> datetime:
|
|
36
|
+
if isinstance(d, str):
|
|
37
|
+
return datetime.fromisoformat(d)
|
|
38
|
+
elif isinstance(d, dict) and "iso" in d:
|
|
39
|
+
return datetime.fromisoformat(d["iso"])
|
|
40
|
+
return cast(datetime, d)
|
|
41
|
+
|
|
42
|
+
def date_decoder(d: Union[str, Dict[str, Any]]) -> date:
|
|
43
|
+
if isinstance(d, str):
|
|
44
|
+
return date.fromisoformat(d)
|
|
45
|
+
elif isinstance(d, dict) and "iso" in d:
|
|
46
|
+
return date.fromisoformat(d["iso"])
|
|
47
|
+
return cast(date, d)
|
|
48
|
+
|
|
49
|
+
default_decoders: Dict[str, Callable[[Any], Any]] = {
|
|
50
|
+
"datetime": datetime_decoder,
|
|
51
|
+
"date": date_decoder,
|
|
52
|
+
"decimal": lambda d: Decimal(d["value"] if isinstance(d, dict) else d),
|
|
53
|
+
"complex": lambda d: complex(d["real"], d["imag"])
|
|
54
|
+
if isinstance(d, dict)
|
|
55
|
+
else d,
|
|
56
|
+
"bytes": lambda d: d["value"].encode("latin-1")
|
|
57
|
+
if isinstance(d, dict)
|
|
58
|
+
else d,
|
|
59
|
+
"set": lambda d: set(d["values"]) if isinstance(d, dict) else d,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Merge custom decoders with default decoders
|
|
63
|
+
decoders = {**default_decoders, **(custom_decoders or {})}
|
|
64
|
+
|
|
65
|
+
# Recursively decode nested structures
|
|
66
|
+
def recursive_decode(value: Any) -> Any:
|
|
67
|
+
if isinstance(value, dict):
|
|
68
|
+
# Check for type marker in the dictionary
|
|
69
|
+
if "__type__" in value:
|
|
70
|
+
type_name = value["__type__"]
|
|
71
|
+
|
|
72
|
+
# Handle built-in type decoders
|
|
73
|
+
if type_name in decoders:
|
|
74
|
+
return decoders[type_name](value)
|
|
75
|
+
|
|
76
|
+
# Handle custom object reconstruction
|
|
77
|
+
if "__module__" in value and "attributes" in value:
|
|
78
|
+
try:
|
|
79
|
+
# Dynamically import the class
|
|
80
|
+
module = __import__(
|
|
81
|
+
value["__module__"], fromlist=[type_name]
|
|
82
|
+
)
|
|
83
|
+
cls_ = getattr(module, type_name)
|
|
84
|
+
|
|
85
|
+
# Create instance and set attributes with recursive decoding
|
|
86
|
+
obj = cls_.__new__(cls_)
|
|
87
|
+
obj.__dict__.update(
|
|
88
|
+
{
|
|
89
|
+
k: recursive_decode(v)
|
|
90
|
+
for k, v in value["attributes"].items()
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
return obj
|
|
94
|
+
except (ImportError, AttributeError) as e:
|
|
95
|
+
logger.error(f"Could not reconstruct {type_name}: {e}")
|
|
96
|
+
return value
|
|
97
|
+
|
|
98
|
+
# Recursively decode dictionary values
|
|
99
|
+
# Conditionally use OrderedDict or regular dict
|
|
100
|
+
dict_type = OrderedDict if use_ordered_dict else dict
|
|
101
|
+
return dict_type((k, recursive_decode(v)) for k, v in value.items())
|
|
102
|
+
|
|
103
|
+
# Recursively decode list or tuple
|
|
104
|
+
elif isinstance(value, (list, tuple)):
|
|
105
|
+
return type(value)(recursive_decode(item) for item in value)
|
|
106
|
+
|
|
107
|
+
return value
|
|
108
|
+
|
|
109
|
+
# Start recursive decoding
|
|
110
|
+
return recursive_decode(dct)
|
|
@@ -26,7 +26,9 @@ class TypePreservationSerializer:
|
|
|
26
26
|
Returns:
|
|
27
27
|
JSON string representation
|
|
28
28
|
"""
|
|
29
|
-
return json.dumps(
|
|
29
|
+
return json.dumps(
|
|
30
|
+
data, cls=TypePreservationEncoder, separators=(",", ":"), **kwargs
|
|
31
|
+
)
|
|
30
32
|
|
|
31
33
|
@classmethod
|
|
32
34
|
def deserialize(
|
compressedfhir-1.0.3/VERSION
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.0.3
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
from datetime import datetime, date
|
|
2
|
-
from decimal import Decimal
|
|
3
|
-
from typing import Type, Any, Dict
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
|
|
7
|
-
from compressedfhir.utilities.json_serializers.type_preservation_decoder import (
|
|
8
|
-
TypePreservationDecoder,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class TestCustomObject:
|
|
13
|
-
def __init__(self, name: str, value: int):
|
|
14
|
-
self.name: str = name
|
|
15
|
-
self.value: int = value
|
|
16
|
-
|
|
17
|
-
def __eq__(self, other: Any) -> bool:
|
|
18
|
-
if not isinstance(other, TestCustomObject):
|
|
19
|
-
return False
|
|
20
|
-
return self.name == other.name and self.value == other.value
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@pytest.mark.parametrize(
|
|
24
|
-
"input_type, input_dict, expected_type",
|
|
25
|
-
[
|
|
26
|
-
(
|
|
27
|
-
"datetime",
|
|
28
|
-
{"__type__": "datetime", "iso": "2023-01-01T00:00:00+00:00"},
|
|
29
|
-
datetime,
|
|
30
|
-
),
|
|
31
|
-
("date", {"__type__": "date", "iso": "2023-01-01"}, date),
|
|
32
|
-
("decimal", {"__type__": "decimal", "value": "3.14"}, Decimal),
|
|
33
|
-
("complex", {"__type__": "complex", "real": 3, "imag": 4}, complex),
|
|
34
|
-
("bytes", {"__type__": "bytes", "value": "test"}, bytes),
|
|
35
|
-
("set", {"__type__": "set", "values": [1, 2, 3]}, set),
|
|
36
|
-
],
|
|
37
|
-
)
|
|
38
|
-
def test_complex_type_decoding(
|
|
39
|
-
input_type: str, input_dict: Dict[str, Any], expected_type: Type[Any]
|
|
40
|
-
) -> None:
|
|
41
|
-
"""
|
|
42
|
-
Test decoding of various complex types
|
|
43
|
-
"""
|
|
44
|
-
decoded = TypePreservationDecoder.decode(input_dict)
|
|
45
|
-
|
|
46
|
-
assert isinstance(decoded, expected_type)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def test_custom_object_decoding() -> None:
|
|
50
|
-
"""
|
|
51
|
-
Test decoding of custom objects
|
|
52
|
-
"""
|
|
53
|
-
custom_obj_dict = {
|
|
54
|
-
"__type__": "TestCustomObject",
|
|
55
|
-
"__module__": __name__,
|
|
56
|
-
"attributes": {"name": "test", "value": 42},
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
decoded = TypePreservationDecoder.decode(custom_obj_dict)
|
|
60
|
-
|
|
61
|
-
assert isinstance(decoded, TestCustomObject)
|
|
62
|
-
assert decoded.name == "test"
|
|
63
|
-
assert decoded.value == 42
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def test_custom_decoder() -> None:
|
|
67
|
-
"""
|
|
68
|
-
Test custom decoder functionality
|
|
69
|
-
"""
|
|
70
|
-
|
|
71
|
-
def custom_decoder(data: Dict[str, Any]) -> Any:
|
|
72
|
-
if data.get("__type__") == "special_type":
|
|
73
|
-
return f"Decoded: {data['value']}"
|
|
74
|
-
return data
|
|
75
|
-
|
|
76
|
-
special_dict = {"__type__": "special_type", "value": "test"}
|
|
77
|
-
|
|
78
|
-
decoded = TypePreservationDecoder.decode(
|
|
79
|
-
special_dict, custom_decoders={"special_type": custom_decoder}
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
assert decoded == "Decoded: test"
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
from datetime import datetime, timezone, date
|
|
2
|
-
from decimal import Decimal
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from compressedfhir.utilities.json_serializers.type_preservation_serializer import (
|
|
6
|
-
TypePreservationSerializer,
|
|
7
|
-
)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class TestCustomObject:
|
|
11
|
-
def __init__(self, name: str, value: int):
|
|
12
|
-
self.name: str = name
|
|
13
|
-
self.value: int = value
|
|
14
|
-
|
|
15
|
-
def __eq__(self, other: Any) -> bool:
|
|
16
|
-
if not isinstance(other, TestCustomObject):
|
|
17
|
-
return False
|
|
18
|
-
return self.name == other.name and self.value == other.value
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_complex_data_serialization() -> None:
|
|
22
|
-
"""
|
|
23
|
-
Test serialization and deserialization of complex data
|
|
24
|
-
"""
|
|
25
|
-
complex_data = {
|
|
26
|
-
"timestamp": datetime.now(timezone.utc),
|
|
27
|
-
"today": date.today(),
|
|
28
|
-
"precise_value": Decimal("3.14159"),
|
|
29
|
-
"complex_number": 3 + 4j,
|
|
30
|
-
"byte_data": b"Hello",
|
|
31
|
-
"unique_items": {1, 2, 3},
|
|
32
|
-
"custom_obj": TestCustomObject("test", 42),
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
# Serialize
|
|
36
|
-
serialized = TypePreservationSerializer.serialize(complex_data)
|
|
37
|
-
|
|
38
|
-
# Deserialize
|
|
39
|
-
deserialized = TypePreservationSerializer.deserialize(serialized)
|
|
40
|
-
|
|
41
|
-
# Verify types
|
|
42
|
-
assert isinstance(deserialized["timestamp"], datetime)
|
|
43
|
-
assert isinstance(deserialized["today"], date)
|
|
44
|
-
assert isinstance(deserialized["precise_value"], Decimal)
|
|
45
|
-
assert isinstance(deserialized["complex_number"], complex)
|
|
46
|
-
assert isinstance(deserialized["byte_data"], bytes)
|
|
47
|
-
assert isinstance(deserialized["unique_items"], set)
|
|
48
|
-
assert isinstance(deserialized["custom_obj"], TestCustomObject)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def test_nested_complex_data() -> None:
|
|
52
|
-
"""
|
|
53
|
-
Test serialization of nested complex data
|
|
54
|
-
"""
|
|
55
|
-
nested_data = {"level1": {"level2": {"timestamp": datetime.now(timezone.utc)}}}
|
|
56
|
-
|
|
57
|
-
serialized = TypePreservationSerializer.serialize(nested_data)
|
|
58
|
-
deserialized = TypePreservationSerializer.deserialize(serialized)
|
|
59
|
-
|
|
60
|
-
assert isinstance(deserialized["level1"]["level2"]["timestamp"], datetime)
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
from datetime import datetime, date
|
|
2
|
-
from decimal import Decimal
|
|
3
|
-
from typing import Any, Dict, Callable
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class TypePreservationDecoder:
|
|
7
|
-
"""
|
|
8
|
-
Advanced JSON decoder for complex type reconstruction
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
@classmethod
|
|
12
|
-
def decode(
|
|
13
|
-
cls,
|
|
14
|
-
dct: Dict[str, Any],
|
|
15
|
-
custom_decoders: Dict[str, Callable[[Any], Any]] | None = None,
|
|
16
|
-
) -> Any:
|
|
17
|
-
"""
|
|
18
|
-
Decode complex types
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
dct: Dictionary to decode
|
|
22
|
-
custom_decoders: Optional additional custom decoders
|
|
23
|
-
|
|
24
|
-
Returns:
|
|
25
|
-
Reconstructed object or original dictionary
|
|
26
|
-
"""
|
|
27
|
-
# Default decoders for built-in types
|
|
28
|
-
default_decoders: Dict[str, Callable[[Any], Any]] = {
|
|
29
|
-
"datetime": lambda d: datetime.fromisoformat(d["iso"]),
|
|
30
|
-
"date": lambda d: date.fromisoformat(d["iso"]),
|
|
31
|
-
"decimal": lambda d: Decimal(d["value"]),
|
|
32
|
-
"complex": lambda d: complex(d["real"], d["imag"]),
|
|
33
|
-
"bytes": lambda d: d["value"].encode("latin-1"),
|
|
34
|
-
"set": lambda d: set(d["values"]),
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
# Merge custom decoders with default decoders
|
|
38
|
-
decoders = {**default_decoders, **(custom_decoders or {})}
|
|
39
|
-
|
|
40
|
-
# Check for type marker
|
|
41
|
-
if "__type__" in dct:
|
|
42
|
-
type_name = dct["__type__"]
|
|
43
|
-
|
|
44
|
-
# Handle built-in type decoders
|
|
45
|
-
if type_name in decoders:
|
|
46
|
-
return decoders[type_name](dct)
|
|
47
|
-
|
|
48
|
-
# Handle custom object reconstruction
|
|
49
|
-
if "__module__" in dct and "attributes" in dct:
|
|
50
|
-
try:
|
|
51
|
-
# Dynamically import the class
|
|
52
|
-
module = __import__(dct["__module__"], fromlist=[type_name])
|
|
53
|
-
cls_ = getattr(module, type_name)
|
|
54
|
-
|
|
55
|
-
# Create instance and set attributes
|
|
56
|
-
obj = cls_.__new__(cls_)
|
|
57
|
-
obj.__dict__.update(dct["attributes"])
|
|
58
|
-
return obj
|
|
59
|
-
except (ImportError, AttributeError) as e:
|
|
60
|
-
print(f"Could not reconstruct {type_name}: {e}")
|
|
61
|
-
return dct
|
|
62
|
-
|
|
63
|
-
return dct
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle_entry_request.py
RENAMED
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle_entry_response.py
RENAMED
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/fhir_bundle_entry_search.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_bundle_entry_list.py
RENAMED
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_bundle_entry_request.py
RENAMED
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_bundle_entry_response.py
RENAMED
|
File without changes
|
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_fhir_resource.py
RENAMED
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_fhir_resource_list.py
RENAMED
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/fhir/test/test_fhir_resource_map.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/compressed_dict/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/json_serializers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/string_compressor/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{compressedfhir-1.0.3 → compressedfhir-1.0.4}/compressedfhir/utilities/test/test_json_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|