Skip to content

Commit de9c5df

Browse files
author
mounika
committed
update
1 parent 98a3694 commit de9c5df

3 files changed

Lines changed: 5207 additions & 202 deletions

File tree

src/app.py

Lines changed: 78 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,100 @@
1-
import os
21
import requests
3-
from flask import Flask, request, render_template, redirect, url_for, session
2+
import os
3+
from flask import Flask, request, render_template, redirect, url_for
44
from dotenv import load_dotenv
5+
from datetime import datetime
56

67
app = Flask(__name__)
7-
app.secret_key = 'supersecretkey'
8+
9+
FHIR_SERVER_BASE_URL = "http://pwebmedcit.services.brown.edu:9091/fhir"
10+
811
load_dotenv()
912

10-
FHIR_SERVER_BASE_URL = os.getenv("FHIR_SERVER_BASE_URL")
1113
username = os.getenv("FHIR_USERNAME")
1214
password = os.getenv("FHIR_PASSWORD")
1315

14-
def request_patient(patient_id, credentials):
15-
req = requests.get(FHIR_SERVER_BASE_URL + "/Patient/" + str(patient_id), auth=credentials)
16-
if req.status_code == 200:
17-
return req.json()
18-
else:
19-
return None
16+
observation_codes = {
17+
"Systolic Blood Pressure": "8480-6",
18+
"Diastolic Blood Pressure": "8462-4",
19+
"Total Cholesterol": "2093-3",
20+
"HDL Cholesterol": "2085-9",
21+
"LDL Cholesterol": "18262-6"
22+
}
2023

21-
def request_observations(patient_id, credentials):
22-
req = requests.get(FHIR_SERVER_BASE_URL + f"/Observation?patient={patient_id}", auth=credentials)
23-
if req.status_code == 200:
24-
return req.json()
24+
def get_observation_value(entry):
25+
if 'valueQuantity' in entry['resource']:
26+
return entry['resource']['valueQuantity']['value']
27+
elif 'valueString' in entry['resource']:
28+
return entry['resource']['valueString']
2529
else:
26-
return None
30+
return 'Not Found'
2731

28-
def extract_patient_data(patient, observations):
29-
data = {}
30-
data['age'] = 2024 - int(patient['birthDate'][:4]) # Simplified age calculation
31-
data['gender'] = patient['gender']
32-
data['race'] = 'unknown' # Default value
33-
34-
for entry in observations.get('entry', []):
35-
resource = entry['resource']
36-
code = resource['code']['coding'][0]['code']
37-
38-
print(f"Processing code: {code}")
39-
40-
value = None
41-
if 'valueString' in resource:
42-
try:
43-
value = float(resource['valueString'])
44-
print(f"Found valueString: {value}")
45-
except ValueError:
46-
print(f"Invalid valueString: {resource['valueString']}")
47-
continue # skip if the valueString is not a valid float
48-
49-
if value is not None:
50-
if code == '2093-3': # Total Cholesterol
51-
data['total_cholesterol'] = value
52-
elif code == '2085-9': # HDL Cholesterol
53-
data['hdl_cholesterol'] = value
54-
elif code == '18262-6': # LDL Cholesterol
55-
data['ldl_cholesterol'] = value
56-
elif code == '8480-6': # Systolic Blood Pressure
57-
data['systolic_bp'] = value
58-
elif code == '8462-4': # Diastolic Blood Pressure
59-
data['diastolic_bp'] = value
32+
def get_latest_observation(entries):
33+
if not entries:
34+
return 'Not Found'
35+
latest_entry = max(entries, key=lambda e: e['resource']['effectiveDateTime'])
36+
return get_observation_value(latest_entry)
6037

61-
print(f"Extracted data: {data}")
62-
return data
38+
def calculate_age(birth_date):
39+
birth_date = datetime.strptime(birth_date, '%Y-%m-%d')
40+
today = datetime.today()
41+
return today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
6342

64-
@app.route('/', methods=['GET', 'POST'])
65-
def index():
66-
if request.method == 'POST':
67-
patient_id = request.form.get('patient_id')
68-
session['patient_id'] = patient_id
69-
credentials = (username, password)
70-
patient = request_patient(patient_id, credentials)
71-
observations = request_observations(patient_id, credentials)
43+
def get_patient_demographics(patient_id, credentials):
44+
response = requests.get(FHIR_SERVER_BASE_URL + f"/Patient/{patient_id}", auth=credentials)
45+
if response.status_code == 200:
46+
patient = response.json()
47+
birth_date = patient['birthDate']
48+
age = calculate_age(birth_date)
49+
sex = patient['gender']
50+
race = next((extension['valueCodeableConcept']['text']
51+
for extension in patient.get('extension', [])
52+
if extension['url'] == 'http://hl7.org/fhir/StructureDefinition/us-core-race'), 'Not Found')
53+
return {'age': age, 'sex': sex, 'race': race}
54+
else:
55+
return {'age': 'Not Found', 'sex': 'Not Found', 'race': 'Not Found'}
7256

73-
if patient and observations:
74-
patient_data = extract_patient_data(patient, observations)
75-
session['patient_data'] = patient_data
57+
def get_patient_observations(patient_id, credentials):
58+
observations = {}
59+
demographics = get_patient_demographics(patient_id, credentials)
60+
for obs_name, code in observation_codes.items():
61+
response = requests.get(FHIR_SERVER_BASE_URL + f"/Observation?patient={patient_id}&code={code}", auth=credentials)
62+
if response.status_code == 200:
63+
data = response.json()
64+
if 'entry' in data and data['entry']:
65+
observations[obs_name] = get_latest_observation(data['entry'])
66+
else:
67+
observations[obs_name] = 'Not Found'
7668
else:
77-
session['patient_data'] = {}
78-
return redirect(url_for('ascvd_risk_calculator'))
69+
observations[obs_name] = 'Not Found'
70+
return observations, demographics
7971

80-
return render_template('index.html')
81-
82-
@app.route('/ascvd', methods=['GET', 'POST'])
83-
def ascvd_risk_calculator():
84-
patient_data = session.get('patient_data', {})
85-
result = None
72+
@app.route('/', methods=['GET', 'POST'])
73+
def index():
74+
observations = None
75+
demographics = None
76+
additional_fields = {}
8677

8778
if request.method == 'POST':
88-
try:
89-
age = request.form.get('age', type=int)
90-
gender = request.form.get('gender')
91-
race = request.form.get('race')
92-
systolic_bp = request.form.get('systolic_bp', type=int)
93-
diastolic_bp = request.form.get('diastolic_bp', type=int)
94-
total_cholesterol = request.form.get('total_cholesterol', type=int)
95-
hdl_cholesterol = request.form.get('hdl_cholesterol', type=int)
96-
ldl_cholesterol = request.form.get('ldl_cholesterol', type=int)
97-
diabetes = request.form.get('diabetes') == 'yes'
98-
smoker = request.form.get('smoker') == 'yes'
99-
on_bp_meds = request.form.get('on_bp_meds') == 'yes'
100-
on_statin = request.form.get('on_statin') == 'yes'
101-
on_aspirin = request.form.get('on_aspirin') == 'yes'
102-
refine_risk = request.form.get('refine_risk') == 'yes'
103-
104-
# Use the fetched data if available, otherwise use user input
105-
age = patient_data.get('age', age)
106-
gender = patient_data.get('gender', gender)
107-
race = patient_data.get('race', race)
108-
systolic_bp = patient_data.get('systolic_bp', systolic_bp)
109-
diastolic_bp = patient_data.get('diastolic_bp', diastolic_bp)
110-
total_cholesterol = patient_data.get('total_cholesterol', total_cholesterol)
111-
hdl_cholesterol = patient_data.get('hdl_cholesterol', hdl_cholesterol)
112-
ldl_cholesterol = patient_data.get('ldl_cholesterol', ldl_cholesterol)
113-
114-
if None in [age, gender, race, systolic_bp, diastolic_bp, total_cholesterol, hdl_cholesterol]:
115-
result = 'Incomplete input. Please enter all required fields.'
116-
else:
117-
risk_score = calculate_ascvd_risk(
118-
age, gender, race, systolic_bp, diastolic_bp, total_cholesterol, hdl_cholesterol, ldl_cholesterol,
119-
diabetes, smoker, on_bp_meds, on_statin, on_aspirin, refine_risk
120-
)
121-
result = f"Estimated 10-year ASCVD Risk: {risk_score:.2f}%"
122-
except ValueError:
123-
result = 'Invalid input. Please enter valid values for all fields.'
124-
125-
return render_template('ascvd.html', result=result, patient_data=patient_data)
79+
patient_id = request.form['patient_id']
80+
additional_fields['diabetes'] = request.form.get('diabetes')
81+
additional_fields['smoker'] = request.form.get('smoker')
82+
additional_fields['hypertension'] = request.form.get('hypertension')
83+
additional_fields['statin'] = request.form.get('statin')
84+
additional_fields['aspirin'] = request.form.get('aspirin')
85+
credentials = (username, password)
86+
observations, demographics = get_patient_observations(patient_id, credentials)
87+
# Capture additional inputs from the user
88+
demographics['age'] = request.form.get('age') or demographics['age']
89+
demographics['sex'] = request.form.get('sex') or demographics['sex']
90+
demographics['race'] = request.form.get('race') or demographics['race']
91+
for obs_name in observation_codes.keys():
92+
form_name = obs_name.replace(' ', '_').lower()
93+
observations[obs_name] = request.form.get(form_name) or observations[obs_name]
12694

127-
def calculate_ascvd_risk(age, gender, race, systolic_bp, diastolic_bp, total_cholesterol, hdl_cholesterol, ldl_cholesterol,
128-
diabetes, smoker, on_bp_meds, on_statin, on_aspirin, refine_risk):
129-
# Simplified calculation for demonstration purposes
130-
risk_score = (age * 0.1 + total_cholesterol * 0.2 + hdl_cholesterol * -0.1 +
131-
systolic_bp * 0.2 + diastolic_bp * 0.1 +
132-
(10 if diabetes else 0) + (20 if smoker else 0))
133-
if on_bp_meds:
134-
risk_score += 10
135-
if on_statin:
136-
risk_score += 10
137-
if on_aspirin:
138-
risk_score += 5
139-
if refine_risk:
140-
risk_score *= 0.9 # Example refinement factor
141-
return risk_score
95+
return render_template('index.html', observations=observations, demographics=demographics, additional_fields=additional_fields)
14296

14397
if __name__ == '__main__':
144-
port = int(os.getenv('PORT', 5000))
145-
app.run(debug=True, port=port)
98+
port_str = os.environ['FHIR_PORT']
99+
port_int = int(port_str)
100+
app.run(debug=True, port=port_int)

src/templates/index.html

Lines changed: 126 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,136 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8">
5-
<title>Enter Patient ID</title>
5+
<title>ASCVD Risk Calculator</title>
6+
<script>
7+
function clearPreviousResults() {
8+
document.getElementById('results').innerHTML = '';
9+
}
10+
</script>
11+
<style>
12+
.container {
13+
display: flex;
14+
flex-direction: column;
15+
align-items: center;
16+
gap: 20px;
17+
}
18+
.form-container, .results-container, .input-fields-container {
19+
width: 100%;
20+
max-width: 600px;
21+
}
22+
.form-container {
23+
display: flex;
24+
flex-direction: column;
25+
align-items: center;
26+
}
27+
.form-group {
28+
margin-bottom: 15px;
29+
}
30+
.form-group label {
31+
display: block;
32+
margin-bottom: 5px;
33+
font-weight: bold;
34+
}
35+
.form-group input[type="text"] {
36+
width: 100%;
37+
padding: 5px;
38+
box-sizing: border-box;
39+
}
40+
.form-group .options {
41+
display: flex;
42+
gap: 10px;
43+
}
44+
.results-container {
45+
display: flex;
46+
flex-direction: column;
47+
gap: 10px;
48+
}
49+
.results-container label {
50+
display: block;
51+
margin-bottom: 5px;
52+
font-weight: bold;
53+
}
54+
.results-container input {
55+
width: 100%;
56+
padding: 5px;
57+
margin-bottom: 10px;
58+
box-sizing: border-box;
59+
}
60+
</style>
661
</head>
762
<body>
863
<div class="container">
9-
<h1>Enter Patient ID</h1>
10-
<form method="post">
11-
<label for="patient_id">Patient ID:</label>
12-
<input type="text" name="patient_id" id="patient_id" required><br>
13-
<input type="submit" value="Submit">
64+
<h1>ASCVD Risk Calculator</h1>
65+
66+
<form method="post" onsubmit="clearPreviousResults()" class="form-container">
67+
<div class="form-group">
68+
<label for="patient_id">Enter a Patient ID:</label>
69+
<input type="text" name="patient_id" id="patient_id" required>
70+
</div>
71+
<input type="submit" value="Search">
1472
</form>
73+
74+
<div id="results" class="results-container">
75+
<form method="post" class="form-container">
76+
{% if demographics %}
77+
<label>Age:</label>
78+
<input type="text" name="age" value="{{ demographics.age if demographics.age != 'Not Found' else '' }}" placeholder="{{ 'Not Found' if demographics.age == 'Not Found' else '' }}" required>
79+
<label>Sex:</label>
80+
<input type="text" name="sex" value="{{ demographics.sex if demographics.sex != 'Not Found' else '' }}" placeholder="{{ 'Not Found' if demographics.sex == 'Not Found' else '' }}" required>
81+
<label>Race:</label>
82+
<input type="text" name="race" value="{{ demographics.race if demographics.race != 'Not Found' else '' }}" placeholder="{{ 'Not Found' if demographics.race == 'Not Found' else '' }}" required>
83+
{% endif %}
84+
85+
{% if observations %}
86+
{% for obs_name, obs_value in observations.items() %}
87+
<label>{{ obs_name }}:</label>
88+
<input type="text" name="{{ obs_name | replace(' ', '_') | lower }}" value="{{ obs_value if obs_value != 'Not Found' else '' }}" placeholder="{{ 'Not Found' if obs_value == 'Not Found' else '' }}" required>
89+
{% endfor %}
90+
{% endif %}
91+
92+
<div class="form-group">
93+
<label>History of Diabetes?</label>
94+
<div class="options">
95+
<label><input type="radio" name="diabetes" value="yes" required> Yes</label>
96+
<label><input type="radio" name="diabetes" value="no" required> No</label>
97+
</div>
98+
</div>
99+
100+
<div class="form-group">
101+
<label>Smoker?</label>
102+
<div class="options">
103+
<label><input type="radio" name="smoker" value="yes" required> Yes</label>
104+
<label><input type="radio" name="smoker" value="no" required> No</label>
105+
</div>
106+
</div>
107+
108+
<div class="form-group">
109+
<label>On hypertension treatment?</label>
110+
<div class="options">
111+
<label><input type="radio" name="hypertension" value="yes" required> Yes</label>
112+
<label><input type="radio" name="hypertension" value="no" required> No</label>
113+
</div>
114+
</div>
115+
116+
<div class="form-group">
117+
<label>On a statin?</label>
118+
<div class="options">
119+
<label><input type="radio" name="statin" value="yes" required> Yes</label>
120+
<label><input type="radio" name="statin" value="no" required> No</label>
121+
</div>
122+
</div>
123+
124+
<div class="form-group">
125+
<label>On Aspirin Therapy?</label>
126+
<div class="options">
127+
<label><input type="radio" name="aspirin" value="yes" required> Yes</label>
128+
<label><input type="radio" name="aspirin" value="no" required> No</label>
129+
</div>
130+
</div>
131+
132+
<input type="submit" value="Submit Additional Info">
133+
</form>
134+
</div>
15135
</div>
16136
</body>
17137
</html>
18-
19-
<style>
20-
.container {
21-
display: grid;
22-
place-items: center;
23-
}
24-
label {
25-
display: block;
26-
margin-top: 10px;
27-
}
28-
</style>

0 commit comments

Comments
 (0)