Links, Suggestions, and SMART apps
How to build advanced decision support with Sero

What we'll be building

In this section, we'll be building a CDS service that calculates the Reynolds Risk score of a patient based on recent clinical observations. Based on the calculation, the service will return a suggestion that suggests the patient be prescribed a medication to manage their high risk. A link to more information about the medication, and a link to a SMART app to further work with the information will be provided as well.
This service will also be invoked with the patient-view hook.
You can find the complete source for this guide in the Sero project at example/cds-hooks-api-guide​

Suggestions and links

We'll first take a closer look at the attributes of a Card from the CDS Hooks specification. There are attributes of cards that can enhance a service's decision support that we haven't worked with yet. Two of those attributes are suggestions and links.
Suggestion and link specification

Suggestions and actions

If we wanted to provide more interactive decision support, such as suggesting that a user read further into an issue, or even prescribe a medication, we'd use suggestions.
Suggestions are described by three attributes. Only the labelproperty - a human-readable description of the action taken by "taking" a suggestion - is required. The following is an example of a suggestion with the label and actions properties. The action is a MedicationRequest POST request.
1
suggestions: [
2
{
3
label: "Create a prescription for Aspirin 80 MG oral Tablet",
4
actions: [
5
{
6
type: "create",
7
description:
8
"Create a prescription for Aspirin 80 MG Oral Tablet",
9
resource: {
10
...
11
// shortened for brevity
12
...
13
},
14
},
15
],
16
request: {
17
method: "POST",
18
url: "MedicationRequest",
19
},
20
},
21
],
Copied!
Actions are actions that the user of the CDS client take when given a suggestion. In an interface that renders CDS cards, like Logica, actions will look like a button that, when pressed, will perform one of the three types of actions: create, update, and delete. If the type of action is create or update, the action must include the resource field that's populated by a FHIR resource to be provided so that the action can be taken.
If we wanted to provide links to external SMART apps or web pages, we would use links.
Links come in two varieties. absolute links take the user to an external website, such as a reference page or a paper. smart links are used to launch external SMART applications. SMART applications are healthcare applications designed to use the SMART on FHIR API. Think of them as healthcare applications that we can launch from a Card when we need enhanced functionality to deal with present FHIR data.
Below is an example of an absolute link to google.com.
1
links: [
2
{
3
label: "Google.com",
4
url: "https://google.com",
5
type: "absolute",
6
},
7
],
Copied!
Below is an example of a smart link to an external SMART app.
1
links: [
2
{
3
label: "Launch the CarePassport patient portal",
4
url: "https://carepassport.net/FHIR/launch.html",
5
type: "smart",
6
},
7
],
Copied!

SMART applications

Reynolds risk score

As opposed to the previous two sections, the service we'll build in this section is tailor-made to provide useful decision support for a real-world issue.
The Reynolds Risk Score is a metric designed to predict someone's risk of having a future heart attack, stroke, or other major heart disease within the next 10 years. It uses different patient observations to make its assessment, including smoking status, systolic blood pressure, and cholesterol levels.
The service we'll build will calculate this risk score based on the most recent patient observations. By some metric, the service will make suggestions to prescribe certain medications if the patients risk is high. Finally, the service will provide a link to a SMART app to further work with the support information that your service provided.

What is a SMART app?

A SMART app is a new class of healthcare application that's designed to be used in healthcare settings that fit the requirements laid out by SMART on FHIR - an open healthcare API. In this example, we are going to register a SMART app called Cardiac Risk. Although the service that we create will also calculate a patients risk score, The Cardiac Risk application will provide enhanced support, such as helpful visualizations, and interventions that could decrease the patient's score.
The version of Cardiac Risk that we'll be using we've modified to work with the latest version of FHIR (you can find the code here).
Cardiac-risk smart app

The code

Imports

In the src directory, create a folder called suggestions-links-fhir. cd into it and create a file called prefetch-context.js. You'll be using the Service and Card classes like in the prior two sections.
suggestions-links-fhir.js
1
import { Service, Card } from "@sero.run/sero";
Copied!

Options

This service is invoked on the patient-view hook like the previous two sections. Like the previous section, we'll use a prefetch template that fetches relevant information needed to determine the Reynolds risk score.
suggestions-links-fhir.js
1
const options = {
2
id: "suggestions-links",
3
title: "patient view with Reynolds Risk Score assessment",
4
hook: "patient-view",
5
description:
6
"Service triggered on the patient-view hook that calculates Reynolds risk score, and makes a MedicationPrescribe suggestion based on the result. Also provides a link to a SMART app to help work with the result",
7
prefetch: {
8
patient: "Patient/{{context.patientId}}",
9
hscrp: "Observation?code=http://loinc.org|30522-7&_sort=date",
10
cholesterolMassOverVolume:
11
"Observation?code=http://loinc.org|2093-3&_sort=date",
12
hdl: "Observation?code=http://loinc.org|2085-9&_sort=date",
13
bloodPressure: "Observation?code=http://loinc.org|55284-4&_sort=date",
14
smokingStatus: "Observation?code=http://loinc.org|72166-2&_sort=date",
15
},
16
};
Copied!
The prefetch template includes four FHIR path search queries for observations matching the loinc code in the search query. For example, to fetch all of the Observations where the patients blood pressure was measured, and sort by date, you'd use Observation?code=http://loinc.org|55284-4&_sort=date.
This will return a Patient and fiveBundle FHIR resources.

Helper functions

To calculate the risk score, we'll be using the following two formulas. This is because the calculation differs by gender.
Reynolds Risk Score for women [1]
Reynolds Risk Score for men [2]
Like the prior section, create util.js and copy in the following code.
util.js
1
/**
2
*
3
* @param {*} patient - patient FHIR object
4
* @param {number} age - the patients age
5
* @param {number} hscrp - hscrp value
6
* @param {number} cholesterol - cholesterol value
7
* @param {number} hdlc - hdlc value
8
* @param {number} systolicBloodPressure - systolic blood pressure
9
* @returns {[number, string]} reynolds risk score - 10-year cardiovascular disease risk
10
*/
11
export function reynoldsRiskScore(
12
age,
13
gender,
14
systolicBloodPressure,
15
hscrp,
16
cholesterol,
17
hdlc,
18
smoking = false
19
) {
20
// parameter coefficients based on gender
21
const params =
22
gender == "female"
23
? {
24
age: 0.0799,
25
systolic: 3.137,
26
hscrp: 0.18,
27
cholesterol: 1.382,
28
hdlc: -1.172,
29
smoker: 0.818,
30
}
31
: {
32
age: 4.385,
33
systolic: 2.607,
34
hscrp: 0.102,
35
cholesterol: 0.963,
36
hdlc: -0.772,
37
smoker: 0.405,
38
};
39
// assign b variables
40
const b1 = params.age * (gender == "female" ? age : Math.log(age)),
41
b2 = params.systolic * Math.log(systolicBloodPressure),
42
b3 = params.hscrp * Math.log(hscrp),
43
b4 = params.cholesterol * Math.log(cholesterol),
44
b5 = params.hdlc * Math.log(hdlc),
45
b6 = params.smoker * (smoking == false ? 0 : 1);
46
const B = b1 + b2 + b3 + b4 + b5 + b6;
47
// calculate the score based on gender
48
let result =
49
gender == "female"
50
? (1 - Math.pow(0.98634, Math.exp(B - 22.325))) * 100
51
: (1 - Math.pow(0.899, Math.exp(B - 33.097))) * 100;
52
// precision rounding
53
Math.round(
54
result < 10
55
? (result = result.toPrecision(1))
56
: (result = result.toPrecision(2))
57
);
58
return [result, getRiskIndicator(result)];
59
}
60
​
61
/**
62
*
63
* @param {*} patient - a fhir4 patient
64
* @returns {number} - the age of the patient
65
*/
66
export function getAge(patient) {
67
const ageDate = new Date(Date.now() - new Date(patient.birthDate).getTime());
68
return Math.abs(ageDate.getUTCFullYear() - 1970);
69
}
70
​
71
/**
72
*
73
* @param {*} patient -a fhir patient
74
* @returns {string} - the patients gender (male | female)
75
*/
76
export function getGender(patient) {
77
return patient.gender;
78
}
79
​
80
/**
81
*
82
* @param {*} value - a fhir4 bundle for blood pressure
83
* @returns {number} - the numerical value of the measurement. There are two components
84
* to the blood pressure and we want the systolic blood pressure, or the
85
* first item ([0])
86
*/
87
export function getBloodPressure(value) {
88
return value.entry[0].resource.component[0].valueQuantity.value;
89
}
90
​
91
/**
92
*
93
* @param {*} value - a fhir4 bundle for hscrp
94
* @returns {number} - the observation value
95
*/
96
export function getHscrp(value) {
97
if (value.entry[0].resource.valueQuantity.unit === "mg/L") {
98
return value.entry[0].resource.valueQuantity.value;
99
} else if (value.entry[0].resource.valueQuantity.unit === "mmol/L") {
100
return value.entry[0].resource.valueQuantity.value / 0.1;
101
}
102
throw `Unanticipated units for Hscrp: ${value.entry[0].resource.valueQuantity.unit}`;
103
}
104
​
105
/**
106
*
107
* @param {*} value - a fhir4 bundle for cholesterol or hdl, as they use the same calculation
108
* @returns {number} - the observation value
109
*/
110
export function getCholesterolAndHdl(value) {
111
if (value.entry[0].resource.valueQuantity.unit === "mg/dL") {
112
return value.entry[0].resource.valueQuantity.value;
113
} else if (value.entry[0].resource.valueQuantity.unit === "mmol/L") {
114
return value.entry[0].resource.valueQuantity.value / 0.026;
115
}
116
throw `Unanticipated cholesterol units: ${value.entry[0].resource.valueQuantity.unit}`;
117
}
118
​
119
/**
120
*
121
* @param {*} value - a fhir4 bundle for the smoking status of the patient
122
* @returns {number} - the smoking status of the observation (true or false)
123
*/
124
export function getSmokingStatus(value) {
125
if (
126
value.entry[0].resource.valueCodeableConcept.coding[0].display ==
127
"Never smoker"
128
) {
129
return false;
130
} else {
131
return true;
132
}
133
}
134
​
135
/**
136
*
137
* @param {number} riskScore - the reynolds risk score numbered 0-100
138
* @returns {string} - card indicator color based on risk score ranges
139
*/
140
function getRiskIndicator(riskScore) {
141
/**
142
* based on "Low", "Moderate", "Moderate-high", "High" risk scores
143
* outlined here: https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0044347
144
*/
145
if (riskScore < 6) {
146
return "info";
147
}
148
if (riskScore >= 6 && riskScore < 10) {
149
return "info";
150
}
151
if (riskScore >= 10 && riskScore < 20) {
152
return "warning";
153
}
154
return "critical";
155
}
156
​
157
/**
158
*
159
* @param {number} riskScore - the reynolds risk score
160
* @returns {string} - low, moderate, moderate-high, and high risk score
161
*/
162
export function riskThreshold(riskScore) {
163
const risk = ["low", "moderate", "moderate-high", "high"];
164
if (riskScore < 6) {
165
return risk[0];
166
}
167
if (riskScore >= 6 && riskScore < 10) {
168
return risk[1];
169
}
170
if (riskScore >= 10 && riskScore < 20) {
171
return risk[2];
172
}
173
return risk[3];
174
}
175
​
Copied!
The first function calculates the risk score based on the patient's gender. The functions that follow, like the helper functions in util.js in the previous section, gather the required observation measurements from the bundles returned from one of the five prefetch tokens.
Import these functions into suggestions-links-fhir.js.
suggestions-links-fhir.js
1
import {
2
reynoldsRiskScore,
3
getAge,
4
getGender,
5
getBloodPressure,
6
getHscrp,
7
getCholesterolAndHdl,
8
getSmokingStatus,
9
riskThreshold,
10
} from "./util.js";
Copied!
The risk score calculation also includes values based on observations of family history of heart disease, as well as a hemoglobin A1c. In this walkthrough, for the sake of simplicity, these two values were omitted.

Changing card properties based on the risk score

The Card class has an indicator parameter whose value can either be info, warning, or critical. Up until this point we've only used the info value, but in this example we are going to change this value dynamically based on the risk score value.
Take a look at getRiskIndicator() inside of util.js. Based on the value of the risk score, the function returns either info, warning, or critical based on where the risk score falls in the threshold [3][4]. When we view the card response inside of Logica and the card's indicator is set to warning, or critical, some of the card's elements will be changed to yellow or red respectively, indicating more immediate levels of urgency.
These thresholds are based on literature that determined the thresholds by doing studies that involved female participants only. In this walkthrough, for the sake of simplicity, we use the same thresholds for both men and women.
Similarly, if the risk score falls into the warning threshold, we are going to send the user the risk score, a suggestion that the user be considered for a prescription for aspirin, an absolute link for more information on the medication, and a smart link to a SMART app that let's the user interactively work with the results of the risk score.
Finally, f the risk score falls into the critical threshold, we are going to send the user the same set of information, but with an additional suggestion that will suggest the user be prescribed Xarelto, which is an anticoagulant. In addition, the absolute link will take the user to a page with more information on the medication.

Data.js

Depending on the type of action, that action might need to include a FHIR resource. For example, if we wanted our action to prescribe a new medication, our action would need a resource attribute, whose value is a FHIR resource bundle. The type of this resource would be a MedicationRequest. A similar process would be needed if we wanted to perform an update action. If, and only if, the type of action was a delete action, we would not need a resource attribute.
This service's two actions will both, when taken, request that a medication be prescribed. They are both create actions, and thus will need corresponding FHIR resource bundles. Both actions will be put in an object called suggestionData inside of a file called data.js.
Create a new file called data.js and paste in the following code.
data.js
1
export const suggestionData = {
2
bloodThinner: {
3
label: "Create a prescription for Rivaroxaban (Xarelto) 10 MG",
4
actions: [
5
{
6
type: "create",
7
description: "Create a prescription for Rivaroxaban (Xarelto) 10 MG",
8
resource: {
9
resourceType: "MedicationRequest",
10
id: "3ba900b2-a795-40a0-8aae-1cfbb02e3ac1",
11
status: "active",
12
intent: "order",
13
medicationCodeableConcept: {
14
coding: [
15
{
16
system: "http://www.nlm.nih.gov/research/umls/rxnorm",
17
code: "429503",
18
display: "Rivaroxaban 10 MG",
19
},
20
],
21
text: "Rivaroxaban 10 MG",
22
},
23
subject: {
24
reference: "urn:uuid:b626136e-aff8-4711-8279-536f07f197b5",
25
},
26
encounter: {
27
reference: "urn:uuid:1d05e39c-e269-438c-a9b2-1a485953a2c8",
28
},
29
authoredOn: "1960-10-23T22:19:43-04:00",
30
requester: {
31
reference: "urn:uuid:0000016d-3a85-4cca-0000-00000000c5b2",
32
display: "Dr. Susan A Clark",
33
},
34
reasonReference: [
35
{
36
reference: "urn:uuid:f810df60-74b0-4745-8fb5-cfe7e4c84a1e",
37
},
38
],
39
},
40
},
41
],
42
request: {
43
method: "POST",
44
url: "MedicationRequest",
45
},
46
},
47
aspirin: {
48
label: "Create a prescription for Aspirin 80 MG oral Tablet",
49
actions: [
50
{
51
type: "create",
52
description: "Create a prescription for Aspirin 80 MG Oral Tablet",
53
resource: {
54
resourceType: "MedicationRequest",
55
id: "16401a10-e311-4287-9986-3988f81b3d7e",
56
status: "active",
57
intent: "order",
58
medicationCodeableConcept: {
59
coding: [
60
{
61
system: "http://www.nlm.nih.gov/research/umls/rxnorm",
62
code: "429503",
63
display: "Aspirin 80 MG",
64
},
65
],
66
text: "Aspirin 80 MG",
67
},
68
subject: {
69
reference: "urn:uuid:b626136e-aff8-4711-8279-536f07f197b5",
70
},
71
encounter: {
72
reference: "urn:uuid:1d05e39c-e269-438c-a9b2-1a485953a2c8",
73
},
74
authoredOn: "1960-10-23T22:19:43-04:00",
75
requester: {
76
reference: "urn:uuid:0000016d-3a85-4cca-0000-00000000c5b2",
77
display: "Dr. Susan A Clark",
78
},
79
reasonReference: [
80
{
81
reference: "urn:uuid:f810df60-74b0-4745-8fb5-cfe7e4c84a1e",
82
},
83
],
84
},
85
},
86
],
87
request: {
88
method: "POST",
89
url: "MedicationRequest",
90
},
91
},
92
};
Copied!
Finally, import data.js inside of suggestions-links-fhir.js.
suggestions-links-fhir.js
1
import { suggestionData } from "./data.js";
Copied!
For this example, the FHIR resource bundles serve as placeholders. They should not be used in production.

Service handler

As previously mentioned, this service will respond with different sets of cards depending on the risk score.
  • If the patient's risk score is less than 10, the service will respond with cards displaying the risk score, and the link to the smart app.
  • If the patient's risk score is between 10 and 20, the service will respond with cards displaying the risk score, a suggestion that aspirin be prescribed, a link to more information about aspirin, and the link to the smart app.
  • If the patient's risk score is more than 20, the service will respond with cards displaying the risk score, a suggestion that xarelto be prescribed, a link to more information about xarelto, and the link to the smart app.
Below is the final result of the request handler.
suggestions-links-fhir.js
1
const handler = async (request) => {
2
const context = request.context;
3
const data = request.prefetch;
4
const age = getAge(data.patient);
5
const gender = getGender(data.patient);
6
const systolic = getBloodPressure(data.bloodPressure);
7
const hscrp = getHscrp(data.hscrp);
8
const cholesterol = getCholesterolAndHdl(data.cholesterolMassOverVolume);
9
const hdlc = getCholesterolAndHdl(data.hdl);
10
const smokingStatus = getSmokingStatus(data.smokingStatus);
11
const riskScore = reynoldsRiskScore(
12
age,
13
gender,
14
systolic,
15
hscrp,
16
cholesterol,
17
hdlc,
18
smokingStatus
19
);
20
const riskThresholdString = riskThreshold(riskScore[0]);
21
/**
22
* Defining the cards.
23
* This assumes that the reynolds risk score card will be sent every time
24
*/
25
let cards = [
26
new Card({
27
detail: `More information on this score:`,
28
source: {
29
label: "Reynold's Risk Score",
30
url: "https://pubmed.ncbi.nlm.nih.gov/17299196/",
31
},
32
summary: `Reynolds risk score: ${riskScore[0]}`,
33
indicator: riskScore[1],
34
links: [
35
{
36
label: "Launch cardiac health SMART app",
37
url: "https://smart-cardiac-risk.fly.dev/launch.html",
38
type: "smart",
39
},
40
],
41
}),
42
];
43
/**
44
* Next, handle adding suggestions
45
*/
46
if (riskScore[1] === "warning" || riskScore[1] === "critical") {
47
cards.push(
48
new Card({
49
detail: `This patient has a ${riskThresholdString} risk of cardiovascular disease over the next 10 years. ${
50
riskScore[1] === "warning"
51
? "Consider prescribing an anti-inflammatory like aspirin."
52
: "Consider prescribing an anti-inflammatory like aspirin, or even a blood thinner like Xarelto."
53
} `,
54
source: {
55
label: "Automate Medical, Inc.",
56
url: "https://www.automatemedical.com/",
57
topic: {
58
display: "Medication alert",
59
version: "0.0.1",
60
},
61
},
62
indicator: riskScore[1],
63
summary: "Medication alert",
64
suggestions:
65
riskScore[1] === "warning"
66
? [suggestionData.aspirin]
67
: [suggestionData.aspirin, suggestionData.bloodThinner],
68
selectionBehavior: "any",
69
links:
70
riskScore[1] === "warning"
71
? [
72
{
73
label: "More information on aspirin",
74
url: "https://medlineplus.gov/druginfo/meds/a682878.html",
75
type: "absolute",
76
},
77
]
78
: [
79
{
80
label: "More information on aspirin",
81
url: "https://medlineplus.gov/druginfo/meds/a682878.html",
82
type: "absolute",
83
},
84
{
85
label: "More information on blood thinners",
86
url: "https://medlineplus.gov/bloodthinners.html",
87
type: "absolute",
88
},
89
],
90
})
91
);
92
}
93
// return the set of cards
94
return {
95
cards: cards,
96
};
97
};
Copied!
Export the service.
suggestions-links-fhir.js
1
export default new Service(options, handler);
Copied!

Running the API

In index.js, import the service.
index.js
1
import { Http, CDSHooks, start } from "@sero.run/sero";
2
​
3
import compareTimeService from "./current-time/current-time.js";
4
import prefetchContext from "./prefetch-context/prefetch-context.js";
5
import suggestionsLinksFhir from "./suggestions-links-fhir/suggestions-links-fhir.js";
6
​
7
const config = {
8
cdsHooks: {
9
services: [compareTimeService, prefetchContext, suggestionsLinksFhir],
10
cors: true,
11
},
12
};
13
​
14
const http = Http(config);
15
CDSHooks(config, http);
16
start(http);
Copied!
Run the server with npm run start.

Deployment

Registering the SMART app in Logica

Logica has the ability to register SMART apps into its interface, which can then be launched within the context of one of the CDS hooks. When we register a SMART app, we can use the EHRs that Logica creates to view patient data within the scope of the SMART app.
In Logica, there is a section where we can register SMART applications. Doing this allows us to take the EHRs that Logica generated for us, and pass that information directly to the SMART app for testing purposes. Because the service we made launches this SMART app, we are going to configure Logica to launch it.
On the left sidebar, select "Apps," and then select the "+" in the upper right. Click on "Manually" to set up the application manually.
SMART app configuration page
Enter the following details into the fields.
The image field can be left blank, or one can be added locally. After clicking save, this will register the cardiac risk smart app inside of Logica. Inside of one of the CDS cards that our service returns, we are going to launch this SMART app in order to provide extra context the calculated risk score.

Calling the API

To re-fresh the list of CDS services, head to the "CDS Hooks" section in Logica, and delete the services you registered.
Deleting the API from Logica
This might need to be done form time to time to re-initialize our API inside of Logica. Now, re-add the API and you should see the list of all three services.
The three services we made registered in Logica
Click on the "patient view with Reynolds risk score" service. To view the three indicator thresholds, and the three risk levels, to two of which we make a MedicationRequest, we need to view three different patients with three different risk scores.
To view the "critical" indicator, and a patient with a high risk score, select "Adams, Daniel X." This will produce the following set of cards.
Daniel's risk score is marked as critical
Here we see five buttons: three links, and two suggestions. From the top - the first button is a smart link that launches the cardiac health SMART app that we registered. Below that is a pair of suggestions, with the action of prescribing 10 MG of Xarelto, and 80MG of Aspirin. The final two buttons are absolute links that takes us to an external link to learn more about blood the suggestion medication prescription.
After selecting the SMART app, we should be directed to an application screen that looks like this.
4-step cardiology report
The SMART app serves to provide more context and enhanced functionality to the decision support of the service. SMART apps are especially useful if information would be hard to convey with cards alone.
If we re-launch this service with the patient "Alexis, Aaron," we should see the following set of cards.
Aaron's risk score is marked as low
If we we-launch the service with the patient "Coleman, Steven F," we should see the following card set.
Steven's risk score is marked as medium-high
This set of cards is relatively similar to the cards sent in response to viewing Daniel's EHR, but is colored yellow, signifying it is less mild. It also suggests an action where the user could prescribe Aspirin instead of Xarelto, and sends an absolute link that sends the user to a page where they can learn more about aspirin.

Conclusion

Congratulations! You have reached the end of this walkthrough. Check out some other resources below.

References

[1] Ridker, P. M., Paynter, N. P., Rifai, N., Gaziano, J. M., & Cook, N. R. (2008). C-reactive protein and parental history improve global cardiovascular risk prediction: the Reynolds Risk Score for men. Circulation, 118(22), 2243–2251. https://doi.org/10.1​
[2] Ridker, P. M., Buring, J. E., Rifai, N., & Cook, N. R. (2007). Development and validation of improved algorithms for the assessment of global cardiovascular risk in women: the Reynolds Risk Score. JAMA, 297(6), 611–619. https://doi.org/10.1001/jama.297.6.611​
[3] Pasternak RC, Abrams J, Greenland P, Smaha LA, Wilson PW, et al. (2003) 34th Bethesda Conference: Task force #1–Identification of coronary heart disease risk: is there a detection gap? J Am Coll Cardiol 41: 1863–1874.
[4] Greenland P, Smith SC Jr, Grundy SM (2001) Improving coronary heart disease risk assessment in asymptomatic people: role of traditional risk factors and noninvasive cardiovascular tests. Circulation 104: 1863–1867.
This walkthrough is for educational purposes only. Although it is designed with a degree of medical accuracy, this is purely to enhance the experience. The code in this walkthrough is not suitable for use in a real-life medical situation or environment.