SDC with Custom Resources
Structured Data Capturing with Aidbox Custom Resources
This is an ALPHA (preview) feature of Aidbox, which was published to collect users feedback. It's available now on 0.4.2-SNAPSHOT Devbox and on edge cluster in the cloud.
Sometimes FHIR granularity is too small to collect patient data from user interfaces. For example, when a physician or a nurse records vital signs in terms of FHIR, they produce a bundle of observations. How to save this data to a FHIR server? How to keep track that this data was collected on the Vitals Form?
One option is to build a transaction bundle with a provenance on the client side and send it to the transaction endpoint. But with this approach it is rather problematic to track data back; update or delete it as a whole.
There is a Structured Data Capture Implementation Guide (SDC IG) which describes how to collect data using Questionnaire and QuestionnaireResponse resources. It is a good idea and a work in progress. One problem we see with it is that the QuestionnaireResponse table is stuffed with all sorts of things and managing this table, doing searches and updates is problematic. Another problem is that Questionnaire/QuestionnaireResponse is too generic and verbose which makes working with them inconvenient.
Aidbox supports Custom Resources, which cover all Questionnaire functionality and flexibility while providing many more features - validation, separate storage, search, references to other FHIR resources, etc. We applied key ideas from the SDC IG to Custom Resources to give you the best of both worlds.

Demonstration Scenario

The first step is to define your custom resource. We recommend using sugar App API. As an example we are defining resource Vitals with temperature, heart_rate, and patient reference:
custom-resource.yaml
1
POST /App
2
3
resourceType: App
4
id: Vitals
5
apiVersion: 1
6
type: app
7
entities:
8
Vitals:
9
attrs:
10
temperature: {type: number}
11
heart_rate: {type: number}
12
ts: {type: dateTime}
13
patient:
14
type: Reference
15
refers: [Patient]
Copied!
Now we can check that Vitals work properly.
Create a new vitals record:
patient
1
PUT /Patient/pt-1
2
3
name:
4
- family: Jackson
5
given: ['John']
Copied!
create-vitals
1
POST /Vitals
2
3
resourceType: Vitals
4
temperature: 36.6
5
heart_rate: 102
6
patient: {id: 'pt-1', resourceType: 'Patient'}
7
ts: '2019-04-09T12:00:00Z'
Copied!
Get all vitals records for the patient pt-1:
get-vitals
1
GET /Vitals?.patient.id=pt-1
Copied!

Debugging Extraction

Now we want to transform Vitals to a set of FHIR Observations. We are using template extraction. You can play and debug extraction using /AlphaSDC/$debug
debug.yaml
1
POST /AlphaSDC/$debug
2
3
source:
4
id: src-1
5
temp: 36.6
6
template:
7
Observation:
8
temp:
9
value: { $path!: [temp] }
Copied!
response.yaml
1
source: {id: src-1, temp: 36.6}
2
template:
3
Observation:
4
temp:
5
value:
6
$path!: [temp]
7
extracted:
8
Observation:
9
temp:
10
value: 36.6
11
id: temp-src-1
12
meta: {sourceId: src-1}
Copied!
template it structured as { [resourceType] : { [id-prefix] : <resource-template> }}, for example to generate Observation with prefix tmp you write {Observation: {tmp: <resource-template>}}
<resource-template> is a template/prototype of a resulting FHIR resource, where some attributes contain substitution rules like { $path!: ["attr", "attr"]}. These rules will be replaced with values from Custom resource, indicated by this path.

Advanced substitution rules examples

resource.yaml
1
resourceType: QuestionnaireResponse
2
id: qr1
3
questionnaire: Questionnaire/q1|1
4
item:
5
- text: What is your height in feet.inches?
6
answer:
7
- valueDecimal: 5.2
8
linkId: ht
9
- text: What is your weight in pounds?
10
answer:
11
- valueDecimal: 122
12
linkId: wt
Copied!
Let's suppose you want to extract the height of a respondent, for this purpose you can use this $path expression ["item, {"linkId": ht"}, "answer", 0, "valueDecimal"]
{"linkId": ht"} - this object is used for filtering, you can declare the objects with as many keys as you need to match item in the specified collection.

Create SDC resource

Now let's define how FHIR Observations will be extracted from Vitals. We have to create AlphaSDC resource for that:
1
PUT /AlphaSDC/Vitals
2
3
template:
4
Observation:
5
temp:
6
subject:
7
$path!: [patient]
8
category:
9
- coding:
10
- {system: 'http://terminology.hl7.org/CodeSystem/observation-category', code: vital-signs, display: Vital Signs}
11
status: final
12
code:
13
coding:
14
- {system: 'http://loinc.org', code: 8310-5, display: Body temperature}
15
effective:
16
dateTime:
17
$path!: [ts]
18
value:
19
Quantity:
20
value:
21
$path!: [temperature]
22
unit: degrees C
23
system: http://unitsofmeasure.org
24
code: Cel
25
hr:
26
subject:
27
$path!: [patient]
28
category:
29
- coding:
30
- {system: 'http://terminology.hl7.org/CodeSystem/observation-category',
31
code: vital-signs, display: Vital Signs}
32
status: final
33
code:
34
coding:
35
- {system: 'http://loinc.org', code: 8867-4, display: Heart rate}
36
effective:
37
dateTime:
38
$path!: [ts]
39
value:
40
Quantity:
41
value:
42
$path!: [heart_rate]
43
unit: beats/minute
44
system: http://unitsofmeasure.org
45
code: /min
Copied!
.id of AlphaSDC resource should be exactly the same as the name of Custom Resource - i.e., Vitals
.template is a template described in the section above.
Now you can test how data is extracted using POST /AlphaSDC/<id>/$extract endpoint:
request
1
POST /AlphaSDC/Vitals/$extract
2
3
resourceType: Vitals
4
temperature: 36.6
5
heart_rate: 102
6
patient: {id: 'pt-1', resourceType: 'Patient'}
7
ts: '2019-04-09T12:00:00Z'
Copied!
You should see the following response:
1
source: <original resource>
2
template: <template>
3
extracted: <result-of-extraction>
Copied!

Make it work on resource save

To make this extraction work on Vitals save, we have to override its Create Operation
1
PUT /Operation/vitals-create
2
3
request: [post, Vitals]
4
action: sdc/create
Copied!
sdc/create action will do extraction and save original custom resource and extracted FHIR resources:
create-vitals
1
POST /Vitals
2
3
resourceType: Vitals
4
id: v-2
5
temperature: 36.6
6
heart_rate: 102
7
patient: {id: 'pt-1', resourceType: 'Patient'}
8
ts: '2019-04-09T12:00:00Z'
Copied!
Now we can see the Observations that have been created:
get-observations
1
GET /Observation?.subject.id=pt-1
2
# or
3
GET /Vitals?.patient.id=pt-1
Copied!
Please provide your feedback and use cases for SDC using github

TBD

  • alternative template languages - JUTE; FHIRPath etc
  • support for Update & Delete
  • template validation
  • $pre-populate Operation with SQL engine