Something I wrote before that might help
Working with ‘persistent’ compositions in openEHR Ehrscape
Most openEHR data instance compositions will use event
style persistence, where a new copy of the composition is normally created each time that data is persisted, and compositions are only updated if an error correction or adjustment is required.
In contrast, a less common requirement in openEHR systems is to be able to maintain a single instance of a composition instance and to continually update that single instance. In openEHR terms this is a persistent
composition.
Common use-cases might be…
- Documents for which a single critical source of truth is important e.g. Cardiac resuscitation wishes.
- Documents which are maintained as curated summaries - e.g. problem lists, allergy lists, immunisation lists
- Documents which track simple workflows e.g. consent to share information status.
The API and data examples provided below make use of the Better (Marand) Ehrscape API and associated JSON formats but the principles can be applied to the formal openEHR REST API and JSON/XML formats.
openEHR / Ehrscape API approach
General approach
-
Run an AQL query to look for a previous instance of the composition (there should only ever by one in a production environment).
-
If no such instance exists, construct the data and then perform a POST /composition call to persist the data as a new composition instance.
-
If there is a previous instance, run the GET /composition call against the composition Uid to retrieve the most recent version, edit the data, then persist the updated data using the PUT / composition call (or DELETE if the composition is to be logically deleted).
Posting the initial composition instance version
This makes use of the normal openEHR/Ehrscape POST /composition call but an understanding of the difference between Marand FLAT/STRUCTURED INPUT and OUTPUT formats is helpful, as the FLAT/STRUCTURED OUTPUT format matches the format returned by GET /composition and is therefore more easily updated by a subsequent PUT.
Marand introduced 2 forms of flattened JSON formats to make openEHR compositions easier to work with, by flattening some complex paths and hiding boilerplate datapoints. Both STRUCTURED and FLAT formats have INPUT and OUTPUT variants. Significantly, the OUTPUT variant matches that returned by the GET /composition call. If you are expecting a sequence of a POST, followed by repeated cycles of GET and PUT /composition, it may make more sense to work purely with the OUTPUT format.
The INPUT format is helpful for simpler use-cases as it uses the ctx
block to sensibly apply defaults to a number of mandatory elements throughout the composition, in particular language and territory descriptors, and mandatory dates such as the ACTION.time attribute i.e this will default to the composition.context.start_time attribute, though in all cases these can be overriden.
The examples below are all based in a simple composition used to track consent to share information. Patient’s can provide or refuse consent - these are recorded by changing the ism_transition
settings in the ACTION.informed_consent
archetype, with only the current consent status being normally visible.
FLAT INPUT JSON example
{
"ctx/language": "en",
"ctx/territory": "GB",
"ctx/composer_name": "Silvia Blake",
"ctx/id_namespace": "Apperta",
"ctx/id_scheme": "Apperta",
"ctx/time": "2019-03-05T13:19:56.211Z",
"ctx/health_care_facility|name": "Home",
"ctx/health_care_facility|id": "0000",
"physical_health_app_admin/consent/ism_transition/current_state|code": "245",
"physical_health_app_admin/consent/ism_transition/current_state|value": "active",
"physical_health_app_admin/consent/ism_transition/careflow_step|code": "at0015",
"physical_health_app_admin/consent/ism_transition/current_state|value": "Informed Consent Provided",
"physical_health_app_admin/consent/procedure_trial_activity:0": "Consent to Physical Health data sharing",
"physical_health_app_admin/consent/consent_description": "Delighted to be consenting",
}
Note that in this example there is no date associated specifically with the consent itself, only the ctx/time
(which is the time that the document was authored).
if we now perform a GET /composition and retrieve this stored data, you will see the following
FLAT OUTPUT JSON
{
"ctx/language": "en",
"ctx/territory": "GB",
"physical_health_app_admin/_uid": "0bd5ea9d-231f-4bb3-bb06-6db4844b1c54::92aac94f-47f7-46fd-8e25-2c41da92698e::2",
"physical_health_app_admin/language|code": "en",
"physical_health_app_admin/language|terminology": "ISO_639-1",
"physical_health_app_admin/territory|code": "GB",
"physical_health_app_admin/territory|terminology": "ISO_3166-1",
"physical_health_app_admin/context/_health_care_facility|id": "0000",
"physical_health_app_admin/context/_health_care_facility|id_scheme": "Apperta",
"physical_health_app_admin/context/_health_care_facility|id_namespace": "Apperta",
"physical_health_app_admin/context/_health_care_facility|name": "Home",
"physical_health_app_admin/context/start_time": "2019-03-22T17:49:50.511Z",
"physical_health_app_admin/context/setting|code": "238",
"physical_health_app_admin/context/setting|value": "other care",
"physical_health_app_admin/context/setting|terminology": "openehr",
"physical_health_app_admin/consent/ism_transition/current_state|code": "245",
"physical_health_app_admin/consent/ism_transition/current_state|value": "active",
"physical_health_app_admin/consent/ism_transition/current_state|terminology": "openehr",
"physical_health_app_admin/consent/ism_transition/careflow_step|code": "at0015",
"physical_health_app_admin/consent/ism_transition/careflow_step|value": "Informed Consent Provided",
"physical_health_app_admin/consent/ism_transition/careflow_step|terminology": "local",
"physical_health_app_admin/consent/procedure_trial_activity:0": "Consent to Physical Health data sharing",
"physical_health_app_admin/consent/consent_description": "Delighted to be consenting",
"physical_health_app_admin/consent/time": "2019-03-05T13:20:19.857Z",
"physical_health_app_admin/consent/language|code": "en",
"physical_health_app_admin/consent/language|terminology": "ISO_639-1",
"physical_health_app_admin/consent/encoding|code": "UTF-8",
"physical_health_app_admin/consent/encoding|terminology": "IANA_character-sets",
"physical_health_app_admin/category|code": "433",
"physical_health_app_admin/category|value": "event",
"physical_health_app_admin/category|terminology": "openehr",
"physical_health_app_admin/composer|name": "Silvia Blake"
},
You will observe that a whole new set of data points has appeared. Let’s work through these individually.
A. The uid that is assigned to the composition when it is first created or is updated. This will be ignored if the composition is re-commited via a PUT, in which case the version number at the end of the string is incremented.
"physical_health_app_admin/_uid": "0bd5ea9d-231f-4bb3-bb06-6db4844b1c54::92aac94f-47f7-46fd-8e25-2c41da92698e::1"}
B. the language and territory information derived from the original ctx/language
and ctx/territory
attributes in the submitted composition. This can be supplied as Boilerplate in the UK. Note that there is a bug in earlier version of the Ehrscape library, including that used by Ethercis which requires the ctx/language
and ctx/territory
attributes to be provided even though this should not be necessary if the exapnded forms are provided.
```JSON
"physical_health_app_admin/language|code": "en",
"physical_health_app_admin/language|terminology": "ISO_639-1",
"physical_health_app_admin/territory|code": "GB",
"physical_health_app_admin/territory|terminology": "ISO_3166-1",
"ctx/language": "en",
"ctx/territory": "GB",
|
``` |
C. The healthcare facility information derived from the original `ctx/health_care_facility' attribute in the submitted composition. For a given template, this can almost certainly be boilerplated unless the place where it is being commited may differ.
|
```JSON
"physical_health_app_admin/context/_health_care_facility|id_scheme": "apperta"
"physical_health_app_admin/context/_health_care_facility|id_namespace": "apperta",
"physical_health_app_admin/context/_health_care_facility|name": "Home",
D. The start_time of the composition, derived from ctx/time
in the submitted composition. This will normally be the date of the original commit, or anty more recent update. It is intended to hold the ‘clinically authored date’, which might in some circumstances be different from the commit date e.g if a document is committed by a member of admin staff some time after the original authoring. Normally though, commit date is sufficiently close.
"physical_health_app_admin/context/start_time": "2019-03-22T17:49:50.511Z",
E. The ‘other care’ setting is added as a default value, which will suffice in many cases.
"physical_health_app_admin/context/setting|code": "238",
"physical_health_app_admin/context/setting|value": "other care",
"physical_health_app_admin/context/setting|terminology": "openehr",
E. The ‘category’ setting is added as a default value of ‘event’, which will suffice in most cases.
"physical_health_app_admin/category|code": "433",
"physical_health_app_admin/category|value": "event",
"physical_health_app_admin/category|terminology": "openehr",
E. The ‘composer’ setting is derived from ctx/composer
.
"physical_health_app_admin/composer|name": "Silvia Blake",
F. These data items will be derived from the UI/data entry
a) Where the patient has consented…
"physical_health_app_admin/consent/ism_transition/current_state|code": "245",
"physical_health_app_admin/consent/ism_transition/current_state|value": "active",
"physical_health_app_admin/consent/ism_transition/current_state|terminology": "openehr",
"physical_health_app_admin/consent/ism_transition/careflow_step|code": "at0015",
"physical_health_app_admin/consent/ism_transition/careflow_step|value": "Informed Consent Provided",
"physical_health_app_admin/consent/ism_transition/careflow_step|terminology": "local",
"physical_health_app_admin/consent/procedure_trial_activity:0": "Consent to Physical Health data sharing",
"physical_health_app_admin/consent/consent_description": "Delighted to be refusing",
b) Where the patient has refused consent…
"physical_health_app_admin/consent/ism_transition/current_state|code": "531",
"physical_health_app_admin/consent/ism_transition/current_state|value": "aborted",
"physical_health_app_admin/consent/ism_transition/current_state|terminology": "openehr",
"physical_health_app_admin/consent/ism_transition/careflow_step|code": "at0016",
"physical_health_app_admin/consent/ism_transition/careflow_step|value": "Informed Consent Refused",
"physical_health_app_admin/consent/ism_transition/careflow_step|terminology": "local",
"physical_health_app_admin/consent/procedure_trial_activity:0": "Consent to Physical Health data sharing",
"physical_health_app_admin/consent/consent_description": "Delighted to be refusing consent",
G. The time that the consent decision ACTION
was actually made. This normally equates to the start_time of the composition and is derived automatically from ctx/time
unless overriden, as here.
"physical_health_app_admin/consent/time": "2019-03-05T13:20:19.857Z",
H. The language of the Consent ACTION - boilerplate in the UK.
"physical_health_app_admin/consent/language|code": "en",
"physical_health_app_admin/consent/language|terminology": "ISO_639-1",
I. H. The encoding of text inside the Consent ACTION - boilerplate in the UK.
"physical_health_app_admin/consent/encoding|code": "UTF-8",
"physical_health_app_admin/consent/encoding|terminology": "IANA_character-sets",
This format is clearly more complex but has the advantage of being directly committable back via PUT /composition
using FLAT OUTPUT
format
STRUCTURED formats
Identical principles apply to the STRUCTURED OUTPUT
JSON format. Note that this is not currently supported by the library used by Ethercis.
{
"ctx/language": "en",
"ctx/territory": "GB",
"physical_health_app_admin": {
"language": [
{
"|code": "en",
"|terminology": "ISO_639-1"
}
],
"territory": [
{
"|code": "GB",
"|terminology": "ISO_3166-1"
}
],
"context": [
{
"_health_care_facility": [
{
"|id": "0000",
"|id_scheme": "HOSPITAL-NS",
"|id_namespace": "HOSPITAL-NS",
"|name": "Home"
}
],
"start_time": [
"2019-03-22T17:49:50.511Z"
],
"setting": [
{
"|code": "238",
"|value": "other care",
"|terminology": "openehr"
}
]
}
],
"consent": [
{
"ism_transition": [
{
"current_state": [
{
"|code": "245",
"|value": "active",
"|terminology": "openehr"
}
]
},
{
"careflow_step": [
{
"|code": "at0015",
"|value": "Informed Consent Provided",
"|terminology": "local"
}
]
}
],
"procedure_trial_activity": [
"Consent to Physical Health data sharing"
],
"consent_description": [
"Delighted to be consenting"
],
"time": [
"2019-03-05T13:20:19.857Z"
],
"language": [
{
"|code": "en",
"|terminology": "ISO_639-1"
}
],
"encoding": [
{
"|code": "UTF-8",
"|terminology": "IANA_character-sets"
}
]
}
],
"category": [
{
"|code": "433",
"|value": "event",
"|terminology": "openehr"
}
],
"composer": [
{
"|name": "Silvia Blake"
}
]
}
}