|
import streamlit as st |
|
import pandas as pd |
|
import plotly.express as px |
|
import os |
|
from openai import OpenAI |
|
import bcrypt |
|
from supabase import create_client, Client |
|
|
|
|
|
supabase_url = st.secrets["SUPABASE_URL"] |
|
supabase_key = st.secrets["SUPABASE_KEY"] |
|
supabase: Client = create_client(supabase_url, supabase_key) |
|
|
|
|
|
client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"]) |
|
|
|
|
|
if 'user' not in st.session_state: |
|
st.session_state.user = None |
|
if 'user_type' not in st.session_state: |
|
st.session_state.user_type = None |
|
|
|
def load_data(username=None): |
|
if username: |
|
response = supabase.table('entries').select('*').eq('username', username).execute() |
|
else: |
|
response = supabase.table('entries').select('*').execute() |
|
return pd.DataFrame(response.data) |
|
|
|
def load_user_data(): |
|
response = supabase.table('users').select('*').execute() |
|
return pd.DataFrame(response.data) |
|
|
|
def save_data(entry): |
|
supabase.table('entries').insert(entry).execute() |
|
|
|
def save_user_data(username, hashed_password, user_type): |
|
supabase.table('users').insert({ |
|
'username': username, |
|
'password': hashed_password, |
|
'user_type': user_type |
|
}).execute() |
|
|
|
def get_user(username): |
|
response = supabase.table('users').select('*').eq('username', username).execute() |
|
return pd.DataFrame(response.data) |
|
|
|
def get_gpt_analysis(entry_text, system_prompt): |
|
try: |
|
response = client.chat.completions.create( |
|
model="gpt-4o-mini", |
|
messages=[ |
|
{"role": "system", "content": system_prompt}, |
|
{"role": "user", "content": entry_text} |
|
] |
|
) |
|
return response.choices[0].message.content |
|
except Exception as e: |
|
st.error(f"Error in GPT analysis: {str(e)}") |
|
return "Analysis unavailable at this time." |
|
|
|
def hash_password(password): |
|
return bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') |
|
|
|
def verify_password(stored_password, provided_password): |
|
return bcrypt.checkpw(provided_password.encode('utf-8'), stored_password.encode('utf-8')) |
|
|
|
def auth(): |
|
if st.session_state.user is None: |
|
st.subheader("Welcome to the HeadPeace 🧠") |
|
|
|
|
|
with st.expander("How to Use This App", expanded=True): |
|
st.markdown(""" |
|
**For Patients:** |
|
1. Register for an account or log in if you already have one. |
|
2. Once logged in, you can: |
|
- Add new migraine entries |
|
- View your past entries |
|
- See a dashboard with visualizations of your migraine patterns |
|
3. Regularly log your migraines for the most accurate insights. |
|
|
|
**Tips for Effective Use:** |
|
- Be consistent in logging migraines |
|
- Include as much detail as possible in each entry |
|
- Regularly review the dashboard to identify patterns |
|
- Discuss app insights with your healthcare provider |
|
""") |
|
tabs = st.tabs(["Login", "Register"]) |
|
|
|
with tabs[0]: |
|
st.subheader("Login") |
|
login_username = st.text_input("Username", key="login_username") |
|
login_password = st.text_input("Password", type="password", key="login_password") |
|
login_button = st.button("Login") |
|
|
|
if login_button: |
|
user_data = get_user(login_username) |
|
if not user_data.empty and verify_password(user_data.iloc[0]['password'], login_password): |
|
st.session_state.user = login_username |
|
st.session_state.user_type = user_data.iloc[0]['user_type'] |
|
st.success("Logged in successfully!") |
|
st.rerun() |
|
else: |
|
st.error("Invalid username or password.") |
|
|
|
with tabs[1]: |
|
st.subheader("Register") |
|
reg_username = st.text_input("Choose a Username", key="reg_username") |
|
reg_password = st.text_input("Choose a Password", type="password", key="reg_password") |
|
confirm_password = st.text_input("Confirm Password", type="password", key="confirm_password") |
|
user_type = st.selectbox("User Type", ["Patient", "Doctor"]) |
|
|
|
doctor_code = "" |
|
if user_type == "Doctor": |
|
doctor_code = st.text_input("Enter Doctor Registration Code", type="password") |
|
|
|
register_button = st.button("Register") |
|
|
|
if register_button: |
|
existing_user = get_user(reg_username) |
|
if not existing_user.empty: |
|
st.error("Username already exists. Please choose a different one.") |
|
elif reg_password != confirm_password: |
|
st.error("Passwords do not match.") |
|
elif len(reg_password) < 8: |
|
st.error("Password must be at least 8 characters long.") |
|
elif user_type == "Doctor" and doctor_code != st.secrets["DOCTOR_CODE"]: |
|
st.error("Invalid doctor registration code.") |
|
else: |
|
hashed_password = hash_password(reg_password) |
|
save_user_data(reg_username, hashed_password, user_type) |
|
st.session_state.user = reg_username |
|
st.session_state.user_type = user_type |
|
st.success("Registered successfully!") |
|
st.rerun() |
|
else: |
|
st.sidebar.write(f"Logged in as {st.session_state.user} ({st.session_state.user_type})") |
|
if st.sidebar.button("Logout"): |
|
st.session_state.user = None |
|
st.session_state.user_type = None |
|
st.rerun() |
|
|
|
def main(): |
|
st.set_page_config(page_title="HeadPeace by Yashogamya", page_icon="yashogamya.png", layout="wide") |
|
header = st.container() |
|
|
|
|
|
col1, col2 = header.columns([1, 1]) |
|
|
|
with col1: |
|
st.title("HeadPeace by Yashogamya") |
|
|
|
with col2: |
|
st.image("yashogamya.png", width=200) |
|
|
|
|
|
st.markdown(""" |
|
<style> |
|
[data-testid="stImage"] { |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
height: 100%; |
|
} |
|
</style> |
|
""", unsafe_allow_html=True) |
|
|
|
auth() |
|
|
|
if st.session_state.user: |
|
if st.session_state.user_type == "Patient": |
|
patient_interface() |
|
elif st.session_state.user_type == "Doctor": |
|
doctor_interface() |
|
|
|
def patient_interface(): |
|
menu = st.sidebar.selectbox("Menu", ["Add Entry", "View Entries", "Dashboard"]) |
|
|
|
if menu == "Add Entry": |
|
add_entry() |
|
elif menu == "View Entries": |
|
view_entries(is_doctor=False) |
|
elif menu == "Dashboard": |
|
display_dashboard(is_doctor=False) |
|
|
|
def doctor_interface(): |
|
menu = st.sidebar.selectbox("Menu", ["View All Entries", "Patient Dashboard"]) |
|
|
|
if menu == "View All Entries": |
|
view_entries(is_doctor=True) |
|
elif menu == "Patient Dashboard": |
|
display_dashboard(is_doctor=True) |
|
|
|
def add_entry(): |
|
st.header("Add New Headache Entry") |
|
with st.sidebar: |
|
st.subheader("How to Add an Entry") |
|
st.markdown(""" |
|
1. **Date**: Select the date of your migraine. |
|
2. **Pain Level**: Rate your pain from 1 (mild) to 10 (severe). |
|
3. **Duration**: Choose how long your migraine lasted. |
|
4. **Triggers**: Select all factors that may have triggered your migraine. |
|
5. **Symptoms**: Check all symptoms you experienced. |
|
6. **Medications**: List any medications you took. |
|
7. **Notes**: Add any additional observations or comments. |
|
8. **Submit**: Click 'Submit Entry' when you're done. |
|
|
|
Tips: |
|
- Be as accurate and detailed as possible. |
|
- If you're unsure about a trigger or symptom, it's okay to leave it unchecked. |
|
- Use the notes section to mention any unusual circumstances or effects. |
|
""") |
|
with st.form("migraine_entry"): |
|
date = st.date_input("Date") |
|
pain_level = st.slider("Pain Level", 1, 10) |
|
duration = st.selectbox("Duration", ["Less than 1 hour", "1-4 hours", "4-8 hours", "8-24 hours", "More than 24 hours"]) |
|
|
|
triggers = st.multiselect("Triggers", [ |
|
"Stress", "Lack of Sleep", "Dehydration", "Skipped Meals", |
|
"Alcohol", "Caffeine", "Chocolate", "Aged Cheeses", |
|
"Processed Meats", "Artificial Sweeteners", "MSG", |
|
"Weather Changes", "Barometric Pressure Changes", |
|
"Bright Lights", "Loud Noises", "Strong Smells", |
|
"Screen Time", "Reading", "Physical Exertion", |
|
"Hormonal Changes", "Medication Overuse", |
|
"Travel", "Altitude Changes", "Other" |
|
]) |
|
|
|
symptoms = st.multiselect("Symptoms", [ |
|
"Throbbing Pain", "Pulsating Pain", "One-sided Pain", |
|
"Nausea", "Vomiting", "Sensitivity to Light", |
|
"Sensitivity to Sound", "Sensitivity to Smells", |
|
"Blurred Vision", "Visual Aura", "Blind Spots", |
|
"Zigzag Lines in Vision", "Tingling or Numbness", |
|
"Difficulty Speaking", "Weakness", "Dizziness", |
|
"Vertigo", "Neck Stiffness", "Confusion", |
|
"Mood Changes", "Food Cravings", "Frequent Urination", |
|
"Fatigue", "Yawning", "Other" |
|
]) |
|
|
|
medications = st.text_input("Medications taken") |
|
notes = st.text_area("Additional Notes") |
|
|
|
submitted = st.form_submit_button("Submit Entry") |
|
if submitted: |
|
entry_text = f"Date: {date}\nPain Level: {pain_level}\nDuration: {duration}\nTriggers: {', '.join(triggers)}\nSymptoms: {', '.join(symptoms)}\nMedications: {medications}\nNotes: {notes}" |
|
|
|
with st.spinner("Analyzing your entry..."): |
|
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.") |
|
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.") |
|
|
|
new_entry = { |
|
'username': st.session_state.user, |
|
'entry_date': date.isoformat(), |
|
'pain_level': pain_level, |
|
'duration': duration, |
|
'triggers': ', '.join(triggers), |
|
'symptoms': ', '.join(symptoms), |
|
'medications': medications, |
|
'notes': notes, |
|
'doctor_analysis': doctor_analysis, |
|
'patient_advice': patient_advice |
|
} |
|
save_data(new_entry) |
|
st.success("Entry added successfully!") |
|
st.subheader("Advice for You:") |
|
st.write(patient_advice) |
|
|
|
def view_entries(is_doctor): |
|
st.header("Migraine Entries") |
|
with st.sidebar: |
|
st.subheader("How to Use Entries View") |
|
if is_doctor: |
|
st.markdown(""" |
|
1. **Browse Entries**: Scroll through all patient entries. |
|
2. **Expand Details**: Click on an entry to see full details. |
|
3. **Analysis**: Read the doctor's analysis for each entry. |
|
4. **Patient History**: Use this view to track patient progress over time. |
|
|
|
Tips: |
|
- Look for patterns in triggers and symptoms across patients. |
|
- Use this information to inform treatment plans and discussions. |
|
""") |
|
else: |
|
st.markdown(""" |
|
1. **Your History**: Browse through your past migraine entries. |
|
2. **Expand Details**: Click on an entry to see full details. |
|
3. **Advice**: Read the personalized advice for each entry. |
|
4. **Track Progress**: Use this view to see how your migraines change over time. |
|
|
|
Tips: |
|
- Look for patterns in your triggers and symptoms. |
|
- Discuss recurring patterns with your doctor. |
|
- Use the advice to make lifestyle adjustments. |
|
""") |
|
if is_doctor: |
|
user_entries = load_data() |
|
st.subheader("All Patient Entries") |
|
else: |
|
user_entries = load_data(st.session_state.user) |
|
st.subheader("Your Entries") |
|
|
|
user_entries = user_entries.sort_values(by='entry_date', ascending=False) |
|
|
|
if not user_entries.empty: |
|
for _, entry in user_entries.iterrows(): |
|
with st.expander(f"Entry for {entry['username']} on {entry['entry_date']} - Pain Level: {entry['pain_level']}"): |
|
st.write(f"Duration: {entry['duration']}") |
|
st.write(f"Triggers: {entry['triggers']}") |
|
st.write(f"Symptoms: {entry['symptoms']}") |
|
st.write(f"Medications: {entry['medications']}") |
|
st.write(f"Notes: {entry['notes']}") |
|
if is_doctor: |
|
st.write("Doctor's Analysis:", entry['doctor_analysis']) |
|
else: |
|
st.write("Advice for Patient:", entry['patient_advice']) |
|
else: |
|
st.info("No entries found.") |
|
|
|
def display_dashboard(is_doctor): |
|
st.header("Headache Dashboard") |
|
with st.sidebar: |
|
st.subheader("How to Use the Dashboard") |
|
if is_doctor: |
|
st.markdown(""" |
|
1. **Select Patient**: Choose a patient from the dropdown to view their data. |
|
2. **Pain Level Trend**: Observe how pain levels change over time. |
|
3. **Common Triggers**: Identify the most frequent migraine triggers. |
|
4. **Common Symptoms**: See the most reported symptoms. |
|
5. **Statistics**: Review key metrics at a glance. |
|
6. **Recent Entries**: Check the latest migraine reports. |
|
|
|
Tips: |
|
- Use these insights to tailor treatment plans. |
|
- Discuss observed patterns with your patients. |
|
- Look for correlations between triggers, symptoms, and pain levels. |
|
""") |
|
else: |
|
st.markdown(""" |
|
1. **Pain Level Trend**: See how your pain levels have changed over time. |
|
2. **Common Triggers**: Identify your most frequent migraine triggers. |
|
3. **Common Symptoms**: Review your most reported symptoms. |
|
4. **Statistics**: Get a quick overview of your migraine patterns. |
|
5. **Recent Entries**: Review your latest migraine reports. |
|
|
|
Tips: |
|
- Use the pain level trend to track the effectiveness of treatments. |
|
- Pay attention to your common triggers and try to avoid them. |
|
- Discuss any patterns you notice with your doctor. |
|
- Use insights from the dashboard to make informed lifestyle choices. |
|
""") |
|
|
|
if is_doctor: |
|
st.subheader("Select Patient") |
|
all_users = load_data()['username'].unique() |
|
selected_user = st.selectbox("Choose a patient", all_users) |
|
user_entries = load_data(selected_user) |
|
else: |
|
user_entries = load_data(st.session_state.user) |
|
|
|
if not user_entries.empty: |
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
st.subheader("Pain Level Over Time") |
|
fig = px.line(user_entries, x='entry_date', y='pain_level', title='Pain Level Over Time') |
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
with col2: |
|
st.subheader("Common Triggers") |
|
all_triggers = ', '.join(user_entries['triggers'].dropna()).split(', ') |
|
trigger_counts = pd.Series(all_triggers).value_counts().head(5) |
|
fig = px.bar(x=trigger_counts.index, y=trigger_counts.values, labels={'x': 'Trigger', 'y': 'Count'}) |
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
col1, col2 = st.columns(2) |
|
|
|
with col1: |
|
st.subheader("Common Symptoms") |
|
all_symptoms = ', '.join(user_entries['symptoms'].dropna()).split(', ') |
|
symptom_counts = pd.Series(all_symptoms).value_counts().head(5) |
|
fig = px.bar(x=symptom_counts.index, y=symptom_counts.values, labels={'x': 'Symptom', 'y': 'Count'}) |
|
st.plotly_chart(fig, use_container_width=True) |
|
|
|
st.subheader("Migraine Statistics") |
|
col1, col2, col3, col4 = st.columns(4) |
|
col1.metric("Total Entries", len(user_entries)) |
|
col2.metric("Average Pain Level", f"{user_entries['pain_level'].mean():.2f}") |
|
col3.metric("Most Common Trigger", trigger_counts.index[0] if not trigger_counts.empty else "N/A") |
|
col4.metric("Most Common Symptom", symptom_counts.index[0] if not symptom_counts.empty else "N/A") |
|
|
|
st.subheader("Recent Entries") |
|
st.dataframe(user_entries[['entry_date', 'pain_level', 'duration', 'triggers', 'symptoms']].sort_values(by='entry_date', ascending=False).head()) |
|
else: |
|
st.info("No entries found.") |
|
|
|
if __name__ == "__main__": |
|
main() |