Skip to content

Commit

Permalink
improve bundle data and ehr emulator
Browse files Browse the repository at this point in the history
  • Loading branch information
Rost-is-love committed Dec 2, 2024
1 parent 7c7bfe9 commit 0e144a3
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 136 deletions.
4 changes: 2 additions & 2 deletions smart-app-launch/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ docker compose up
```

aidbox - http://localhost:8080
keycloak - http://localhost:88888
keycloak - http://localhost:8888
growth-chart - http://localhost:9000

## EHR launch

### Patient launch

Open http://localhost:7070 (ehr emulator)
Open http://localhost:7070/ehr.html (ehr emulator)


Need launch uri
Expand Down
56 changes: 55 additions & 1 deletion smart-app-launch/aidbox.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"type": "transaction",
"type": "batch",
"entry": [
{
"request": {
Expand Down Expand Up @@ -27,6 +27,41 @@
]
}
},
{
"request": {
"method": "PUT",
"url": "/Client/ehr"
},
"resource": {
"id": "ehr",
"secret": "verysecret",
"grant_types": [
"basic"
],
"resourceType": "Client"
}
},
{
"request": {
"method": "PUT",
"url": "/AccessPolicy/demo-clients-allow"
},
"resource": {
"id": "demo-clients-allow",
"link": [
{
"id": "ehr",
"resourceType": "Client"
},
{
"id": "growth_chart",
"resourceType": "Client"
}
],
"engine": "allow",
"resourceType": "AccessPolicy"
}
},
{
"request": {
"method": "PUT",
Expand All @@ -51,6 +86,25 @@
"token_endpoint": "http://keycloak:8888/realms/patients/protocol/openid-connect/token",
"userinfo-source": "id-token"
}
},

{
"request": {
"method": "POST",
"url": "/Patient/$load"
},
"resource": {
"source": "https://storage.googleapis.com/aidbox-public/synthea/v2/100/fhir/Patient.ndjson.gz"
}
},
{
"request": {
"method": "POST",
"url": "/Observation/$load"
},
"resource": {
"source": "https://storage.googleapis.com/aidbox-public/synthea/v2/100/fhir/Observation.ndjson.gz"
}
}
]
}
12 changes: 9 additions & 3 deletions smart-app-launch/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,17 @@ services:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
command: start-dev --import-realm

ehr:
image: nginx:alpine
ports:
- "7070:80"
volumes:
- ./ehr.html:/usr/share/nginx/html/ehr.html:ro

aidbox_db:
image: healthsamurai/aidboxdb:17
pull_policy: always
volumes:
- aidbox_pg_data:/data:delegated
environment:
Expand All @@ -36,6 +44,7 @@ services:

aidbox:
image: healthsamurai/aidboxone:edge
pull_policy: always
depends_on:
- aidbox_db
ports:
Expand All @@ -44,17 +53,14 @@ services:
- ./aidbox.json:/tmp/aidbox.json
environment:
BOX_INIT_BUNDLE: file:///tmp/aidbox.json
AIDBOX_TERMINOLOGY_SERVICE_BASE_URL: https://tx.fhir.org/r4
AIDBOX_FHIR_PACKAGES: hl7.fhir.r4.core#4.0.1
AIDBOX_FHIR_SCHEMA_VALIDATION: true
AIDBOX_CREATED_AT_URL: https://aidbox.app/ex/createdAt
AIDBOX_CLIENT_SECRET: vbDPgXnz0H
AIDBOX_CORRECT_AIDBOX_FORMAT: true
AIDBOX_ADMIN_PASSWORD: password
AIDBOX_COMPLIANCE: enabled
BOX_SEARCH_FHIR__COMPARISONS: true
PGHOST: aidbox_db
BOX_COMPATIBILITY_VALIDATION_JSON__SCHEMA_REGEX: '#{:fhir-datetime}'
PGUSER: aidbox
AIDBOX_PORT: 8080
PGDATABASE: aidbox
Expand Down
214 changes: 84 additions & 130 deletions smart-app-launch/ehr.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,152 +7,106 @@

<h1>EHR Emulator</h1>

<form id="loginForm">
<h2>Login</h2>
<label>
Username: <input type="text" id="username" required />
</label><br/>
<label>
Password: <input type="password" id="password" required />
</label><br/>
<button type="submit">Login</button>
</form>
<div id="patientList" style="display:none;">
<div id="patientList">
<h1>Patient List</h1>
<div id="user-info"></div>
<ul id="patients"></ul>
</div>

<script>
let accessToken = '';
// Client credentials
const client_id = 'ehr';
const client_secret = 'verysecret';

// const username = 'provider';
// accessToken = '';
// showUserInfo(username);
// const fhirUrl = 'http://localhost:8080/fhir/Patient?_count=20&birthdate=ge2000'; // Replace with your FHIR server endpoint
// fetch(fhirUrl, {method: 'GET', headers: {'Authorization': 'Bearer ' + accessToken}})
// .then(response => response.json())
// .then(fhirData => {displayPatients(fhirData);})
// .catch(error => {console.error('Error fetching patients:', error);});
// Encode credentials in Base64
const credentials = btoa(client_id + ':' + client_secret);

document.getElementById('loginForm').addEventListener('submit', function(e){
e.preventDefault();
// Headers for Basic Authentication
const headers = {
'Authorization': 'Basic ' + credentials
};

const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const client_id = 'ehr'; // Replace with your client_id
// Display client ID as user info
showUserInfo(client_id);

// Token endpoint URL
const tokenUrl = 'http://localhost:8080/auth/token'; // Replace with your token endpoint
// FHIR API endpoint
const fhirUrl = 'http://localhost:8080/fhir/Patient?_count=20&birthdate=ge2000';

// Prepare data for token request
const data = new URLSearchParams();
data.append('grant_type', 'password');
data.append('username', username);
data.append('password', password);
data.append('client_id', client_id);
// data.append('client_secret', client_secret);
// Fetch patients using Basic Auth
fetch(fhirUrl, {
method: 'GET',
headers: headers
})
.then(response => response.json())
.then(fhirData => {
displayPatients(fhirData);
})
.catch(error => {
console.error('Error fetching patients:', error);
});

function showUserInfo(username) {
const userInfo = document.getElementById('user-info');
userInfo.innerHTML = 'Using client ID: ' + username;
}

// Request an access token
fetch(tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: data.toString()
})
.then(response => response.json())
.then(tokenData => {
if(tokenData.access_token){
showUserInfo(username);
accessToken = tokenData.access_token;
// Use the access token to fetch patients
const fhirUrl = 'http://localhost:8080/fhir/Patient?_count=20&birthdate=ge2000';
fetch(fhirUrl, {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + tokenData.access_token
}
})
.then(response => response.json())
.then(fhirData => {
displayPatients(fhirData);
})
.catch(error => {
console.error('Error fetching patients:', error);
});
} else {
console.error('Authentication failed:', tokenData);
alert('Authentication failed: ' + (tokenData.error_description || 'Unknown error'));
}
})
.catch(error => {
console.error('Error during authentication:', error);
});
});
function launch(patientId) {
const fhirUrl = 'http://localhost:8080/rpc';
let body = {
method: "aidbox.smart/get-launch-uri",
params: {
user: "practitioner",
iss: "http://localhost:8080/fhir",
client: "growth_chart",
ctx: { patient: patientId }
}
};
fetch(fhirUrl, {
method: 'POST',
headers: {
'Authorization': 'Basic ' + credentials,
'Content-Type': 'application/json'
},
body: JSON.stringify(body)
})
.then(response => response.json())
.then(resp => {
window.location = resp.result.uri;
})
.catch(error => {
console.error('Error launching app:', error);
});
}

function showUserInfo(username) {
const userInfo = document.getElementById('user-info');
userInfo.innerHTML = 'You logged in as ' + username;
}
function displayPatients(fhirData){
const patientList = document.getElementById('patientList');
patientList.style.display = 'block';

function launch(token, patientId) {
const fhirUrl = 'http://localhost:8080/rpc';
let body = {
method: "aidbox.smart/get-launch-uri",
params: {
user: "practitioner",
iss: "http://localhost:8080/fhir",
client: "growth_chart",
ctx: {patient: patientId}
}
}
fetch(fhirUrl, {
method: 'POST',
headers: {'Authorization': 'Bearer ' + token,
'content-type': 'application/json'},
body: JSON.stringify(body)
})
.then(response => response.json())
.then(resp => {
// console.log("resp", resp)
window.location = resp.result.uri
})
.catch(error => {
console.error('Error fetching patients:', error);
});
}
const patientsUl = document.getElementById('patients');
patientsUl.innerHTML = '';

function displayPatients(fhirData){
document.getElementById('loginForm').style.display = 'none';
document.getElementById('patientList').style.display = 'block';

const patientsUl = document.getElementById('patients');
patientsUl.innerHTML = '';

if(fhirData && fhirData.entry){
fhirData.entry.forEach(function(entry){
const patient = entry.resource;
let name = 'Unknown';
let id = patient.id;
if(patient.name && patient.name.length > 0){
const nameObj = patient.name[0];
const given = nameObj.given ? nameObj.given.join(' ') : '';
const family = nameObj.family || '';
const birthDate = patient.birthDate || '';
name = `${given} ${family} (${birthDate}) <span style="color: blue" onclick="launch('${accessToken}', '${id}')">Launch Growth chart app</span>`;
}
const li = document.createElement('li');
li.innerHTML = name;
patientsUl.appendChild(li);
});
} else {
const li = document.createElement('li');
li.textContent = 'No patients found.';
patientsUl.appendChild(li);
}
}
if(fhirData && fhirData.entry){
fhirData.entry.forEach(function(entry){
const patient = entry.resource;
let name = 'Unknown';
let id = patient.id;
if(patient.name && patient.name.length > 0){
const nameObj = patient.name[0];
const given = nameObj.given ? nameObj.given.join(' ') : '';
const family = nameObj.family || '';
const birthDate = patient.birthDate || '';
name = `${given} ${family} (${birthDate})`;
}
const li = document.createElement('li');
li.innerHTML = `${name} <span style="color: blue; cursor: pointer;" onclick="launch('${id}')">Launch Growth Chart App</span>`;
patientsUl.appendChild(li);
});
} else {
const li = document.createElement('li');
li.textContent = 'No patients found.';
patientsUl.appendChild(li);
}
}
</script>
</body>
</html>

0 comments on commit 0e144a3

Please sign in to comment.