Skip to content

Commit

Permalink
initial commit of v1
Browse files Browse the repository at this point in the history
  • Loading branch information
Carl Anderson committed Nov 19, 2019
1 parent f01777e commit e8c3414
Show file tree
Hide file tree
Showing 8 changed files with 3,222 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
78 changes: 76 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,76 @@
# demo-auc-app
A demonstration SMART app that serves AUC external guidance for PAMA
# smart-auc-demo
A demonstration SMART app that serves AUC external guidance for PAMA.

Instructions
------------

```sh
npm install
npm run demo
```

Visit http://localhost:3001/ to log in (use any username and password)

<img src="./images/login.png" alt="alt text" width="256">

Select a patient age, gender, indication, and order to determine whether it
is within recommended guidelines.

<div>
<form action="evaluate" method="POST">
<table>
<caption>Enter Patient Demographics</caption>
<tr>
<th><label for="gender">Gender</label></th>
<td>
<select name="gender" required>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
</td>
</tr>
<tr>
<th><label for="age">Age</label></th>
<td><input name="age" required></td>
</tr>
<tr>
<th><label for="indication">Indication</label></th>
<td>
<input list="indications" name="indication" required>
<datalist id="indications">
<option value="13213009">congenital heart disease</option>
<option value="25064002">headache</option>
<option value="279039007">lower back pain</option>
<option value="423341008">optic disc edema</option>
<option value="27355003">toothache</option>
</datalist>
</td>
</tr>
<tr>
<th><label for="procedure">Procedure</label></th>
<td>
<input list="procedures" name="procedure">
<datalist id="procedures">
<option value="75561">cardiac mri</option>
<option value="70450">ct scan - no contrast material</option>
<option value="71275">cta - with contrast material</option>
<option value="72133">lumbar spine ct scan</option>
<option value="70544">mra - head</option>
</datalist>
</td>
</tr>
</table>
<input type="submit">
</form>
</div>

V1 TODO
-------

- [x] Mock a simple UI on paper / in Visio
- [x] Create a static page form to serve as the starting point
- [x] Fake sign-in
- [x] Enter Demographics (age and gender)
- [x] Select a condition
- [x] Select an appropriate imaging procedure
Binary file added images/login.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>Demo AUC Guidance App</title>
</head>

<body>
<div>
<form action="evaluate" method="POST">
<table>
<caption>Enter Patient Demographics</caption>
<tr>
<th><label for="gender">Gender</label></th>
<td>
<select name="gender" required>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
</td>
</tr>
<tr>
<th><label for="age">Age</label></th>
<td><input name="age" required></td>
</tr>
<tr>
<th><label for="indication">Indication</label></th>
<td>
<input list="indications" name="indication" required>
<datalist id="indications">
<option value="13213009">congenital heart disease</option>
<option value="25064002">headache</option>
<option value="279039007">lower back pain</option>
<option value="423341008">optic disc edema</option>
<option value="27355003">toothache</option>
</datalist>
</td>
</tr>
<tr>
<th><label for="procedure">Procedure</label></th>
<td>
<input list="procedures" name="procedure">
<datalist id="procedures">
<option value="75561">cardiac mri</option>
<option value="70450">ct scan - no contrast material</option>
<option value="71275">cta - with contrast material</option>
<option value="72133">lumbar spine ct scan</option>
<option value="70544">mra - head</option>
</datalist>
</td>
</tr>
</table>
<input type="submit">
</form>
</div>
</body>

</html>
114 changes: 114 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"use strict";
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const path = require('path');
const port = process.env.PORT || '3001';
const login = `<a href="http://localhost:${port}/">login</a>`;

const CPT = {
_FHIR_CODING_SYSTEM: 'http://www.ama-assn.org/go/cpt',
CARDIAC_MRI: '75561',
CT_HEAD_NO_CONTRAST: '70450',
CTA_WITH_CONTRAST: '71275',
LUMBAR_SPINE_CT: '72133',
MRA_HEAD: '70544',
};

const SNOMED = {
_FHIR_CODING_SYSTEM: 'http://snomed.info/sct',
CONGENITAL_HEART_DISEASE: '13213009',
HEADACHE: '25064002',
LOW_BACK_PAIN: '279039007',
OPTIC_DISC_EDEMA: '423341008',
TOOTHACHE: '27355003'
};

class Reasons {
static covers(subset, set) {
if (subset.size > set.size) {
return false;
}
for (const member of subset) {
if (!set.has(member)) {
return false;
}
}
return true;
}

constructor(appropriate, notAppropriate) {
this.appropriate = appropriate.map(x => new Set(x));
this.notAppropriate = notAppropriate.map(x => new Set(x));
}

getRating(reasons) {
if (this.appropriate.filter(s => Reasons.covers(s, reasons)).length) {
return 'appropriate';
}
if (this.notAppropriate.filter(s => Reasons.covers(s, reasons)).length) {
return 'not-appropriate';
}
return 'no-guidelines-apply';
}
}

const cptReasons = {
'no-procedures-for': new Reasons([[SNOMED.TOOTHACHE]], []),
[CPT.CT_HEAD_NO_CONTRAST]: new Reasons([[SNOMED.HEADACHE, SNOMED.OPTIC_DISC_EDEMA]], []),
[CPT.MRA_HEAD]: new Reasons([], []),
[CPT.CTA_WITH_CONTRAST]: new Reasons([], [[SNOMED.CONGENITAL_HEART_DISEASE]]),
[CPT.LUMBAR_SPINE_CT]: new Reasons([], [[SNOMED.LOW_BACK_PAIN]]),
[CPT.CARDIAC_MRI]: new Reasons([[SNOMED.CONGENITAL_HEART_DISEASE]], []),
};

const app = express();
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true
}));
app.use(bodyParser.urlencoded({extended : true}));
app.use(bodyParser.json());

app.get('/', function(request, response) {
if (request.session.loggedin) {
response.redirect('/consult');
} else {
response.sendFile(path.join(__dirname + '/login.html'));
}
});

app.get('/consult', function(request, response) {
if (request.session.loggedin) {
response.sendFile(path.join(__dirname + '/index.html'));
} else {
response.send(`Please ${login} to view this page!`).end();
}
});

app.post('/evaluate', function(request, response) {
if (!request.session.loggedin) {
response.status(404).send(`You must ${login} to access this.`);
} else {
const reasons = cptReasons[request.body.procedure];
const rating = reasons.getRating(new Set([request.body.indication]));
response.status(200).send(rating);
}
response.end();
});

app.post('/auth', function(request, response) {
const username = request.body.username;
const password = request.body.password;
if (username && password) {
request.session.loggedin = true;
request.session.username = username;
response.redirect('/');
} else {
response.send('Please enter Username and Password!');
}
response.end();
});

app.listen(port);
49 changes: 49 additions & 0 deletions login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!DOCTYPE html>
<!-- See: https://codeshack.io/basic-login-system-nodejs-express-mysql/ -->
<html>
<head>
<meta charset="utf-8">
<title>Login</title>
<style>
.login-form {
width: 300px;
margin: 0 auto;
font-family: Tahoma, Geneva, sans-serif;
}
.login-form h1 {
text-align: center;
color: #4d4d4d;
font-size: 24px;
padding: 20px 0 20px 0;
}
.login-form input[type="password"],
.login-form input[type="text"] {
width: 100%;
padding: 15px;
border: 1px solid #dddddd;
margin-bottom: 15px;
box-sizing:border-box;
}
.login-form input[type="submit"] {
width: 100%;
padding: 15px;
background-color: #535b63;
border: 0;
box-sizing: border-box;
cursor: pointer;
font-weight: bold;
color: #ffffff;
}
</style>
</head>
<body>
<div class="login-form">
<h1>Login</h1>
<form action="auth" method="POST">
<input type="text" name="username" placeholder="Username" required>
<input type="password" name="password" placeholder="Password" required>
<input type="submit">
</form>
</div>
</body>
</html>
Loading

0 comments on commit e8c3414

Please sign in to comment.