compressedfhir 1.0.1__tar.gz → 1.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of compressedfhir might be problematic. Click here for more details.

Files changed (72) hide show
  1. compressedfhir-1.0.3/PKG-INFO +139 -0
  2. compressedfhir-1.0.3/README.md +112 -0
  3. compressedfhir-1.0.3/VERSION +1 -0
  4. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle.py +4 -3
  5. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry.py +1 -1
  6. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_request.py +7 -5
  7. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_response.py +8 -5
  8. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_resource.py +37 -27
  9. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_resource_list.py +1 -1
  10. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/compressed_dict.py +54 -16
  11. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/compressed_dict_storage_mode.py +7 -7
  12. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/fhir_json_encoder.py +1 -1
  13. compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py +82 -0
  14. compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test/test_type_preservation_encoder.py +59 -0
  15. compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py +60 -0
  16. compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/type_preservation_decoder.py +63 -0
  17. compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/type_preservation_encoder.py +50 -0
  18. compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/type_preservation_serializer.py +55 -0
  19. compressedfhir-1.0.3/compressedfhir/utilities/ordered_dict_to_dict_converter/__init__.py +0 -0
  20. compressedfhir-1.0.3/compressedfhir/utilities/ordered_dict_to_dict_converter/ordered_dict_to_dict_converter.py +24 -0
  21. compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/__init__.py +0 -0
  22. compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/v1/__init__.py +0 -0
  23. compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/v1/string_compressor.py +99 -0
  24. compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/v1/test/__init__.py +0 -0
  25. compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/v1/test/test_string_compressor.py +189 -0
  26. compressedfhir-1.0.3/compressedfhir/utilities/test/__init__.py +0 -0
  27. compressedfhir-1.0.3/compressedfhir.egg-info/PKG-INFO +139 -0
  28. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/SOURCES.txt +15 -0
  29. compressedfhir-1.0.3/tests/__init__.py +0 -0
  30. compressedfhir-1.0.1/PKG-INFO +0 -28
  31. compressedfhir-1.0.1/README.md +0 -1
  32. compressedfhir-1.0.1/VERSION +0 -1
  33. compressedfhir-1.0.1/compressedfhir.egg-info/PKG-INFO +0 -28
  34. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/LICENSE +0 -0
  35. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/MANIFEST.in +0 -0
  36. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/Makefile +0 -0
  37. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/__init__.py +0 -0
  38. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/__init__.py +0 -0
  39. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/base_resource_list.py +0 -0
  40. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_list.py +0 -0
  41. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_search.py +0 -0
  42. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_identifier.py +0 -0
  43. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_link.py +0 -0
  44. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_meta.py +0 -0
  45. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_resource_map.py +0 -0
  46. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/__init__.py +0 -0
  47. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_bundle_entry.py +0 -0
  48. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_bundle_entry_list.py +0 -0
  49. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_bundle_entry_request.py +0 -0
  50. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_bundle_entry_response.py +0 -0
  51. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_fhir_bundle.py +0 -0
  52. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_fhir_resource.py +0 -0
  53. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_fhir_resource_list.py +0 -0
  54. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_fhir_resource_map.py +0 -0
  55. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/py.typed +0 -0
  56. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/__init__.py +0 -0
  57. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/__init__.py +0 -0
  58. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/__init__.py +0 -0
  59. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/compressed_dict_access_error.py +0 -0
  60. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/test/__init__.py +0 -0
  61. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py +0 -0
  62. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/json_helpers.py +0 -0
  63. {compressedfhir-1.0.1/compressedfhir/utilities/test → compressedfhir-1.0.3/compressedfhir/utilities/json_serializers}/__init__.py +0 -0
  64. {compressedfhir-1.0.1/tests → compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test}/__init__.py +0 -0
  65. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/test/test_fhir_json_encoder.py +0 -0
  66. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/test/test_json_helpers.py +0 -0
  67. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/dependency_links.txt +0 -0
  68. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/not-zip-safe +0 -0
  69. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/requires.txt +0 -0
  70. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/top_level.txt +0 -0
  71. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/setup.cfg +0 -0
  72. {compressedfhir-1.0.1 → compressedfhir-1.0.3}/setup.py +0 -0
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: compressedfhir
3
+ Version: 1.0.3
4
+ Summary: Stores FHIR JSON resources in compressed form in memory
5
+ Home-page: https://github.com/icanbwell/compressed-fhir
6
+ Author: Imran Qureshi
7
+ Author-email: imran.qureshi@icanbwell.com
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: msgpack>=1.0.0
16
+ Requires-Dist: orjson>=3.10.16
17
+ Dynamic: author
18
+ Dynamic: author-email
19
+ Dynamic: classifier
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: license-file
24
+ Dynamic: requires-dist
25
+ Dynamic: requires-python
26
+ Dynamic: summary
27
+
28
+ # Compressed FHIR
29
+
30
+ A Python library for storing FHIR JSON resources in a compressed form in memory, optimizing memory usage while maintaining fast access to FHIR data.
31
+
32
+ ## Overview
33
+
34
+ Compressed FHIR is a specialized library that provides efficient memory storage for FHIR (Fast Healthcare Interoperability Resources) JSON resources. It uses zlib or msgpack for compression while ensuring quick access to the stored healthcare data.
35
+
36
+ ## Features
37
+
38
+ - Efficient memory storage of FHIR resources
39
+ - Fast access to compressed FHIR data
40
+ - Compatible with standard FHIR JSON formats
41
+ - Minimal memory footprint
42
+ - Python 3.10+ support
43
+
44
+ ## Installation
45
+
46
+ You can install the package using pip:
47
+
48
+ ```bash
49
+ pip install compressedfhir
50
+ ```
51
+
52
+ Or using pipenv:
53
+
54
+ ```bash
55
+ pipenv install compressedfhir
56
+ ```
57
+
58
+ ## Requirements
59
+
60
+ - Python 3.10 or higher
61
+ - msgpack >= 1.0.0
62
+ - orjson >= 3.10.16
63
+
64
+ ## Usage
65
+
66
+ ```python
67
+ from typing import Any
68
+ from collections import OrderedDict
69
+
70
+ from compressedfhir.fhir.fhir_resource import FhirResource
71
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode import CompressedDictStorageMode
72
+
73
+
74
+ resource1 = FhirResource(
75
+ initial_dict={"resourceType": "Observation", "id": "456"},
76
+ storage_mode=CompressedDictStorageMode.default(),
77
+ )
78
+
79
+ my_dict: OrderedDict[str,Any] = resource1.dict()
80
+ my_plain_dict: dict[str, Any] = resource1.to_plain_dict()
81
+ my_json: str = resource1.json()
82
+
83
+ with resource1.transaction():
84
+ assert "email" not in resource1
85
+ assert resource1.get("name") == "Custom Mode User"
86
+ assert resource1.get("active") is True
87
+ ```
88
+
89
+ ## Development Setup
90
+
91
+ 1. Clone the repository:
92
+ ```bash
93
+ git clone https://github.com/icanbwell/compressed-fhir.git
94
+ cd compressed-fhir
95
+ ```
96
+
97
+ 2. Install dependencies using pipenv:
98
+ ```bash
99
+ pipenv install --dev
100
+ ```
101
+
102
+ 3. Set up pre-commit hooks:
103
+ ```bash
104
+ pre-commit install
105
+ ```
106
+
107
+ ## Docker Support
108
+
109
+ The project includes Docker support for development and deployment:
110
+
111
+ ```bash
112
+ # Build the Docker image
113
+ docker build -t compressed-fhir .
114
+
115
+ # Run using docker-compose
116
+ docker-compose up
117
+ ```
118
+
119
+ ## Contributing
120
+
121
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
122
+
123
+ ## License
124
+
125
+ This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
126
+
127
+ ## Authors
128
+
129
+ - Imran Qureshi (imran.qureshi@icanbwell.com)
130
+
131
+ ## Support
132
+
133
+ For support, please open an issue in the GitHub repository or contact the maintainers.
134
+
135
+ ## Project Status
136
+
137
+ Current status: Beta
138
+
139
+ The project is under active development. Please check the GitHub repository for the latest updates and changes.
@@ -0,0 +1,112 @@
1
+ # Compressed FHIR
2
+
3
+ A Python library for storing FHIR JSON resources in a compressed form in memory, optimizing memory usage while maintaining fast access to FHIR data.
4
+
5
+ ## Overview
6
+
7
+ Compressed FHIR is a specialized library that provides efficient memory storage for FHIR (Fast Healthcare Interoperability Resources) JSON resources. It uses zlib or msgpack for compression while ensuring quick access to the stored healthcare data.
8
+
9
+ ## Features
10
+
11
+ - Efficient memory storage of FHIR resources
12
+ - Fast access to compressed FHIR data
13
+ - Compatible with standard FHIR JSON formats
14
+ - Minimal memory footprint
15
+ - Python 3.10+ support
16
+
17
+ ## Installation
18
+
19
+ You can install the package using pip:
20
+
21
+ ```bash
22
+ pip install compressedfhir
23
+ ```
24
+
25
+ Or using pipenv:
26
+
27
+ ```bash
28
+ pipenv install compressedfhir
29
+ ```
30
+
31
+ ## Requirements
32
+
33
+ - Python 3.10 or higher
34
+ - msgpack >= 1.0.0
35
+ - orjson >= 3.10.16
36
+
37
+ ## Usage
38
+
39
+ ```python
40
+ from typing import Any
41
+ from collections import OrderedDict
42
+
43
+ from compressedfhir.fhir.fhir_resource import FhirResource
44
+ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode import CompressedDictStorageMode
45
+
46
+
47
+ resource1 = FhirResource(
48
+ initial_dict={"resourceType": "Observation", "id": "456"},
49
+ storage_mode=CompressedDictStorageMode.default(),
50
+ )
51
+
52
+ my_dict: OrderedDict[str,Any] = resource1.dict()
53
+ my_plain_dict: dict[str, Any] = resource1.to_plain_dict()
54
+ my_json: str = resource1.json()
55
+
56
+ with resource1.transaction():
57
+ assert "email" not in resource1
58
+ assert resource1.get("name") == "Custom Mode User"
59
+ assert resource1.get("active") is True
60
+ ```
61
+
62
+ ## Development Setup
63
+
64
+ 1. Clone the repository:
65
+ ```bash
66
+ git clone https://github.com/icanbwell/compressed-fhir.git
67
+ cd compressed-fhir
68
+ ```
69
+
70
+ 2. Install dependencies using pipenv:
71
+ ```bash
72
+ pipenv install --dev
73
+ ```
74
+
75
+ 3. Set up pre-commit hooks:
76
+ ```bash
77
+ pre-commit install
78
+ ```
79
+
80
+ ## Docker Support
81
+
82
+ The project includes Docker support for development and deployment:
83
+
84
+ ```bash
85
+ # Build the Docker image
86
+ docker build -t compressed-fhir .
87
+
88
+ # Run using docker-compose
89
+ docker-compose up
90
+ ```
91
+
92
+ ## Contributing
93
+
94
+ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
95
+
96
+ ## License
97
+
98
+ This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
99
+
100
+ ## Authors
101
+
102
+ - Imran Qureshi (imran.qureshi@icanbwell.com)
103
+
104
+ ## Support
105
+
106
+ For support, please open an issue in the GitHub repository or contact the maintainers.
107
+
108
+ ## Project Status
109
+
110
+ Current status: Beta
111
+
112
+ The project is under active development. Please check the GitHub repository for the latest updates and changes.
@@ -0,0 +1 @@
1
+ 1.0.3
@@ -13,6 +13,9 @@ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode im
13
13
  )
14
14
  from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
15
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
+ )
16
19
 
17
20
 
18
21
  class FhirBundle:
@@ -286,6 +289,4 @@ class FhirBundle:
286
289
 
287
290
  :return: Plain dictionary representation of the Bundle
288
291
  """
289
- return cast(
290
- Dict[str, Any], json.loads(json.dumps(self.dict(), cls=FhirJSONEncoder))
291
- )
292
+ return OrderedDictToDictConverter.convert(self.dict())
@@ -217,7 +217,7 @@ class FhirBundleEntry:
217
217
  return (
218
218
  f"BundleEntry({self.resource.resource_type}/{self.resource.id})"
219
219
  if self.resource
220
- else f"BundleEntry(Empty)"
220
+ else "BundleEntry(Empty)"
221
221
  )
222
222
 
223
223
  def copy(self) -> "FhirBundleEntry":
@@ -43,14 +43,16 @@ class FhirBundleEntryRequest:
43
43
  def from_dict(
44
44
  cls, d: Dict[str, Any] | OrderedDict[str, Any]
45
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"])
46
52
  return cls(
47
53
  url=d.get("url", "https://example.com"),
48
54
  method=d.get("method", "GET"),
49
- ifModifiedSince=(
50
- datetime.fromisoformat(d["ifModifiedSince"])
51
- if "ifModifiedSince" in d
52
- else None
53
- ),
55
+ ifModifiedSince=date_if_modified_since,
54
56
  ifNoneMatch=d["ifNoneMatch"] if "ifNoneMatch" in d else None,
55
57
  ifNoneExist=d["ifNoneExist"] if "ifNoneExist" in d else None,
56
58
  )
@@ -41,13 +41,16 @@ class FhirBundleEntryResponse:
41
41
  def from_dict(
42
42
  cls, d: Dict[str, Any] | OrderedDict[str, Any]
43
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
+
44
51
  return cls(
45
52
  status=d["status"] if "status" in d else "200",
46
- lastModified=(
47
- datetime.fromisoformat(d["lastModified"])
48
- if "lastModified" in d
49
- else None
50
- ),
53
+ lastModified=date_last_modified,
51
54
  etag=d["etag"] if "etag" in d else None,
52
55
  location=d["location"] if "location" in d else None,
53
56
  )
@@ -65,17 +65,6 @@ class FhirResource(CompressedDict[str, Any]):
65
65
  """Convert the resource to a JSON string."""
66
66
  return json.dumps(obj=self.dict(), cls=FhirJSONEncoder)
67
67
 
68
- @classmethod
69
- def from_json(cls, json_str: str) -> "FhirResource":
70
- """
71
- Create a FhirResource object from a JSON string.
72
-
73
- :param json_str: The JSON string to convert.
74
- :return: A FhirResource object.
75
- """
76
- data = json.loads(json_str)
77
- return cls.from_dict(data)
78
-
79
68
  def __deepcopy__(self, memo: Dict[int, Any]) -> "FhirResource":
80
69
  """Create a copy of the resource."""
81
70
  return FhirResource(
@@ -112,22 +101,6 @@ class FhirResource(CompressedDict[str, Any]):
112
101
 
113
102
  return result
114
103
 
115
- @classmethod
116
- def from_dict(
117
- cls,
118
- d: Dict[str, Any],
119
- *,
120
- storage_mode: CompressedDictStorageMode = CompressedDictStorageMode.default(),
121
- ) -> "FhirResource":
122
- """
123
- Creates a FhirResource object from a dictionary.
124
-
125
- :param d: The dictionary to convert.
126
- :param storage_mode: The storage mode for the CompressedDict.
127
- :return: A FhirResource object.
128
- """
129
- return cls(initial_dict=d, storage_mode=storage_mode)
130
-
131
104
  def remove_nulls(self) -> None:
132
105
  """
133
106
  Removes None values from the resource dictionary.
@@ -161,3 +134,40 @@ class FhirResource(CompressedDict[str, Any]):
161
134
  else:
162
135
  assert isinstance(value, FhirMeta)
163
136
  self["meta"] = value.dict()
137
+
138
+ @classmethod
139
+ @override
140
+ def from_json(cls, json_str: str) -> "FhirResource":
141
+ """
142
+ Creates a FhirResource object from a JSON string.
143
+
144
+ :param json_str: JSON string representing the resource.
145
+ :return: A FhirResource object.
146
+ """
147
+ return cast(FhirResource, super().from_json(json_str=json_str))
148
+
149
+ @classmethod
150
+ @override
151
+ def from_dict(
152
+ cls,
153
+ d: Dict[str, Any],
154
+ *,
155
+ storage_mode: CompressedDictStorageMode = CompressedDictStorageMode.default(),
156
+ properties_to_cache: List[str] | None = None,
157
+ ) -> "FhirResource":
158
+ """
159
+ Creates a FhirResource object from a dictionary.
160
+
161
+ :param d: Dictionary representing the resource.
162
+ :param storage_mode: Storage mode for the CompressedDict.
163
+ :param properties_to_cache: List of properties to cache.
164
+ :return: A FhirResource object.
165
+ """
166
+ return cast(
167
+ FhirResource,
168
+ super().from_dict(
169
+ d=d,
170
+ storage_mode=storage_mode,
171
+ properties_to_cache=properties_to_cache,
172
+ ),
173
+ )
@@ -103,7 +103,7 @@ class FhirResourceList(BaseResourceList[FhirResource]):
103
103
  if len(self) == 0:
104
104
  return resources_by_type
105
105
 
106
- entry: FhirResource
106
+ resource: FhirResource
107
107
  for resource in [r for r in self if r is not None]:
108
108
  resource_type: str = resource.resource_type or "unknown"
109
109
  if resource_type not in resources_by_type:
@@ -1,5 +1,4 @@
1
1
  import copy
2
- import json
3
2
  from collections.abc import KeysView, ValuesView, ItemsView, MutableMapping
4
3
  from contextlib import contextmanager
5
4
  from typing import Dict, Optional, Iterator, cast, List, Any, overload, OrderedDict
@@ -14,7 +13,12 @@ from compressedfhir.utilities.compressed_dict.v1.compressed_dict_storage_mode im
14
13
  CompressedDictStorageMode,
15
14
  CompressedDictStorageType,
16
15
  )
17
- from compressedfhir.utilities.fhir_json_encoder import FhirJSONEncoder
16
+ from compressedfhir.utilities.json_serializers.type_preservation_serializer import (
17
+ TypePreservationSerializer,
18
+ )
19
+ from compressedfhir.utilities.ordered_dict_to_dict_converter.ordered_dict_to_dict_converter import (
20
+ OrderedDictToDictConverter,
21
+ )
18
22
 
19
23
 
20
24
  class CompressedDict[K, V](MutableMapping[K, V]):
@@ -146,7 +150,7 @@ class CompressedDict[K, V](MutableMapping[K, V]):
146
150
  # For serialized modes, deserialize
147
151
  working_dict = (
148
152
  self._deserialize_dict(
149
- serialized_dict=self._serialized_dict,
153
+ serialized_dict_bytes=self._serialized_dict,
150
154
  storage_type=self._storage_mode.storage_type,
151
155
  )
152
156
  if self._serialized_dict
@@ -172,9 +176,9 @@ class CompressedDict[K, V](MutableMapping[K, V]):
172
176
  assert isinstance(dictionary, OrderedDict)
173
177
  if storage_type == "compressed":
174
178
  # Serialize to JSON and compress with zlib
175
- json_str = json.dumps(
176
- dictionary, separators=(",", ":"), cls=FhirJSONEncoder
177
- ) # Most compact JSON representation
179
+ json_str = TypePreservationSerializer.serialize(
180
+ dictionary, separators=(",", ":")
181
+ )
178
182
  return zlib.compress(
179
183
  json_str.encode("utf-8"), level=zlib.Z_BEST_COMPRESSION
180
184
  )
@@ -195,34 +199,37 @@ class CompressedDict[K, V](MutableMapping[K, V]):
195
199
  @staticmethod
196
200
  def _deserialize_dict(
197
201
  *,
198
- serialized_dict: bytes,
202
+ serialized_dict_bytes: bytes,
199
203
  storage_type: CompressedDictStorageType,
200
204
  ) -> OrderedDict[K, V]:
201
205
  """
202
206
  Deserialize entire dictionary from MessagePack
203
207
 
204
208
  Args:
205
- serialized_dict: Serialized dictionary bytes
209
+ serialized_dict_bytes: Serialized dictionary bytes
206
210
 
207
211
  Returns:
208
212
  Deserialized dictionary
209
213
  """
210
- assert serialized_dict is not None, "Serialized dictionary cannot be None"
214
+ assert serialized_dict_bytes is not None, "Serialized dictionary cannot be None"
215
+ assert isinstance(serialized_dict_bytes, bytes)
211
216
 
212
217
  if storage_type == "compressed":
213
218
  # Decompress and parse JSON
214
- decompressed = zlib.decompress(serialized_dict)
215
- decoded_text = decompressed.decode("utf-8")
219
+ decompressed_bytes: bytes = zlib.decompress(serialized_dict_bytes)
220
+ decoded_text: str = decompressed_bytes.decode("utf-8")
216
221
  # noinspection PyTypeChecker
217
- decompressed_dict = json.loads(decoded_text, object_pairs_hook=OrderedDict)
222
+ decompressed_dict = TypePreservationSerializer.deserialize(
223
+ decoded_text, object_pairs_hook=OrderedDict
224
+ )
218
225
  assert isinstance(decompressed_dict, OrderedDict)
219
226
  return cast(OrderedDict[K, V], decompressed_dict)
220
227
 
221
228
  # Decompress if needed
222
229
  to_unpack = (
223
- zlib.decompress(serialized_dict)
230
+ zlib.decompress(serialized_dict_bytes)
224
231
  if storage_type == "compressed_msgpack"
225
- else serialized_dict
232
+ else serialized_dict_bytes
226
233
  )
227
234
 
228
235
  # Deserialize
@@ -630,6 +637,37 @@ class CompressedDict[K, V](MutableMapping[K, V]):
630
637
  Returns:
631
638
  Plain dictionary
632
639
  """
633
- return cast(
634
- Dict[K, V], json.loads(json.dumps(self.dict(), cls=FhirJSONEncoder))
640
+ return OrderedDictToDictConverter.convert(self.dict())
641
+
642
+ @classmethod
643
+ def from_json(cls, json_str: str) -> "CompressedDict[K, V]":
644
+ """
645
+ Create a FhirResource object from a JSON string.
646
+
647
+ :param json_str: The JSON string to convert.
648
+ :return: A FhirResource object.
649
+ """
650
+ data = TypePreservationSerializer.deserialize(json_str)
651
+ return cls.from_dict(data)
652
+
653
+ @classmethod
654
+ def from_dict(
655
+ cls,
656
+ d: Dict[K, V],
657
+ *,
658
+ storage_mode: CompressedDictStorageMode = CompressedDictStorageMode.default(),
659
+ properties_to_cache: List[K] | None = None,
660
+ ) -> "CompressedDict[K, V]":
661
+ """
662
+ Creates a FhirResource object from a dictionary.
663
+
664
+ :param d: The dictionary to convert.
665
+ :param storage_mode: The storage mode for the CompressedDict.
666
+ :param properties_to_cache: Optional list of properties to cache
667
+ :return: A FhirResource object.
668
+ """
669
+ return cls(
670
+ initial_dict=d,
671
+ storage_mode=storage_mode,
672
+ properties_to_cache=properties_to_cache,
635
673
  )
@@ -4,13 +4,13 @@ from typing import Literal, TypeAlias
4
4
  CompressedDictStorageType: TypeAlias = Literal[
5
5
  "raw", "compressed", "msgpack", "compressed_msgpack"
6
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
- """
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
14
 
15
15
 
16
16
  @dataclasses.dataclass(slots=True)
@@ -18,7 +18,7 @@ class FhirJSONEncoder(json.JSONEncoder):
18
18
  def default(self, o: Any) -> Any:
19
19
  # Existing type handlers
20
20
  if dataclasses.is_dataclass(o):
21
- return dataclasses.asdict(o) # type:ignore
21
+ return dataclasses.asdict(o) # type:ignore[arg-type]
22
22
 
23
23
  if isinstance(o, Enum):
24
24
  return o.value
@@ -0,0 +1,82 @@
1
+ from datetime import datetime, date
2
+ from decimal import Decimal
3
+ from typing import Type, Any, Dict
4
+
5
+ import pytest
6
+
7
+ from compressedfhir.utilities.json_serializers.type_preservation_decoder import (
8
+ TypePreservationDecoder,
9
+ )
10
+
11
+
12
+ class TestCustomObject:
13
+ def __init__(self, name: str, value: int):
14
+ self.name: str = name
15
+ self.value: int = value
16
+
17
+ def __eq__(self, other: Any) -> bool:
18
+ if not isinstance(other, TestCustomObject):
19
+ return False
20
+ return self.name == other.name and self.value == other.value
21
+
22
+
23
+ @pytest.mark.parametrize(
24
+ "input_type, input_dict, expected_type",
25
+ [
26
+ (
27
+ "datetime",
28
+ {"__type__": "datetime", "iso": "2023-01-01T00:00:00+00:00"},
29
+ datetime,
30
+ ),
31
+ ("date", {"__type__": "date", "iso": "2023-01-01"}, date),
32
+ ("decimal", {"__type__": "decimal", "value": "3.14"}, Decimal),
33
+ ("complex", {"__type__": "complex", "real": 3, "imag": 4}, complex),
34
+ ("bytes", {"__type__": "bytes", "value": "test"}, bytes),
35
+ ("set", {"__type__": "set", "values": [1, 2, 3]}, set),
36
+ ],
37
+ )
38
+ def test_complex_type_decoding(
39
+ input_type: str, input_dict: Dict[str, Any], expected_type: Type[Any]
40
+ ) -> None:
41
+ """
42
+ Test decoding of various complex types
43
+ """
44
+ decoded = TypePreservationDecoder.decode(input_dict)
45
+
46
+ assert isinstance(decoded, expected_type)
47
+
48
+
49
+ def test_custom_object_decoding() -> None:
50
+ """
51
+ Test decoding of custom objects
52
+ """
53
+ custom_obj_dict = {
54
+ "__type__": "TestCustomObject",
55
+ "__module__": __name__,
56
+ "attributes": {"name": "test", "value": 42},
57
+ }
58
+
59
+ decoded = TypePreservationDecoder.decode(custom_obj_dict)
60
+
61
+ assert isinstance(decoded, TestCustomObject)
62
+ assert decoded.name == "test"
63
+ assert decoded.value == 42
64
+
65
+
66
+ def test_custom_decoder() -> None:
67
+ """
68
+ Test custom decoder functionality
69
+ """
70
+
71
+ def custom_decoder(data: Dict[str, Any]) -> Any:
72
+ if data.get("__type__") == "special_type":
73
+ return f"Decoded: {data['value']}"
74
+ return data
75
+
76
+ special_dict = {"__type__": "special_type", "value": "test"}
77
+
78
+ decoded = TypePreservationDecoder.decode(
79
+ special_dict, custom_decoders={"special_type": custom_decoder}
80
+ )
81
+
82
+ assert decoded == "Decoded: test"