-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathtest_profile_bp.py
More file actions
258 lines (204 loc) · 8.86 KB
/
Copy pathtest_profile_bp.py
File metadata and controls
258 lines (204 loc) · 8.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
"""
US Core Blood Pressure Profile Class API Tests
Mirrors examples/typescript-us-core/profile-bp.test.ts.
"""
import warnings
from fhir_types.hl7_fhir_r4_core.base import CodeableConcept, Coding, Quantity, Reference
from fhir_types.hl7_fhir_r4_core.observation import Observation, ObservationComponent
from fhir_types.hl7_fhir_r4_core.base import Meta
from fhir_types.hl7_fhir_us_core.profiles.observation_uscore_blood_pressure_profile import UscoreBloodPressureProfile
# Pydantic warns when extensions list contains plain dicts instead of Extension
# model instances — this is expected with the current push_extension approach.
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic")
CANONICAL_URL = "http://hl7.org/fhir/us/core/StructureDefinition/us-core-blood-pressure"
VSCAT_CODING = Coding(code="vital-signs", system="http://terminology.hl7.org/CodeSystem/observation-category")
def _make_bp() -> UscoreBloodPressureProfile:
return UscoreBloodPressureProfile.create(
status="final",
subject=Reference(reference="Patient/pt-1"),
)
# ---------------------------------------------------------------------------
# demo
# ---------------------------------------------------------------------------
def test_import_profiled_observation_from_api_and_read_components() -> None:
api_response = Observation(
resourceType="Observation",
meta=Meta(profile=[CANONICAL_URL]),
status="final",
category=[CodeableConcept(coding=[VSCAT_CODING])],
code=CodeableConcept(coding=[Coding(code="85354-9", system="http://loinc.org", display="Blood pressure panel")]),
subject=Reference(reference="Patient/pt-1"),
effectiveDateTime="2024-06-15",
component=[
ObservationComponent(
code=CodeableConcept(coding=[Coding(code="8480-6", system="http://loinc.org")]),
valueQuantity=Quantity(value=120, unit="mmHg", system="http://unitsofmeasure.org", code="mm[Hg]"),
),
ObservationComponent(
code=CodeableConcept(coding=[Coding(code="8462-4", system="http://loinc.org")]),
valueQuantity=Quantity(value=80, unit="mmHg", system="http://unitsofmeasure.org", code="mm[Hg]"),
),
],
)
profile = UscoreBloodPressureProfile.from_resource(api_response)
assert profile.get_systolic() == {
"value": 120,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]",
}
assert profile.get_diastolic() == {
"value": 80,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]",
}
assert profile.get_effective_date_time() == "2024-06-15"
def test_apply_profile_to_bare_observation_and_populate_it() -> None:
bare_observation = Observation(resourceType="Observation", status="preliminary", code=CodeableConcept())
profile = UscoreBloodPressureProfile.apply(bare_observation)
profile.set_status("final")
profile.set_code(CodeableConcept(coding=[Coding(code="85354-9", system="http://loinc.org")]))
profile.set_subject(Reference(reference="Patient/pt-1"))
profile.set_vscat({})
profile.set_effective_date_time("2024-06-15")
profile.set_systolic({"value": 120, "unit": "mmHg"})
profile.set_diastolic({"value": 80, "unit": "mmHg"})
assert profile.validate()["errors"] == []
meta = profile.to_resource().meta
assert meta is not None
assert meta.profile is not None
assert CANONICAL_URL in meta.profile
# ---------------------------------------------------------------------------
# US Core blood pressure profile
# ---------------------------------------------------------------------------
def test_canonical_url_is_exposed() -> None:
assert UscoreBloodPressureProfile.canonical_url == CANONICAL_URL
def test_create_auto_sets_code_and_meta_profile() -> None:
profile = _make_bp()
obs = profile.to_resource()
assert obs.resourceType == "Observation"
coding = obs.code.coding
assert coding is not None
assert coding[0].code == "85354-9"
assert coding[0].system == "http://loinc.org"
meta = obs.meta
assert meta is not None
assert meta.profile == [CANONICAL_URL]
def test_freshly_created_profile_is_not_yet_valid_missing_effective() -> None:
profile = _make_bp()
errors = profile.validate()["errors"]
assert errors == [
"UscoreBloodPressureProfile: at least one of effectiveDateTime, effectivePeriod is required",
]
def test_create_auto_populates_component_with_systolic_diastolic_stubs() -> None:
profile = _make_bp()
obs = profile.to_resource()
assert obs.component is not None
assert len(obs.component) == 2
def test_set_systolic_get_systolic_get_systolic_raw() -> None:
profile = _make_bp()
profile.set_systolic({"value": 120, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"})
assert profile.get_systolic() == {
"value": 120,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]",
}
raw = profile.get_systolic("raw")
assert raw is not None
assert raw.valueQuantity is not None
assert raw.valueQuantity.value == 120
coding = raw.code.coding
assert coding is not None
assert coding[0].code == "8480-6"
def test_set_diastolic_get_diastolic_get_diastolic_raw() -> None:
profile = _make_bp()
profile.set_diastolic({"value": 80, "unit": "mmHg", "system": "http://unitsofmeasure.org", "code": "mm[Hg]"})
assert profile.get_diastolic() == {
"value": 80,
"unit": "mmHg",
"system": "http://unitsofmeasure.org",
"code": "mm[Hg]",
}
raw = profile.get_diastolic("raw")
assert raw is not None
assert raw.valueQuantity is not None
assert raw.valueQuantity.value == 80
coding = raw.code.coding
assert coding is not None
assert coding[0].code == "8462-4"
def test_both_systolic_and_diastolic_are_in_the_component_array() -> None:
profile = _make_bp()
obs = profile.to_resource()
assert obs.component is not None
assert len(obs.component) == 2
def test_set_systolic_replaces_an_existing_systolic_component() -> None:
profile = _make_bp()
profile.set_systolic({"value": 130, "unit": "mmHg"})
obs = profile.to_resource()
assert obs.component is not None
assert len(obs.component) == 2
raw = profile.get_systolic("raw")
assert raw is not None
assert raw.valueQuantity is not None
assert raw.valueQuantity.value == 130
def test_set_vscat_adds_category_with_discriminator_values() -> None:
profile = _make_bp()
profile.set_vscat({"text": "Vital Signs"})
flat = profile.get_vscat()
assert flat is not None
assert flat["text"] == "Vital Signs"
def test_set_effective_date_time_get_effective_date_time() -> None:
profile = _make_bp()
profile.set_effective_date_time("2024-06-15T10:30:00Z")
assert profile.get_effective_date_time() == "2024-06-15T10:30:00Z"
assert profile.get_value_quantity() is None
def test_fluent_chaining_across_all_accessor_types() -> None:
profile = _make_bp()
result = (
profile.set_status("final")
.set_vscat({"text": "Vital Signs"})
.set_effective_date_time("2024-06-15")
.set_subject(Reference(reference="Patient/pt-2"))
)
assert result is profile
assert profile.get_status() == "final"
vscat = profile.get_vscat()
assert vscat is not None
assert vscat["text"] == "Vital Signs"
assert profile.get_effective_date_time() == "2024-06-15"
subject = profile.get_subject()
assert subject is not None
assert subject.reference == "Patient/pt-2"
def test_set_systolic_with_no_args_inserts_discriminator_only_component() -> None:
profile = _make_bp()
profile.set_systolic()
assert profile.get_systolic() is not None
def test_create_with_custom_category_preserves_user_values_and_adds_required_vscat() -> None:
custom = UscoreBloodPressureProfile.create(
status="final",
subject=Reference(reference="Patient/pt-1"),
category=[CodeableConcept(text="My Category")],
)
obs = custom.to_resource()
assert obs.category is not None
assert len(obs.category) == 2
def test_create_with_empty_category_still_adds_required_vscat() -> None:
custom = UscoreBloodPressureProfile.create(
status="final",
subject=Reference(reference="Patient/pt-1"),
category=[],
)
obs = custom.to_resource()
assert obs.category is not None
assert len(obs.category) == 1
def test_create_with_category_already_containing_vscat_does_not_duplicate_it() -> None:
custom = UscoreBloodPressureProfile.create(
status="final",
subject=Reference(reference="Patient/pt-1"),
category=[CodeableConcept(coding=[VSCAT_CODING])],
)
obs = custom.to_resource()
assert obs.category is not None
assert len(obs.category) == 1