HengJay commited on
Commit
14c6a7b
·
1 Parent(s): f3abf64

Remove Cerner login page.

Browse files
Files changed (1) hide show
  1. pages/Cerner_LogIn.py +0 -371
pages/Cerner_LogIn.py DELETED
@@ -1,371 +0,0 @@
1
- import streamlit as st
2
- import requests
3
- import base64
4
- import uuid
5
- import json
6
- import os
7
- from datetime import datetime
8
- from dotenv import load_dotenv
9
-
10
- load_dotenv()
11
-
12
- CERNER_CLIENT_ID = st.secrets["CERNER_CLIENT_ID"]
13
- CERNER_CLIENT_SECRET = st.secrets["CERNER_CLIENT_SECRET"]
14
- CERNER_TENANT_ID = st.secrets["CERNER_TENANT_ID"]
15
- CERNER_AUTH_SERVER_URL = f"https://authorization.cerner.com/tenants/{CERNER_TENANT_ID}/protocols/oauth2/profiles/smart-v1/personas/provider/authorize"
16
- CERNER_TOKEN_ENDPOINT = f"https://authorization.cerner.com/tenants/{CERNER_TENANT_ID}/protocols/oauth2/profiles/smart-v1/token"
17
- CERNER_REDIRECT_URI = "https://huggingface.co/spaces/HengJay/snomed-ct-assistant" #"http://localhost:8501" #"https://huggingface.co/spaces/HengJay/snomed-ct-assistant"
18
- CERNER_AUDIENCE_URL = f"https://fhir-ehr.cerner.com/r4/{CERNER_TENANT_ID}"
19
-
20
- def get_fhir_url():
21
- params = {
22
- "response_type": "code",
23
- "client_id": CERNER_CLIENT_ID,
24
- "redirect_uri": CERNER_REDIRECT_URI,
25
- "scope": "user/Practitioner.read user/Patient.read user/Observation.read openid profile",
26
- "state": str(uuid.uuid4()),
27
- "aud": CERNER_AUDIENCE_URL
28
- }
29
- oauth2_url = requests.Request('GET', CERNER_AUTH_SERVER_URL, params=params).prepare().url
30
- return oauth2_url
31
-
32
- def get_fhir_url_launch(launch):
33
- params = {
34
- "response_type": "code",
35
- "client_id": CERNER_CLIENT_ID,
36
- "redirect_uri": CERNER_REDIRECT_URI,
37
- "scope": "user/Practitioner.read user/Patient.read user/Observation.read launch openid profile",
38
- "state": str(uuid.uuid4()),
39
- "aud": CERNER_AUDIENCE_URL,
40
- "launch": launch
41
- }
42
- oauth2_url = requests.Request('GET', CERNER_AUTH_SERVER_URL, params=params).prepare().url
43
- return oauth2_url
44
-
45
- def get_fhir_token(auth_code):
46
- auth_string = f"{CERNER_CLIENT_ID}:{CERNER_CLIENT_SECRET}".encode("ascii")
47
- base64_bytes = base64.b64encode(auth_string)
48
- base64_string = base64_bytes.decode("ascii")
49
-
50
- auth_headers = {
51
- "Authorization": f"Basic {base64_string}",
52
- }
53
-
54
- data = {
55
- "grant_type": "authorization_code",
56
- "code": auth_code,
57
- "redirect_uri": CERNER_REDIRECT_URI,
58
- "client_id": CERNER_CLIENT_ID,
59
- "client_secret": CERNER_CLIENT_SECRET
60
- }
61
-
62
- response = requests.post(CERNER_TOKEN_ENDPOINT, headers=auth_headers, data=data)
63
- return response.json()
64
-
65
- def standalone_launch(url: str, text: str= None, color="#aa8ccc"):
66
- st.markdown(
67
- f"""
68
- <a href="{url}" target="_self">
69
- <div style="
70
- display: inline-block;
71
- padding: 0.5em 1em;
72
- color: #FFFFFF;
73
- background-color: {color};
74
- border-radius: 3px;
75
- text-decoration: none;">
76
- {text}
77
- </div>
78
- </a>
79
- """,
80
- unsafe_allow_html=True
81
- )
82
-
83
- def get_practitioner(user_profile_url, access_token):
84
- headers = {
85
- "Accept": "application/fhir+json",
86
- "Authorization": f"Bearer {access_token}"
87
- }
88
-
89
- response = requests.get(user_profile_url, headers=headers)
90
- return response.json()
91
-
92
- def get_patient(access_token, person_id):
93
- base_url = f"https://fhir-ehr.cerner.com/r4/{CERNER_TENANT_ID}/Patient"
94
- headers = {
95
- "Accept": "application/fhir+json",
96
- "Authorization": f"Bearer {access_token}"
97
- }
98
- query_params = {
99
- "_id": person_id
100
- }
101
-
102
- response = requests.get(base_url, headers=headers, params=query_params)
103
- return response.json()
104
-
105
- def get_observation(access_token, person_id):
106
- base_url = f"https://fhir-ehr.cerner.com/r4/{CERNER_TENANT_ID}/Observation"
107
- headers = {
108
- "Accept": "application/fhir+json",
109
- "Authorization": f"Bearer {access_token}"
110
- }
111
- query_params = {
112
- "patient": person_id
113
- }
114
-
115
- response = requests.get(base_url, headers=headers, params=query_params)
116
- return response.json()
117
-
118
- def fhir_params():
119
- query_params = st.experimental_get_query_params()
120
- auth_code = query_params.get("code")
121
- iss_param = query_params.get("iss")
122
- launch_param = query_params.get("launch")
123
- print(f"fhir_params: {auth_code}, {iss_param}, {launch_param}")
124
- return auth_code, iss_param, launch_param
125
-
126
- def main():
127
- logo, title = st.columns([1,1])
128
- logo_image = 'logo.jpg'
129
-
130
- with logo:
131
- st.image(logo_image)
132
-
133
- auth_code, iss_param, launch_param = fhir_params()
134
-
135
- if auth_code is None and iss_param is None:
136
- fhir_login_url = get_fhir_url()
137
- st.subheader("Our App's Launch Capabilities")
138
- st.markdown("""
139
- 1. EHR Launch: Seamlessly integrates with your EHR system.
140
- 2. Standalone Launch: No need for an EHR to start up. This app can independently access FHIR data as long as it's authorized and provided the relevant iss URL.
141
- """)
142
- st.subheader("How It Works")
143
- st.markdown("""
144
- * When the app gets a launch request, it seeks permission to access FHIR data.
145
- * It does this by directing the browser to the EHR's authorization point.
146
- * Depending on certain rules and potential user approval, the EHR authorization system either approves or denies the request.
147
- * If approved, an authorization code is sent to the app, which is then swapped for an access token.
148
- * This access token is your key to the EHR's resource data.
149
- * Should a refresh token be provided with the access token, the app can utilize it to obtain a fresh access token once the original expires.
150
- """)
151
-
152
- standalone_launch(fhir_login_url, "Login")
153
-
154
- if iss_param is not None:
155
- fhir_login_url = get_fhir_url_launch(launch_param[0])
156
- st.write(f"""
157
- <meta http-equiv="refresh" content="0; URL={fhir_login_url}">
158
- """, unsafe_allow_html=True)
159
-
160
- if auth_code:
161
- if 'token' in st.session_state and 'access_token' in st.session_state.token:
162
- token = st.session_state.token
163
- else:
164
- token = get_fhir_token(auth_code[0])
165
- if token.get('error') == 'invalid_grant':
166
- fhir_login_url = get_fhir_url()
167
- st.markdown("""
168
- Session Expired
169
- """)
170
- standalone_launch(fhir_login_url, "Login")
171
- return
172
- else:
173
- st.session_state.token = token
174
-
175
- access_token = token.get('access_token')
176
- st.session_state.access_token = access_token
177
-
178
- header, payload, signature = token.get('id_token').split('.')
179
- decoded_payload = base64.urlsafe_b64decode(payload + '==').decode('utf-8')
180
-
181
- person_id = None
182
-
183
- if 'person_id' not in st.session_state:
184
- st.session_state.person_id = token.get('patient', "12724065") # If no person_id is provided, app defaults to WILMA SMART
185
-
186
- person_id = st.session_state.person_id
187
-
188
- if 'profile_data' not in st.session_state:
189
- st.session_state.profile_data = json.loads(decoded_payload)
190
- if 'practitioner_data' not in st.session_state:
191
- st.session_state.practitioner_data = get_practitioner(st.session_state.profile_data.get('profile'), access_token)
192
- if 'patient_data' not in st.session_state:
193
- patient_data = get_patient(access_token, person_id)
194
- if 'entry' in patient_data:
195
- st.session_state.patient_data = patient_data
196
- else:
197
- st.session_state.patient_data = None
198
- if 'observation_data' not in st.session_state:
199
- observation_data = get_observation(access_token, person_id)
200
- if 'entry' in observation_data:
201
- st.session_state.observation_data = observation_data
202
- else:
203
- st.session_state.observation_data = None
204
-
205
- resource_list = ['Profile', 'Practitioner', 'Patient', 'Observation']
206
- resource = st.sidebar.selectbox("Resource:", resource_list, index=0)
207
-
208
- if resource == 'Profile':
209
- with title:
210
- st.markdown('')
211
- st.subheader('Cerner Profile')
212
- profile_data = st.session_state.profile_data
213
- profile_username = profile_data["sub"]
214
- profile_full_name = profile_data["name"]
215
- profile_token_iat = profile_data["iat"]
216
- profile_token_exp = profile_data["exp"]
217
- profile_token_iat = datetime.utcfromtimestamp(profile_token_iat).strftime('%Y-%m-%d %H:%M:%S UTC')
218
- profile_token_exp = datetime.utcfromtimestamp(profile_token_exp).strftime('%Y-%m-%d %H:%M:%S UTC')
219
- st.markdown("*This data shows profile details of the user currently signed in*")
220
- st.markdown(f"""
221
- * **Username:** {profile_username}
222
- * **Name:** {profile_full_name}
223
- * **Token Issued:** {profile_token_iat}
224
- * **Token Expiration:** {profile_token_exp}
225
- """)
226
- if st.checkbox('Show JSON Response'):
227
- st.json(st.session_state.profile_data)
228
- if resource == 'Practitioner':
229
- with title:
230
- st.markdown('')
231
- st.subheader('Practitioner Resource')
232
- practitioner_data = st.session_state.practitioner_data
233
- practitioner_name = practitioner_data["name"][0]["text"]
234
- practitioner_npi = next(identifier["value"] for identifier in practitioner_data["identifier"] if identifier["type"]["coding"][0]["code"] == "NPI")
235
- practitioner_email = next(telecom["value"] for telecom in practitioner_data["telecom"] if telecom["system"] == "email")
236
- st.markdown("*This data shows details in the practitioner endpoint of the user currently signed in*")
237
- st.markdown(f"""
238
- * **Practitioner Name:** {practitioner_name}
239
- * **NPI:** {practitioner_npi}
240
- * **Email:** {practitioner_email}
241
- """)
242
- if st.checkbox('Show JSON Response'):
243
- st.json(st.session_state.practitioner_data)
244
- if resource == 'Patient':
245
- with title:
246
- st.markdown('')
247
- st.subheader('Patient Resource')
248
- if st.session_state.patient_data is None:
249
- st.markdown("No patient data available.")
250
- else:
251
- patient_data = st.session_state.patient_data
252
- patient_name = patient_data['entry'][0]['resource']['name'][0]['text']
253
- patient_status = patient_data['entry'][0]['resource']['active']
254
- if patient_status is True:
255
- patient_status = 'active'
256
- else:
257
- patient_status = 'inactive'
258
- patient_phone = next((telecom['value'] for telecom in patient_data['entry'][0]['resource']['telecom'] if telecom['system'] == 'phone'), None)
259
- patient_address = patient_data['entry'][0]['resource']['address'][0]
260
- patient_address = f"{patient_address['line'][0]}, {patient_address['city']}, {patient_address['state']} {patient_address['postalCode']}, {patient_address['country']}"
261
- patient_email = next((telecom['value'] for telecom in patient_data['entry'][0]['resource']['telecom'] if telecom['system'] == 'email'), None)
262
- patient_dob = patient_data['entry'][0]['resource']['birthDate']
263
- patient_gender = patient_data['entry'][0]['resource']['gender']
264
- patient_pref_lang = patient_data['entry'][0]['resource']['communication'][0]['language']['text']
265
- patient_marital_status = patient_data['entry'][0]['resource']['maritalStatus']['text']
266
- contact_person = patient_data['entry'][0]['resource']['contact'][0]
267
- contact_person_name = contact_person['name']['text']
268
- contact_person_phone = contact_person['telecom'][0]['value']
269
- contact_person_relationship = contact_person['relationship'][0]['text']
270
- st.markdown(f"*This data shows details in the patient endpoint of patient ID: {person_id}*")
271
- st.markdown(f"""
272
- * **Name:** {patient_name}
273
- * **Status:** {patient_status}
274
- * **Phone:** {patient_phone}
275
- * **Address:** {patient_address}
276
- * **Email:** {patient_email}
277
- * **DOB:** {patient_dob}
278
- * **Gender:** {patient_gender}
279
- * **Preferred Language:** {patient_pref_lang}
280
- * **Marital Status:** {patient_marital_status}
281
- * **Contact Person Name:** {contact_person_name}
282
- * **Contact Person Phone:** {contact_person_phone}
283
- * **Contact Person Relationship:** {contact_person_relationship}
284
- """)
285
- if st.checkbox('Show JSON Response'):
286
- st.json(st.session_state.patient_data)
287
- if resource == 'Observation':
288
- with title:
289
- st.markdown('')
290
- st.subheader('Observation Resource')
291
- if st.session_state.observation_data is None:
292
- st.markdown("No patient data available.")
293
- else:
294
- observation_data = st.session_state.observation_data
295
- weight_with_date = []
296
- bp_with_date = []
297
- for i in observation_data.get('entry', []):
298
- resource = i.get('resource', {})
299
- status = resource.get('status', '')
300
- if status == 'final':
301
- category_list = resource.get('category', [])
302
- for category in category_list:
303
- category_coding = category.get('coding', [])
304
- for coding in category_coding:
305
- if coding.get('code', '') == 'vital-signs':
306
- code_info = resource.get('code', {})
307
- code_coding = code_info.get('coding', [])
308
- for code in code_coding:
309
- display = code.get('display', '')
310
- if display.lower() in ['weight measured']:
311
- value_quantity = resource.get('valueQuantity', {})
312
- value = value_quantity.get('value', 'N/A')
313
- unit = value_quantity.get('unit', '')
314
- effective_date = resource.get('effectiveDateTime', 'N/A')
315
- weight_with_date.append({
316
- 'Value': value,
317
- 'Unit': unit,
318
- 'Date': effective_date
319
- })
320
- code_data = resource.get('code', {})
321
- if any(coding.get('code', '') == '85354-9' for coding in code_data.get('coding', [])):
322
- effective_date_time = resource.get('effectiveDateTime', 'N/A')
323
-
324
- systolic_pressure = None
325
- diastolic_pressure = None
326
-
327
- for component in resource.get('component', []):
328
- code_comp_data = component.get('code', {})
329
-
330
- # Systolic blood pressure
331
- if any(coding.get('code', '') in ['8460-8', '8480-6'] for coding in code_comp_data.get('coding', [])):
332
- value_quantity = component.get('valueQuantity', {})
333
- systolic_pressure = {
334
- 'value': value_quantity.get('value', 'N/A'),
335
- 'unit': value_quantity.get('unit', '')
336
- }
337
-
338
- # Diastolic blood pressure
339
- if any(coding.get('code', '') in ['8454-1', '8462-4'] for coding in code_comp_data.get('coding', [])):
340
- value_quantity = component.get('valueQuantity', {})
341
- diastolic_pressure = {
342
- 'value': value_quantity.get('value', 'N/A'),
343
- 'unit': value_quantity.get('unit', '')
344
- }
345
-
346
- if systolic_pressure and diastolic_pressure:
347
- bp_with_date.append({
348
- 'Date': effective_date_time,
349
- 'Systolic': systolic_pressure,
350
- 'Diastolic': diastolic_pressure
351
- })
352
- st.markdown(f"*This data shows details in the observation endpoint of patient ID: {person_id}*")
353
- if weight_with_date:
354
- st.subheader("Weight")
355
- for i in weight_with_date:
356
- st.markdown(f"""
357
- **Date:** {i['Date']}
358
- * **Weight:** {i['Value']}{i['Unit']}
359
- """)
360
- if bp_with_date:
361
- st.subheader("Blood Pressure")
362
- for j in bp_with_date:
363
- st.markdown(f"""
364
- **Date:** {j['Date']}
365
- * **Systolic:** {j['Systolic']['value']}{j['Systolic']['unit']}, **Diastolic:** {j['Diastolic']['value']}{j['Diastolic']['unit']}
366
- """)
367
- if st.checkbox('Show JSON Response'):
368
- st.json(st.session_state.observation_data)
369
-
370
- if __name__ == "__main__":
371
- main()