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.
- compressedfhir-1.0.3/PKG-INFO +139 -0
- compressedfhir-1.0.3/README.md +112 -0
- compressedfhir-1.0.3/VERSION +1 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle.py +4 -3
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry.py +1 -1
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_request.py +7 -5
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_response.py +8 -5
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_resource.py +37 -27
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_resource_list.py +1 -1
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/compressed_dict.py +54 -16
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/compressed_dict_storage_mode.py +7 -7
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/fhir_json_encoder.py +1 -1
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test/test_type_preservation_decoder.py +82 -0
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test/test_type_preservation_encoder.py +59 -0
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test/test_type_preservation_serializer.py +60 -0
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/type_preservation_decoder.py +63 -0
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/type_preservation_encoder.py +50 -0
- compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/type_preservation_serializer.py +55 -0
- compressedfhir-1.0.3/compressedfhir/utilities/ordered_dict_to_dict_converter/__init__.py +0 -0
- compressedfhir-1.0.3/compressedfhir/utilities/ordered_dict_to_dict_converter/ordered_dict_to_dict_converter.py +24 -0
- compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/__init__.py +0 -0
- compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/v1/__init__.py +0 -0
- compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/v1/string_compressor.py +99 -0
- compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/v1/test/__init__.py +0 -0
- compressedfhir-1.0.3/compressedfhir/utilities/string_compressor/v1/test/test_string_compressor.py +189 -0
- compressedfhir-1.0.3/compressedfhir/utilities/test/__init__.py +0 -0
- compressedfhir-1.0.3/compressedfhir.egg-info/PKG-INFO +139 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/SOURCES.txt +15 -0
- compressedfhir-1.0.3/tests/__init__.py +0 -0
- compressedfhir-1.0.1/PKG-INFO +0 -28
- compressedfhir-1.0.1/README.md +0 -1
- compressedfhir-1.0.1/VERSION +0 -1
- compressedfhir-1.0.1/compressedfhir.egg-info/PKG-INFO +0 -28
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/LICENSE +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/MANIFEST.in +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/Makefile +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/__init__.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/__init__.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/base_resource_list.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_list.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_search.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_identifier.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_link.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_meta.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_resource_map.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/__init__.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_bundle_entry.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_bundle_entry_list.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_bundle_entry_request.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_bundle_entry_response.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_fhir_bundle.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_fhir_resource.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_fhir_resource_list.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/test/test_fhir_resource_map.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/py.typed +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/__init__.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/__init__.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/__init__.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/compressed_dict_access_error.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/test/__init__.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/compressed_dict/v1/test/test_compressed_dict.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/json_helpers.py +0 -0
- {compressedfhir-1.0.1/compressedfhir/utilities/test → compressedfhir-1.0.3/compressedfhir/utilities/json_serializers}/__init__.py +0 -0
- {compressedfhir-1.0.1/tests → compressedfhir-1.0.3/compressedfhir/utilities/json_serializers/test}/__init__.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/test/test_fhir_json_encoder.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/utilities/test/test_json_helpers.py +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/dependency_links.txt +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/not-zip-safe +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/requires.txt +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir.egg-info/top_level.txt +0 -0
- {compressedfhir-1.0.1 → compressedfhir-1.0.3}/setup.cfg +0 -0
- {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
|
|
290
|
-
Dict[str, Any], json.loads(json.dumps(self.dict(), cls=FhirJSONEncoder))
|
|
291
|
-
)
|
|
292
|
+
return OrderedDictToDictConverter.convert(self.dict())
|
{compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_request.py
RENAMED
|
@@ -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
|
)
|
{compressedfhir-1.0.1 → compressedfhir-1.0.3}/compressedfhir/fhir/fhir_bundle_entry_response.py
RENAMED
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
176
|
-
dictionary, separators=(",", ":")
|
|
177
|
-
)
|
|
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
|
-
|
|
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
|
-
|
|
209
|
+
serialized_dict_bytes: Serialized dictionary bytes
|
|
206
210
|
|
|
207
211
|
Returns:
|
|
208
212
|
Deserialized dictionary
|
|
209
213
|
"""
|
|
210
|
-
assert
|
|
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
|
-
|
|
215
|
-
decoded_text =
|
|
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 =
|
|
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(
|
|
230
|
+
zlib.decompress(serialized_dict_bytes)
|
|
224
231
|
if storage_type == "compressed_msgpack"
|
|
225
|
-
else
|
|
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
|
|
634
|
-
|
|
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"
|