Update app.py
Browse files
app.py
CHANGED
|
@@ -4,8 +4,14 @@ import plotly.express as px
|
|
| 4 |
import os
|
| 5 |
from openai import OpenAI
|
| 6 |
import bcrypt
|
|
|
|
| 7 |
|
| 8 |
-
# Set up
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])
|
| 10 |
|
| 11 |
# Initialize session state
|
|
@@ -14,33 +20,31 @@ if 'user' not in st.session_state:
|
|
| 14 |
if 'user_type' not in st.session_state:
|
| 15 |
st.session_state.user_type = None
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
def load_data():
|
| 24 |
-
if os.path.exists(CSV_FILE):
|
| 25 |
-
return pd.read_csv(CSV_FILE)
|
| 26 |
-
return pd.DataFrame(columns=['user', 'date', 'pain_level', 'duration', 'triggers', 'symptoms', 'medications', 'notes', 'doctor_analysis', 'patient_advice'])
|
| 27 |
|
| 28 |
-
# Function to load user data
|
| 29 |
-
# @st.cache_data
|
| 30 |
def load_user_data():
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
return pd.DataFrame(columns=['username', 'password', 'user_type'])
|
| 34 |
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
df.to_csv(CSV_FILE, index=False)
|
| 38 |
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
-
# Function to get GPT analysis
|
| 44 |
def get_gpt_analysis(entry_text, system_prompt):
|
| 45 |
try:
|
| 46 |
response = client.chat.completions.create(
|
|
@@ -55,15 +59,12 @@ def get_gpt_analysis(entry_text, system_prompt):
|
|
| 55 |
st.error(f"Error in GPT analysis: {str(e)}")
|
| 56 |
return "Analysis unavailable at this time."
|
| 57 |
|
| 58 |
-
# Password hashing function
|
| 59 |
def hash_password(password):
|
| 60 |
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|
| 61 |
|
| 62 |
-
# Password verification function
|
| 63 |
def verify_password(stored_password, provided_password):
|
| 64 |
return bcrypt.checkpw(provided_password.encode('utf-8'), stored_password.encode('utf-8'))
|
| 65 |
|
| 66 |
-
# Login/Register function
|
| 67 |
def auth():
|
| 68 |
if st.session_state.user is None:
|
| 69 |
st.subheader("User Authentication")
|
|
@@ -76,8 +77,7 @@ def auth():
|
|
| 76 |
login_button = st.button("Login")
|
| 77 |
|
| 78 |
if login_button:
|
| 79 |
-
|
| 80 |
-
user_data = user_df[user_df['username'] == login_username]
|
| 81 |
if not user_data.empty and verify_password(user_data.iloc[0]['password'], login_password):
|
| 82 |
st.session_state.user = login_username
|
| 83 |
st.session_state.user_type = user_data.iloc[0]['user_type']
|
|
@@ -95,18 +95,16 @@ def auth():
|
|
| 95 |
register_button = st.button("Register")
|
| 96 |
|
| 97 |
if register_button:
|
| 98 |
-
|
| 99 |
-
if
|
| 100 |
st.error("Username already exists. Please choose a different one.")
|
| 101 |
elif reg_password != confirm_password:
|
| 102 |
st.error("Passwords do not match.")
|
| 103 |
-
elif len(reg_password) <
|
| 104 |
st.error("Password must be at least 8 characters long.")
|
| 105 |
else:
|
| 106 |
hashed_password = hash_password(reg_password)
|
| 107 |
-
|
| 108 |
-
user_df = pd.concat([user_df, new_user], ignore_index=True)
|
| 109 |
-
save_user_data(user_df)
|
| 110 |
st.session_state.user = reg_username
|
| 111 |
st.session_state.user_type = user_type
|
| 112 |
st.success("Registered successfully!")
|
|
@@ -118,7 +116,6 @@ def auth():
|
|
| 118 |
st.session_state.user_type = None
|
| 119 |
st.rerun()
|
| 120 |
|
| 121 |
-
# Main app
|
| 122 |
def main():
|
| 123 |
st.set_page_config(page_title="Migraine Diary App", page_icon="🧠", layout="wide")
|
| 124 |
st.title("Migraine Diary App")
|
|
@@ -184,48 +181,43 @@ def add_entry():
|
|
| 184 |
|
| 185 |
submitted = st.form_submit_button("Submit Entry")
|
| 186 |
if submitted:
|
| 187 |
-
df = load_data()
|
| 188 |
entry_text = f"Date: {date}\nPain Level: {pain_level}\nDuration: {duration}\nTriggers: {', '.join(triggers)}\nSymptoms: {', '.join(symptoms)}\nMedications: {medications}\nNotes: {notes}"
|
| 189 |
|
| 190 |
with st.spinner("Analyzing your entry..."):
|
| 191 |
doctor_analysis = get_gpt_analysis(entry_text, "You are a neurologist specializing in migraine management. Provide a technical analysis of the patient's migraine diary entry, including potential correlations, patterns, and suggestions for the treating physician. Keep it short and to the point the doctor is busy.")
|
| 192 |
patient_advice = get_gpt_analysis(entry_text, "You are a supportive health coach specializing in migraine management. Provide friendly, easy-to-understand advice for the patient based on their migraine diary entry. Include actionable tips for managing their condition and potential lifestyle adjustments.")
|
| 193 |
|
| 194 |
-
new_entry =
|
| 195 |
-
'
|
| 196 |
-
'
|
| 197 |
-
'pain_level':
|
| 198 |
-
'duration':
|
| 199 |
-
'triggers':
|
| 200 |
-
'symptoms':
|
| 201 |
-
'medications':
|
| 202 |
-
'notes':
|
| 203 |
-
'doctor_analysis':
|
| 204 |
-
'patient_advice':
|
| 205 |
-
}
|
| 206 |
-
|
| 207 |
-
save_data(df)
|
| 208 |
st.success("Entry added successfully!")
|
| 209 |
st.subheader("Advice for You:")
|
| 210 |
st.write(patient_advice)
|
| 211 |
|
| 212 |
def view_entries(is_doctor):
|
| 213 |
st.header("Migraine Entries")
|
| 214 |
-
df = load_data()
|
| 215 |
if is_doctor:
|
| 216 |
-
user_entries =
|
| 217 |
st.subheader("All Patient Entries")
|
| 218 |
else:
|
| 219 |
-
user_entries =
|
| 220 |
-
print(st.session_state.user)
|
| 221 |
-
print(user_entries)
|
| 222 |
st.subheader("Your Entries")
|
| 223 |
|
| 224 |
-
user_entries = user_entries.sort_values(by='
|
| 225 |
|
| 226 |
if not user_entries.empty:
|
| 227 |
for _, entry in user_entries.iterrows():
|
| 228 |
-
with st.expander(f"Entry for {entry['
|
| 229 |
st.write(f"Duration: {entry['duration']}")
|
| 230 |
st.write(f"Triggers: {entry['triggers']}")
|
| 231 |
st.write(f"Symptoms: {entry['symptoms']}")
|
|
@@ -240,21 +232,21 @@ def view_entries(is_doctor):
|
|
| 240 |
|
| 241 |
def display_dashboard(is_doctor):
|
| 242 |
st.header("Migraine Dashboard")
|
| 243 |
-
df = load_data()
|
| 244 |
|
| 245 |
if is_doctor:
|
| 246 |
st.subheader("Select Patient")
|
| 247 |
-
|
| 248 |
-
|
|
|
|
| 249 |
else:
|
| 250 |
-
user_entries =
|
| 251 |
|
| 252 |
if not user_entries.empty:
|
| 253 |
col1, col2 = st.columns(2)
|
| 254 |
|
| 255 |
with col1:
|
| 256 |
st.subheader("Pain Level Over Time")
|
| 257 |
-
fig = px.line(user_entries, x='
|
| 258 |
st.plotly_chart(fig, use_container_width=True)
|
| 259 |
|
| 260 |
with col2:
|
|
@@ -264,14 +256,24 @@ def display_dashboard(is_doctor):
|
|
| 264 |
fig = px.bar(x=trigger_counts.index, y=trigger_counts.values, labels={'x': 'Trigger', 'y': 'Count'})
|
| 265 |
st.plotly_chart(fig, use_container_width=True)
|
| 266 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
st.subheader("Migraine Statistics")
|
| 268 |
-
col1, col2, col3 = st.columns(
|
| 269 |
col1.metric("Total Entries", len(user_entries))
|
| 270 |
col2.metric("Average Pain Level", f"{user_entries['pain_level'].mean():.2f}")
|
| 271 |
col3.metric("Most Common Trigger", trigger_counts.index[0] if not trigger_counts.empty else "N/A")
|
|
|
|
| 272 |
|
| 273 |
st.subheader("Recent Entries")
|
| 274 |
-
st.dataframe(user_entries[['
|
| 275 |
else:
|
| 276 |
st.info("No entries found.")
|
| 277 |
|
|
|
|
| 4 |
import os
|
| 5 |
from openai import OpenAI
|
| 6 |
import bcrypt
|
| 7 |
+
from supabase import create_client, Client
|
| 8 |
|
| 9 |
+
# Set up Supabase client
|
| 10 |
+
supabase_url = st.secrets["SUPABASE_URL"]
|
| 11 |
+
supabase_key = st.secrets["SUPABASE_KEY"]
|
| 12 |
+
supabase: Client = create_client(supabase_url, supabase_key)
|
| 13 |
+
|
| 14 |
+
# Set up OpenAI client
|
| 15 |
client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])
|
| 16 |
|
| 17 |
# Initialize session state
|
|
|
|
| 20 |
if 'user_type' not in st.session_state:
|
| 21 |
st.session_state.user_type = None
|
| 22 |
|
| 23 |
+
def load_data(username=None):
|
| 24 |
+
if username:
|
| 25 |
+
response = supabase.table('entries').select('*').eq('username', username).execute()
|
| 26 |
+
else:
|
| 27 |
+
response = supabase.table('entries').select('*').execute()
|
| 28 |
+
return pd.DataFrame(response.data)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
|
|
|
|
|
|
| 30 |
def load_user_data():
|
| 31 |
+
response = supabase.table('users').select('*').execute()
|
| 32 |
+
return pd.DataFrame(response.data)
|
|
|
|
| 33 |
|
| 34 |
+
def save_data(entry):
|
| 35 |
+
supabase.table('entries').insert(entry).execute()
|
|
|
|
| 36 |
|
| 37 |
+
def save_user_data(username, hashed_password, user_type):
|
| 38 |
+
supabase.table('users').insert({
|
| 39 |
+
'username': username,
|
| 40 |
+
'password': hashed_password,
|
| 41 |
+
'user_type': user_type
|
| 42 |
+
}).execute()
|
| 43 |
+
|
| 44 |
+
def get_user(username):
|
| 45 |
+
response = supabase.table('users').select('*').eq('username', username).execute()
|
| 46 |
+
return pd.DataFrame(response.data)
|
| 47 |
|
|
|
|
| 48 |
def get_gpt_analysis(entry_text, system_prompt):
|
| 49 |
try:
|
| 50 |
response = client.chat.completions.create(
|
|
|
|
| 59 |
st.error(f"Error in GPT analysis: {str(e)}")
|
| 60 |
return "Analysis unavailable at this time."
|
| 61 |
|
|
|
|
| 62 |
def hash_password(password):
|
| 63 |
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8')
|
| 64 |
|
|
|
|
| 65 |
def verify_password(stored_password, provided_password):
|
| 66 |
return bcrypt.checkpw(provided_password.encode('utf-8'), stored_password.encode('utf-8'))
|
| 67 |
|
|
|
|
| 68 |
def auth():
|
| 69 |
if st.session_state.user is None:
|
| 70 |
st.subheader("User Authentication")
|
|
|
|
| 77 |
login_button = st.button("Login")
|
| 78 |
|
| 79 |
if login_button:
|
| 80 |
+
user_data = get_user(login_username)
|
|
|
|
| 81 |
if not user_data.empty and verify_password(user_data.iloc[0]['password'], login_password):
|
| 82 |
st.session_state.user = login_username
|
| 83 |
st.session_state.user_type = user_data.iloc[0]['user_type']
|
|
|
|
| 95 |
register_button = st.button("Register")
|
| 96 |
|
| 97 |
if register_button:
|
| 98 |
+
existing_user = get_user(reg_username)
|
| 99 |
+
if not existing_user.empty:
|
| 100 |
st.error("Username already exists. Please choose a different one.")
|
| 101 |
elif reg_password != confirm_password:
|
| 102 |
st.error("Passwords do not match.")
|
| 103 |
+
elif len(reg_password) < 8:
|
| 104 |
st.error("Password must be at least 8 characters long.")
|
| 105 |
else:
|
| 106 |
hashed_password = hash_password(reg_password)
|
| 107 |
+
save_user_data(reg_username, hashed_password, user_type)
|
|
|
|
|
|
|
| 108 |
st.session_state.user = reg_username
|
| 109 |
st.session_state.user_type = user_type
|
| 110 |
st.success("Registered successfully!")
|
|
|
|
| 116 |
st.session_state.user_type = None
|
| 117 |
st.rerun()
|
| 118 |
|
|
|
|
| 119 |
def main():
|
| 120 |
st.set_page_config(page_title="Migraine Diary App", page_icon="🧠", layout="wide")
|
| 121 |
st.title("Migraine Diary App")
|
|
|
|
| 181 |
|
| 182 |
submitted = st.form_submit_button("Submit Entry")
|
| 183 |
if submitted:
|
|
|
|
| 184 |
entry_text = f"Date: {date}\nPain Level: {pain_level}\nDuration: {duration}\nTriggers: {', '.join(triggers)}\nSymptoms: {', '.join(symptoms)}\nMedications: {medications}\nNotes: {notes}"
|
| 185 |
|
| 186 |
with st.spinner("Analyzing your entry..."):
|
| 187 |
doctor_analysis = get_gpt_analysis(entry_text, "You are a neurologist specializing in migraine management. Provide a technical analysis of the patient's migraine diary entry, including potential correlations, patterns, and suggestions for the treating physician. Keep it short and to the point the doctor is busy.")
|
| 188 |
patient_advice = get_gpt_analysis(entry_text, "You are a supportive health coach specializing in migraine management. Provide friendly, easy-to-understand advice for the patient based on their migraine diary entry. Include actionable tips for managing their condition and potential lifestyle adjustments.")
|
| 189 |
|
| 190 |
+
new_entry = {
|
| 191 |
+
'username': st.session_state.user,
|
| 192 |
+
'entry_date': date.isoformat(),
|
| 193 |
+
'pain_level': pain_level,
|
| 194 |
+
'duration': duration,
|
| 195 |
+
'triggers': ', '.join(triggers),
|
| 196 |
+
'symptoms': ', '.join(symptoms),
|
| 197 |
+
'medications': medications,
|
| 198 |
+
'notes': notes,
|
| 199 |
+
'doctor_analysis': doctor_analysis,
|
| 200 |
+
'patient_advice': patient_advice
|
| 201 |
+
}
|
| 202 |
+
save_data(new_entry)
|
|
|
|
| 203 |
st.success("Entry added successfully!")
|
| 204 |
st.subheader("Advice for You:")
|
| 205 |
st.write(patient_advice)
|
| 206 |
|
| 207 |
def view_entries(is_doctor):
|
| 208 |
st.header("Migraine Entries")
|
|
|
|
| 209 |
if is_doctor:
|
| 210 |
+
user_entries = load_data()
|
| 211 |
st.subheader("All Patient Entries")
|
| 212 |
else:
|
| 213 |
+
user_entries = load_data(st.session_state.user)
|
|
|
|
|
|
|
| 214 |
st.subheader("Your Entries")
|
| 215 |
|
| 216 |
+
user_entries = user_entries.sort_values(by='entry_date', ascending=False)
|
| 217 |
|
| 218 |
if not user_entries.empty:
|
| 219 |
for _, entry in user_entries.iterrows():
|
| 220 |
+
with st.expander(f"Entry for {entry['username']} on {entry['entry_date']} - Pain Level: {entry['pain_level']}"):
|
| 221 |
st.write(f"Duration: {entry['duration']}")
|
| 222 |
st.write(f"Triggers: {entry['triggers']}")
|
| 223 |
st.write(f"Symptoms: {entry['symptoms']}")
|
|
|
|
| 232 |
|
| 233 |
def display_dashboard(is_doctor):
|
| 234 |
st.header("Migraine Dashboard")
|
|
|
|
| 235 |
|
| 236 |
if is_doctor:
|
| 237 |
st.subheader("Select Patient")
|
| 238 |
+
all_users = load_data()['username'].unique()
|
| 239 |
+
selected_user = st.selectbox("Choose a patient", all_users)
|
| 240 |
+
user_entries = load_data(selected_user)
|
| 241 |
else:
|
| 242 |
+
user_entries = load_data(st.session_state.user)
|
| 243 |
|
| 244 |
if not user_entries.empty:
|
| 245 |
col1, col2 = st.columns(2)
|
| 246 |
|
| 247 |
with col1:
|
| 248 |
st.subheader("Pain Level Over Time")
|
| 249 |
+
fig = px.line(user_entries, x='entry_date', y='pain_level', title='Pain Level Over Time')
|
| 250 |
st.plotly_chart(fig, use_container_width=True)
|
| 251 |
|
| 252 |
with col2:
|
|
|
|
| 256 |
fig = px.bar(x=trigger_counts.index, y=trigger_counts.values, labels={'x': 'Trigger', 'y': 'Count'})
|
| 257 |
st.plotly_chart(fig, use_container_width=True)
|
| 258 |
|
| 259 |
+
col1, col2 = st.columns(2)
|
| 260 |
+
|
| 261 |
+
with col1:
|
| 262 |
+
st.subheader("Common Symptoms")
|
| 263 |
+
all_symptoms = ', '.join(user_entries['symptoms'].dropna()).split(', ')
|
| 264 |
+
symptom_counts = pd.Series(all_symptoms).value_counts().head(5)
|
| 265 |
+
fig = px.bar(x=symptom_counts.index, y=symptom_counts.values, labels={'x': 'Symptom', 'y': 'Count'})
|
| 266 |
+
st.plotly_chart(fig, use_container_width=True)
|
| 267 |
+
|
| 268 |
st.subheader("Migraine Statistics")
|
| 269 |
+
col1, col2, col3, col4 = st.columns(4)
|
| 270 |
col1.metric("Total Entries", len(user_entries))
|
| 271 |
col2.metric("Average Pain Level", f"{user_entries['pain_level'].mean():.2f}")
|
| 272 |
col3.metric("Most Common Trigger", trigger_counts.index[0] if not trigger_counts.empty else "N/A")
|
| 273 |
+
col4.metric("Most Common Symptom", symptom_counts.index[0] if not symptom_counts.empty else "N/A")
|
| 274 |
|
| 275 |
st.subheader("Recent Entries")
|
| 276 |
+
st.dataframe(user_entries[['entry_date', 'pain_level', 'duration', 'triggers', 'symptoms']].sort_values(by='entry_date', ascending=False).head())
|
| 277 |
else:
|
| 278 |
st.info("No entries found.")
|
| 279 |
|