Yew Chong commited on
Commit
ac9e946
2 Parent(s): 13f230e 3fe6513

Merge branch 'main' of https://github.com/yewey2/mediscenario-LLM

Browse files
Files changed (1) hide show
  1. dashboard_pull11.py +333 -0
dashboard_pull11.py ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ from datetime import timedelta, date
3
+ import firebase_admin
4
+ from firebase_admin import credentials, storage, firestore
5
+ import streamlit as st
6
+ import streamlit_authenticator as stauth
7
+ import pandas as pd
8
+ import plotly.express as px
9
+ import plotly.graph_objects as go
10
+ import json, os, dotenv
11
+ from dotenv import load_dotenv
12
+ load_dotenv()
13
+
14
+
15
+
16
+ os.environ["FIREBASE_CREDENTIAL"] = dotenv.get_key(dotenv.find_dotenv(), "FIREBASE_CREDENTIAL")
17
+ cred = credentials.Certificate(json.loads(os.environ.get("FIREBASE_CREDENTIAL")))
18
+
19
+ # Initialize Firebase (if not already initialized)
20
+ if not firebase_admin._apps:
21
+ firebase_admin.initialize_app(cred, {'storageBucket': 'healthhack-store.appspot.com'})
22
+
23
+ #firebase_admin.initialize_app(cred,{'storageBucket': 'healthhack-store.appspot.com'}) # connecting to firebase
24
+ db = firestore.client()
25
+
26
+ docs = db.collection("clinical_scores").stream()
27
+
28
+ # Create a list of dictionaries from the documents
29
+ data = []
30
+ for doc in docs:
31
+ doc_dict = doc.to_dict()
32
+ doc_dict['document_id'] = doc.id # In case you need the document ID later
33
+ data.append(doc_dict)
34
+
35
+ # Create a DataFrame
36
+ df = pd.DataFrame(data)
37
+
38
+ #print(df)
39
+
40
+ # Exception handling for irregular grading, e.g. A-, B+
41
+ def standardize_grade(value):
42
+ if pd.isna(value):
43
+ return value
44
+ value = str(value).upper().strip() # Convert to string, uppercase and remove leading/trailing spaces
45
+ if value and value[0] in ['A', 'B', 'C', 'D', 'E']:
46
+ return value[0] # Return the first character if it's A, B, C, D, or E
47
+ return value # Return the original value if no match
48
+
49
+ # Columns to check
50
+ columns_to_check = ['hx_others_score', 'hx_AS_score', 'differentials_score', 'global_score']
51
+
52
+ # Apply the function to the specified columns
53
+ df[columns_to_check] = df[columns_to_check].applymap(standardize_grade)
54
+
55
+ login_info = {
56
+ "student1": "password",
57
+ "student2": "password",
58
+ "student3": "password",
59
+ "admin":"admin"
60
+ }
61
+ # Initialize username variable
62
+ username = None
63
+
64
+ def set_username(x):
65
+ st.session_state.username = x
66
+
67
+ def validate_username(username, password):
68
+ if login_info.get(username) == password:
69
+ set_username(username)
70
+ else:
71
+ st.warning("Wrong username or password")
72
+ return None
73
+
74
+ if not st.session_state.get("username"):
75
+ ## ask to login
76
+ st.title("Login")
77
+ username = st.text_input("Username:")
78
+ password = st.text_input("Password:", type="password")
79
+ login_button = st.button("Login", on_click=validate_username, args=[username, password])
80
+
81
+ if st.session_state.get("username"):
82
+ username = st.session_state.get("username")
83
+ st.title(f"Hello there, {st.session_state.username}")
84
+
85
+ # Display logout button
86
+ if st.button('Logout'):
87
+ # Remove username from session state
88
+ del st.session_state.username
89
+ # Rerun the app to go back to the login view
90
+ st.rerun()
91
+
92
+ # Convert date from string to datetime if it's not already in datetime format
93
+ df['date'] = pd.to_datetime(df['date'], errors='coerce')
94
+
95
+ # Streamlit page configuration
96
+ #st.set_page_config(page_title="Interactive Data Dashboard", layout="wide")
97
+
98
+ # Use df_selection for filtering data based on authenticated user
99
+ if username != 'admin':
100
+ df_selection = df[df['name'] == username]
101
+ else:
102
+ df_selection = df # Admin sees all data
103
+
104
+ # Chart Title: Student Performance Dashboard
105
+ st.title(":bar_chart: Student Performance Dashboard")
106
+ st.markdown("##")
107
+
108
+ # Chart 1: Total attempts
109
+ if df_selection.empty:
110
+ st.error("No data available to display.")
111
+ else:
112
+ # Total attempts by name (filtered)
113
+ total_attempts_by_name = df_selection.groupby("name")['date'].count().reset_index()
114
+ total_attempts_by_name.columns = ['name', 'total_attempts']
115
+
116
+ # For a single point or multiple points, use a scatter plot
117
+ fig_total_attempts = px.scatter(
118
+ total_attempts_by_name,
119
+ x="name",
120
+ y="total_attempts",
121
+ title="<b>Total Attempts</b>",
122
+ size='total_attempts', # Adjust the size of points
123
+ color_discrete_sequence=["#0083B8"] * len(total_attempts_by_name),
124
+ template="plotly_white",
125
+ text='total_attempts' # Display total_attempts as text labels
126
+ )
127
+
128
+ # Add text annotation for each point
129
+ for line in range(0, total_attempts_by_name.shape[0]):
130
+ fig_total_attempts.add_annotation(
131
+ text=str(total_attempts_by_name['total_attempts'].iloc[line]),
132
+ x=total_attempts_by_name['name'].iloc[line],
133
+ y=total_attempts_by_name['total_attempts'].iloc[line],
134
+ showarrow=True,
135
+ font=dict(family="Courier New, monospace", size=18, color="#ffffff"),
136
+ align="center",
137
+ arrowhead=2,
138
+ arrowsize=1,
139
+ arrowwidth=2,
140
+ arrowcolor="#636363",
141
+ ax=20,
142
+ ay=-30,
143
+ bordercolor="#c7c7c7",
144
+ borderwidth=2,
145
+ borderpad=4,
146
+ bgcolor="#ff7f0e",
147
+ opacity=0.8
148
+ )
149
+
150
+ # Update traces for styling
151
+ fig_total_attempts.update_traces(marker=dict(size=12), selector=dict(mode='markers+text'))
152
+
153
+ # Display the scatter plot in Streamlit
154
+ st.plotly_chart(fig_total_attempts, use_container_width=True)
155
+
156
+ # Chart 2 (students only): Personal scores over time
157
+ if username != 'admin':
158
+ # Sort the DataFrame by 'date' in chronological order
159
+ df_selection = df_selection.sort_values(by='date')
160
+ #fig = px.bar(df_selection, x='date', y='global_score', title='Your scores!')
161
+
162
+ if len(df_selection) > 1:
163
+ # # If more than one point, use a bar chart
164
+ # fig = px.bar(df_selection, x='date', y='global_score', title='Global Score Over Time')
165
+ # # fig.update_yaxes(
166
+ # # tickmode='array',
167
+ # # tickvals=[1, 2, 3, 4, 5], # Reverse the order of tickvals
168
+ # # ticktext=['A', 'B','C','D','E'] # Reverse the order of ticktext
169
+ # # )
170
+ # Mapping dictionary
171
+ grade_to_score = {'A': 100, 'B': 80, 'C': 60, 'D': 40, 'E': 20}
172
+
173
+ # Apply mapping to convert letter grades to numerical scores
174
+ df_selection['numeric_score'] = df_selection['global_score'].map(grade_to_score)
175
+
176
+ # Sort the DataFrame by 'date' in chronological order
177
+ df_selection = df_selection.sort_values(by='date')
178
+
179
+ # Check if there's more than one point in the DataFrame
180
+ if len(df_selection) > 1:
181
+ # Create a bar chart using Plotly Express
182
+ fig = px.bar(df_selection, x='date', y='numeric_score', title='Your scores over time')
183
+ else:
184
+ # Create a bar chart with just one point
185
+ fig = px.bar(df_selection, x='date', y='numeric_score', title='Global Score')
186
+
187
+ # Manually set the y-axis ticks and labels
188
+ fig.update_yaxes(
189
+ tickmode='array',
190
+ tickvals=list(grade_to_score.values()), # Positions for the ticks
191
+ ticktext=list(grade_to_score.keys()), # Text labels for the ticks
192
+ range=[0, 120] # Extend the range a bit beyond 100 to accommodate 'A'
193
+ )
194
+
195
+ # # Use st.plotly_chart to display the chart in Streamlit
196
+ # st.plotly_chart(fig, use_container_width=True)
197
+
198
+ else:
199
+ # For a single point, use a scatter plot
200
+ fig = px.scatter(df_selection, x='date', y='global_score', title='Global Score',
201
+ text='global_score', size_max=60)
202
+ # Add text annotation
203
+ for line in range(0,df_selection.shape[0]):
204
+ fig.add_annotation(text=df_selection['global_score'].iloc[line],
205
+ x=df_selection['date'].iloc[line], y=df_selection['global_score'].iloc[line],
206
+ showarrow=True, font=dict(family="Courier New, monospace", size=18, color="#ffffff"),
207
+ align="center", arrowhead=2, arrowsize=1, arrowwidth=2, arrowcolor="#636363",
208
+ ax=20, ay=-30, bordercolor="#c7c7c7", borderwidth=2, borderpad=4, bgcolor="#ff7f0e",
209
+ opacity=0.8)
210
+ fig.update_traces(marker=dict(size=12), selector=dict(mode='markers+text'))
211
+
212
+ # Display the chart in Streamlit
213
+ st.plotly_chart(fig, use_container_width=True)
214
+
215
+ # Show students their scores over time
216
+ st.dataframe(df_selection[['date', 'global_score', 'name']])
217
+
218
+
219
+ # Chart 3 (admin only): Global score chart
220
+ # Define the order of categories explicitly
221
+ order_of_categories = ['A', 'B', 'C', 'D', 'E']
222
+
223
+ # Convert global_score to a categorical type with the specified order
224
+ df_selection['global_score'] = pd.Categorical(df_selection['global_score'], categories=order_of_categories, ordered=True)
225
+
226
+ # Plot the histogram
227
+ fig_score_distribution = px.histogram(
228
+ df_selection,
229
+ x="global_score",
230
+ title="<b>Global Score Distribution</b>",
231
+ color_discrete_sequence=["#33CFA5"],
232
+ category_orders={"global_score": ["A", "B", "C", "D", "E"]}
233
+ )
234
+ if username == 'admin':
235
+ st.plotly_chart(fig_score_distribution, use_container_width=True)
236
+
237
+
238
+ # Chart 4 (admin only): Students with <5 attempts (filtered)
239
+ if username == 'admin':
240
+ students_with_less_than_5_attempts = total_attempts_by_name[total_attempts_by_name['total_attempts'] < 5]
241
+ fig_less_than_5_attempts = px.bar(
242
+ students_with_less_than_5_attempts,
243
+ x="name",
244
+ y="total_attempts",
245
+ title="<b>Students with <5 Attempts</b>",
246
+ color_discrete_sequence=["#D62728"] * len(students_with_less_than_5_attempts),
247
+ template="plotly_white",
248
+ )
249
+
250
+ if username == 'admin':
251
+ st.plotly_chart(fig_less_than_5_attempts, use_container_width=True)
252
+
253
+
254
+ # Selection of a student for detailed view (<5 attempts) - based on filtered data
255
+ if username == 'admin':
256
+ selected_student_less_than_5 = st.selectbox("Select a student with less than 5 attempts to view details:", students_with_less_than_5_attempts['name'])
257
+ if selected_student_less_than_5:
258
+ st.write(df_selection[df_selection['name'] == selected_student_less_than_5])
259
+
260
+ # Chart 5 (admin only): Students with at least one global score of 'C', 'D', 'E' (filtered)
261
+ if username == 'admin':
262
+ students_with_cde = df_selection[df_selection['global_score'].isin(['C', 'D', 'E'])].groupby("name")['date'].count().reset_index()
263
+ students_with_cde.columns = ['name', 'total_attempts']
264
+ fig_students_with_cde = px.bar(
265
+ students_with_cde,
266
+ x="name",
267
+ y="total_attempts",
268
+ title="<b>Students with at least one global score of 'C', 'D', 'E'</b>",
269
+ color_discrete_sequence=["#FF7F0E"] * len(students_with_cde),
270
+ template="plotly_white",
271
+ )
272
+ st.plotly_chart(fig_students_with_cde, use_container_width=True)
273
+
274
+ # Selection of a student for detailed view (score of 'C', 'D', 'E') - based on filtered data
275
+ if username == 'admin':
276
+ selected_student_cde = st.selectbox("Select a student with at least one score of 'C', 'D', 'E' to view details:", students_with_cde['name'])
277
+ if selected_student_cde:
278
+ st.write(df_selection[df_selection['name'] == selected_student_cde])
279
+
280
+ # Chart 7 (all): Radar Chart
281
+
282
+ # Mapping grades to numeric values
283
+ grade_to_numeric = {'A': 90, 'B': 70, 'C': 50, 'D': 30, 'E': 10}
284
+ df.replace(grade_to_numeric, inplace=True)
285
+
286
+ # Calculate average numeric scores for each category
287
+ average_scores = df.groupby('name')[['hx_PC_score', 'hx_AS_score', 'hx_others_score', 'differentials_score']].mean().reset_index()
288
+
289
+ if username == 'admin':
290
+ st.title('Average Scores Radar Chart')
291
+ else:
292
+ st.title('Performance in each segment as compared to your friends!')
293
+
294
+ # Categories for the radar chart
295
+ categories = ['Presenting complaint', 'Associated symptoms', '(Others)', 'Differentials']
296
+
297
+ st.markdown("""
298
+ ###
299
+ Double click on the names in the legend to include/exclude them from the plot.
300
+ """)
301
+
302
+
303
+ # Custom colors for better contrast
304
+ colors = ['gold', 'cyan', 'magenta', 'green']
305
+
306
+ # Plotly Radar Chart
307
+ fig = go.Figure()
308
+
309
+ for index, row in average_scores.iterrows():
310
+ fig.add_trace(go.Scatterpolar(
311
+ r=[row['hx_PC_score'], row['hx_AS_score'], row['hx_others_score'], row['differentials_score']],
312
+ theta=categories,
313
+ fill='toself',
314
+ name=row['name'],
315
+ line=dict(color=colors[index % len(colors)])
316
+ ))
317
+
318
+ fig.update_layout(
319
+ polar=dict(
320
+ radialaxis=dict(
321
+ visible=True,
322
+ range=[0, 100], # Numeric range
323
+ tickvals=[10, 30, 50, 70, 90], # Positions for the grade labels
324
+ ticktext=['E', 'D', 'C', 'B', 'A'] # Grade labels
325
+ )),
326
+ showlegend=True,
327
+ height=600, # Set the height of the figure
328
+ width=600 # Set the width of the figure
329
+ )
330
+
331
+ # Display the figure in Streamlit
332
+ st.plotly_chart(fig, use_container_width=True)
333
+