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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: oehrpy
3
- Version: 0.1.0
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=APYpX-MhgKllz8USb1XLnW8zI4BN3ZTe5Wxv1DjIex4,17053
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=kTY6j0cTDWfJxinWizODzhZ2xO9NcAGjkyFgaYArvtQ,20740
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=QQtWk5QZD-Xh8VAPScPQYnXu-zoQar94ZFv2JPSzQUc,50511
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.0.dist-info/METADATA,sha256=Tf-pVSj8lJ4wPt7rFBMnAkTjRgcnznH8p2ZC5WYm91E,11972
16
- oehrpy-0.1.0.dist-info/WHEEL,sha256=zEMcRr9Kr03x1ozGwg5v9NQBKn3kndp6LSoSlVg-jhU,87
17
- oehrpy-0.1.0.dist-info/licenses/LICENSE,sha256=2RQ4UN7dGDVJh_e9NTzuaUzdMWMDsy1jgLPxKxJahus,1073
18
- oehrpy-0.1.0.dist-info/RECORD,,
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,,
@@ -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=:{self.ehr_id_param}]")
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 = :ehr_id")
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=:{param_name}]"
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=:{alias}_archetype_id]"
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 = :{template_id_param}"
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 = :{param_name}")
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 = :{param_name}")
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} >= :{start_param}")
420
+ self.where(f"{path} >= ${start_param}")
421
421
  self._parameters[start_param] = start
422
422
  if end:
423
- self.where(f"{path} <= :{end_param}")
423
+ self.where(f"{path} <= ${end_param}")
424
424
  self._parameters[end_param] = end
425
425
  return self
426
426
 
@@ -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=data.get("archetype_details", {}).get("template_id", {}).get("value"),
119
- archetype_id=data.get("archetype_details", {}).get("archetype_id", {}).get("value"),
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
- headers["Prefer"] = "return=representation"
330
- params = {}
331
- if subject_id:
332
- params["subject_id"] = subject_id
333
- if subject_namespace:
334
- params["subject_namespace"] = subject_namespace
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
- params=params if params else None,
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/{composition_uid}",
444
- params={"format": format_str} if format_str else None,
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 base UID without version
473
- base_uid = composition_uid.split("::")[0] if "::" in composition_uid else composition_uid
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/{base_uid}",
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
- base_uid = composition_uid.split("::")[0] if "::" in composition_uid else composition_uid
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/{base_uid}",
547
+ f"/rest/openehr/v1/ehr/{ehr_id}/composition/{versioned_object_uid}",
512
548
  )
513
549
  self._handle_response(response)
514
550
 
@@ -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