oehrpy 0.1.0__py3-none-any.whl → 0.1.1__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.
- {oehrpy-0.1.0.dist-info → oehrpy-0.1.1.dist-info}/METADATA +1 -1
- {oehrpy-0.1.0.dist-info → oehrpy-0.1.1.dist-info}/RECORD +7 -7
- openehr_sdk/aql/builder.py +9 -9
- openehr_sdk/client/ehrbase.py +54 -18
- openehr_sdk/rm/rm_types.py +1 -1
- {oehrpy-0.1.0.dist-info → oehrpy-0.1.1.dist-info}/WHEEL +0 -0
- {oehrpy-0.1.0.dist-info → oehrpy-0.1.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: oehrpy
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.1
|
|
4
4
|
Summary: A Python SDK for openEHR with type-safe Reference Model classes, template builders, and EHRBase client
|
|
5
5
|
Project-URL: Homepage, https://github.com/platzhersh/oehrpy
|
|
6
6
|
Project-URL: Documentation, https://github.com/platzhersh/oehrpy#readme
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
openehr_sdk/__init__.py,sha256=Rzs3NFgVa-P2ljZu2-OXcgkKBrhE_mAwWf9HnnVwrB8,1295
|
|
2
2
|
openehr_sdk/aql/__init__.py,sha256=auR0SKnnnAhzRzq2nU0YZSzUPKmAzYoXXdWs9Kxzoq8,958
|
|
3
|
-
openehr_sdk/aql/builder.py,sha256=
|
|
3
|
+
openehr_sdk/aql/builder.py,sha256=XptfUJVuMbUAa-LY-XFWxWDXFzBbMSHZStNloz2PAGo,17053
|
|
4
4
|
openehr_sdk/client/__init__.py,sha256=swqiwHCbwyIvq_newgkUEfGq6FvHV_CFfU_fYw2rw08,626
|
|
5
|
-
openehr_sdk/client/ehrbase.py,sha256=
|
|
5
|
+
openehr_sdk/client/ehrbase.py,sha256=m3Ml8MZ0YBzY6oWOWgpxc2aqWytfTth35E7e3FVWr-k,22242
|
|
6
6
|
openehr_sdk/rm/__init__.py,sha256=yS5JAFaUuoo3D-k_Q9V78j2cBQKTW4EMK9LYEl34ZAc,457
|
|
7
|
-
openehr_sdk/rm/rm_types.py,sha256=
|
|
7
|
+
openehr_sdk/rm/rm_types.py,sha256=7DLcfubGplYPIhF6byBsrnEO0ayOw_bBeJbzrDqbZ-Q,50518
|
|
8
8
|
openehr_sdk/serialization/__init__.py,sha256=aucmds3NXlq1nQrjQNZnYR6PzqT63lAcCLh3og2AtM8,748
|
|
9
9
|
openehr_sdk/serialization/canonical.py,sha256=hQMLaKWne9Ep_mv0VrT7kJa0y89C37YEB5owRpACRag,6325
|
|
10
10
|
openehr_sdk/serialization/flat.py,sha256=w8hh6jGGsVHzvnjm1RlMoAhAxuhLumfuV_rrC5sH60A,13122
|
|
@@ -12,7 +12,7 @@ openehr_sdk/templates/__init__.py,sha256=_9Fwua5Mw_mM5n9CaKqXql12GSQyVvqdc_-J-jx
|
|
|
12
12
|
openehr_sdk/templates/builder_generator.py,sha256=oTYcsvmwGasUfGU3zmW9O79eCFyyprO4hkGJ5SvMB5I,13679
|
|
13
13
|
openehr_sdk/templates/builders.py,sha256=CJuOrobrCNWBbEyHbVJTzGqxApZ7CAyZrhUmY9ctvWg,13665
|
|
14
14
|
openehr_sdk/templates/opt_parser.py,sha256=WzOFYUHgAYTn7hk8LWghqP2gjiG9t3LWlYpQ3-999pg,12423
|
|
15
|
-
oehrpy-0.1.
|
|
16
|
-
oehrpy-0.1.
|
|
17
|
-
oehrpy-0.1.
|
|
18
|
-
oehrpy-0.1.
|
|
15
|
+
oehrpy-0.1.1.dist-info/METADATA,sha256=oxbOaOJ4OQ0btd6x7NvUsWlz6Leo5N1KdeuJpWjb80g,11972
|
|
16
|
+
oehrpy-0.1.1.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
|
|
17
|
+
oehrpy-0.1.1.dist-info/licenses/LICENSE,sha256=2RQ4UN7dGDVJh_e9NTzuaUzdMWMDsy1jgLPxKxJahus,1073
|
|
18
|
+
oehrpy-0.1.1.dist-info/RECORD,,
|
openehr_sdk/aql/builder.py
CHANGED
|
@@ -66,7 +66,7 @@ class FromClause:
|
|
|
66
66
|
|
|
67
67
|
# EHR clause
|
|
68
68
|
if self.ehr_id_param:
|
|
69
|
-
parts.append(f"EHR {self.ehr_alias}[ehr_id/value
|
|
69
|
+
parts.append(f"EHR {self.ehr_alias}[ehr_id/value=${self.ehr_id_param}]")
|
|
70
70
|
else:
|
|
71
71
|
parts.append(f"EHR {self.ehr_alias}")
|
|
72
72
|
|
|
@@ -178,7 +178,7 @@ class AQLBuilder:
|
|
|
178
178
|
... .select("c/context/start_time/value", alias="time")
|
|
179
179
|
... .from_ehr("e")
|
|
180
180
|
... .contains_composition("c", "IDCR - Vital Signs Encounter.v1")
|
|
181
|
-
... .where("e/ehr_id/value =
|
|
181
|
+
... .where("e/ehr_id/value = $ehr_id")
|
|
182
182
|
... .order_by("c/context/start_time/value", descending=True)
|
|
183
183
|
... .limit(10)
|
|
184
184
|
... .build()
|
|
@@ -269,7 +269,7 @@ class AQLBuilder:
|
|
|
269
269
|
|
|
270
270
|
if archetype_id:
|
|
271
271
|
param_name = f"{alias}_archetype_id"
|
|
272
|
-
containment = f"{rm_type} {alias}[archetype_id/value
|
|
272
|
+
containment = f"{rm_type} {alias}[archetype_id/value=${param_name}]"
|
|
273
273
|
self._parameters[param_name] = archetype_id
|
|
274
274
|
else:
|
|
275
275
|
containment = f"{rm_type} {alias}"
|
|
@@ -299,7 +299,7 @@ class AQLBuilder:
|
|
|
299
299
|
self._from_clause = FromClause()
|
|
300
300
|
|
|
301
301
|
if archetype_id:
|
|
302
|
-
containment = f"COMPOSITION {alias}[archetype_id/value
|
|
302
|
+
containment = f"COMPOSITION {alias}[archetype_id/value=${alias}_archetype_id]"
|
|
303
303
|
self._parameters[f"{alias}_archetype_id"] = archetype_id
|
|
304
304
|
else:
|
|
305
305
|
containment = f"COMPOSITION {alias}"
|
|
@@ -309,7 +309,7 @@ class AQLBuilder:
|
|
|
309
309
|
# Add template_id filter as parameterized WHERE clause
|
|
310
310
|
if template_id:
|
|
311
311
|
self._where_clause.add(
|
|
312
|
-
f"{alias}/archetype_details/template_id/value =
|
|
312
|
+
f"{alias}/archetype_details/template_id/value = ${template_id_param}"
|
|
313
313
|
)
|
|
314
314
|
self._parameters[template_id_param] = template_id
|
|
315
315
|
|
|
@@ -373,7 +373,7 @@ class AQLBuilder:
|
|
|
373
373
|
Returns:
|
|
374
374
|
Self for method chaining.
|
|
375
375
|
"""
|
|
376
|
-
return self.where(f"{ehr_alias}/ehr_id/value =
|
|
376
|
+
return self.where(f"{ehr_alias}/ehr_id/value = ${param_name}")
|
|
377
377
|
|
|
378
378
|
def where_template(
|
|
379
379
|
self,
|
|
@@ -391,7 +391,7 @@ class AQLBuilder:
|
|
|
391
391
|
Returns:
|
|
392
392
|
Self for method chaining.
|
|
393
393
|
"""
|
|
394
|
-
self.where(f"{composition_alias}/archetype_details/template_id/value =
|
|
394
|
+
self.where(f"{composition_alias}/archetype_details/template_id/value = ${param_name}")
|
|
395
395
|
if template_id:
|
|
396
396
|
self._parameters[param_name] = template_id
|
|
397
397
|
return self
|
|
@@ -417,10 +417,10 @@ class AQLBuilder:
|
|
|
417
417
|
Self for method chaining.
|
|
418
418
|
"""
|
|
419
419
|
if start:
|
|
420
|
-
self.where(f"{path} >=
|
|
420
|
+
self.where(f"{path} >= ${start_param}")
|
|
421
421
|
self._parameters[start_param] = start
|
|
422
422
|
if end:
|
|
423
|
-
self.where(f"{path} <=
|
|
423
|
+
self.where(f"{path} <= ${end_param}")
|
|
424
424
|
self._parameters[end_param] = end
|
|
425
425
|
return self
|
|
426
426
|
|
openehr_sdk/client/ehrbase.py
CHANGED
|
@@ -110,13 +110,25 @@ class CompositionResponse:
|
|
|
110
110
|
@classmethod
|
|
111
111
|
def from_response(cls, data: dict[str, Any], ehr_id: str | None = None) -> CompositionResponse:
|
|
112
112
|
"""Create from API response."""
|
|
113
|
+
# Try canonical format first (uid is a dict with "value" key)
|
|
113
114
|
uid_data = data.get("uid")
|
|
114
115
|
uid = uid_data.get("value", "") if isinstance(uid_data, dict) else uid_data or ""
|
|
116
|
+
|
|
117
|
+
template_id = data.get("archetype_details", {}).get("template_id", {}).get("value")
|
|
118
|
+
archetype_id = data.get("archetype_details", {}).get("archetype_id", {}).get("value")
|
|
119
|
+
|
|
120
|
+
# For FLAT format responses, extract uid from */_uid key
|
|
121
|
+
if not uid:
|
|
122
|
+
for key, value in data.items():
|
|
123
|
+
if key.endswith("/_uid") and isinstance(value, str):
|
|
124
|
+
uid = value
|
|
125
|
+
break
|
|
126
|
+
|
|
115
127
|
return cls(
|
|
116
128
|
uid=uid,
|
|
117
129
|
ehr_id=ehr_id,
|
|
118
|
-
template_id=
|
|
119
|
-
archetype_id=
|
|
130
|
+
template_id=template_id,
|
|
131
|
+
archetype_id=archetype_id,
|
|
120
132
|
composition=data,
|
|
121
133
|
)
|
|
122
134
|
|
|
@@ -318,24 +330,37 @@ class EHRBaseClient:
|
|
|
318
330
|
Returns:
|
|
319
331
|
EHRResponse with the created EHR details.
|
|
320
332
|
"""
|
|
321
|
-
headers = {}
|
|
333
|
+
headers: dict[str, str] = {"Prefer": "return=representation"}
|
|
322
334
|
if ehr_id:
|
|
323
|
-
headers["Prefer"] = "return=representation"
|
|
324
335
|
response = await self.client.put(
|
|
325
336
|
f"/rest/openehr/v1/ehr/{ehr_id}",
|
|
326
337
|
headers=headers,
|
|
327
338
|
)
|
|
328
339
|
else:
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
340
|
+
body = None
|
|
341
|
+
if subject_id and subject_namespace:
|
|
342
|
+
body = {
|
|
343
|
+
"_type": "EHR_STATUS",
|
|
344
|
+
"archetype_node_id": "openEHR-EHR-EHR_STATUS.generic.v1",
|
|
345
|
+
"name": {"value": "EHR Status"},
|
|
346
|
+
"subject": {
|
|
347
|
+
"external_ref": {
|
|
348
|
+
"id": {
|
|
349
|
+
"_type": "GENERIC_ID",
|
|
350
|
+
"value": subject_id,
|
|
351
|
+
"scheme": "id_scheme",
|
|
352
|
+
},
|
|
353
|
+
"namespace": subject_namespace,
|
|
354
|
+
"type": "PERSON",
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
"is_modifiable": True,
|
|
358
|
+
"is_queryable": True,
|
|
359
|
+
}
|
|
335
360
|
response = await self.client.post(
|
|
336
361
|
"/rest/openehr/v1/ehr",
|
|
337
362
|
headers=headers,
|
|
338
|
-
|
|
363
|
+
json=body,
|
|
339
364
|
)
|
|
340
365
|
|
|
341
366
|
data = self._handle_response(response)
|
|
@@ -439,9 +464,17 @@ class EHRBaseClient:
|
|
|
439
464
|
"""
|
|
440
465
|
format_str = format.value if isinstance(format, CompositionFormat) else format
|
|
441
466
|
|
|
467
|
+
# Extract versioned object UID (uuid::system::version -> uuid::system)
|
|
468
|
+
uid_parts = composition_uid.split("::")
|
|
469
|
+
versioned_object_uid = "::".join(uid_parts[:2]) if len(uid_parts) >= 2 else composition_uid
|
|
470
|
+
|
|
471
|
+
params: dict[str, str] = {}
|
|
472
|
+
if format_str:
|
|
473
|
+
params["format"] = format_str
|
|
474
|
+
|
|
442
475
|
response = await self.client.get(
|
|
443
|
-
f"/rest/openehr/v1/ehr/{ehr_id}/composition/{
|
|
444
|
-
params=
|
|
476
|
+
f"/rest/openehr/v1/ehr/{ehr_id}/composition/{versioned_object_uid}",
|
|
477
|
+
params=params if params else None,
|
|
445
478
|
)
|
|
446
479
|
|
|
447
480
|
data = self._handle_response(response)
|
|
@@ -469,8 +502,9 @@ class EHRBaseClient:
|
|
|
469
502
|
"""
|
|
470
503
|
format_str = format.value if isinstance(format, CompositionFormat) else format
|
|
471
504
|
|
|
472
|
-
# Extract
|
|
473
|
-
|
|
505
|
+
# Extract versioned object UID (uuid::system::version -> uuid::system)
|
|
506
|
+
uid_parts = composition_uid.split("::")
|
|
507
|
+
versioned_object_uid = "::".join(uid_parts[:2]) if len(uid_parts) >= 2 else composition_uid
|
|
474
508
|
|
|
475
509
|
headers = {
|
|
476
510
|
"Prefer": "return=representation",
|
|
@@ -485,7 +519,7 @@ class EHRBaseClient:
|
|
|
485
519
|
params["format"] = format_str
|
|
486
520
|
|
|
487
521
|
response = await self.client.put(
|
|
488
|
-
f"/rest/openehr/v1/ehr/{ehr_id}/composition/{
|
|
522
|
+
f"/rest/openehr/v1/ehr/{ehr_id}/composition/{versioned_object_uid}",
|
|
489
523
|
json=composition,
|
|
490
524
|
headers=headers,
|
|
491
525
|
params=params if params else None,
|
|
@@ -505,10 +539,12 @@ class EHRBaseClient:
|
|
|
505
539
|
ehr_id: The EHR ID.
|
|
506
540
|
composition_uid: The composition UID.
|
|
507
541
|
"""
|
|
508
|
-
|
|
542
|
+
# Extract versioned object UID (uuid::system::version -> uuid::system)
|
|
543
|
+
uid_parts = composition_uid.split("::")
|
|
544
|
+
versioned_object_uid = "::".join(uid_parts[:2]) if len(uid_parts) >= 2 else composition_uid
|
|
509
545
|
|
|
510
546
|
response = await self.client.delete(
|
|
511
|
-
f"/rest/openehr/v1/ehr/{ehr_id}/composition/{
|
|
547
|
+
f"/rest/openehr/v1/ehr/{ehr_id}/composition/{versioned_object_uid}",
|
|
512
548
|
)
|
|
513
549
|
self._handle_response(response)
|
|
514
550
|
|
openehr_sdk/rm/rm_types.py
CHANGED
|
@@ -1030,7 +1030,7 @@ class HISTORY(BaseModel):
|
|
|
1030
1030
|
archetype_details: ARCHETYPED | None = None
|
|
1031
1031
|
feeder_audit: FEEDER_AUDIT | None = None
|
|
1032
1032
|
links: list[LINK] | None = None
|
|
1033
|
-
origin: DV_DATE_TIME | None
|
|
1033
|
+
origin: DV_DATE_TIME | None = None
|
|
1034
1034
|
period: DV_DURATION | None = None
|
|
1035
1035
|
duration: DV_DURATION | None = None
|
|
1036
1036
|
summary: Any | None = None
|
|
File without changes
|
|
File without changes
|