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.
Files changed (59) hide show
  1. compressedfhir/__init__.py +0 -0
  2. compressedfhir/fhir/__init__.py +0 -0
  3. compressedfhir/fhir/base_resource_list.py +165 -0
  4. compressedfhir/fhir/fhir_bundle.py +295 -0
  5. compressedfhir/fhir/fhir_bundle_entry.py +240 -0
  6. compressedfhir/fhir/fhir_bundle_entry_list.py +97 -0
  7. compressedfhir/fhir/fhir_bundle_entry_request.py +73 -0
  8. compressedfhir/fhir/fhir_bundle_entry_response.py +67 -0
  9. compressedfhir/fhir/fhir_bundle_entry_search.py +75 -0
  10. compressedfhir/fhir/fhir_identifier.py +84 -0
  11. compressedfhir/fhir/fhir_link.py +63 -0
  12. compressedfhir/fhir/fhir_meta.py +47 -0
  13. compressedfhir/fhir/fhir_resource.py +170 -0
  14. compressedfhir/fhir/fhir_resource_list.py +149 -0
  15. compressedfhir/fhir/fhir_resource_map.py +193 -0
  16. compressedfhir/fhir/test/__init__.py +0 -0
  17. compressedfhir/fhir/test/test_bundle_entry.py +129 -0
  18. compressedfhir/fhir/test/test_bundle_entry_list.py +187 -0
  19. compressedfhir/fhir/test/test_bundle_entry_request.py +74 -0
  20. compressedfhir/fhir/test/test_bundle_entry_response.py +65 -0
  21. compressedfhir/fhir/test/test_fhir_bundle.py +245 -0
  22. compressedfhir/fhir/test/test_fhir_resource.py +104 -0
  23. compressedfhir/fhir/test/test_fhir_resource_list.py +160 -0
  24. compressedfhir/fhir/test/test_fhir_resource_map.py +293 -0
  25. compressedfhir/py.typed +0 -0
  26. compressedfhir/utilities/__init__.py +0 -0
  27. compressedfhir/utilities/compressed_dict/__init__.py +0 -0
  28. compressedfhir/utilities/compressed_dict/v1/__init__.py +0 -0
  29. compressedfhir/utilities/compressed_dict/v1/compressed_dict.py +701 -0
  30. compressedfhir/utilities/compressed_dict/v1/compressed_dict_access_error.py +2 -0
  31. compressedfhir/utilities/compressed_dict/v1/compressed_dict_storage_mode.py +50 -0
  32. compressedfhir/utilities/compressed_dict/v1/test/__init__.py +0 -0
  33. compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py +467 -0
  34. compressedfhir/utilities/fhir_json_encoder.py +71 -0
  35. compressedfhir/utilities/json_helpers.py +181 -0
  36. compressedfhir/utilities/json_serializers/__init__.py +0 -0
  37. compressedfhir/utilities/json_serializers/test/__init__.py +0 -0
  38. compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py +165 -0
  39. compressedfhir/utilities/json_serializers/test/test_type_preservation_encoder.py +71 -0
  40. compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py +197 -0
  41. compressedfhir/utilities/json_serializers/type_preservation_decoder.py +135 -0
  42. compressedfhir/utilities/json_serializers/type_preservation_encoder.py +55 -0
  43. compressedfhir/utilities/json_serializers/type_preservation_serializer.py +57 -0
  44. compressedfhir/utilities/ordered_dict_to_dict_converter/__init__.py +0 -0
  45. compressedfhir/utilities/ordered_dict_to_dict_converter/ordered_dict_to_dict_converter.py +24 -0
  46. compressedfhir/utilities/string_compressor/__init__.py +0 -0
  47. compressedfhir/utilities/string_compressor/v1/__init__.py +0 -0
  48. compressedfhir/utilities/string_compressor/v1/string_compressor.py +99 -0
  49. compressedfhir/utilities/string_compressor/v1/test/__init__.py +0 -0
  50. compressedfhir/utilities/string_compressor/v1/test/test_string_compressor.py +189 -0
  51. compressedfhir/utilities/test/__init__.py +0 -0
  52. compressedfhir/utilities/test/test_fhir_json_encoder.py +177 -0
  53. compressedfhir/utilities/test/test_json_helpers.py +99 -0
  54. compressedfhir-3.0.2.dist-info/METADATA +139 -0
  55. compressedfhir-3.0.2.dist-info/RECORD +59 -0
  56. compressedfhir-3.0.2.dist-info/WHEEL +5 -0
  57. compressedfhir-3.0.2.dist-info/licenses/LICENSE +201 -0
  58. compressedfhir-3.0.2.dist-info/top_level.txt +2 -0
  59. tests/__init__.py +0 -0
File without changes
File without changes
@@ -0,0 +1,165 @@
1
+ import copy
2
+ import json
3
+ from contextlib import contextmanager
4
+ from typing import (
5
+ Deque,
6
+ List,
7
+ AsyncGenerator,
8
+ Optional,
9
+ Any,
10
+ Dict,
11
+ Iterator,
12
+ override,
13
+ Iterable,
14
+ Generator,
15
+ )
16
+
17
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict import (
18
+ CompressedDict,
19
+ )
20
+ from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
21
+
22
+
23
+ class BaseResourceList[T: CompressedDict[str, Any]](Deque[T]):
24
+ """
25
+ Represents a list of FHIR resources.
26
+ """
27
+
28
+ __slots__: List[str] = []
29
+
30
+ def __init__(
31
+ self,
32
+ iterable: Iterable[T] | None = None,
33
+ maxlen: Optional[int] = None,
34
+ ) -> None:
35
+ # if iterable is not None:
36
+ # assert all([isinstance(r, CompressedDict) for r in iterable]), f"type {type(next(iter(iterable)))}"
37
+ super().__init__(iterable, maxlen=maxlen) if iterable else super().__init__()
38
+
39
+ @override
40
+ def append(self, x: T, /) -> None:
41
+ super().append(x)
42
+ # assert isinstance(x, CompressedDict), f"{type(x)} is not a CompressedDict"
43
+
44
+ def json(self) -> str:
45
+ """
46
+ Convert the list of FhirResource objects to a JSON string.
47
+
48
+ :return: JSON string representation of the list.
49
+ """
50
+ return json.dumps([r.dict() for r in self], cls=FhirJSONEncoder)
51
+
52
+ def get_resources(self) -> "BaseResourceList[T]":
53
+ """
54
+ Get all resources in the list.
55
+
56
+ :return: A list of FhirResource objects.
57
+ """
58
+ # this is here to keep compatibility with FhirResourceMap
59
+ return self
60
+
61
+ async def consume_resource_batch_async(
62
+ self,
63
+ *,
64
+ batch_size: Optional[int],
65
+ ) -> AsyncGenerator["BaseResourceList[T]", None]:
66
+ """
67
+ Consume resources in batches asynchronously.
68
+
69
+ :param batch_size: The size of each batch.
70
+ :return: An async generator yielding batches of FhirResourceList.
71
+ """
72
+ batch: BaseResourceList[T]
73
+ if batch_size is None:
74
+ batch = BaseResourceList[T]()
75
+ while self:
76
+ batch.append(self.popleft())
77
+ yield batch
78
+ elif batch_size <= 0:
79
+ raise ValueError("Batch size must be greater than 0.")
80
+ else:
81
+ while self:
82
+ batch = BaseResourceList[T]()
83
+ for _ in range(min(batch_size, len(self))):
84
+ batch.append(self.popleft())
85
+ yield batch
86
+
87
+ def consume_resource_batch(
88
+ self,
89
+ *,
90
+ batch_size: Optional[int],
91
+ ) -> Generator["BaseResourceList[T]", None, None]:
92
+ """
93
+ Consume resources in batches asynchronously.
94
+
95
+ :param batch_size: The size of each batch.
96
+ :return: An async generator yielding batches of FhirResourceList.
97
+ """
98
+ batch: BaseResourceList[T]
99
+ if batch_size is None:
100
+ batch = BaseResourceList[T]()
101
+ while self:
102
+ batch.append(self.popleft())
103
+ yield batch
104
+ elif batch_size <= 0:
105
+ raise ValueError("Batch size must be greater than 0.")
106
+ else:
107
+ while self:
108
+ batch = BaseResourceList[T]()
109
+ for _ in range(min(batch_size, len(self))):
110
+ batch.append(self.popleft())
111
+ yield batch
112
+
113
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "BaseResourceList[T]":
114
+ """
115
+ Create a copy of the FhirResourceList.
116
+
117
+ :return: A new FhirResourceList object with the same resources.
118
+ """
119
+ return BaseResourceList[T]([copy.deepcopy(r) for r in self])
120
+
121
+ def __repr__(self) -> str:
122
+ """
123
+ Return a string representation of the FhirResourceList.
124
+
125
+ :return: A string representation of the FhirResourceList.
126
+ """
127
+ return f"FhirResourceList(resources: {len(self)})"
128
+
129
+ @contextmanager
130
+ def transaction(self) -> Iterator["BaseResourceList[T]"]:
131
+ """
132
+ Opens a transaction for all resources in the list.
133
+
134
+ Deserializes the dictionary before entering the context
135
+ Serializes the dictionary after exiting the context
136
+
137
+ Raises:
138
+ CompressedDictAccessError: If methods are called outside the context
139
+
140
+ """
141
+ try:
142
+ self.start_transaction()
143
+
144
+ yield self
145
+
146
+ finally:
147
+ self.end_transaction()
148
+
149
+ def start_transaction(self) -> "BaseResourceList[T]":
150
+ """
151
+ Starts a transaction for each resource in the list. Use transaction() for a contextmanager for simpler usage.
152
+ """
153
+
154
+ for resource in self:
155
+ resource.start_transaction()
156
+ return self
157
+
158
+ def end_transaction(self) -> "BaseResourceList[T]":
159
+ """
160
+ Ends a transaction. Use transaction() for a context_manager for simpler usage.
161
+
162
+ """
163
+ for resource in self:
164
+ resource.end_transaction()
165
+ return self
@@ -0,0 +1,295 @@
1
+ import copy
2
+ import json
3
+ from typing import Any, Dict, List, Optional, cast, OrderedDict
4
+
5
+ from compressedfhir.fhir.fhir_bundle_entry import FhirBundleEntry
6
+ from compressedfhir.fhir.fhir_bundle_entry_list import FhirBundleEntryList
7
+ from compressedfhir.fhir.fhir_identifier import FhirIdentifier
8
+ from compressedfhir.fhir.fhir_link import FhirLink
9
+ from compressedfhir.fhir.fhir_meta import FhirMeta
10
+ from compressedfhir.fhir.fhir_resource import FhirResource
11
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode import (
12
+ CompressedDictStorageMode,
13
+ )
14
+ from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
15
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
16
+ from compressedfhir.utilities.ordered_dict_to_dict_converter.ordered_dict_to_dict_converter import (
17
+ OrderedDictToDictConverter,
18
+ )
19
+
20
+
21
+ class FhirBundle:
22
+ """
23
+ FhirBundle represents a FHIR Bundle resource.
24
+ """
25
+
26
+ __slots__ = [
27
+ "entry",
28
+ "total",
29
+ "id_",
30
+ "identifier",
31
+ "timestamp",
32
+ "type_",
33
+ "link",
34
+ "meta",
35
+ ]
36
+
37
+ def __init__(
38
+ self,
39
+ *,
40
+ id_: Optional[str] = None,
41
+ identifier: Optional[FhirIdentifier] = None,
42
+ timestamp: Optional[str] = None,
43
+ type_: str,
44
+ entry: Optional[FhirBundleEntryList] = None,
45
+ total: Optional[int] = None,
46
+ link: Optional[List[FhirLink]] = None,
47
+ meta: Optional[FhirMeta] = None,
48
+ ) -> None:
49
+ """
50
+ Initializes a FhirBundle object.
51
+
52
+
53
+ :param id_: The ID of the Bundle.
54
+ :param identifier: The identifier of the Bundle.
55
+ :param timestamp: The timestamp of the Bundle.
56
+ :param type_: The type of the Bundle (e.g., "searchset").
57
+ :param entry: The entries in the Bundle.
58
+ :param total: The total number of entries in the Bundle.
59
+ :param link: The links associated with the Bundle.
60
+ :param meta: The metadata associated with the Bundle.
61
+ """
62
+ self.entry: FhirBundleEntryList = entry or FhirBundleEntryList()
63
+ self.total: Optional[int] = total
64
+ self.id_: Optional[str] = id_
65
+ self.identifier: Optional[FhirIdentifier] = identifier
66
+ self.timestamp: Optional[str] = timestamp
67
+ self.type_: str = type_
68
+ self.link: Optional[List[FhirLink]] = link
69
+ self.meta: Optional[FhirMeta] = meta
70
+
71
+ def dict(self) -> OrderedDict[str, Any]:
72
+ entries: List[Dict[str, Any]] | None = (
73
+ [entry.dict() for entry in self.entry] if self.entry else None
74
+ )
75
+ result: OrderedDict[str, Any] = OrderedDict[str, Any](
76
+ {"type": self.type_, "resourceType": "Bundle"}
77
+ )
78
+
79
+ if self.id_ is not None:
80
+ result["id"] = self.id_
81
+ if self.identifier is not None:
82
+ result["identifier"] = self.identifier.dict()
83
+ if self.timestamp is not None:
84
+ result["timestamp"] = self.timestamp
85
+ if self.total is not None:
86
+ result["total"] = self.total
87
+ if entries and len(entries) > 0:
88
+ result["entry"] = entries
89
+ if self.link:
90
+ result["link"] = [link.dict() for link in self.link]
91
+ if self.meta:
92
+ result["meta"] = self.meta.dict()
93
+ return FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(result)
94
+
95
+ @classmethod
96
+ def construct(
97
+ cls,
98
+ *,
99
+ id_: Optional[str] = None,
100
+ identifier: Optional[FhirIdentifier] = None,
101
+ timestamp: Optional[str] = None,
102
+ type_: str,
103
+ entry: Optional[FhirBundleEntryList] = None,
104
+ total: Optional[int] = None,
105
+ link: Optional[List[FhirLink]] = None,
106
+ meta: Optional[FhirMeta] = None,
107
+ ) -> "FhirBundle":
108
+ """
109
+ Constructs a FhirBundle object from keyword arguments.
110
+
111
+ :param id_: The ID of the Bundle.
112
+ :param identifier: The identifier of the Bundle.
113
+ :param timestamp: The timestamp of the Bundle.
114
+ :param type_: The type of the Bundle (e.g., "searchset").
115
+ :param entry: The entries in the Bundle.
116
+ :param total: The total number of entries in the Bundle.
117
+ :param link: The links associated with the Bundle.
118
+ :param meta: The metadata associated with the Bundle.
119
+ """
120
+ return cls(
121
+ id_=id_,
122
+ identifier=identifier,
123
+ timestamp=timestamp,
124
+ type_=type_,
125
+ entry=entry,
126
+ total=total,
127
+ link=link,
128
+ meta=meta,
129
+ )
130
+
131
+ @classmethod
132
+ def from_dict(
133
+ cls,
134
+ data: OrderedDict[str, Any] | Dict[str, Any],
135
+ storage_mode: CompressedDictStorageMode | None = None,
136
+ ) -> "FhirBundle":
137
+ """
138
+ Creates a FhirBundle object from a dictionary.
139
+
140
+ :param data: A dictionary containing the Bundle data.
141
+ :param storage_mode: The storage mode for the Bundle.
142
+ :return: A FhirBundle object.
143
+ """
144
+ if storage_mode is None:
145
+ storage_mode = CompressedDictStorageMode.default()
146
+
147
+ bundle = cls(
148
+ id_=data.get("id") if isinstance(data.get("id"), str) else None,
149
+ identifier=(
150
+ FhirIdentifier.from_dict(data["identifier"])
151
+ if "identifier" in data
152
+ else None
153
+ ),
154
+ timestamp=(
155
+ data.get("timestamp")
156
+ if isinstance(data.get("timestamp"), str)
157
+ else None
158
+ ),
159
+ type_=(
160
+ cast(str, data.get("type"))
161
+ if isinstance(data.get("type"), str)
162
+ else "collection"
163
+ ),
164
+ total=data.get("total") if isinstance(data.get("total"), int) else None,
165
+ entry=(
166
+ FhirBundleEntryList(
167
+ [
168
+ FhirBundleEntry.from_dict(entry, storage_mode=storage_mode)
169
+ for entry in (data.get("entry", []) or [])
170
+ ]
171
+ )
172
+ if "entry" in data
173
+ else FhirBundleEntryList()
174
+ ),
175
+ link=[FhirLink.from_dict(link) for link in (data.get("link", []) or [])],
176
+ meta=data.get("meta"),
177
+ )
178
+ return bundle
179
+
180
+ @staticmethod
181
+ def add_diagnostics_to_operation_outcomes(
182
+ *, resource: FhirResource, diagnostics_coding: List[Dict[str, Any]]
183
+ ) -> FhirResource:
184
+ """
185
+ Adds diagnostic coding to OperationOutcome resources to identify which call resulted in that OperationOutcome
186
+ being returned by the server
187
+
188
+
189
+ :param resource: The resource to add the diagnostics to
190
+ :param diagnostics_coding: The diagnostics coding to add
191
+ :return: The resource with the diagnostics added
192
+ """
193
+ with resource.transaction():
194
+ if resource.resource_type == "OperationOutcome":
195
+ issues = resource.get("issue")
196
+ if issues:
197
+ for issue in issues:
198
+ coding: List[Dict[str, Any]] = issue.setdefault(
199
+ "details", {}
200
+ ).setdefault("coding", [])
201
+ coding.extend(diagnostics_coding)
202
+ return resource
203
+
204
+ def json(self) -> str:
205
+ """
206
+ Converts the Bundle to a JSON string.
207
+
208
+ :return: JSON string representation of the Bundle
209
+ """
210
+ bundle_dict = self.dict()
211
+ return json.dumps(bundle_dict, cls=FhirJSONEncoder)
212
+
213
+ def copy(self) -> "FhirBundle":
214
+ return copy.deepcopy(self)
215
+
216
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirBundle":
217
+ """
218
+ Creates a copy of the Bundle.
219
+
220
+ :return: A new FhirBundle object with the same attributes
221
+ """
222
+ return FhirBundle(
223
+ entry=copy.deepcopy(self.entry) if self.entry else None,
224
+ total=self.total,
225
+ id_=self.id_,
226
+ identifier=copy.deepcopy(self.identifier) if self.identifier else None,
227
+ timestamp=self.timestamp,
228
+ type_=self.type_,
229
+ link=[copy.deepcopy(link) for link in self.link] if self.link else None,
230
+ )
231
+
232
+ def __repr__(self) -> str:
233
+ """
234
+ Returns a string representation of the Bundle.
235
+
236
+ :return: String representation of the Bundle
237
+ """
238
+ properties: List[str] = []
239
+ if self.id_:
240
+ properties.append(f"id_={self.id_}")
241
+ if self.type_:
242
+ properties.append(f"type_={self.type_}")
243
+ if self.timestamp:
244
+ properties.append(f"timestamp={self.timestamp}")
245
+ if self.total:
246
+ properties.append(f"total={self.total}")
247
+ if self.entry:
248
+ properties.append(f"entry={len(self.entry)}")
249
+ if self.link:
250
+ properties.append(f"link={len(self.link)}")
251
+
252
+ return f"FhirBundle({', '.join(properties)})"
253
+
254
+ def get_count_by_resource_type(self) -> Dict[str, int]:
255
+ """
256
+ Gets the count of resources by resource type.
257
+
258
+ :return: The count of resources by resource type
259
+ """
260
+ resources_by_type: Dict[str, int] = dict()
261
+ if self.entry is None:
262
+ return resources_by_type
263
+
264
+ entry: FhirBundleEntry
265
+ for entry in [e for e in self.entry if e is not None]:
266
+ if entry.resource is not None:
267
+ resource = entry.resource
268
+ resource_type: str = resource.resource_type or "unknown"
269
+ if resource_type not in resources_by_type:
270
+ resources_by_type[resource_type] = 0
271
+ resources_by_type[resource_type] += 1
272
+ return resources_by_type
273
+
274
+ @property
275
+ def id(self) -> Optional[str]:
276
+ """Get the ID of the Bundle."""
277
+ return self.id_
278
+
279
+ @id.setter
280
+ def id(self, value: Optional[str]) -> None:
281
+ """Set the ID of the Bundle."""
282
+ self.id_ = value
283
+
284
+ @property
285
+ def resource_type(self) -> str:
286
+ """Get the resource type of the Bundle."""
287
+ return "Bundle"
288
+
289
+ def to_plain_dict(self) -> Dict[str, Any]:
290
+ """
291
+ Converts the Bundle to a plain dictionary.
292
+
293
+ :return: Plain dictionary representation of the Bundle
294
+ """
295
+ return OrderedDictToDictConverter.convert(self.dict())
@@ -0,0 +1,240 @@
1
+ import copy
2
+ import json
3
+ from typing import Any, Dict, Optional, List, OrderedDict
4
+
5
+ from compressedfhir.fhir.fhir_bundle_entry_search import FhirBundleEntrySearch
6
+ from compressedfhir.fhir.fhir_link import FhirLink
7
+ from compressedfhir.fhir.fhir_bundle_entry_request import FhirBundleEntryRequest
8
+ from compressedfhir.fhir.fhir_bundle_entry_response import (
9
+ FhirBundleEntryResponse,
10
+ )
11
+ from compressedfhir.fhir.fhir_resource import FhirResource
12
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict import (
13
+ CompressedDict,
14
+ )
15
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode import (
16
+ CompressedDictStorageMode,
17
+ )
18
+ from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
19
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
20
+
21
+
22
+ class FhirBundleEntry:
23
+ """
24
+ Represents a single entry in a FHIR Bundle.
25
+ """
26
+
27
+ __slots__ = [
28
+ "_resource",
29
+ "request",
30
+ "response",
31
+ "fullUrl",
32
+ "link",
33
+ "search",
34
+ "storage_mode",
35
+ ]
36
+
37
+ # noinspection PyPep8Naming
38
+ def __init__(
39
+ self,
40
+ *,
41
+ fullUrl: Optional[str] = None,
42
+ resource: Dict[str, Any] | FhirResource | OrderedDict[str, Any] | None,
43
+ request: Optional[FhirBundleEntryRequest] = None,
44
+ response: Optional[FhirBundleEntryResponse] = None,
45
+ link: Optional[List[FhirLink]] = None,
46
+ search: Optional[FhirBundleEntrySearch] = None,
47
+ storage_mode: CompressedDictStorageMode | None = None,
48
+ ) -> None:
49
+ """
50
+ Initializes a BundleEntry object.
51
+
52
+ :param fullUrl: The full URL of the entry.
53
+ :param resource: The FHIR resource associated with the entry.
54
+ :param request: The request information associated with the entry.
55
+ :param response: The response information associated with the entry.
56
+ :param storage_mode: The storage mode for the resource.
57
+ """
58
+ if storage_mode is None:
59
+ storage_mode = CompressedDictStorageMode.default()
60
+ self._resource: Optional[FhirResource] = (
61
+ resource
62
+ if isinstance(resource, CompressedDict)
63
+ else (
64
+ FhirResource(initial_dict=resource, storage_mode=storage_mode)
65
+ if resource is not None
66
+ else None
67
+ )
68
+ )
69
+ self.request: Optional[FhirBundleEntryRequest] = request
70
+ self.response: Optional[FhirBundleEntryResponse] = response
71
+ self.fullUrl: Optional[str] = fullUrl
72
+ self.link: Optional[List[FhirLink]] = link
73
+ self.search: Optional[FhirBundleEntrySearch] = search
74
+ self.storage_mode: CompressedDictStorageMode = storage_mode
75
+
76
+ # noinspection PyPep8Naming
77
+ @classmethod
78
+ def construct(
79
+ cls,
80
+ *,
81
+ fullUrl: Optional[str] = None,
82
+ resource: Dict[str, Any] | FhirResource | OrderedDict[str, Any] | None,
83
+ request: Optional[FhirBundleEntryRequest] = None,
84
+ response: Optional[FhirBundleEntryResponse] = None,
85
+ link: Optional[List[FhirLink]] = None,
86
+ search: Optional[FhirBundleEntrySearch] = None,
87
+ storage_mode: CompressedDictStorageMode | None = None,
88
+ ) -> "FhirBundleEntry":
89
+ """
90
+ Constructs a BundleEntry object with the given parameters.
91
+
92
+ :param fullUrl: The full URL of the entry.
93
+ :param resource: The FHIR resource associated with the entry.
94
+ :param request: The request information associated with the entry.
95
+ :param response: The response information associated with the entry.
96
+ :param link: The links associated with the entry.
97
+ :param search: The search information associated with the entry.
98
+ :param storage_mode: The storage mode for the resource.
99
+ """
100
+ if storage_mode is None:
101
+ storage_mode = CompressedDictStorageMode.default()
102
+ return cls(
103
+ fullUrl=fullUrl,
104
+ resource=resource,
105
+ request=request,
106
+ response=response,
107
+ link=link,
108
+ search=search,
109
+ storage_mode=storage_mode,
110
+ )
111
+
112
+ @property
113
+ def resource(self) -> Optional[FhirResource]:
114
+ """
115
+ Returns the FHIR resource associated with the entry.
116
+
117
+ :return: The FHIR resource.
118
+ """
119
+ return self._resource
120
+
121
+ @resource.setter
122
+ def resource(self, value: Dict[str, Any] | FhirResource | None) -> None:
123
+ """
124
+ Sets the FHIR resource associated with the entry.
125
+
126
+ :param value: The FHIR resource to set.
127
+ """
128
+ if value is not None:
129
+ if isinstance(value, CompressedDict):
130
+ self._resource = value
131
+ else:
132
+ if self._resource is None:
133
+ self._resource = FhirResource(
134
+ initial_dict=value, storage_mode=self.storage_mode
135
+ )
136
+ else:
137
+ self._resource.replace(value=value)
138
+ else:
139
+ self._resource = None
140
+
141
+ def dict(self) -> OrderedDict[str, Any]:
142
+ result: OrderedDict[str, Any] = OrderedDict[str, Any]()
143
+ if self.fullUrl is not None:
144
+ result["fullUrl"] = self.fullUrl
145
+ if self.resource is not None:
146
+ result["resource"] = self.resource.dict()
147
+ if self.request is not None:
148
+ result["request"] = self.request.dict()
149
+ if self.response is not None:
150
+ result["response"] = self.response.dict()
151
+ if self.link is not None:
152
+ result["link"] = [link.dict() for link in self.link]
153
+ if self.search is not None:
154
+ result["search"] = self.search.dict()
155
+ return FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(result)
156
+
157
+ @classmethod
158
+ def from_dict(
159
+ cls,
160
+ d: OrderedDict[str, Any] | Dict[str, Any],
161
+ storage_mode: CompressedDictStorageMode | None = None,
162
+ ) -> "FhirBundleEntry":
163
+ if storage_mode is None:
164
+ storage_mode = CompressedDictStorageMode.default()
165
+ return cls(
166
+ fullUrl=d["fullUrl"] if "fullUrl" in d else None,
167
+ resource=(
168
+ FhirResource(initial_dict=d["resource"], storage_mode=storage_mode)
169
+ if "resource" in d
170
+ else None
171
+ ),
172
+ request=(
173
+ FhirBundleEntryRequest.from_dict(d["request"])
174
+ if "request" in d
175
+ else None
176
+ ),
177
+ response=(
178
+ FhirBundleEntryResponse.from_dict(d["response"])
179
+ if "response" in d
180
+ else None
181
+ ),
182
+ link=(
183
+ [FhirLink.from_dict(link) for link in d["link"]]
184
+ if "link" in d
185
+ else None
186
+ ),
187
+ search=(
188
+ FhirBundleEntrySearch.from_dict(d["search"]) if "search" in d else None
189
+ ),
190
+ storage_mode=storage_mode,
191
+ )
192
+
193
+ def json(self) -> str:
194
+ """
195
+ Converts the BundleEntry object to a JSON string.
196
+
197
+ :return: A JSON string representation of the BundleEntry.
198
+ """
199
+ return json.dumps(obj=self.dict(), cls=FhirJSONEncoder)
200
+
201
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirBundleEntry":
202
+ """
203
+ Creates a copy of the BundleEntry object.
204
+
205
+ :return: A new BundleEntry object with the same attributes.
206
+ """
207
+ return FhirBundleEntry(
208
+ fullUrl=self.fullUrl,
209
+ resource=copy.deepcopy(self.resource) if self.resource else None,
210
+ request=copy.deepcopy(self.request) if self.request else None,
211
+ response=copy.deepcopy(self.response) if self.response else None,
212
+ link=[copy.deepcopy(link) for link in self.link] if self.link else None,
213
+ search=copy.deepcopy(self.search) if self.search else None,
214
+ storage_mode=self.storage_mode,
215
+ )
216
+
217
+ def __repr__(self) -> str:
218
+ """
219
+ Returns a string representation of the BundleEntry object.
220
+
221
+ :return: A string representation of the BundleEntry.
222
+ """
223
+ return (
224
+ f"BundleEntry({self.resource.resource_type}/{self.resource.id})"
225
+ if self.resource
226
+ else "BundleEntry(Empty)"
227
+ )
228
+
229
+ def copy(self) -> "FhirBundleEntry":
230
+ """
231
+ Creates a copy of the BundleEntry object.
232
+
233
+ :return: A new BundleEntry object with the same attributes.
234
+ """
235
+ return copy.deepcopy(self)
236
+
237
+ @property
238
+ def resource_type_and_id(self) -> Optional[str]:
239
+ """Get the key from the resource."""
240
+ return self.resource.resource_type_and_id if self.resource else None