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
@@ -0,0 +1,97 @@
1
+ import copy
2
+ from typing import (
3
+ Deque,
4
+ Optional,
5
+ AsyncGenerator,
6
+ List,
7
+ Any,
8
+ Dict,
9
+ Set,
10
+ override,
11
+ Iterable,
12
+ )
13
+
14
+ from compressedfhir.fhir.fhir_bundle_entry import FhirBundleEntry
15
+
16
+
17
+ class FhirBundleEntryList(Deque[FhirBundleEntry]):
18
+ """
19
+ Represents a list of FHIR Bundle entries.
20
+ """
21
+
22
+ __slots__: List[str] = ["_keys"]
23
+
24
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
25
+ super().__init__(*args, **kwargs)
26
+ self._keys: Set[str] = set()
27
+ for entry in self:
28
+ key = getattr(entry, "resource_type_and_id", None)
29
+ if key is not None:
30
+ self._keys.add(key)
31
+
32
+ async def consume_resource_async(
33
+ self,
34
+ *,
35
+ batch_size: Optional[int],
36
+ ) -> AsyncGenerator["FhirBundleEntryList", None]:
37
+ """
38
+ Consume bundle entries in batches asynchronously.
39
+
40
+ :param batch_size: The size of each batch.
41
+ :return: An async generator yielding batches of FhirResourceList.
42
+ """
43
+ if batch_size is None:
44
+ while self:
45
+ yield FhirBundleEntryList([self.popleft()])
46
+ elif batch_size <= 0:
47
+ raise ValueError("Batch size must be greater than 0.")
48
+ else:
49
+ while self:
50
+ batch = FhirBundleEntryList()
51
+ for _ in range(min(batch_size, len(self))):
52
+ batch.append(self.popleft())
53
+ yield batch
54
+
55
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirBundleEntryList":
56
+ """
57
+ Create a shallow copy of the FhirBundleEntryList.
58
+
59
+ :return: A new FhirBundleEntryList instance with the same entries.
60
+ """
61
+ return FhirBundleEntryList([copy.deepcopy(entry) for entry in self])
62
+
63
+ def __repr__(self) -> str:
64
+ """
65
+ Return a string representation of the FhirBundleEntryList.
66
+
67
+ :return: A string representation of the FhirBundleEntryList.
68
+ """
69
+ return f"FhirBundleEntryList(entries: {len(self)})"
70
+
71
+ @override
72
+ def append(self, x: FhirBundleEntry, /) -> None:
73
+ """
74
+ Append an entry to the FhirBundleEntryList.
75
+
76
+ :param x: The entry to append.
77
+ """
78
+ if not isinstance(x, FhirBundleEntry):
79
+ raise TypeError("Only FhirBundleEntry instances can be appended.")
80
+
81
+ # check that we don't have a duplicate entry
82
+ key: Optional[str] = x.resource_type_and_id
83
+ if key is not None and key in self._keys:
84
+ return
85
+ if key is not None:
86
+ self._keys.add(key)
87
+ super().append(x)
88
+
89
+ @override
90
+ def extend(self, iterable: Iterable[FhirBundleEntry], /) -> None:
91
+ """
92
+ Extend the FhirBundleEntryList with entries from an iterable.
93
+
94
+ :param iterable: The iterable containing entries to extend.
95
+ """
96
+ for entry in iterable:
97
+ self.append(entry)
@@ -0,0 +1,73 @@
1
+ from datetime import datetime
2
+ from typing import Any, Dict, Optional, OrderedDict
3
+
4
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
5
+
6
+
7
+ class FhirBundleEntryRequest:
8
+ """
9
+ FHIR Bundle Entry Request class for encapsulating the request to be sent to FHIR server
10
+ """
11
+
12
+ __slots__ = ["url", "method", "ifModifiedSince", "ifNoneMatch", "ifNoneExist"]
13
+
14
+ # noinspection PyPep8Naming
15
+ def __init__(
16
+ self,
17
+ *,
18
+ url: str,
19
+ method: str = "GET",
20
+ ifNoneMatch: Optional[str] = None,
21
+ ifModifiedSince: Optional[datetime] = None,
22
+ ifNoneExist: Optional[str] = None,
23
+ ) -> None:
24
+ self.url: str = url or "https://example.com"
25
+ self.method: str = method or "GET"
26
+ self.ifModifiedSince: Optional[datetime] = ifModifiedSince
27
+ self.ifNoneMatch: Optional[str] = ifNoneMatch
28
+ self.ifNoneExist: Optional[str] = ifNoneExist
29
+
30
+ def dict(self) -> OrderedDict[str, Any]:
31
+ result: OrderedDict[str, Any] = OrderedDict[str, Any](
32
+ {"url": self.url, "method": self.method}
33
+ )
34
+ if self.ifModifiedSince is not None:
35
+ result["ifModifiedSince"] = self.ifModifiedSince.isoformat()
36
+ if self.ifNoneMatch is not None:
37
+ result["ifNoneMatch"] = self.ifNoneMatch
38
+ if self.ifNoneExist is not None:
39
+ result["ifNoneExist"] = self.ifNoneExist
40
+ return FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(result)
41
+
42
+ @classmethod
43
+ def from_dict(
44
+ cls, d: Dict[str, Any] | OrderedDict[str, Any]
45
+ ) -> "FhirBundleEntryRequest":
46
+ date_if_modified_since: Optional[datetime] = None
47
+ if "ifModifiedSince" in d:
48
+ if isinstance(d["ifModifiedSince"], datetime):
49
+ date_if_modified_since = d["ifModifiedSince"]
50
+ elif isinstance(d["ifModifiedSince"], str):
51
+ date_if_modified_since = datetime.fromisoformat(d["ifModifiedSince"])
52
+ return cls(
53
+ url=d.get("url", "https://example.com"),
54
+ method=d.get("method", "GET"),
55
+ ifModifiedSince=date_if_modified_since,
56
+ ifNoneMatch=d["ifNoneMatch"] if "ifNoneMatch" in d else None,
57
+ ifNoneExist=d["ifNoneExist"] if "ifNoneExist" in d else None,
58
+ )
59
+
60
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirBundleEntryRequest":
61
+ return FhirBundleEntryRequest(
62
+ url=self.url,
63
+ method=self.method,
64
+ ifModifiedSince=self.ifModifiedSince,
65
+ ifNoneMatch=self.ifNoneMatch,
66
+ ifNoneExist=self.ifNoneExist,
67
+ )
68
+
69
+ def __repr__(self) -> str:
70
+ return (
71
+ f"FhirBundleEntryRequest(url={self.url}, method={self.method}, "
72
+ f"ifModifiedSince={self.ifModifiedSince}, ifNoneMatch={self.ifNoneMatch})"
73
+ )
@@ -0,0 +1,67 @@
1
+ from datetime import datetime
2
+ from typing import Any, Dict, Optional, OrderedDict
3
+
4
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
5
+
6
+
7
+ class FhirBundleEntryResponse:
8
+ """
9
+ FHIR Bundle Entry Response class for encapsulating the response from FHIR server when processing bundle entries
10
+ """
11
+
12
+ __slots__ = ["status", "lastModified", "etag", "location"]
13
+
14
+ # noinspection PyPep8Naming
15
+ def __init__(
16
+ self,
17
+ *,
18
+ status: str = "200",
19
+ etag: Optional[str] = None,
20
+ lastModified: Optional[datetime] = None,
21
+ location: Optional[str] = None,
22
+ ) -> None:
23
+ self.status: str = status or "200"
24
+ if isinstance(status, int):
25
+ self.status = str(status)
26
+ self.lastModified: Optional[datetime] = lastModified
27
+ self.etag: Optional[str] = etag
28
+ self.location: Optional[str] = location
29
+
30
+ def dict(self) -> OrderedDict[str, Any]:
31
+ result: OrderedDict[str, Any] = OrderedDict[str, Any]({"status": self.status})
32
+ if self.lastModified is not None:
33
+ result["lastModified"] = self.lastModified.isoformat()
34
+ if self.etag is not None:
35
+ result["etag"] = self.etag
36
+ if self.location is not None:
37
+ result["location"] = self.location
38
+ return FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(result)
39
+
40
+ @classmethod
41
+ def from_dict(
42
+ cls, d: Dict[str, Any] | OrderedDict[str, Any]
43
+ ) -> "FhirBundleEntryResponse":
44
+ date_last_modified: Optional[datetime] = None
45
+ if "lastModified" in d:
46
+ if isinstance(d["lastModified"], datetime):
47
+ date_last_modified = d["lastModified"]
48
+ elif isinstance(d["lastModified"], str):
49
+ date_last_modified = datetime.fromisoformat(d["lastModified"])
50
+
51
+ return cls(
52
+ status=d["status"] if "status" in d else "200",
53
+ lastModified=date_last_modified,
54
+ etag=d["etag"] if "etag" in d else None,
55
+ location=d["location"] if "location" in d else None,
56
+ )
57
+
58
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirBundleEntryResponse":
59
+ return FhirBundleEntryResponse(
60
+ status=self.status,
61
+ lastModified=self.lastModified,
62
+ etag=self.etag,
63
+ location=self.location,
64
+ )
65
+
66
+ def __repr__(self) -> str:
67
+ return f"FhirBundleEntryResponse(status: {self.status}, lastModified: {self.lastModified}, etag: {self.etag})"
@@ -0,0 +1,75 @@
1
+ import json
2
+ from typing import Optional, Dict, Any, OrderedDict
3
+
4
+ from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
5
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
6
+
7
+
8
+ class FhirBundleEntrySearch:
9
+ """
10
+ FhirBundleEntrySearch is a class that represents a search entry in a FHIR Bundle.
11
+ It contains the resource, search mode, and search parameters.
12
+ """
13
+
14
+ __slots__ = ["mode", "score"]
15
+
16
+ def __init__(self, *, mode: Optional[str], score: Optional[float]) -> None:
17
+ """
18
+ Initializes the FhirBundleEntrySearch with the given parameters.
19
+
20
+ """
21
+ self.mode: Optional[str] = mode
22
+ self.score: Optional[float] = score
23
+
24
+ def dict(self) -> OrderedDict[str, Any]:
25
+ """
26
+ Converts the FhirBundleEntrySearch instance to a dictionary.
27
+
28
+ :return: A dictionary representation of the FhirBundleEntrySearch instance.
29
+ """
30
+ result: OrderedDict[str, Any] = OrderedDict[str, Any]()
31
+ if self.mode is not None:
32
+ result["mode"] = self.mode
33
+ if self.score is not None:
34
+ result["score"] = self.score
35
+
36
+ return FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(result)
37
+
38
+ @classmethod
39
+ def from_dict(
40
+ cls, data: Dict[str, Any] | OrderedDict[str, Any]
41
+ ) -> "FhirBundleEntrySearch":
42
+ """
43
+ Creates a FhirBundleEntrySearch object from a dictionary.
44
+
45
+ :param data: A dictionary containing the search entry data.
46
+ :return: An instance of FhirBundleEntrySearch.
47
+ """
48
+ return cls(
49
+ mode=data.get("mode"),
50
+ score=data.get("score"),
51
+ )
52
+
53
+ def to_json(self) -> str:
54
+ """
55
+ Converts the FhirBundleEntrySearch instance to a JSON string.
56
+
57
+ :return: A JSON string representation of the FhirBundleEntrySearch instance.
58
+ """
59
+ return json.dumps(self.dict(), cls=FhirJSONEncoder)
60
+
61
+ def __repr__(self) -> str:
62
+ """
63
+ Returns a string representation of the FhirBundleEntrySearch instance.
64
+
65
+ :return: A string representation of the FhirBundleEntrySearch instance.
66
+ """
67
+ return f"FhirBundleEntrySearch(mode={self.mode}, score={self.score})"
68
+
69
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirBundleEntrySearch":
70
+ """
71
+ Creates a copy of the FhirBundleEntrySearch instance.
72
+
73
+ :return: A new FhirBundleEntrySearch instance with the same attributes.
74
+ """
75
+ return FhirBundleEntrySearch(mode=self.mode, score=self.score)
@@ -0,0 +1,84 @@
1
+ import json
2
+ from typing import Optional, Dict, Any, OrderedDict
3
+
4
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
5
+
6
+
7
+ class FhirIdentifier:
8
+ """
9
+ FHIR Identifier class for representing identifiers in FHIR resources.
10
+ """
11
+
12
+ __slots__ = ["use", "system", "value"]
13
+
14
+ def __init__(
15
+ self,
16
+ use: Optional[str] = None,
17
+ system: Optional[str] = None,
18
+ value: Optional[str] = None,
19
+ ) -> None:
20
+ """
21
+ Initialize the FhirIdentifier object.
22
+
23
+ :param use: The purpose of the identifier (e.g., official, secondary).
24
+ :param system: The namespace for the identifier.
25
+ :param value: The actual identifier value.
26
+ """
27
+ self.use: Optional[str] = use
28
+ self.system: Optional[str] = system
29
+ self.value: Optional[str] = value
30
+
31
+ def dict(self) -> OrderedDict[str, Any]:
32
+ """
33
+ Convert the FhirIdentifier object to a dictionary.
34
+
35
+ :return: A dictionary representation of the FhirIdentifier object.
36
+ """
37
+ result: OrderedDict[str, Any] = OrderedDict[str, Any]()
38
+ if self.use is not None:
39
+ result["use"] = self.use
40
+ if self.system is not None:
41
+ result["system"] = self.system
42
+ if self.value is not None:
43
+ result["value"] = self.value
44
+ return FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(result)
45
+
46
+ @classmethod
47
+ def from_dict(
48
+ cls, data: Dict[str, Any] | OrderedDict[str, Any]
49
+ ) -> "FhirIdentifier":
50
+ """
51
+ Create a FhirIdentifier object from a dictionary.
52
+
53
+ :param data: A dictionary containing the identifier data.
54
+ :return: An instance of FhirIdentifier.
55
+ """
56
+ return cls(
57
+ use=data.get("use"),
58
+ system=data.get("system"),
59
+ value=data.get("value"),
60
+ )
61
+
62
+ def json(self) -> str:
63
+ """
64
+ Convert the FhirIdentifier object to a JSON string.
65
+ :return: A JSON string representation of the FhirIdentifier object.
66
+ """
67
+ return json.dumps(self.dict())
68
+
69
+ def __repr__(self) -> str:
70
+ """
71
+ Return a string representation of the FhirIdentifier object.
72
+ :return: A string representation of the FhirIdentifier object.
73
+ """
74
+ return (
75
+ f"FhirIdentifier(use={self.use}, system={self.system}, value={self.value})"
76
+ )
77
+
78
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirIdentifier":
79
+ """
80
+ Create a copy of the FhirIdentifier object.
81
+
82
+ :return: A new FhirIdentifier object with the same attributes.
83
+ """
84
+ return FhirIdentifier(use=self.use, system=self.system, value=self.value)
@@ -0,0 +1,63 @@
1
+ import json
2
+ from typing import Any, Dict, OrderedDict
3
+
4
+ from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
5
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
6
+
7
+
8
+ class FhirLink:
9
+ """
10
+ Represents a link in a FHIR
11
+ """
12
+
13
+ __slots__ = ["url", "relation"]
14
+
15
+ def __init__(self, *, url: str, relation: str) -> None:
16
+ """
17
+ Initializes a FhirLink instance.
18
+
19
+ :param url: The URL of the link.
20
+ :param relation: The relation type of the link.
21
+ """
22
+ self.url: str = url
23
+ self.relation: str = relation
24
+
25
+ def dict(self) -> OrderedDict[str, Any]:
26
+ """
27
+ Converts the FhirLink instance to a dictionary.
28
+
29
+ :return: A dictionary representation of the link.
30
+ """
31
+ result = OrderedDict[str, Any]({"url": self.url, "relation": self.relation})
32
+ return FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(result)
33
+
34
+ def json(self) -> str:
35
+ """
36
+ Converts the FhirLink instance to a JSON string.
37
+
38
+ :return: A JSON string representation of the link.
39
+ """
40
+ return json.dumps(self.dict(), cls=FhirJSONEncoder)
41
+
42
+ def __repr__(self) -> str:
43
+ return f"FhirLink(url={self.url}, relation={self.relation})"
44
+
45
+ @classmethod
46
+ def from_dict(cls, data: Dict[str, Any] | OrderedDict[str, Any]) -> "FhirLink":
47
+ """
48
+ Populates the FhirLink instance from a dictionary.
49
+
50
+ :param data: A dictionary containing the link data.
51
+ """
52
+ return cls(
53
+ url=data.get("url") or "unknown",
54
+ relation=data.get("relation") or "unknown",
55
+ )
56
+
57
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirLink":
58
+ """
59
+ Creates a copy of the FhirLink instance.
60
+
61
+ :return: A new FhirLink instance with the same attributes.
62
+ """
63
+ return FhirLink(url=self.url, relation=self.relation)
@@ -0,0 +1,47 @@
1
+ import dataclasses
2
+ from typing import Any, Dict, OrderedDict
3
+
4
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
5
+
6
+
7
+ @dataclasses.dataclass(slots=True)
8
+ class FhirMeta:
9
+ """
10
+ FhirMeta represents the meta information of a FHIR resource.
11
+ """
12
+
13
+ version_id: str | None = None
14
+ last_updated: str | None = None
15
+ source: str | None = None
16
+ profile: list[str] | None = None
17
+ security: list[Dict[str, Any]] | None = None
18
+ tag: list[str] | None = None
19
+
20
+ def dict(self) -> OrderedDict[str, Any]:
21
+ result: OrderedDict[str, Any] = OrderedDict[str, Any]()
22
+ if self.version_id is not None:
23
+ result["versionId"] = self.version_id
24
+ if self.last_updated is not None:
25
+ result["lastUpdated"] = self.last_updated
26
+ if self.source is not None:
27
+ result["source"] = self.source
28
+ if self.profile is not None:
29
+ result["profile"] = [p for p in self.profile if p]
30
+ if self.security is not None:
31
+ result["security"] = [
32
+ FhirClientJsonHelpers.remove_empty_elements(s) for s in self.security
33
+ ]
34
+ if self.tag is not None:
35
+ result["tag"] = [t for t in self.tag if t]
36
+ return FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(result)
37
+
38
+ @classmethod
39
+ def from_dict(cls, data: Dict[str, Any]) -> "FhirMeta":
40
+ return cls(
41
+ version_id=data.get("versionId"),
42
+ last_updated=data.get("lastUpdated"),
43
+ source=data.get("source"),
44
+ profile=data.get("profile"),
45
+ security=data.get("security"),
46
+ tag=data.get("tag"),
47
+ )
@@ -0,0 +1,170 @@
1
+ import copy
2
+ import json
3
+ from typing import Any, Optional, Dict, List, cast, OrderedDict, override
4
+
5
+ from compressedfhir.fhir.fhir_meta import FhirMeta
6
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict import (
7
+ CompressedDict,
8
+ )
9
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode import (
10
+ CompressedDictStorageMode,
11
+ )
12
+ from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
13
+ from compressedfhir.utilities.json_helpers import FhirClientJsonHelpers
14
+
15
+
16
+ class FhirResource(CompressedDict[str, Any]):
17
+ """
18
+ FhirResource is a class that represents a FHIR resource.
19
+ """
20
+
21
+ __slots__ = CompressedDict.__slots__
22
+
23
+ def __init__(
24
+ self,
25
+ initial_dict: Dict[str, Any] | OrderedDict[str, Any] | None = None,
26
+ *,
27
+ meta: Optional[FhirMeta] = None,
28
+ storage_mode: CompressedDictStorageMode | None = None,
29
+ properties_to_cache: Optional[List[str]] = None,
30
+ ) -> None:
31
+ if storage_mode is None:
32
+ storage_mode = CompressedDictStorageMode.default()
33
+ if meta is not None:
34
+ initial_dict = initial_dict or {}
35
+ initial_dict["meta"] = meta.dict()
36
+ super().__init__(
37
+ initial_dict=initial_dict,
38
+ storage_mode=storage_mode,
39
+ properties_to_cache=properties_to_cache or ["resourceType", "id", "meta"],
40
+ )
41
+
42
+ @classmethod
43
+ def construct(cls, **kwargs: Any) -> "FhirResource":
44
+ """
45
+ Constructs a FhirResource object from keyword arguments.
46
+
47
+ :param kwargs: Keyword arguments to initialize the resource.
48
+ :return: A FhirResource object.
49
+ """
50
+ return cls(initial_dict=kwargs)
51
+
52
+ @property
53
+ def resource_type(self) -> Optional[str]:
54
+ """Get the resource type from the resource dictionary."""
55
+ return self.get("resourceType")
56
+
57
+ @property
58
+ def resource_type_and_id(self) -> Optional[str]:
59
+ """Get the resource type and ID from the resource dictionary."""
60
+ return (
61
+ f"{self.resource_type}/{self.id}"
62
+ if self.resource_type and self.id
63
+ else None
64
+ )
65
+
66
+ def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirResource":
67
+ """Create a copy of the resource."""
68
+ return FhirResource(
69
+ initial_dict=super().raw_dict(),
70
+ storage_mode=self._storage_mode,
71
+ )
72
+
73
+ def __repr__(self) -> str:
74
+ """Custom string representation for debugging."""
75
+ return f"FhirResource({self.resource_type}/{self.id})"
76
+
77
+ def copy(self) -> "FhirResource":
78
+ """
79
+ Creates a copy of the BundleEntry object.
80
+
81
+ :return: A new BundleEntry object with the same attributes.
82
+ """
83
+ return copy.deepcopy(self)
84
+
85
+ @property
86
+ def id(self) -> Optional[str]:
87
+ """Get the ID from the resource dictionary."""
88
+ return self.get("id")
89
+
90
+ @id.setter
91
+ def id(self, value: Optional[str]) -> None:
92
+ """Set the ID of the Bundle."""
93
+ self["id"] = value
94
+
95
+ @property
96
+ def meta(self) -> FhirMeta | None:
97
+ """Get the meta information from the resource dictionary."""
98
+ return (
99
+ FhirMeta.from_dict(cast(Dict[str, Any], self.get("meta")))
100
+ if "meta" in self
101
+ else None
102
+ )
103
+
104
+ @meta.setter
105
+ def meta(self, value: FhirMeta | None) -> None:
106
+ """Set the meta information of the resource."""
107
+ if value is None:
108
+ self.pop("meta", None)
109
+ else:
110
+ assert isinstance(value, FhirMeta)
111
+ self["meta"] = value.dict()
112
+
113
+ @classmethod
114
+ @override
115
+ def from_json(cls, json_str: str) -> "FhirResource":
116
+ """
117
+ Creates a FhirResource object from a JSON string.
118
+
119
+ :param json_str: JSON string representing the resource.
120
+ :return: A FhirResource object.
121
+ """
122
+ return cast(FhirResource, super().from_json(json_str=json_str))
123
+
124
+ @classmethod
125
+ @override
126
+ def from_dict(
127
+ cls,
128
+ d: Dict[str, Any],
129
+ *,
130
+ storage_mode: CompressedDictStorageMode | None = None,
131
+ properties_to_cache: List[str] | None = None,
132
+ ) -> "FhirResource":
133
+ """
134
+ Creates a FhirResource object from a dictionary.
135
+
136
+ :param d: Dictionary representing the resource.
137
+ :param storage_mode: Storage mode for the CompressedDict.
138
+ :param properties_to_cache: List of properties to cache.
139
+ :return: A FhirResource object.
140
+ """
141
+ if storage_mode is None:
142
+ storage_mode = CompressedDictStorageMode.default()
143
+ return cast(
144
+ FhirResource,
145
+ super().from_dict(
146
+ d=d,
147
+ storage_mode=storage_mode,
148
+ properties_to_cache=properties_to_cache,
149
+ ),
150
+ )
151
+
152
+ @override
153
+ def json(self) -> str:
154
+ """Convert the resource to a JSON string."""
155
+
156
+ # working_dict preserves the python types so create a fhir friendly version
157
+ raw_dict: OrderedDict[str, Any] = self.raw_dict()
158
+
159
+ raw_dict = FhirClientJsonHelpers.remove_empty_elements_from_ordered_dict(
160
+ raw_dict
161
+ )
162
+ return json.dumps(obj=raw_dict, cls=FhirJSONEncoder)
163
+
164
+ def to_fhir_dict(self) -> Dict[str, Any]:
165
+ """
166
+ Convert the resource to a FHIR-compliant dictionary.
167
+
168
+ :return: A dictionary representation of the resource.
169
+ """
170
+ return cast(Dict[str, Any], json.loads(self.json()))