compressedfhir 3.0.2__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.
- compressedfhir/__init__.py +0 -0
- compressedfhir/fhir/__init__.py +0 -0
- compressedfhir/fhir/base_resource_list.py +165 -0
- compressedfhir/fhir/fhir_bundle.py +295 -0
- compressedfhir/fhir/fhir_bundle_entry.py +240 -0
- compressedfhir/fhir/fhir_bundle_entry_list.py +97 -0
- compressedfhir/fhir/fhir_bundle_entry_request.py +73 -0
- compressedfhir/fhir/fhir_bundle_entry_response.py +67 -0
- compressedfhir/fhir/fhir_bundle_entry_search.py +75 -0
- compressedfhir/fhir/fhir_identifier.py +84 -0
- compressedfhir/fhir/fhir_link.py +63 -0
- compressedfhir/fhir/fhir_meta.py +47 -0
- compressedfhir/fhir/fhir_resource.py +170 -0
- compressedfhir/fhir/fhir_resource_list.py +149 -0
- compressedfhir/fhir/fhir_resource_map.py +193 -0
- compressedfhir/fhir/test/__init__.py +0 -0
- compressedfhir/fhir/test/test_bundle_entry.py +129 -0
- compressedfhir/fhir/test/test_bundle_entry_list.py +187 -0
- compressedfhir/fhir/test/test_bundle_entry_request.py +74 -0
- compressedfhir/fhir/test/test_bundle_entry_response.py +65 -0
- compressedfhir/fhir/test/test_fhir_bundle.py +245 -0
- compressedfhir/fhir/test/test_fhir_resource.py +104 -0
- compressedfhir/fhir/test/test_fhir_resource_list.py +160 -0
- compressedfhir/fhir/test/test_fhir_resource_map.py +293 -0
- compressedfhir/py.typed +0 -0
- compressedfhir/utilities/__init__.py +0 -0
- compressedfhir/utilities/compressed_dict/__init__.py +0 -0
- compressedfhir/utilities/compressed_dict/v1/__init__.py +0 -0
- compressedfhir/utilities/compressed_dict/v1/compressed_dict.py +701 -0
- compressedfhir/utilities/compressed_dict/v1/compressed_dict_access_error.py +2 -0
- compressedfhir/utilities/compressed_dict/v1/compressed_dict_storage_mode.py +50 -0
- compressedfhir/utilities/compressed_dict/v1/test/__init__.py +0 -0
- compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py +467 -0
- compressedfhir/utilities/fhir_json_encoder.py +71 -0
- compressedfhir/utilities/json_helpers.py +181 -0
- compressedfhir/utilities/json_serializers/__init__.py +0 -0
- compressedfhir/utilities/json_serializers/test/__init__.py +0 -0
- compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py +165 -0
- compressedfhir/utilities/json_serializers/test/test_type_preservation_encoder.py +71 -0
- compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py +197 -0
- compressedfhir/utilities/json_serializers/type_preservation_decoder.py +135 -0
- compressedfhir/utilities/json_serializers/type_preservation_encoder.py +55 -0
- compressedfhir/utilities/json_serializers/type_preservation_serializer.py +57 -0
- compressedfhir/utilities/ordered_dict_to_dict_converter/__init__.py +0 -0
- compressedfhir/utilities/ordered_dict_to_dict_converter/ordered_dict_to_dict_converter.py +24 -0
- compressedfhir/utilities/string_compressor/__init__.py +0 -0
- compressedfhir/utilities/string_compressor/v1/__init__.py +0 -0
- compressedfhir/utilities/string_compressor/v1/string_compressor.py +99 -0
- compressedfhir/utilities/string_compressor/v1/test/__init__.py +0 -0
- compressedfhir/utilities/string_compressor/v1/test/test_string_compressor.py +189 -0
- compressedfhir/utilities/test/__init__.py +0 -0
- compressedfhir/utilities/test/test_fhir_json_encoder.py +177 -0
- compressedfhir/utilities/test/test_json_helpers.py +99 -0
- compressedfhir-3.0.2.dist-info/METADATA +139 -0
- compressedfhir-3.0.2.dist-info/RECORD +59 -0
- compressedfhir-3.0.2.dist-info/WHEEL +5 -0
- compressedfhir-3.0.2.dist-info/licenses/LICENSE +201 -0
- compressedfhir-3.0.2.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
from typing import Literal, TypeAlias
|
|
3
|
+
|
|
4
|
+
CompressedDictStorageType: TypeAlias = Literal[
|
|
5
|
+
"raw", "compressed", "msgpack", "compressed_msgpack"
|
|
6
|
+
]
|
|
7
|
+
###
|
|
8
|
+
# CompressedDictStorageType is a type alias for the different storage types
|
|
9
|
+
# raw: No compression
|
|
10
|
+
# compressed: Compressed using zlib
|
|
11
|
+
# msgpack: Compressed using msgpack
|
|
12
|
+
# compressed_msgpack: Compressed using msgpack with zlib
|
|
13
|
+
###
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclasses.dataclass(slots=True)
|
|
17
|
+
class CompressedDictStorageMode:
|
|
18
|
+
"""
|
|
19
|
+
CompressedDictStorageMode is a dataclass that defines the storage mode
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
storage_type: CompressedDictStorageType = "compressed"
|
|
23
|
+
"""
|
|
24
|
+
CompressedDictStorageType is a type alias for the different storage types
|
|
25
|
+
raw: No compression
|
|
26
|
+
compressed: Compressed using zlib
|
|
27
|
+
msgpack: Compressed using msgpack
|
|
28
|
+
compressed_msgpack: Compressed using msgpack with zlib
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def default(cls) -> "CompressedDictStorageMode":
|
|
33
|
+
"""
|
|
34
|
+
Returns the default storage mode
|
|
35
|
+
"""
|
|
36
|
+
return cls(storage_type="compressed")
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def raw(cls) -> "CompressedDictStorageMode":
|
|
40
|
+
"""
|
|
41
|
+
Returns the default storage mode
|
|
42
|
+
"""
|
|
43
|
+
return cls(storage_type="raw")
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def compressed(cls) -> "CompressedDictStorageMode":
|
|
47
|
+
"""
|
|
48
|
+
Returns the default storage mode
|
|
49
|
+
"""
|
|
50
|
+
return cls(storage_type="compressed")
|
|
File without changes
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from typing import Any, cast
|
|
5
|
+
|
|
6
|
+
from compressedfhir.utilities.compressed_dict.v1.compressed_dict import (
|
|
7
|
+
CompressedDict,
|
|
8
|
+
)
|
|
9
|
+
from compressedfhir.utilities.compressed_dict.v1.compressed_dict_access_error import (
|
|
10
|
+
CompressedDictAccessError,
|
|
11
|
+
)
|
|
12
|
+
from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode import (
|
|
13
|
+
CompressedDictStorageMode,
|
|
14
|
+
CompressedDictStorageType,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestCompressedDict:
|
|
19
|
+
def test_init_empty(self) -> None:
|
|
20
|
+
"""Test initialization with no initial data"""
|
|
21
|
+
cd: CompressedDict[str, Any] = CompressedDict(
|
|
22
|
+
storage_mode=CompressedDictStorageMode(storage_type="raw"),
|
|
23
|
+
properties_to_cache=[],
|
|
24
|
+
)
|
|
25
|
+
assert len(cd) == 0
|
|
26
|
+
assert cd._storage_mode.storage_type == "raw"
|
|
27
|
+
|
|
28
|
+
def test_init_with_dict(self) -> None:
|
|
29
|
+
"""Test initialization with initial dictionary"""
|
|
30
|
+
initial_data = {"a": 1, "b": 2, "c": 3}
|
|
31
|
+
cd = CompressedDict(
|
|
32
|
+
initial_dict=initial_data,
|
|
33
|
+
storage_mode=CompressedDictStorageMode(storage_type="raw"),
|
|
34
|
+
properties_to_cache=[],
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
with cd.transaction():
|
|
38
|
+
assert len(cd) == 3
|
|
39
|
+
assert cd["a"] == 1
|
|
40
|
+
assert cd["b"] == 2
|
|
41
|
+
assert cd["c"] == 3
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize("storage_type", ["raw", "msgpack", "compressed_msgpack"])
|
|
44
|
+
def test_storage_modes(self, storage_type: CompressedDictStorageType) -> None:
|
|
45
|
+
"""Test different storage modes"""
|
|
46
|
+
initial_data = {"key": "value"}
|
|
47
|
+
cd = CompressedDict(
|
|
48
|
+
initial_dict=initial_data,
|
|
49
|
+
storage_mode=CompressedDictStorageMode(storage_type=storage_type),
|
|
50
|
+
properties_to_cache=[],
|
|
51
|
+
)
|
|
52
|
+
assert cd._storage_mode.storage_type == storage_type
|
|
53
|
+
with cd.transaction():
|
|
54
|
+
assert cd["key"] == "value"
|
|
55
|
+
|
|
56
|
+
def test_setitem_and_getitem(self) -> None:
|
|
57
|
+
"""Test setting and getting items"""
|
|
58
|
+
cd: CompressedDict[str, Any] = CompressedDict(
|
|
59
|
+
storage_mode=CompressedDictStorageMode(storage_type="compressed_msgpack"),
|
|
60
|
+
properties_to_cache=[],
|
|
61
|
+
)
|
|
62
|
+
with cd.transaction():
|
|
63
|
+
cd["key1"] = "value1"
|
|
64
|
+
cd["key2"] = 42
|
|
65
|
+
cd["key3"] = {"nested": "dict"}
|
|
66
|
+
|
|
67
|
+
with cd.transaction():
|
|
68
|
+
assert cd["key1"] == "value1"
|
|
69
|
+
assert cd["key2"] == 42
|
|
70
|
+
assert cd["key3"] == {"nested": "dict"}
|
|
71
|
+
|
|
72
|
+
def test_delitem(self) -> None:
|
|
73
|
+
"""Test deleting items"""
|
|
74
|
+
cd = CompressedDict(
|
|
75
|
+
initial_dict={"a": 1, "b": 2},
|
|
76
|
+
storage_mode=CompressedDictStorageMode(storage_type="compressed_msgpack"),
|
|
77
|
+
properties_to_cache=[],
|
|
78
|
+
)
|
|
79
|
+
with cd.transaction():
|
|
80
|
+
del cd["a"]
|
|
81
|
+
|
|
82
|
+
with cd.transaction():
|
|
83
|
+
assert len(cd) == 1
|
|
84
|
+
|
|
85
|
+
with cd.transaction():
|
|
86
|
+
assert not cd.__contains__("a")
|
|
87
|
+
assert "a" not in cd
|
|
88
|
+
assert "b" in cd
|
|
89
|
+
|
|
90
|
+
def test_contains(self) -> None:
|
|
91
|
+
"""Test key existence checks"""
|
|
92
|
+
cd = CompressedDict(
|
|
93
|
+
initial_dict={"a": 1, "b": 2},
|
|
94
|
+
storage_mode=CompressedDictStorageMode(),
|
|
95
|
+
properties_to_cache=[],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
with cd.transaction():
|
|
99
|
+
assert "a" in cd
|
|
100
|
+
assert "b" in cd
|
|
101
|
+
assert "c" not in cd
|
|
102
|
+
|
|
103
|
+
def test_keys_and_values(self) -> None:
|
|
104
|
+
"""Test keys and values methods"""
|
|
105
|
+
initial_data = {"a": 1, "b": 2, "c": 3}
|
|
106
|
+
cd: CompressedDict[str, int] = CompressedDict(
|
|
107
|
+
initial_dict=initial_data,
|
|
108
|
+
storage_mode=CompressedDictStorageMode(),
|
|
109
|
+
properties_to_cache=[],
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
with cd.transaction():
|
|
113
|
+
assert set(cd.keys()) == {"a", "b", "c"}
|
|
114
|
+
assert set(cd.values()) == {1, 2, 3}
|
|
115
|
+
|
|
116
|
+
def test_items(self) -> None:
|
|
117
|
+
"""Test items method"""
|
|
118
|
+
initial_data = {"a": 1, "b": 2, "c": 3}
|
|
119
|
+
cd = CompressedDict(
|
|
120
|
+
initial_dict=initial_data,
|
|
121
|
+
storage_mode=CompressedDictStorageMode(),
|
|
122
|
+
properties_to_cache=[],
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
with cd.transaction():
|
|
126
|
+
assert set(cd.items()) == {("a", 1), ("b", 2), ("c", 3)}
|
|
127
|
+
|
|
128
|
+
def test_get_method(self) -> None:
|
|
129
|
+
"""Test get method with default"""
|
|
130
|
+
cd: CompressedDict[str, Any] = CompressedDict(
|
|
131
|
+
initial_dict={"a": 1},
|
|
132
|
+
storage_mode=CompressedDictStorageMode(),
|
|
133
|
+
properties_to_cache=[],
|
|
134
|
+
)
|
|
135
|
+
with cd.transaction():
|
|
136
|
+
assert cd.get("a") == 1
|
|
137
|
+
assert cd.get("b") is None
|
|
138
|
+
assert cd.get("b", "default") == "default"
|
|
139
|
+
|
|
140
|
+
def test_to_dict(self) -> None:
|
|
141
|
+
"""Test conversion to standard dictionary"""
|
|
142
|
+
initial_data = {"a": 1, "b": 2}
|
|
143
|
+
cd = CompressedDict(
|
|
144
|
+
initial_dict=initial_data,
|
|
145
|
+
storage_mode=CompressedDictStorageMode(),
|
|
146
|
+
properties_to_cache=[],
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
with cd.transaction():
|
|
150
|
+
assert cd.dict() == initial_data
|
|
151
|
+
|
|
152
|
+
def test_complex_nested_structures(self) -> None:
|
|
153
|
+
"""Test storage of complex nested structures"""
|
|
154
|
+
complex_data = {
|
|
155
|
+
"nested_dict": {"inner_key": "inner_value"},
|
|
156
|
+
"list": [1, 2, 3],
|
|
157
|
+
"mixed": [{"a": 1}, 2, "three"],
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# Test each storage storage_type
|
|
161
|
+
for storage_type in ["raw", "msgpack", "compressed_msgpack"]:
|
|
162
|
+
cd = CompressedDict(
|
|
163
|
+
initial_dict=complex_data,
|
|
164
|
+
storage_mode=CompressedDictStorageMode(
|
|
165
|
+
storage_type=cast(CompressedDictStorageType, storage_type)
|
|
166
|
+
),
|
|
167
|
+
properties_to_cache=[],
|
|
168
|
+
)
|
|
169
|
+
with cd.transaction():
|
|
170
|
+
assert cd["nested_dict"] == {"inner_key": "inner_value"}
|
|
171
|
+
assert cd["list"] == [1, 2, 3]
|
|
172
|
+
assert cd["mixed"] == [{"a": 1}, 2, "three"]
|
|
173
|
+
|
|
174
|
+
def test_repr(self) -> None:
|
|
175
|
+
"""Test string representation"""
|
|
176
|
+
cd = CompressedDict(
|
|
177
|
+
initial_dict={"a": 1, "b": 2},
|
|
178
|
+
storage_mode=CompressedDictStorageMode(),
|
|
179
|
+
properties_to_cache=[],
|
|
180
|
+
)
|
|
181
|
+
repr_str = repr(cd)
|
|
182
|
+
|
|
183
|
+
assert repr_str == "CompressedDict(storage_type='compressed', keys=2)"
|
|
184
|
+
|
|
185
|
+
def test_error_handling(self) -> None:
|
|
186
|
+
"""Test error scenarios"""
|
|
187
|
+
cd: CompressedDict[str, Any] = CompressedDict(
|
|
188
|
+
storage_mode=CompressedDictStorageMode(), properties_to_cache=[]
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
# Test KeyError
|
|
192
|
+
with pytest.raises(KeyError):
|
|
193
|
+
with cd.transaction():
|
|
194
|
+
_ = cd["non_existent_key"]
|
|
195
|
+
|
|
196
|
+
@pytest.mark.parametrize("storage_type", ["raw", "msgpack", "compressed_msgpack"])
|
|
197
|
+
def test_large_data_handling(self, storage_type: CompressedDictStorageType) -> None:
|
|
198
|
+
"""Test handling of large datasets"""
|
|
199
|
+
large_data = {f"key_{i}": f"value_{i}" for i in range(1000)}
|
|
200
|
+
|
|
201
|
+
cd = CompressedDict(
|
|
202
|
+
initial_dict=large_data,
|
|
203
|
+
storage_mode=CompressedDictStorageMode(storage_type=storage_type),
|
|
204
|
+
properties_to_cache=[],
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
assert len(cd) == 1000
|
|
208
|
+
with cd.transaction():
|
|
209
|
+
assert cd["key_500"] == "value_500"
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def test_transaction_basic_raw_storage() -> None:
|
|
213
|
+
"""
|
|
214
|
+
Test basic transaction functionality with raw storage mode
|
|
215
|
+
"""
|
|
216
|
+
storage_mode = CompressedDictStorageMode(storage_type="raw")
|
|
217
|
+
initial_dict = {"key1": "value1", "key2": "value2"}
|
|
218
|
+
|
|
219
|
+
compressed_dict = CompressedDict(
|
|
220
|
+
initial_dict=initial_dict, storage_mode=storage_mode, properties_to_cache=None
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
# Verify initial state
|
|
224
|
+
assert compressed_dict._transaction_depth == 0
|
|
225
|
+
|
|
226
|
+
# Use transaction context
|
|
227
|
+
with compressed_dict.transaction() as d:
|
|
228
|
+
assert compressed_dict._transaction_depth == 1
|
|
229
|
+
assert d._working_dict is not None
|
|
230
|
+
assert d._working_dict == initial_dict
|
|
231
|
+
|
|
232
|
+
# Modify the dictionary
|
|
233
|
+
d["key3"] = "value3"
|
|
234
|
+
|
|
235
|
+
# After transaction
|
|
236
|
+
assert compressed_dict._transaction_depth == 0
|
|
237
|
+
assert compressed_dict.raw_dict() == {
|
|
238
|
+
"key1": "value1",
|
|
239
|
+
"key2": "value2",
|
|
240
|
+
"key3": "value3",
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def test_transaction_nested_context() -> None:
|
|
245
|
+
"""
|
|
246
|
+
Test nested transaction contexts
|
|
247
|
+
"""
|
|
248
|
+
storage_mode = CompressedDictStorageMode(storage_type="msgpack")
|
|
249
|
+
initial_dict = {"key1": "value1"}
|
|
250
|
+
|
|
251
|
+
compressed_dict = CompressedDict(
|
|
252
|
+
initial_dict=initial_dict, storage_mode=storage_mode, properties_to_cache=None
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
with compressed_dict.transaction():
|
|
256
|
+
assert compressed_dict._transaction_depth == 1
|
|
257
|
+
|
|
258
|
+
with compressed_dict.transaction():
|
|
259
|
+
assert compressed_dict._transaction_depth == 2
|
|
260
|
+
compressed_dict["key2"] = "value2"
|
|
261
|
+
|
|
262
|
+
assert compressed_dict._transaction_depth == 1
|
|
263
|
+
|
|
264
|
+
assert compressed_dict._transaction_depth == 0
|
|
265
|
+
assert compressed_dict.raw_dict() == {"key1": "value1", "key2": "value2"}
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def test_transaction_access_error() -> None:
|
|
269
|
+
"""
|
|
270
|
+
Test that accessing or modifying dictionary outside transaction raises an error
|
|
271
|
+
"""
|
|
272
|
+
storage_mode = CompressedDictStorageMode(storage_type="compressed_msgpack")
|
|
273
|
+
compressed_dict = CompressedDict(
|
|
274
|
+
initial_dict={"key1": "value1"},
|
|
275
|
+
storage_mode=storage_mode,
|
|
276
|
+
properties_to_cache=None,
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
# Test getdict outside transaction
|
|
280
|
+
with pytest.raises(CompressedDictAccessError):
|
|
281
|
+
compressed_dict._get_dict()
|
|
282
|
+
|
|
283
|
+
# Test __getitem__ outside transaction
|
|
284
|
+
with pytest.raises(CompressedDictAccessError):
|
|
285
|
+
_ = compressed_dict["key1"]
|
|
286
|
+
|
|
287
|
+
# Test __setitem__ outside transaction
|
|
288
|
+
with pytest.raises(CompressedDictAccessError):
|
|
289
|
+
compressed_dict["key2"] = "value2"
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def test_transaction_different_storage_modes() -> None:
|
|
293
|
+
"""
|
|
294
|
+
Test transaction with different storage modes
|
|
295
|
+
"""
|
|
296
|
+
storage_modes = [
|
|
297
|
+
CompressedDictStorageMode(storage_type="raw"),
|
|
298
|
+
CompressedDictStorageMode(storage_type="msgpack"),
|
|
299
|
+
CompressedDictStorageMode(storage_type="compressed_msgpack"),
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
for storage_mode in storage_modes:
|
|
303
|
+
initial_dict = {"key1": "value1"}
|
|
304
|
+
|
|
305
|
+
compressed_dict = CompressedDict(
|
|
306
|
+
initial_dict=initial_dict,
|
|
307
|
+
storage_mode=storage_mode,
|
|
308
|
+
properties_to_cache=None,
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
with compressed_dict.transaction() as d:
|
|
312
|
+
d["key2"] = "value2"
|
|
313
|
+
|
|
314
|
+
assert compressed_dict.raw_dict() == {"key1": "value1", "key2": "value2"}
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def test_transaction_with_properties_to_cache() -> None:
|
|
318
|
+
"""
|
|
319
|
+
Test transaction with properties to cache
|
|
320
|
+
"""
|
|
321
|
+
storage_mode = CompressedDictStorageMode(storage_type="raw")
|
|
322
|
+
initial_dict = {"key1": "value1", "important_prop": "cached_value"}
|
|
323
|
+
|
|
324
|
+
compressed_dict = CompressedDict(
|
|
325
|
+
initial_dict=initial_dict,
|
|
326
|
+
storage_mode=storage_mode,
|
|
327
|
+
properties_to_cache=["important_prop"],
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
with compressed_dict.transaction() as d:
|
|
331
|
+
d["key2"] = "value2"
|
|
332
|
+
|
|
333
|
+
assert compressed_dict.raw_dict() == {
|
|
334
|
+
"key1": "value1",
|
|
335
|
+
"important_prop": "cached_value",
|
|
336
|
+
"key2": "value2",
|
|
337
|
+
}
|
|
338
|
+
assert compressed_dict._cached_properties == {"important_prop": "cached_value"}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def test_transaction_error_handling() -> None:
|
|
342
|
+
"""
|
|
343
|
+
Test error handling during transaction
|
|
344
|
+
"""
|
|
345
|
+
storage_mode = CompressedDictStorageMode(storage_type="compressed")
|
|
346
|
+
compressed_dict = CompressedDict(
|
|
347
|
+
initial_dict={"key1": "value1"},
|
|
348
|
+
storage_mode=storage_mode,
|
|
349
|
+
properties_to_cache=None,
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
try:
|
|
353
|
+
with compressed_dict.transaction():
|
|
354
|
+
compressed_dict["key2"] = "value2"
|
|
355
|
+
raise ValueError("Simulated error")
|
|
356
|
+
except ValueError:
|
|
357
|
+
# Ensure transaction depth is reset even after an error
|
|
358
|
+
assert compressed_dict._transaction_depth == 0
|
|
359
|
+
|
|
360
|
+
# Verify the dictionary state remains unchanged
|
|
361
|
+
with compressed_dict.transaction() as d:
|
|
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,71 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import json
|
|
3
|
+
import uuid
|
|
4
|
+
from datetime import datetime, date, time
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
# Optional: Import for additional type support
|
|
11
|
+
try:
|
|
12
|
+
import ipaddress
|
|
13
|
+
except ImportError:
|
|
14
|
+
ipaddress = None # type:ignore[assignment]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FhirJSONEncoder(json.JSONEncoder):
|
|
18
|
+
def default(self, o: Any) -> Any:
|
|
19
|
+
# Existing type handlers
|
|
20
|
+
if dataclasses.is_dataclass(o):
|
|
21
|
+
return dataclasses.asdict(o) # type:ignore[arg-type]
|
|
22
|
+
|
|
23
|
+
if isinstance(o, Enum):
|
|
24
|
+
return o.value
|
|
25
|
+
|
|
26
|
+
if isinstance(o, Decimal):
|
|
27
|
+
# Custom Decimal conversion
|
|
28
|
+
if o == o.to_integral_value():
|
|
29
|
+
return int(o)
|
|
30
|
+
else:
|
|
31
|
+
return float(o)
|
|
32
|
+
|
|
33
|
+
if isinstance(o, bytes):
|
|
34
|
+
return o.decode("utf-8")
|
|
35
|
+
|
|
36
|
+
if isinstance(o, (datetime, date)):
|
|
37
|
+
return o.isoformat()
|
|
38
|
+
|
|
39
|
+
if isinstance(o, time):
|
|
40
|
+
return o.isoformat()
|
|
41
|
+
|
|
42
|
+
if hasattr(o, "to_dict"):
|
|
43
|
+
return o.to_dict()
|
|
44
|
+
|
|
45
|
+
# New type handlers
|
|
46
|
+
|
|
47
|
+
# UUID handling
|
|
48
|
+
if isinstance(o, uuid.UUID):
|
|
49
|
+
return str(o)
|
|
50
|
+
|
|
51
|
+
# Set and frozenset handling
|
|
52
|
+
if isinstance(o, (set, frozenset)):
|
|
53
|
+
return list(o)
|
|
54
|
+
|
|
55
|
+
# Complex number handling
|
|
56
|
+
if isinstance(o, complex):
|
|
57
|
+
return {"real": o.real, "imag": o.imag}
|
|
58
|
+
|
|
59
|
+
# Path-like objects
|
|
60
|
+
if isinstance(o, (Path, Path)):
|
|
61
|
+
return str(o)
|
|
62
|
+
|
|
63
|
+
# IP Address handling (if ipaddress module is available)
|
|
64
|
+
if ipaddress and isinstance(o, (ipaddress.IPv4Address, ipaddress.IPv6Address)):
|
|
65
|
+
return str(o)
|
|
66
|
+
|
|
67
|
+
# Custom object serialization fallback
|
|
68
|
+
if hasattr(o, "__dict__"):
|
|
69
|
+
return o.__dict__
|
|
70
|
+
|
|
71
|
+
return super().default(o)
|