compressedfhir 1.0.3__py3-none-any.whl → 1.0.5__py3-none-any.whl
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/fhir/fhir_resource.py +13 -28
- compressedfhir/fhir/fhir_resource_map.py +1 -1
- compressedfhir/fhir/test/test_fhir_resource.py +0 -121
- compressedfhir/utilities/compressed_dict/v1/compressed_dict.py +34 -12
- compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py +111 -4
- compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py +77 -9
- compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py +111 -0
- compressedfhir/utilities/json_serializers/type_preservation_decoder.py +83 -36
- compressedfhir/utilities/json_serializers/type_preservation_serializer.py +3 -1
- {compressedfhir-1.0.3.dist-info → compressedfhir-1.0.5.dist-info}/METADATA +1 -1
- {compressedfhir-1.0.3.dist-info → compressedfhir-1.0.5.dist-info}/RECORD +14 -14
- {compressedfhir-1.0.3.dist-info → compressedfhir-1.0.5.dist-info}/WHEEL +0 -0
- {compressedfhir-1.0.3.dist-info → compressedfhir-1.0.5.dist-info}/licenses/LICENSE +0 -0
- {compressedfhir-1.0.3.dist-info → compressedfhir-1.0.5.dist-info}/top_level.txt +0 -0
|
@@ -61,14 +61,10 @@ class FhirResource(CompressedDict[str, Any]):
|
|
|
61
61
|
else None
|
|
62
62
|
)
|
|
63
63
|
|
|
64
|
-
def json(self) -> str:
|
|
65
|
-
"""Convert the resource to a JSON string."""
|
|
66
|
-
return json.dumps(obj=self.dict(), cls=FhirJSONEncoder)
|
|
67
|
-
|
|
68
64
|
def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirResource":
|
|
69
65
|
"""Create a copy of the resource."""
|
|
70
66
|
return FhirResource(
|
|
71
|
-
initial_dict=super().
|
|
67
|
+
initial_dict=super().raw_dict(),
|
|
72
68
|
storage_mode=self._storage_mode,
|
|
73
69
|
)
|
|
74
70
|
|
|
@@ -84,29 +80,6 @@ class FhirResource(CompressedDict[str, Any]):
|
|
|
84
80
|
"""
|
|
85
81
|
return copy.deepcopy(self)
|
|
86
82
|
|
|
87
|
-
@override
|
|
88
|
-
def dict(self, *, remove_nulls: bool = True) -> OrderedDict[str, Any]:
|
|
89
|
-
"""
|
|
90
|
-
Converts the FhirResource object to a dictionary.
|
|
91
|
-
|
|
92
|
-
:param remove_nulls: If True, removes None values from the dictionary.
|
|
93
|
-
:return: A dictionary representation of the FhirResource object.
|
|
94
|
-
"""
|
|
95
|
-
ordered_dict = super().dict()
|
|
96
|
-
result: OrderedDict[str, Any] = copy.deepcopy(ordered_dict)
|
|
97
|
-
if remove_nulls:
|
|
98
|
-
result = FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(
|
|
99
|
-
result
|
|
100
|
-
)
|
|
101
|
-
|
|
102
|
-
return result
|
|
103
|
-
|
|
104
|
-
def remove_nulls(self) -> None:
|
|
105
|
-
"""
|
|
106
|
-
Removes None values from the resource dictionary.
|
|
107
|
-
"""
|
|
108
|
-
self.replace(value=self.dict(remove_nulls=True))
|
|
109
|
-
|
|
110
83
|
@property
|
|
111
84
|
def id(self) -> Optional[str]:
|
|
112
85
|
"""Get the ID from the resource dictionary."""
|
|
@@ -171,3 +144,15 @@ class FhirResource(CompressedDict[str, Any]):
|
|
|
171
144
|
properties_to_cache=properties_to_cache,
|
|
172
145
|
),
|
|
173
146
|
)
|
|
147
|
+
|
|
148
|
+
@override
|
|
149
|
+
def json(self) -> str:
|
|
150
|
+
"""Convert the resource to a JSON string."""
|
|
151
|
+
|
|
152
|
+
# working_dict preserves the python types so create a fhir friendly version
|
|
153
|
+
raw_dict: OrderedDict[str, Any] = self.raw_dict()
|
|
154
|
+
|
|
155
|
+
raw_dict = FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(
|
|
156
|
+
raw_dict
|
|
157
|
+
)
|
|
158
|
+
return json.dumps(obj=raw_dict, cls=FhirJSONEncoder)
|
|
@@ -43,7 +43,7 @@ class FhirResourceMap:
|
|
|
43
43
|
"""
|
|
44
44
|
result: OrderedDict[str, Any] = OrderedDict[str, Any]()
|
|
45
45
|
for key, value in self._resource_map.items():
|
|
46
|
-
result[key] = [resource.dict(
|
|
46
|
+
result[key] = [resource.dict() for resource in value]
|
|
47
47
|
return result
|
|
48
48
|
|
|
49
49
|
def get(self, *, resource_type: str) -> Optional[FhirResourceList]:
|
|
@@ -102,124 +102,3 @@ class TestFhirResource:
|
|
|
102
102
|
assert parsed_json == initial_data
|
|
103
103
|
assert "resourceType" in parsed_json
|
|
104
104
|
assert "id" in parsed_json
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
class TestFhirResourceRemoveNulls:
|
|
108
|
-
def test_remove_nulls_simple_dict(self) -> None:
|
|
109
|
-
"""
|
|
110
|
-
Test removing None values from a simple dictionary
|
|
111
|
-
"""
|
|
112
|
-
initial_dict: Dict[str, Any] = {
|
|
113
|
-
"name": "John Doe",
|
|
114
|
-
"age": None,
|
|
115
|
-
"active": True,
|
|
116
|
-
"email": None,
|
|
117
|
-
}
|
|
118
|
-
resource = FhirResource(initial_dict=initial_dict)
|
|
119
|
-
resource.remove_nulls()
|
|
120
|
-
|
|
121
|
-
with resource.transaction():
|
|
122
|
-
# Check that None values are removed
|
|
123
|
-
assert "age" not in resource
|
|
124
|
-
assert "email" not in resource
|
|
125
|
-
assert resource.get("name") == "John Doe"
|
|
126
|
-
assert resource.get("active") is True
|
|
127
|
-
|
|
128
|
-
def test_remove_nulls_nested_dict(self) -> None:
|
|
129
|
-
"""
|
|
130
|
-
Test removing None values from a nested dictionary
|
|
131
|
-
"""
|
|
132
|
-
initial_dict: Dict[str, Any] = {
|
|
133
|
-
"patient": {
|
|
134
|
-
"name": "Jane Smith",
|
|
135
|
-
"contact": None,
|
|
136
|
-
"address": {"street": None, "city": "New York"},
|
|
137
|
-
},
|
|
138
|
-
"status": None,
|
|
139
|
-
}
|
|
140
|
-
resource = FhirResource(initial_dict=initial_dict)
|
|
141
|
-
resource.remove_nulls()
|
|
142
|
-
|
|
143
|
-
with resource.transaction():
|
|
144
|
-
assert "status" not in resource
|
|
145
|
-
assert "contact" not in resource.get("patient", {})
|
|
146
|
-
assert resource.get("patient", {}).get("address", {}).get("street") is None
|
|
147
|
-
assert (
|
|
148
|
-
resource.get("patient", {}).get("address", {}).get("city") == "New York"
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
def test_remove_nulls_list_of_dicts(self) -> None:
|
|
152
|
-
"""
|
|
153
|
-
Test removing None values from a list of dictionaries
|
|
154
|
-
"""
|
|
155
|
-
initial_dict: Dict[str, Any] = {
|
|
156
|
-
"patients": [
|
|
157
|
-
{"name": "Alice", "age": None},
|
|
158
|
-
{"name": "Bob", "age": 30},
|
|
159
|
-
{"name": None, "active": False},
|
|
160
|
-
]
|
|
161
|
-
}
|
|
162
|
-
resource = FhirResource(initial_dict=initial_dict)
|
|
163
|
-
resource.remove_nulls()
|
|
164
|
-
|
|
165
|
-
with resource.transaction():
|
|
166
|
-
assert len(resource.get("patients", [])) == 3
|
|
167
|
-
assert resource.get("patients", [])[0].get("name") == "Alice"
|
|
168
|
-
assert resource.get("patients", [])[1].get("name") == "Bob"
|
|
169
|
-
assert resource.get("patients", [])[1].get("age") == 30
|
|
170
|
-
|
|
171
|
-
def test_remove_nulls_empty_dict(self) -> None:
|
|
172
|
-
"""
|
|
173
|
-
Test removing None values from an empty dictionary
|
|
174
|
-
"""
|
|
175
|
-
resource = FhirResource(initial_dict={})
|
|
176
|
-
resource.remove_nulls()
|
|
177
|
-
|
|
178
|
-
assert len(resource) == 0
|
|
179
|
-
|
|
180
|
-
def test_remove_nulls_no_changes(self) -> None:
|
|
181
|
-
"""
|
|
182
|
-
Test removing None values when no None values exist
|
|
183
|
-
"""
|
|
184
|
-
initial_dict: Dict[str, Any] = {
|
|
185
|
-
"name": "Test User",
|
|
186
|
-
"active": True,
|
|
187
|
-
"score": 100,
|
|
188
|
-
}
|
|
189
|
-
resource = FhirResource(initial_dict=initial_dict)
|
|
190
|
-
original_dict = resource.copy()
|
|
191
|
-
resource.remove_nulls()
|
|
192
|
-
|
|
193
|
-
assert resource == original_dict
|
|
194
|
-
|
|
195
|
-
def test_remove_nulls_with_custom_storage_mode(self) -> None:
|
|
196
|
-
"""
|
|
197
|
-
Test removing None values with a custom storage mode
|
|
198
|
-
"""
|
|
199
|
-
initial_dict: Dict[str, Any] = {
|
|
200
|
-
"name": "Custom Mode User",
|
|
201
|
-
"email": None,
|
|
202
|
-
"active": True,
|
|
203
|
-
}
|
|
204
|
-
resource = FhirResource(
|
|
205
|
-
initial_dict=initial_dict, storage_mode=CompressedDictStorageMode.default()
|
|
206
|
-
)
|
|
207
|
-
resource.remove_nulls()
|
|
208
|
-
|
|
209
|
-
with resource.transaction():
|
|
210
|
-
assert "email" not in resource
|
|
211
|
-
assert resource.get("name") == "Custom Mode User"
|
|
212
|
-
assert resource.get("active") is True
|
|
213
|
-
|
|
214
|
-
def test_remove_nulls_preserves_false_and_zero_values(self) -> None:
|
|
215
|
-
"""
|
|
216
|
-
Test that False and 0 values are not removed
|
|
217
|
-
"""
|
|
218
|
-
initial_dict: Dict[str, Any] = {"active": False, "score": 0, "name": None}
|
|
219
|
-
resource = FhirResource(initial_dict=initial_dict)
|
|
220
|
-
resource.remove_nulls()
|
|
221
|
-
|
|
222
|
-
with resource.transaction():
|
|
223
|
-
assert resource.get("active") is False
|
|
224
|
-
assert resource.get("score") == 0
|
|
225
|
-
assert "name" not in resource
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import copy
|
|
2
|
+
import json
|
|
2
3
|
from collections.abc import KeysView, ValuesView, ItemsView, MutableMapping
|
|
3
4
|
from contextlib import contextmanager
|
|
4
5
|
from typing import Dict, Optional, Iterator, cast, List, Any, overload, OrderedDict
|
|
@@ -13,6 +14,7 @@ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode im
|
|
|
13
14
|
CompressedDictStorageMode,
|
|
14
15
|
CompressedDictStorageType,
|
|
15
16
|
)
|
|
17
|
+
from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
|
|
16
18
|
from compressedfhir.utilities.json_serializers.type_preservation_serializer import (
|
|
17
19
|
TypePreservationSerializer,
|
|
18
20
|
)
|
|
@@ -176,9 +178,7 @@ class CompressedDict[K, V](MutableMapping[K, V]):
|
|
|
176
178
|
assert isinstance(dictionary, OrderedDict)
|
|
177
179
|
if storage_type == "compressed":
|
|
178
180
|
# Serialize to JSON and compress with zlib
|
|
179
|
-
json_str = TypePreservationSerializer.serialize(
|
|
180
|
-
dictionary, separators=(",", ":")
|
|
181
|
-
)
|
|
181
|
+
json_str = TypePreservationSerializer.serialize(dictionary)
|
|
182
182
|
return zlib.compress(
|
|
183
183
|
json_str.encode("utf-8"), level=zlib.Z_BEST_COMPRESSION
|
|
184
184
|
)
|
|
@@ -219,9 +219,7 @@ class CompressedDict[K, V](MutableMapping[K, V]):
|
|
|
219
219
|
decompressed_bytes: bytes = zlib.decompress(serialized_dict_bytes)
|
|
220
220
|
decoded_text: str = decompressed_bytes.decode("utf-8")
|
|
221
221
|
# noinspection PyTypeChecker
|
|
222
|
-
decompressed_dict = TypePreservationSerializer.deserialize(
|
|
223
|
-
decoded_text, object_pairs_hook=OrderedDict
|
|
224
|
-
)
|
|
222
|
+
decompressed_dict = TypePreservationSerializer.deserialize(decoded_text)
|
|
225
223
|
assert isinstance(decompressed_dict, OrderedDict)
|
|
226
224
|
return cast(OrderedDict[K, V], decompressed_dict)
|
|
227
225
|
|
|
@@ -427,19 +425,43 @@ class CompressedDict[K, V](MutableMapping[K, V]):
|
|
|
427
425
|
"""
|
|
428
426
|
return self._get_dict().items()
|
|
429
427
|
|
|
430
|
-
def
|
|
428
|
+
def raw_dict(self) -> OrderedDict[K, V]:
|
|
431
429
|
"""
|
|
432
|
-
|
|
430
|
+
Returns the raw dictionary. Deserializes if necessary.
|
|
431
|
+
Note that this dictionary preserves the python types so it is not FHIR friendly.
|
|
432
|
+
Use dict() if you want a FHIR friendly version.
|
|
433
433
|
|
|
434
434
|
Returns:
|
|
435
|
-
|
|
435
|
+
raw dictionary
|
|
436
436
|
"""
|
|
437
437
|
if self._working_dict:
|
|
438
438
|
return self._working_dict
|
|
439
439
|
else:
|
|
440
|
-
# if the working dict is None, return it but don't store it in the self._working_dict to keep memory low
|
|
440
|
+
# if the working dict is not None, return it but don't store it in the self._working_dict to keep memory low
|
|
441
441
|
return self.create_working_dict()
|
|
442
442
|
|
|
443
|
+
def dict(self) -> OrderedDict[K, V]:
|
|
444
|
+
"""
|
|
445
|
+
Convert to a FHIR friendly dictionary where the python types like datetime are converted to string versions
|
|
446
|
+
|
|
447
|
+
Returns:
|
|
448
|
+
FHIR friendly dictionary
|
|
449
|
+
"""
|
|
450
|
+
return cast(
|
|
451
|
+
OrderedDict[K, V],
|
|
452
|
+
json.loads(
|
|
453
|
+
self.json(),
|
|
454
|
+
object_pairs_hook=lambda pairs: OrderedDict(pairs),
|
|
455
|
+
),
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
def json(self) -> str:
|
|
459
|
+
"""Convert the resource to a JSON string."""
|
|
460
|
+
|
|
461
|
+
raw_dict: OrderedDict[K, V] = self.raw_dict()
|
|
462
|
+
|
|
463
|
+
return json.dumps(obj=raw_dict, cls=FhirJSONEncoder)
|
|
464
|
+
|
|
443
465
|
def __repr__(self) -> str:
|
|
444
466
|
"""
|
|
445
467
|
String representation of the dictionary
|
|
@@ -563,7 +585,7 @@ class CompressedDict[K, V](MutableMapping[K, V]):
|
|
|
563
585
|
"""
|
|
564
586
|
# Create a new instance with the same storage mode
|
|
565
587
|
new_instance = CompressedDict(
|
|
566
|
-
initial_dict=copy.deepcopy(self.
|
|
588
|
+
initial_dict=copy.deepcopy(self.raw_dict()),
|
|
567
589
|
storage_mode=self._storage_mode,
|
|
568
590
|
properties_to_cache=self._properties_to_cache,
|
|
569
591
|
)
|
|
@@ -637,7 +659,7 @@ class CompressedDict[K, V](MutableMapping[K, V]):
|
|
|
637
659
|
Returns:
|
|
638
660
|
Plain dictionary
|
|
639
661
|
"""
|
|
640
|
-
return OrderedDictToDictConverter.convert(self.
|
|
662
|
+
return OrderedDictToDictConverter.convert(self.raw_dict())
|
|
641
663
|
|
|
642
664
|
@classmethod
|
|
643
665
|
def from_json(cls, json_str: str) -> "CompressedDict[K, V]":
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
1
3
|
import pytest
|
|
2
4
|
from typing import Any, cast
|
|
3
5
|
|
|
@@ -232,7 +234,7 @@ def test_transaction_basic_raw_storage() -> None:
|
|
|
232
234
|
|
|
233
235
|
# After transaction
|
|
234
236
|
assert compressed_dict._transaction_depth == 0
|
|
235
|
-
assert compressed_dict.
|
|
237
|
+
assert compressed_dict.raw_dict() == {
|
|
236
238
|
"key1": "value1",
|
|
237
239
|
"key2": "value2",
|
|
238
240
|
"key3": "value3",
|
|
@@ -260,7 +262,7 @@ def test_transaction_nested_context() -> None:
|
|
|
260
262
|
assert compressed_dict._transaction_depth == 1
|
|
261
263
|
|
|
262
264
|
assert compressed_dict._transaction_depth == 0
|
|
263
|
-
assert compressed_dict.
|
|
265
|
+
assert compressed_dict.raw_dict() == {"key1": "value1", "key2": "value2"}
|
|
264
266
|
|
|
265
267
|
|
|
266
268
|
def test_transaction_access_error() -> None:
|
|
@@ -309,7 +311,7 @@ def test_transaction_different_storage_modes() -> None:
|
|
|
309
311
|
with compressed_dict.transaction() as d:
|
|
310
312
|
d["key2"] = "value2"
|
|
311
313
|
|
|
312
|
-
assert compressed_dict.
|
|
314
|
+
assert compressed_dict.raw_dict() == {"key1": "value1", "key2": "value2"}
|
|
313
315
|
|
|
314
316
|
|
|
315
317
|
def test_transaction_with_properties_to_cache() -> None:
|
|
@@ -328,7 +330,7 @@ def test_transaction_with_properties_to_cache() -> None:
|
|
|
328
330
|
with compressed_dict.transaction() as d:
|
|
329
331
|
d["key2"] = "value2"
|
|
330
332
|
|
|
331
|
-
assert compressed_dict.
|
|
333
|
+
assert compressed_dict.raw_dict() == {
|
|
332
334
|
"key1": "value1",
|
|
333
335
|
"important_prop": "cached_value",
|
|
334
336
|
"key2": "value2",
|
|
@@ -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
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from datetime import datetime, date
|
|
2
2
|
from decimal import Decimal
|
|
3
|
-
from typing import Type, Any, Dict
|
|
4
|
-
|
|
3
|
+
from typing import Type, Any, Dict, Optional
|
|
5
4
|
import pytest
|
|
6
5
|
|
|
7
6
|
from compressedfhir.utilities.json_serializers.type_preservation_decoder import (
|
|
@@ -10,14 +9,27 @@ from compressedfhir.utilities.json_serializers.type_preservation_decoder import
|
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
class TestCustomObject:
|
|
13
|
-
def __init__(
|
|
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
|
+
):
|
|
14
19
|
self.name: str = name
|
|
15
20
|
self.value: int = value
|
|
21
|
+
self.created_at: Optional[datetime] = created_at
|
|
22
|
+
self.nested_data: Optional[Dict[str, Any]] = nested_data
|
|
16
23
|
|
|
17
24
|
def __eq__(self, other: Any) -> bool:
|
|
18
25
|
if not isinstance(other, TestCustomObject):
|
|
19
26
|
return False
|
|
20
|
-
return
|
|
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
|
+
)
|
|
21
33
|
|
|
22
34
|
|
|
23
35
|
@pytest.mark.parametrize(
|
|
@@ -42,7 +54,6 @@ def test_complex_type_decoding(
|
|
|
42
54
|
Test decoding of various complex types
|
|
43
55
|
"""
|
|
44
56
|
decoded = TypePreservationDecoder.decode(input_dict)
|
|
45
|
-
|
|
46
57
|
assert isinstance(decoded, expected_type)
|
|
47
58
|
|
|
48
59
|
|
|
@@ -55,9 +66,7 @@ def test_custom_object_decoding() -> None:
|
|
|
55
66
|
"__module__": __name__,
|
|
56
67
|
"attributes": {"name": "test", "value": 42},
|
|
57
68
|
}
|
|
58
|
-
|
|
59
69
|
decoded = TypePreservationDecoder.decode(custom_obj_dict)
|
|
60
|
-
|
|
61
70
|
assert isinstance(decoded, TestCustomObject)
|
|
62
71
|
assert decoded.name == "test"
|
|
63
72
|
assert decoded.value == 42
|
|
@@ -74,9 +83,68 @@ def test_custom_decoder() -> None:
|
|
|
74
83
|
return data
|
|
75
84
|
|
|
76
85
|
special_dict = {"__type__": "special_type", "value": "test"}
|
|
77
|
-
|
|
78
86
|
decoded = TypePreservationDecoder.decode(
|
|
79
87
|
special_dict, custom_decoders={"special_type": custom_decoder}
|
|
80
88
|
)
|
|
81
|
-
|
|
82
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"
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from datetime import datetime, timezone, date
|
|
2
3
|
from decimal import Decimal
|
|
4
|
+
from logging import Logger
|
|
3
5
|
from typing import Any
|
|
4
6
|
|
|
5
7
|
from compressedfhir.utilities.json_serializers.type_preservation_serializer import (
|
|
@@ -58,3 +60,112 @@ def test_nested_complex_data() -> None:
|
|
|
58
60
|
deserialized = TypePreservationSerializer.deserialize(serialized)
|
|
59
61
|
|
|
60
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
|
|
@@ -1,63 +1,110 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from collections import OrderedDict
|
|
1
3
|
from datetime import datetime, date
|
|
2
4
|
from decimal import Decimal
|
|
3
|
-
from
|
|
5
|
+
from logging import Logger
|
|
6
|
+
from typing import Any, Dict, Callable, Optional, Union, cast, List
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
class TypePreservationDecoder:
|
|
7
10
|
"""
|
|
8
|
-
Advanced JSON decoder for complex type reconstruction
|
|
11
|
+
Advanced JSON decoder for complex type reconstruction with nested type support
|
|
9
12
|
"""
|
|
10
13
|
|
|
11
14
|
@classmethod
|
|
12
15
|
def decode(
|
|
13
16
|
cls,
|
|
14
|
-
dct: Dict[str, Any],
|
|
15
|
-
custom_decoders: Dict[str, Callable[[Any], Any]]
|
|
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,
|
|
16
20
|
) -> Any:
|
|
17
21
|
"""
|
|
18
|
-
Decode complex types
|
|
22
|
+
Decode complex types, including nested datetime fields
|
|
19
23
|
|
|
20
24
|
Args:
|
|
21
25
|
dct: Dictionary to decode
|
|
22
26
|
custom_decoders: Optional additional custom decoders
|
|
27
|
+
use_ordered_dict: Flag to control whether to use OrderedDict or not
|
|
23
28
|
|
|
24
29
|
Returns:
|
|
25
30
|
Reconstructed object or original dictionary
|
|
26
31
|
"""
|
|
27
|
-
|
|
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
|
+
|
|
28
49
|
default_decoders: Dict[str, Callable[[Any], Any]] = {
|
|
29
|
-
"datetime":
|
|
30
|
-
"date":
|
|
31
|
-
"decimal": lambda d: Decimal(d["value"]),
|
|
32
|
-
"complex": lambda d: complex(d["real"], d["imag"])
|
|
33
|
-
|
|
34
|
-
|
|
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,
|
|
35
60
|
}
|
|
36
61
|
|
|
37
62
|
# Merge custom decoders with default decoders
|
|
38
63
|
decoders = {**default_decoders, **(custom_decoders or {})}
|
|
39
64
|
|
|
40
|
-
#
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
#
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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(
|
|
@@ -11,16 +11,16 @@ compressedfhir/fhir/fhir_bundle_entry_search.py,sha256=uYVJxuNN3gt3Q6BZ5FhRs47x7
|
|
|
11
11
|
compressedfhir/fhir/fhir_identifier.py,sha256=tA_nmhBaYHu5zjJdE0IWMFEF8lrIPV3_nu-yairiIKw,2711
|
|
12
12
|
compressedfhir/fhir/fhir_link.py,sha256=jf2RrwmsPrKW3saP77y42xVqI0xwHFYXxm6YHQJk7gU,1922
|
|
13
13
|
compressedfhir/fhir/fhir_meta.py,sha256=vNI4O6SoG4hJRHyd-bJ_QnYFTfBHyR3UA6h21ByQmWo,1669
|
|
14
|
-
compressedfhir/fhir/fhir_resource.py,sha256=
|
|
14
|
+
compressedfhir/fhir/fhir_resource.py,sha256=5v_xcAUCFcqzQodT8uiw292NUG86gWJ4UbyhY2Vy79c,5084
|
|
15
15
|
compressedfhir/fhir/fhir_resource_list.py,sha256=qlAAwWWphtFicBxPG8iriz2eOHGcrWJk5kGThmvkbPE,4480
|
|
16
|
-
compressedfhir/fhir/fhir_resource_map.py,sha256=
|
|
16
|
+
compressedfhir/fhir/fhir_resource_map.py,sha256=XFJ0o5_kLUeHYKp1q_Bxsoyp2-rLX7P4c9FwQ7YfGWM,6571
|
|
17
17
|
compressedfhir/fhir/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
18
|
compressedfhir/fhir/test/test_bundle_entry.py,sha256=Ki2sSu1V1WZkAM6UTCghtzjvjYYI8UcF6AXnx8FWlMI,5115
|
|
19
19
|
compressedfhir/fhir/test/test_bundle_entry_list.py,sha256=KtMrbQYezdEw9FJbBzwSePdJK2R9P03mSRfo59T-6iM,6041
|
|
20
20
|
compressedfhir/fhir/test/test_bundle_entry_request.py,sha256=9bN3Vt9BAXPLjH7FFt_MYSdanFJzWk9HbA0C9kZxPXY,2853
|
|
21
21
|
compressedfhir/fhir/test/test_bundle_entry_response.py,sha256=jk5nUi07_q-yz-qz2YR86vU91e3DVxc2cptrS6tsCco,2539
|
|
22
22
|
compressedfhir/fhir/test/test_fhir_bundle.py,sha256=Kt1IpxEnUuPOJBDWsdy4cC7kR3FR-uPOf7PB9ejJ7ZM,8700
|
|
23
|
-
compressedfhir/fhir/test/test_fhir_resource.py,sha256=
|
|
23
|
+
compressedfhir/fhir/test/test_fhir_resource.py,sha256=nsSLs-sKDaYpoTVyXuBNnKJ0-somxDNX368lpTf3HUw,3828
|
|
24
24
|
compressedfhir/fhir/test/test_fhir_resource_list.py,sha256=SrSPJ1yWU4UgMUCht6JwgKh2Y5JeTS4-Wky0kWZOXH8,5664
|
|
25
25
|
compressedfhir/fhir/test/test_fhir_resource_map.py,sha256=jtQ5fq_jhmFfhHGyK5mdiwIQiO-Sfp2eG9mco_Tr9Qk,10995
|
|
26
26
|
compressedfhir/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -28,19 +28,19 @@ compressedfhir/utilities/fhir_json_encoder.py,sha256=hn-ZuDrTEdYZmILk_5_k4R72PQB
|
|
|
28
28
|
compressedfhir/utilities/json_helpers.py,sha256=lEiPapLN0p-kLu6PFm-h971ieXRxwPB2M-8FCZ2Buo8,5642
|
|
29
29
|
compressedfhir/utilities/compressed_dict/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
30
|
compressedfhir/utilities/compressed_dict/v1/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
-
compressedfhir/utilities/compressed_dict/v1/compressed_dict.py,sha256=
|
|
31
|
+
compressedfhir/utilities/compressed_dict/v1/compressed_dict.py,sha256=C2mblG2P8qx0XWpDJO-7OGBqyQRTp0WelaUbwopI7qc,22049
|
|
32
32
|
compressedfhir/utilities/compressed_dict/v1/compressed_dict_access_error.py,sha256=xuwED0KGZcQORIcZRfi--5CdXplHJ5vYLBUqpbDi344,132
|
|
33
33
|
compressedfhir/utilities/compressed_dict/v1/compressed_dict_storage_mode.py,sha256=mEdtJjPX2I9DqP0Ly_VsZZWhEMNTI1psqQ8iJtUQ2oE,1412
|
|
34
34
|
compressedfhir/utilities/compressed_dict/v1/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py,sha256=
|
|
35
|
+
compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py,sha256=5yUnjkmP3A4dSPzDXY3u1YBQ8BxCANdtCF9uGF1T9i4,15840
|
|
36
36
|
compressedfhir/utilities/json_serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
-
compressedfhir/utilities/json_serializers/type_preservation_decoder.py,sha256=
|
|
37
|
+
compressedfhir/utilities/json_serializers/type_preservation_decoder.py,sha256=Af2ZsLZiUF9kUhRvkV7i6Ctf_OtTND_lb5PezHtolJU,4382
|
|
38
38
|
compressedfhir/utilities/json_serializers/type_preservation_encoder.py,sha256=f7RL67l7QtDbijCPq1ki6axrLte1vH--bi1AsN7Y3yk,1646
|
|
39
|
-
compressedfhir/utilities/json_serializers/type_preservation_serializer.py,sha256=
|
|
39
|
+
compressedfhir/utilities/json_serializers/type_preservation_serializer.py,sha256=cE1ka2RxKy_8P0xhgqvPyWqJ3C0Br-aqIHP9BPkCg7A,1523
|
|
40
40
|
compressedfhir/utilities/json_serializers/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
-
compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py,sha256=
|
|
41
|
+
compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py,sha256=GQotwYQJe9VZQotvLWmQWMkSIBne53bolmgflBoR7DU,4752
|
|
42
42
|
compressedfhir/utilities/json_serializers/test/test_type_preservation_encoder.py,sha256=O4VczBdsJF35WozZiwSdJ8638qDn01JQsai2wTXu5Vo,1737
|
|
43
|
-
compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py,sha256=
|
|
43
|
+
compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py,sha256=dTYdgI1wMgWU0DJCNJlbMmsnhr-Q_2SPXeydLsn70rA,6043
|
|
44
44
|
compressedfhir/utilities/ordered_dict_to_dict_converter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
45
|
compressedfhir/utilities/ordered_dict_to_dict_converter/ordered_dict_to_dict_converter.py,sha256=CMerJQD7O0vMyGtUp1rKSerZA1tDZeY5GTQT3AykL4w,831
|
|
46
46
|
compressedfhir/utilities/string_compressor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -51,9 +51,9 @@ compressedfhir/utilities/string_compressor/v1/test/test_string_compressor.py,sha
|
|
|
51
51
|
compressedfhir/utilities/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
52
|
compressedfhir/utilities/test/test_fhir_json_encoder.py,sha256=6pbNmZp5eBWY66bHjgjm_pZVhs5HDKP8hCGnwNFzpEw,5171
|
|
53
53
|
compressedfhir/utilities/test/test_json_helpers.py,sha256=V0R9oHDQAs0m0012niEz50sHJxMSUQvA3km7kK8HgjE,3860
|
|
54
|
-
compressedfhir-1.0.
|
|
54
|
+
compressedfhir-1.0.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
55
55
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
|
-
compressedfhir-1.0.
|
|
57
|
-
compressedfhir-1.0.
|
|
58
|
-
compressedfhir-1.0.
|
|
59
|
-
compressedfhir-1.0.
|
|
56
|
+
compressedfhir-1.0.5.dist-info/METADATA,sha256=jitzP2jc8_vqD1C9RYDNyetYivS_DOHuUplgJZy8t20,3456
|
|
57
|
+
compressedfhir-1.0.5.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
58
|
+
compressedfhir-1.0.5.dist-info/top_level.txt,sha256=YMKdvBBdiCzFbpI9fG8BUDjaRd-f4R0qAvUoVETpoWw,21
|
|
59
|
+
compressedfhir-1.0.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|