Spaces:
Build error
Build error
Upload 22 files
Browse files- AfyaMindSpace/.gitattributes +35 -0
- AfyaMindSpace/.vscode/settings.json +5 -0
- AfyaMindSpace/README.md +13 -0
- AfyaMindSpace/__pycache__/analytics.cpython-313.pyc +0 -0
- AfyaMindSpace/__pycache__/auth.cpython-313.pyc +0 -0
- AfyaMindSpace/__pycache__/chatbox.cpython-313.pyc +0 -0
- AfyaMindSpace/__pycache__/forum.cpython-313.pyc +0 -0
- AfyaMindSpace/__pycache__/goals.cpython-313.pyc +0 -0
- AfyaMindSpace/__pycache__/meditation.cpython-313.pyc +0 -0
- AfyaMindSpace/analytics.py +469 -0
- AfyaMindSpace/app.py +685 -0
- AfyaMindSpace/app.py.new +0 -0
- AfyaMindSpace/auth.py +244 -0
- AfyaMindSpace/chatbox.py +193 -0
- AfyaMindSpace/forum.py +563 -0
- AfyaMindSpace/forum_data.json +1 -0
- AfyaMindSpace/goals.py +778 -0
- AfyaMindSpace/goals_data.json +1 -0
- AfyaMindSpace/meditation.py +563 -0
- AfyaMindSpace/meditation_data.json +1 -0
- AfyaMindSpace/requirements.txt +5 -0
- AfyaMindSpace/user_data.json +1 -0
AfyaMindSpace/.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
AfyaMindSpace/.vscode/settings.json
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"files.associations": {
|
| 3 |
+
"plyconfig.json": "jsonc"
|
| 4 |
+
}
|
| 5 |
+
}
|
AfyaMindSpace/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: AfyaMindSpace
|
| 3 |
+
emoji: 🏢
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
+
sdk: streamlit
|
| 7 |
+
sdk_version: 1.41.1
|
| 8 |
+
app_file: app.py
|
| 9 |
+
pinned: false
|
| 10 |
+
short_description: AfyaMind Space is a technology-driven mental health platform
|
| 11 |
+
---
|
| 12 |
+
|
| 13 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
AfyaMindSpace/__pycache__/analytics.cpython-313.pyc
ADDED
|
Binary file (20.3 kB). View file
|
|
|
AfyaMindSpace/__pycache__/auth.cpython-313.pyc
ADDED
|
Binary file (10 kB). View file
|
|
|
AfyaMindSpace/__pycache__/chatbox.cpython-313.pyc
ADDED
|
Binary file (11.7 kB). View file
|
|
|
AfyaMindSpace/__pycache__/forum.cpython-313.pyc
ADDED
|
Binary file (23.3 kB). View file
|
|
|
AfyaMindSpace/__pycache__/goals.cpython-313.pyc
ADDED
|
Binary file (28.3 kB). View file
|
|
|
AfyaMindSpace/__pycache__/meditation.cpython-313.pyc
ADDED
|
Binary file (19.8 kB). View file
|
|
|
AfyaMindSpace/analytics.py
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import pandas as pd
|
| 3 |
+
import altair as alt
|
| 4 |
+
from datetime import datetime, timedelta
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
# Function to analyze mood data and provide insights
|
| 8 |
+
def analyze_mood_data(mood_history):
|
| 9 |
+
if not mood_history:
|
| 10 |
+
return None
|
| 11 |
+
|
| 12 |
+
# Convert mood history to DataFrame
|
| 13 |
+
mood_df = pd.DataFrame(mood_history, columns=["timestamp", "mood"])
|
| 14 |
+
mood_df["date"] = pd.to_datetime(mood_df["timestamp"]).dt.date
|
| 15 |
+
mood_df["time"] = pd.to_datetime(mood_df["timestamp"]).dt.time
|
| 16 |
+
mood_df["hour"] = pd.to_datetime(mood_df["timestamp"]).dt.hour
|
| 17 |
+
mood_df["day_of_week"] = pd.to_datetime(mood_df["timestamp"]).dt.day_name()
|
| 18 |
+
|
| 19 |
+
# Basic statistics
|
| 20 |
+
total_entries = len(mood_df)
|
| 21 |
+
unique_days = mood_df["date"].nunique()
|
| 22 |
+
most_common_mood = mood_df["mood"].value_counts().index[0]
|
| 23 |
+
mood_counts = mood_df["mood"].value_counts().to_dict()
|
| 24 |
+
|
| 25 |
+
# Time patterns
|
| 26 |
+
mood_by_hour = mood_df.groupby("hour")["mood"].value_counts().unstack().fillna(0)
|
| 27 |
+
mood_by_day = mood_df.groupby("day_of_week")["mood"].value_counts().unstack().fillna(0)
|
| 28 |
+
|
| 29 |
+
# Reorder days of week
|
| 30 |
+
days_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
| 31 |
+
if not mood_by_day.empty:
|
| 32 |
+
mood_by_day = mood_by_day.reindex(days_order)
|
| 33 |
+
|
| 34 |
+
# Mood transitions (how mood changes from one entry to the next)
|
| 35 |
+
mood_transitions = {}
|
| 36 |
+
if len(mood_df) > 1:
|
| 37 |
+
for i in range(len(mood_df) - 1):
|
| 38 |
+
current_mood = mood_df.iloc[i]["mood"]
|
| 39 |
+
next_mood = mood_df.iloc[i+1]["mood"]
|
| 40 |
+
|
| 41 |
+
if current_mood not in mood_transitions:
|
| 42 |
+
mood_transitions[current_mood] = {}
|
| 43 |
+
|
| 44 |
+
if next_mood not in mood_transitions[current_mood]:
|
| 45 |
+
mood_transitions[current_mood][next_mood] = 0
|
| 46 |
+
|
| 47 |
+
mood_transitions[current_mood][next_mood] += 1
|
| 48 |
+
|
| 49 |
+
# Mood streaks
|
| 50 |
+
mood_streaks = {}
|
| 51 |
+
current_streak = 1
|
| 52 |
+
current_mood = None
|
| 53 |
+
|
| 54 |
+
for mood in mood_df["mood"]:
|
| 55 |
+
if mood == current_mood:
|
| 56 |
+
current_streak += 1
|
| 57 |
+
else:
|
| 58 |
+
if current_mood is not None:
|
| 59 |
+
if current_mood not in mood_streaks or current_streak > mood_streaks[current_mood]:
|
| 60 |
+
mood_streaks[current_mood] = current_streak
|
| 61 |
+
|
| 62 |
+
current_mood = mood
|
| 63 |
+
current_streak = 1
|
| 64 |
+
|
| 65 |
+
# Add the last streak
|
| 66 |
+
if current_mood is not None and (current_mood not in mood_streaks or current_streak > mood_streaks[current_mood]):
|
| 67 |
+
mood_streaks[current_mood] = current_streak
|
| 68 |
+
|
| 69 |
+
# Recent trends (last 7 days)
|
| 70 |
+
today = datetime.now().date()
|
| 71 |
+
one_week_ago = today - timedelta(days=7)
|
| 72 |
+
recent_df = mood_df[pd.to_datetime(mood_df["date"]) >= pd.Timestamp(one_week_ago)]
|
| 73 |
+
|
| 74 |
+
recent_mood_counts = recent_df["mood"].value_counts().to_dict() if not recent_df.empty else {}
|
| 75 |
+
recent_most_common = recent_df["mood"].value_counts().index[0] if not recent_df.empty and len(recent_df["mood"].value_counts()) > 0 else None
|
| 76 |
+
|
| 77 |
+
# Calculate mood variability (how much the mood changes)
|
| 78 |
+
mood_values = {
|
| 79 |
+
"happy": 5,
|
| 80 |
+
"calm": 4,
|
| 81 |
+
"anxious": 3,
|
| 82 |
+
"sad": 2,
|
| 83 |
+
"angry": 1
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
mood_df["mood_value"] = mood_df["mood"].map(mood_values)
|
| 87 |
+
mood_variability = mood_df["mood_value"].std() if len(mood_df) > 1 else 0
|
| 88 |
+
|
| 89 |
+
# Prepare data for visualization
|
| 90 |
+
mood_over_time = mood_df[["date", "mood"]].copy()
|
| 91 |
+
mood_over_time["date"] = pd.to_datetime(mood_over_time["date"])
|
| 92 |
+
|
| 93 |
+
return {
|
| 94 |
+
"total_entries": total_entries,
|
| 95 |
+
"unique_days": unique_days,
|
| 96 |
+
"most_common_mood": most_common_mood,
|
| 97 |
+
"mood_counts": mood_counts,
|
| 98 |
+
"mood_by_hour": mood_by_hour,
|
| 99 |
+
"mood_by_day": mood_by_day,
|
| 100 |
+
"mood_transitions": mood_transitions,
|
| 101 |
+
"mood_streaks": mood_streaks,
|
| 102 |
+
"recent_mood_counts": recent_mood_counts,
|
| 103 |
+
"recent_most_common": recent_most_common,
|
| 104 |
+
"mood_variability": mood_variability,
|
| 105 |
+
"mood_over_time": mood_over_time
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
# Function to generate insights based on mood analysis
|
| 109 |
+
def generate_mood_insights(analysis):
|
| 110 |
+
if not analysis:
|
| 111 |
+
return []
|
| 112 |
+
|
| 113 |
+
insights = []
|
| 114 |
+
|
| 115 |
+
# Most common mood
|
| 116 |
+
insights.append(f"Your most common mood is '{analysis['most_common_mood']}'. You've recorded this mood {analysis['mood_counts'].get(analysis['most_common_mood'], 0)} times.")
|
| 117 |
+
|
| 118 |
+
# Recent trends
|
| 119 |
+
if analysis["recent_most_common"]:
|
| 120 |
+
if analysis["recent_most_common"] == analysis["most_common_mood"]:
|
| 121 |
+
insights.append(f"Your mood has been consistent. '{analysis['recent_most_common']}' is your most common mood recently and overall.")
|
| 122 |
+
else:
|
| 123 |
+
insights.append(f"Your mood has shifted recently. While your overall most common mood is '{analysis['most_common_mood']}', recently you've been feeling '{analysis['recent_most_common']}' more often.")
|
| 124 |
+
|
| 125 |
+
# Mood variability
|
| 126 |
+
if analysis["mood_variability"] < 0.5:
|
| 127 |
+
insights.append("Your mood has been very stable, with little variation.")
|
| 128 |
+
elif analysis["mood_variability"] < 1.0:
|
| 129 |
+
insights.append("Your mood shows moderate variation, which is typical.")
|
| 130 |
+
else:
|
| 131 |
+
insights.append("Your mood shows significant variation, with frequent changes.")
|
| 132 |
+
|
| 133 |
+
# Time patterns
|
| 134 |
+
if not analysis["mood_by_hour"].empty:
|
| 135 |
+
# Find the hour with the most positive moods (happy, calm)
|
| 136 |
+
# Handle both DataFrame and Series cases
|
| 137 |
+
positive_moods = analysis["mood_by_hour"].get(["happy", "calm"], pd.Series(0))
|
| 138 |
+
if not positive_moods.empty:
|
| 139 |
+
# Check if positive_moods is a Series or DataFrame
|
| 140 |
+
if isinstance(positive_moods, pd.Series):
|
| 141 |
+
best_hour = positive_moods.idxmax() if not positive_moods.empty else None
|
| 142 |
+
else: # It's a DataFrame
|
| 143 |
+
best_hour = positive_moods.sum(axis=1).idxmax() if not positive_moods.sum(axis=1).empty else None
|
| 144 |
+
|
| 145 |
+
if best_hour is not None:
|
| 146 |
+
am_pm = "AM" if best_hour < 12 else "PM"
|
| 147 |
+
hour_12 = best_hour % 12
|
| 148 |
+
if hour_12 == 0:
|
| 149 |
+
hour_12 = 12
|
| 150 |
+
insights.append(f"You tend to feel your best around {hour_12} {am_pm}.")
|
| 151 |
+
|
| 152 |
+
# Find the hour with the most negative moods (sad, angry, anxious)
|
| 153 |
+
negative_moods = analysis["mood_by_hour"].get(["sad", "angry", "anxious"], pd.Series(0))
|
| 154 |
+
if not negative_moods.empty:
|
| 155 |
+
# Check if negative_moods is a Series or DataFrame
|
| 156 |
+
if isinstance(negative_moods, pd.Series):
|
| 157 |
+
worst_hour = negative_moods.idxmax() if not negative_moods.empty else None
|
| 158 |
+
else: # It's a DataFrame
|
| 159 |
+
worst_hour = negative_moods.sum(axis=1).idxmax() if not negative_moods.sum(axis=1).empty else None
|
| 160 |
+
|
| 161 |
+
if worst_hour is not None:
|
| 162 |
+
am_pm = "AM" if worst_hour < 12 else "PM"
|
| 163 |
+
hour_12 = worst_hour % 12
|
| 164 |
+
if hour_12 == 0:
|
| 165 |
+
hour_12 = 12
|
| 166 |
+
insights.append(f"You tend to experience more challenging emotions around {hour_12} {am_pm}.")
|
| 167 |
+
|
| 168 |
+
# Day of week patterns
|
| 169 |
+
if not analysis["mood_by_day"].empty:
|
| 170 |
+
# Find the day with the most positive moods
|
| 171 |
+
positive_moods = analysis["mood_by_day"].get(["happy", "calm"], pd.Series(0))
|
| 172 |
+
if not positive_moods.empty:
|
| 173 |
+
# Check if positive_moods is a Series or DataFrame
|
| 174 |
+
if isinstance(positive_moods, pd.Series):
|
| 175 |
+
best_day = positive_moods.idxmax() if not positive_moods.empty else None
|
| 176 |
+
else: # It's a DataFrame
|
| 177 |
+
best_day = positive_moods.sum(axis=1).idxmax() if not positive_moods.sum(axis=1).empty else None
|
| 178 |
+
|
| 179 |
+
if best_day is not None:
|
| 180 |
+
insights.append(f"{best_day} tends to be your best day of the week emotionally.")
|
| 181 |
+
|
| 182 |
+
# Find the day with the most negative moods
|
| 183 |
+
negative_moods = analysis["mood_by_day"].get(["sad", "angry", "anxious"], pd.Series(0))
|
| 184 |
+
if not negative_moods.empty:
|
| 185 |
+
# Check if negative_moods is a Series or DataFrame
|
| 186 |
+
if isinstance(negative_moods, pd.Series):
|
| 187 |
+
worst_day = negative_moods.idxmax() if not negative_moods.empty else None
|
| 188 |
+
else: # It's a DataFrame
|
| 189 |
+
worst_day = negative_moods.sum(axis=1).idxmax() if not negative_moods.sum(axis=1).empty else None
|
| 190 |
+
|
| 191 |
+
if worst_day is not None:
|
| 192 |
+
insights.append(f"{worst_day} tends to be your most challenging day emotionally.")
|
| 193 |
+
|
| 194 |
+
# Mood transitions
|
| 195 |
+
if analysis["mood_transitions"]:
|
| 196 |
+
for mood, transitions in analysis["mood_transitions"].items():
|
| 197 |
+
most_common_next = max(transitions.items(), key=lambda x: x[1])[0] if transitions else None
|
| 198 |
+
if most_common_next and most_common_next != mood:
|
| 199 |
+
insights.append(f"When you feel '{mood}', you most often transition to feeling '{most_common_next}' next.")
|
| 200 |
+
|
| 201 |
+
# Mood streaks
|
| 202 |
+
if analysis["mood_streaks"]:
|
| 203 |
+
longest_streak_mood = max(analysis["mood_streaks"].items(), key=lambda x: x[1])[0]
|
| 204 |
+
longest_streak = analysis["mood_streaks"][longest_streak_mood]
|
| 205 |
+
if longest_streak > 1:
|
| 206 |
+
insights.append(f"Your longest mood streak was feeling '{longest_streak_mood}' for {longest_streak} consecutive recordings.")
|
| 207 |
+
|
| 208 |
+
# Consistency in tracking
|
| 209 |
+
tracking_consistency = (analysis["unique_days"] / 7) * 100 if analysis["unique_days"] <= 7 else 100
|
| 210 |
+
if tracking_consistency < 50:
|
| 211 |
+
insights.append("You're tracking your mood occasionally. More consistent tracking will provide better insights.")
|
| 212 |
+
elif tracking_consistency < 80:
|
| 213 |
+
insights.append("You're tracking your mood fairly regularly. Keep it up!")
|
| 214 |
+
else:
|
| 215 |
+
insights.append("You're very consistent with tracking your mood, which gives you the most accurate insights.")
|
| 216 |
+
|
| 217 |
+
return insights
|
| 218 |
+
|
| 219 |
+
# Function to recommend actions based on mood analysis
|
| 220 |
+
def recommend_mood_actions(analysis):
|
| 221 |
+
if not analysis:
|
| 222 |
+
return {}
|
| 223 |
+
|
| 224 |
+
recommendations = {
|
| 225 |
+
"happy": [
|
| 226 |
+
"Maintain your positive state by engaging in activities you enjoy",
|
| 227 |
+
"Share your positive energy with others through connection",
|
| 228 |
+
"Practice gratitude to reinforce positive emotions",
|
| 229 |
+
"Document what contributed to your happiness for future reference"
|
| 230 |
+
],
|
| 231 |
+
"calm": [
|
| 232 |
+
"Continue mindfulness practices that help maintain this balanced state",
|
| 233 |
+
"Use this calm state for creative or focused work",
|
| 234 |
+
"Practice deep breathing to maintain your sense of calm",
|
| 235 |
+
"Engage in gentle movement like walking or stretching"
|
| 236 |
+
],
|
| 237 |
+
"anxious": [
|
| 238 |
+
"Try grounding techniques like the 5-4-3-2-1 sensory exercise",
|
| 239 |
+
"Practice deep breathing or progressive muscle relaxation",
|
| 240 |
+
"Limit caffeine and other stimulants",
|
| 241 |
+
"Break large tasks into smaller, manageable steps",
|
| 242 |
+
"Consider talking to someone you trust about your feelings"
|
| 243 |
+
],
|
| 244 |
+
"sad": [
|
| 245 |
+
"Engage in light physical activity like walking",
|
| 246 |
+
"Reach out to a supportive friend or family member",
|
| 247 |
+
"Practice self-compassion and avoid self-criticism",
|
| 248 |
+
"Listen to uplifting music or watch a comforting show",
|
| 249 |
+
"Set small, achievable goals for the day"
|
| 250 |
+
],
|
| 251 |
+
"angry": [
|
| 252 |
+
"Take a timeout and practice deep breathing",
|
| 253 |
+
"Engage in physical activity to release tension",
|
| 254 |
+
"Write down your thoughts to process them",
|
| 255 |
+
"Use 'I' statements when communicating your feelings",
|
| 256 |
+
"Practice progressive muscle relaxation to release physical tension"
|
| 257 |
+
]
|
| 258 |
+
}
|
| 259 |
+
|
| 260 |
+
# Determine which recommendations to prioritize based on recent moods
|
| 261 |
+
prioritized_recommendations = {}
|
| 262 |
+
|
| 263 |
+
if analysis["recent_mood_counts"]:
|
| 264 |
+
# Sort recent moods by frequency
|
| 265 |
+
sorted_moods = sorted(analysis["recent_mood_counts"].items(), key=lambda x: x[1], reverse=True)
|
| 266 |
+
|
| 267 |
+
# Add recommendations for the top 2 most frequent moods
|
| 268 |
+
for mood, count in sorted_moods[:2]:
|
| 269 |
+
if mood in recommendations:
|
| 270 |
+
prioritized_recommendations[mood] = recommendations[mood]
|
| 271 |
+
else:
|
| 272 |
+
# If no recent data, use overall most common mood
|
| 273 |
+
most_common = analysis["most_common_mood"]
|
| 274 |
+
if most_common in recommendations:
|
| 275 |
+
prioritized_recommendations[most_common] = recommendations[most_common]
|
| 276 |
+
|
| 277 |
+
return prioritized_recommendations
|
| 278 |
+
|
| 279 |
+
# UI components for mood analytics
|
| 280 |
+
def mood_analytics_page():
|
| 281 |
+
st.subheader("Mood Analytics & Insights")
|
| 282 |
+
|
| 283 |
+
if not st.session_state.get("mood_history", []):
|
| 284 |
+
st.warning("No mood data available. Start tracking your mood to see analytics.")
|
| 285 |
+
return
|
| 286 |
+
|
| 287 |
+
# Analyze mood data
|
| 288 |
+
analysis = analyze_mood_data(st.session_state.mood_history)
|
| 289 |
+
|
| 290 |
+
if not analysis:
|
| 291 |
+
st.warning("Not enough mood data to generate analytics.")
|
| 292 |
+
return
|
| 293 |
+
|
| 294 |
+
# Display basic statistics
|
| 295 |
+
st.write(f"**Total mood entries:** {analysis['total_entries']}")
|
| 296 |
+
st.write(f"**Days with mood tracking:** {analysis['unique_days']}")
|
| 297 |
+
st.write(f"**Most common mood:** {analysis['most_common_mood']}")
|
| 298 |
+
|
| 299 |
+
# Mood distribution chart
|
| 300 |
+
st.subheader("Mood Distribution")
|
| 301 |
+
|
| 302 |
+
mood_counts_df = pd.DataFrame({
|
| 303 |
+
"mood": list(analysis["mood_counts"].keys()),
|
| 304 |
+
"count": list(analysis["mood_counts"].values())
|
| 305 |
+
})
|
| 306 |
+
|
| 307 |
+
# Define color scale for moods
|
| 308 |
+
color_scale = {
|
| 309 |
+
"happy": "#FFD700", # Gold
|
| 310 |
+
"calm": "#90EE90", # Light green
|
| 311 |
+
"anxious": "#ADD8E6", # Light blue
|
| 312 |
+
"sad": "#A9A9A9", # Dark gray
|
| 313 |
+
"angry": "#FF6347" # Tomato
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
chart = alt.Chart(mood_counts_df).mark_bar().encode(
|
| 317 |
+
x=alt.X("mood:N", title="Mood", sort=list(color_scale.keys())),
|
| 318 |
+
y=alt.Y("count:Q", title="Frequency"),
|
| 319 |
+
color=alt.Color("mood:N", scale=alt.Scale(
|
| 320 |
+
domain=list(color_scale.keys()),
|
| 321 |
+
range=list(color_scale.values())
|
| 322 |
+
))
|
| 323 |
+
).properties(
|
| 324 |
+
title="Overall Mood Distribution",
|
| 325 |
+
width=600,
|
| 326 |
+
height=300
|
| 327 |
+
)
|
| 328 |
+
|
| 329 |
+
st.altair_chart(chart, use_container_width=True)
|
| 330 |
+
|
| 331 |
+
# Mood over time
|
| 332 |
+
st.subheader("Mood Over Time")
|
| 333 |
+
|
| 334 |
+
# Prepare data for time series chart
|
| 335 |
+
mood_time_df = analysis["mood_over_time"].copy()
|
| 336 |
+
|
| 337 |
+
# Create a time series chart
|
| 338 |
+
time_chart = alt.Chart(mood_time_df).mark_line(point=True).encode(
|
| 339 |
+
x=alt.X("date:T", title="Date"),
|
| 340 |
+
y=alt.Y("mood:N", title="Mood", sort=list(reversed(list(color_scale.keys())))),
|
| 341 |
+
color=alt.Color("mood:N", scale=alt.Scale(
|
| 342 |
+
domain=list(color_scale.keys()),
|
| 343 |
+
range=list(color_scale.values())
|
| 344 |
+
))
|
| 345 |
+
).properties(
|
| 346 |
+
title="Mood Trends Over Time",
|
| 347 |
+
width=600,
|
| 348 |
+
height=300
|
| 349 |
+
)
|
| 350 |
+
|
| 351 |
+
st.altair_chart(time_chart, use_container_width=True)
|
| 352 |
+
|
| 353 |
+
# Mood by time of day
|
| 354 |
+
st.subheader("Mood Patterns by Time of Day")
|
| 355 |
+
|
| 356 |
+
if not analysis["mood_by_hour"].empty:
|
| 357 |
+
# Convert the mood by hour data to a format suitable for Altair
|
| 358 |
+
hour_data = []
|
| 359 |
+
for hour, moods in analysis["mood_by_hour"].iterrows():
|
| 360 |
+
for mood, count in moods.items():
|
| 361 |
+
if count > 0: # Only include non-zero counts
|
| 362 |
+
hour_data.append({
|
| 363 |
+
"hour": hour,
|
| 364 |
+
"mood": mood,
|
| 365 |
+
"count": count
|
| 366 |
+
})
|
| 367 |
+
|
| 368 |
+
hour_df = pd.DataFrame(hour_data)
|
| 369 |
+
|
| 370 |
+
if not hour_df.empty:
|
| 371 |
+
hour_chart = alt.Chart(hour_df).mark_bar().encode(
|
| 372 |
+
x=alt.X("hour:O", title="Hour of Day"),
|
| 373 |
+
y=alt.Y("count:Q", title="Frequency"),
|
| 374 |
+
color=alt.Color("mood:N", scale=alt.Scale(
|
| 375 |
+
domain=list(color_scale.keys()),
|
| 376 |
+
range=list(color_scale.values())
|
| 377 |
+
)),
|
| 378 |
+
tooltip=["hour", "mood", "count"]
|
| 379 |
+
).properties(
|
| 380 |
+
title="Mood Distribution by Hour of Day",
|
| 381 |
+
width=600,
|
| 382 |
+
height=300
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
st.altair_chart(hour_chart, use_container_width=True)
|
| 386 |
+
else:
|
| 387 |
+
st.write("Not enough data to show mood patterns by time of day.")
|
| 388 |
+
else:
|
| 389 |
+
st.write("Not enough data to show mood patterns by time of day.")
|
| 390 |
+
|
| 391 |
+
# Mood by day of week
|
| 392 |
+
st.subheader("Mood Patterns by Day of Week")
|
| 393 |
+
|
| 394 |
+
if not analysis["mood_by_day"].empty:
|
| 395 |
+
# Convert the mood by day data to a format suitable for Altair
|
| 396 |
+
day_data = []
|
| 397 |
+
for day, moods in analysis["mood_by_day"].iterrows():
|
| 398 |
+
for mood, count in moods.items():
|
| 399 |
+
if count > 0: # Only include non-zero counts
|
| 400 |
+
day_data.append({
|
| 401 |
+
"day": day,
|
| 402 |
+
"mood": mood,
|
| 403 |
+
"count": count
|
| 404 |
+
})
|
| 405 |
+
|
| 406 |
+
day_df = pd.DataFrame(day_data)
|
| 407 |
+
|
| 408 |
+
if not day_df.empty:
|
| 409 |
+
# Define the order of days
|
| 410 |
+
days_order = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
|
| 411 |
+
|
| 412 |
+
day_chart = alt.Chart(day_df).mark_bar().encode(
|
| 413 |
+
x=alt.X("day:N", title="Day of Week", sort=days_order),
|
| 414 |
+
y=alt.Y("count:Q", title="Frequency"),
|
| 415 |
+
color=alt.Color("mood:N", scale=alt.Scale(
|
| 416 |
+
domain=list(color_scale.keys()),
|
| 417 |
+
range=list(color_scale.values())
|
| 418 |
+
)),
|
| 419 |
+
tooltip=["day", "mood", "count"]
|
| 420 |
+
).properties(
|
| 421 |
+
title="Mood Distribution by Day of Week",
|
| 422 |
+
width=600,
|
| 423 |
+
height=300
|
| 424 |
+
)
|
| 425 |
+
|
| 426 |
+
st.altair_chart(day_chart, use_container_width=True)
|
| 427 |
+
else:
|
| 428 |
+
st.write("Not enough data to show mood patterns by day of week.")
|
| 429 |
+
else:
|
| 430 |
+
st.write("Not enough data to show mood patterns by day of week.")
|
| 431 |
+
|
| 432 |
+
# Insights
|
| 433 |
+
st.subheader("Mood Insights")
|
| 434 |
+
|
| 435 |
+
insights = generate_mood_insights(analysis)
|
| 436 |
+
|
| 437 |
+
for insight in insights:
|
| 438 |
+
st.markdown(f"- {insight}")
|
| 439 |
+
|
| 440 |
+
# Recommendations
|
| 441 |
+
st.subheader("Recommended Actions")
|
| 442 |
+
|
| 443 |
+
recommendations = recommend_mood_actions(analysis)
|
| 444 |
+
|
| 445 |
+
if recommendations:
|
| 446 |
+
for mood, actions in recommendations.items():
|
| 447 |
+
with st.expander(f"When you're feeling {mood}"):
|
| 448 |
+
for action in actions:
|
| 449 |
+
st.markdown(f"- {action}")
|
| 450 |
+
else:
|
| 451 |
+
st.write("Continue tracking your mood to receive personalized recommendations.")
|
| 452 |
+
|
| 453 |
+
# Export data option
|
| 454 |
+
st.subheader("Export Your Mood Data")
|
| 455 |
+
|
| 456 |
+
if st.button("Export Mood Data as CSV"):
|
| 457 |
+
# Create a DataFrame from mood history
|
| 458 |
+
export_df = pd.DataFrame(st.session_state.mood_history, columns=["timestamp", "mood"])
|
| 459 |
+
|
| 460 |
+
# Convert to CSV
|
| 461 |
+
csv = export_df.to_csv(index=False)
|
| 462 |
+
|
| 463 |
+
# Create a download button
|
| 464 |
+
st.download_button(
|
| 465 |
+
label="Download CSV",
|
| 466 |
+
data=csv,
|
| 467 |
+
file_name="mood_history.csv",
|
| 468 |
+
mime="text/csv"
|
| 469 |
+
)
|
AfyaMindSpace/app.py
ADDED
|
@@ -0,0 +1,685 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
from datetime import datetime, timedelta
|
| 3 |
+
import random
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import altair as alt
|
| 6 |
+
|
| 7 |
+
# Import custom modules
|
| 8 |
+
from auth import auth_page
|
| 9 |
+
from forum import forum_page
|
| 10 |
+
from meditation import meditation_page
|
| 11 |
+
from goals import goals_page
|
| 12 |
+
from analytics import mood_analytics_page
|
| 13 |
+
from chatbox import create_chat_interface
|
| 14 |
+
|
| 15 |
+
# Set page configuration
|
| 16 |
+
st.set_page_config(
|
| 17 |
+
page_title="AfyaMind Space",
|
| 18 |
+
page_icon="🧠",
|
| 19 |
+
layout="wide",
|
| 20 |
+
initial_sidebar_state="expanded"
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
# Custom CSS for better UI
|
| 24 |
+
st.markdown("""
|
| 25 |
+
<style>
|
| 26 |
+
.main-header {
|
| 27 |
+
font-size: 2.5rem;
|
| 28 |
+
color: #4A90E2;
|
| 29 |
+
text-align: center;
|
| 30 |
+
margin-bottom: 1rem;
|
| 31 |
+
}
|
| 32 |
+
.subheader {
|
| 33 |
+
font-size: 1.5rem;
|
| 34 |
+
color: #6C757D;
|
| 35 |
+
margin-bottom: 1rem;
|
| 36 |
+
}
|
| 37 |
+
.resource-card {
|
| 38 |
+
background-color: #F8F9FA;
|
| 39 |
+
padding: 1rem;
|
| 40 |
+
border-radius: 0.5rem;
|
| 41 |
+
margin-bottom: 1rem;
|
| 42 |
+
border-left: 4px solid #4A90E2;
|
| 43 |
+
}
|
| 44 |
+
.resource-title {
|
| 45 |
+
font-weight: bold;
|
| 46 |
+
color: #4A90E2;
|
| 47 |
+
}
|
| 48 |
+
.resource-type {
|
| 49 |
+
color: #6C757D;
|
| 50 |
+
font-size: 0.8rem;
|
| 51 |
+
}
|
| 52 |
+
.action-button {
|
| 53 |
+
margin-right: 0.5rem;
|
| 54 |
+
}
|
| 55 |
+
</style>
|
| 56 |
+
""", unsafe_allow_html=True)
|
| 57 |
+
|
| 58 |
+
# Mock database for user data and resources
|
| 59 |
+
if "mood_history" not in st.session_state:
|
| 60 |
+
st.session_state.mood_history = []
|
| 61 |
+
|
| 62 |
+
if "streak" not in st.session_state:
|
| 63 |
+
st.session_state.streak = 0
|
| 64 |
+
|
| 65 |
+
if "last_login" not in st.session_state:
|
| 66 |
+
st.session_state.last_login = None
|
| 67 |
+
|
| 68 |
+
if "personalized_resources" not in st.session_state:
|
| 69 |
+
st.session_state.personalized_resources = []
|
| 70 |
+
|
| 71 |
+
if "saved_resources" not in st.session_state:
|
| 72 |
+
st.session_state.saved_resources = []
|
| 73 |
+
|
| 74 |
+
if "liked_resources" not in st.session_state:
|
| 75 |
+
st.session_state.liked_resources = []
|
| 76 |
+
|
| 77 |
+
if "comments" not in st.session_state:
|
| 78 |
+
st.session_state.comments = {}
|
| 79 |
+
|
| 80 |
+
if "user_name" not in st.session_state:
|
| 81 |
+
st.session_state.user_name = ""
|
| 82 |
+
|
| 83 |
+
if "show_comment_input" not in st.session_state:
|
| 84 |
+
st.session_state.show_comment_input = {}
|
| 85 |
+
|
| 86 |
+
# Enhanced resources with more detailed information and tags
|
| 87 |
+
resources = {
|
| 88 |
+
"stress_management": [
|
| 89 |
+
{
|
| 90 |
+
"title": "5 Ways to Manage Stress",
|
| 91 |
+
"link": "https://example.com/stress-management",
|
| 92 |
+
"type": "Article",
|
| 93 |
+
"description": "Learn practical techniques to manage daily stress and anxiety.",
|
| 94 |
+
"tags": ["stress", "anxiety", "coping skills"],
|
| 95 |
+
"difficulty": "Beginner"
|
| 96 |
+
},
|
| 97 |
+
{
|
| 98 |
+
"title": "Mindfulness Techniques",
|
| 99 |
+
"link": "https://example.com/mindfulness",
|
| 100 |
+
"type": "Video",
|
| 101 |
+
"description": "A 10-minute guided video on mindfulness practices for stress reduction.",
|
| 102 |
+
"tags": ["mindfulness", "meditation", "stress"],
|
| 103 |
+
"difficulty": "Beginner"
|
| 104 |
+
},
|
| 105 |
+
{
|
| 106 |
+
"title": "Stress Relief Tips",
|
| 107 |
+
"link": "https://example.com/stress-relief",
|
| 108 |
+
"type": "Podcast",
|
| 109 |
+
"description": "Expert advice on managing stress in school and social situations.",
|
| 110 |
+
"tags": ["stress", "school", "social anxiety"],
|
| 111 |
+
"difficulty": "Intermediate"
|
| 112 |
+
}
|
| 113 |
+
],
|
| 114 |
+
"mindfulness": [
|
| 115 |
+
{
|
| 116 |
+
"title": "Introduction to Mindfulness",
|
| 117 |
+
"link": "https://example.com/intro-mindfulness",
|
| 118 |
+
"type": "Article",
|
| 119 |
+
"description": "A beginner's guide to mindfulness and its benefits for mental health.",
|
| 120 |
+
"tags": ["mindfulness", "beginners", "mental health"],
|
| 121 |
+
"difficulty": "Beginner"
|
| 122 |
+
},
|
| 123 |
+
{
|
| 124 |
+
"title": "Guided Meditation",
|
| 125 |
+
"link": "https://example.com/guided-meditation",
|
| 126 |
+
"type": "Video",
|
| 127 |
+
"description": "A 15-minute guided meditation for anxiety and stress relief.",
|
| 128 |
+
"tags": ["meditation", "anxiety", "relaxation"],
|
| 129 |
+
"difficulty": "Beginner"
|
| 130 |
+
},
|
| 131 |
+
{
|
| 132 |
+
"title": "Daily Mindfulness Practices",
|
| 133 |
+
"link": "https://example.com/daily-mindfulness",
|
| 134 |
+
"type": "Podcast",
|
| 135 |
+
"description": "Simple mindfulness exercises you can incorporate into your daily routine.",
|
| 136 |
+
"tags": ["mindfulness", "daily practice", "habits"],
|
| 137 |
+
"difficulty": "Intermediate"
|
| 138 |
+
}
|
| 139 |
+
],
|
| 140 |
+
"emotional_intelligence": [
|
| 141 |
+
{
|
| 142 |
+
"title": "Understanding Emotions",
|
| 143 |
+
"link": "https://example.com/understanding-emotions",
|
| 144 |
+
"type": "Article",
|
| 145 |
+
"description": "Learn to identify and understand different emotions and their triggers.",
|
| 146 |
+
"tags": ["emotions", "self-awareness", "emotional intelligence"],
|
| 147 |
+
"difficulty": "Beginner"
|
| 148 |
+
},
|
| 149 |
+
{
|
| 150 |
+
"title": "Building Emotional Resilience",
|
| 151 |
+
"link": "https://example.com/emotional-resilience",
|
| 152 |
+
"type": "Video",
|
| 153 |
+
"description": "Techniques to build emotional resilience and bounce back from challenges.",
|
| 154 |
+
"tags": ["resilience", "coping", "emotional strength"],
|
| 155 |
+
"difficulty": "Intermediate"
|
| 156 |
+
},
|
| 157 |
+
{
|
| 158 |
+
"title": "Emotional Intelligence 101",
|
| 159 |
+
"link": "https://example.com/emotional-intelligence",
|
| 160 |
+
"type": "Podcast",
|
| 161 |
+
"description": "An introduction to emotional intelligence and why it matters for teens.",
|
| 162 |
+
"tags": ["emotional intelligence", "teens", "social skills"],
|
| 163 |
+
"difficulty": "Beginner"
|
| 164 |
+
}
|
| 165 |
+
],
|
| 166 |
+
"depression": [
|
| 167 |
+
{
|
| 168 |
+
"title": "Understanding Depression in Teens",
|
| 169 |
+
"link": "https://example.com/teen-depression",
|
| 170 |
+
"type": "Article",
|
| 171 |
+
"description": "Signs, symptoms, and coping strategies for depression in adolescents.",
|
| 172 |
+
"tags": ["depression", "teens", "mental health"],
|
| 173 |
+
"difficulty": "Intermediate"
|
| 174 |
+
},
|
| 175 |
+
{
|
| 176 |
+
"title": "Overcoming Negative Thoughts",
|
| 177 |
+
"link": "https://example.com/negative-thoughts",
|
| 178 |
+
"type": "Video",
|
| 179 |
+
"description": "Cognitive techniques to challenge and reframe negative thinking patterns.",
|
| 180 |
+
"tags": ["cognitive techniques", "negative thoughts", "depression"],
|
| 181 |
+
"difficulty": "Intermediate"
|
| 182 |
+
}
|
| 183 |
+
],
|
| 184 |
+
"social_skills": [
|
| 185 |
+
{
|
| 186 |
+
"title": "Building Healthy Relationships",
|
| 187 |
+
"link": "https://example.com/healthy-relationships",
|
| 188 |
+
"type": "Article",
|
| 189 |
+
"description": "Tips for developing and maintaining healthy friendships and relationships.",
|
| 190 |
+
"tags": ["relationships", "communication", "social skills"],
|
| 191 |
+
"difficulty": "Beginner"
|
| 192 |
+
},
|
| 193 |
+
{
|
| 194 |
+
"title": "Effective Communication Skills",
|
| 195 |
+
"link": "https://example.com/communication-skills",
|
| 196 |
+
"type": "Video",
|
| 197 |
+
"description": "Learn how to express yourself clearly and listen effectively to others.",
|
| 198 |
+
"tags": ["communication", "listening", "expression"],
|
| 199 |
+
"difficulty": "Intermediate"
|
| 200 |
+
}
|
| 201 |
+
]
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
# Function to toggle comment input visibility
|
| 205 |
+
def toggle_comment_input(resource_title):
|
| 206 |
+
if resource_title in st.session_state.show_comment_input:
|
| 207 |
+
st.session_state.show_comment_input[resource_title] = not st.session_state.show_comment_input[resource_title]
|
| 208 |
+
else:
|
| 209 |
+
st.session_state.show_comment_input[resource_title] = True
|
| 210 |
+
|
| 211 |
+
# Function to add comment
|
| 212 |
+
def add_comment(resource_title, comment):
|
| 213 |
+
if resource_title not in st.session_state.comments:
|
| 214 |
+
st.session_state.comments[resource_title] = []
|
| 215 |
+
st.session_state.comments[resource_title].append(comment)
|
| 216 |
+
st.session_state.show_comment_input[resource_title] = False
|
| 217 |
+
|
| 218 |
+
# Function to like resource
|
| 219 |
+
def like_resource(resource):
|
| 220 |
+
if resource not in st.session_state.liked_resources:
|
| 221 |
+
st.session_state.liked_resources.append(resource)
|
| 222 |
+
|
| 223 |
+
# Function to bookmark resource
|
| 224 |
+
def bookmark_resource(resource):
|
| 225 |
+
if resource not in st.session_state.saved_resources:
|
| 226 |
+
st.session_state.saved_resources.append(resource)
|
| 227 |
+
|
| 228 |
+
# Function to track mood with improved UI
|
| 229 |
+
def track_mood():
|
| 230 |
+
st.markdown("<h2 class='subheader'>Track Your Mood</h2>", unsafe_allow_html=True)
|
| 231 |
+
|
| 232 |
+
col1, col2 = st.columns([3, 2])
|
| 233 |
+
|
| 234 |
+
with col1:
|
| 235 |
+
# Mood tracking with emojis
|
| 236 |
+
st.write("How are you feeling today?")
|
| 237 |
+
mood_options = {
|
| 238 |
+
"😊 Happy": "happy",
|
| 239 |
+
"😢 Sad": "sad",
|
| 240 |
+
"😰 Anxious": "anxious",
|
| 241 |
+
"😠 Angry": "angry",
|
| 242 |
+
"😌 Calm": "calm"
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
mood_cols = st.columns(len(mood_options))
|
| 246 |
+
selected_mood = None
|
| 247 |
+
|
| 248 |
+
for i, (emoji_mood, mood_value) in enumerate(mood_options.items()):
|
| 249 |
+
with mood_cols[i]:
|
| 250 |
+
if st.button(emoji_mood, key=f"mood_{mood_value}"):
|
| 251 |
+
selected_mood = mood_value
|
| 252 |
+
|
| 253 |
+
if selected_mood:
|
| 254 |
+
# Check if this is a new day login
|
| 255 |
+
today = datetime.now().date()
|
| 256 |
+
if st.session_state.last_login is None or datetime.strptime(st.session_state.last_login, "%Y-%m-%d").date() < today:
|
| 257 |
+
st.session_state.streak += 1
|
| 258 |
+
st.session_state.last_login = today.strftime("%Y-%m-%d")
|
| 259 |
+
|
| 260 |
+
# Record the mood
|
| 261 |
+
st.session_state.mood_history.append((datetime.now().strftime("%Y-%m-%d %H:%M:%S"), selected_mood))
|
| 262 |
+
st.success(f"Your mood '{selected_mood}' has been recorded. Thank you for sharing!")
|
| 263 |
+
|
| 264 |
+
# Show streak celebration if streak is a multiple of 5
|
| 265 |
+
if st.session_state.streak > 0 and st.session_state.streak % 5 == 0:
|
| 266 |
+
st.balloons()
|
| 267 |
+
st.success(f"🎉 Congratulations! You've maintained a {st.session_state.streak}-day streak of mood tracking!")
|
| 268 |
+
|
| 269 |
+
with col2:
|
| 270 |
+
# Display mood statistics and streak
|
| 271 |
+
if st.session_state.mood_history:
|
| 272 |
+
st.write(f"🔥 Current streak: {st.session_state.streak} days")
|
| 273 |
+
|
| 274 |
+
# Create a mood history dataframe for visualization
|
| 275 |
+
if len(st.session_state.mood_history) > 0:
|
| 276 |
+
mood_df = pd.DataFrame(st.session_state.mood_history, columns=["timestamp", "mood"])
|
| 277 |
+
mood_df["date"] = pd.to_datetime(mood_df["timestamp"]).dt.date
|
| 278 |
+
|
| 279 |
+
# Count moods for the past week
|
| 280 |
+
today = datetime.now().date()
|
| 281 |
+
one_week_ago = today - timedelta(days=7)
|
| 282 |
+
recent_moods = mood_df[pd.to_datetime(mood_df["date"]) >= pd.Timestamp(one_week_ago)]
|
| 283 |
+
|
| 284 |
+
if not recent_moods.empty:
|
| 285 |
+
mood_counts = recent_moods["mood"].value_counts().reset_index()
|
| 286 |
+
mood_counts.columns = ["mood", "count"]
|
| 287 |
+
|
| 288 |
+
# Create a bar chart
|
| 289 |
+
chart = alt.Chart(mood_counts).mark_bar().encode(
|
| 290 |
+
x=alt.X("mood:N", title="Mood"),
|
| 291 |
+
y=alt.Y("count:Q", title="Frequency"),
|
| 292 |
+
color=alt.Color("mood:N", scale=alt.Scale(
|
| 293 |
+
domain=["happy", "calm", "anxious", "sad", "angry"],
|
| 294 |
+
range=["#FFD700", "#90EE90", "#ADD8E6", "#A9A9A9", "#FF6347"]
|
| 295 |
+
))
|
| 296 |
+
).properties(
|
| 297 |
+
title="Your Mood Over the Past Week",
|
| 298 |
+
width=300,
|
| 299 |
+
height=200
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
st.altair_chart(chart, use_container_width=True)
|
| 303 |
+
else:
|
| 304 |
+
st.info("Track your mood for a week to see mood patterns.")
|
| 305 |
+
else:
|
| 306 |
+
st.info("Start tracking your mood to see statistics.")
|
| 307 |
+
|
| 308 |
+
# Function to provide personalized resources with improved recommendation algorithm
|
| 309 |
+
def recommend_resources():
|
| 310 |
+
st.markdown("<h2 class='subheader'>Personalized Resources</h2>", unsafe_allow_html=True)
|
| 311 |
+
|
| 312 |
+
if not st.session_state.mood_history:
|
| 313 |
+
st.warning("No mood data available. Please track your mood first.")
|
| 314 |
+
return
|
| 315 |
+
|
| 316 |
+
# Get the latest mood and the mood history
|
| 317 |
+
latest_mood = st.session_state.mood_history[-1][1]
|
| 318 |
+
|
| 319 |
+
# Create a more sophisticated recommendation algorithm
|
| 320 |
+
# Count mood frequencies from the last 5 entries
|
| 321 |
+
recent_moods = [mood for _, mood in st.session_state.mood_history[-5:]]
|
| 322 |
+
mood_counts = {}
|
| 323 |
+
for mood in recent_moods:
|
| 324 |
+
if mood in mood_counts:
|
| 325 |
+
mood_counts[mood] += 1
|
| 326 |
+
else:
|
| 327 |
+
mood_counts[mood] = 1
|
| 328 |
+
|
| 329 |
+
# Determine primary and secondary mood states
|
| 330 |
+
primary_mood = max(mood_counts, key=mood_counts.get)
|
| 331 |
+
|
| 332 |
+
# Resource recommendation logic based on mood patterns
|
| 333 |
+
recommended_categories = []
|
| 334 |
+
|
| 335 |
+
if primary_mood in ["sad", "angry"]:
|
| 336 |
+
recommended_categories.extend(["stress_management", "emotional_intelligence"])
|
| 337 |
+
if mood_counts.get("sad", 0) > mood_counts.get("angry", 0):
|
| 338 |
+
recommended_categories.append("depression")
|
| 339 |
+
|
| 340 |
+
if primary_mood == "anxious":
|
| 341 |
+
recommended_categories.extend(["mindfulness", "stress_management"])
|
| 342 |
+
|
| 343 |
+
if primary_mood in ["happy", "calm"]:
|
| 344 |
+
recommended_categories.extend(["mindfulness", "emotional_intelligence"])
|
| 345 |
+
if "social_skills" in resources:
|
| 346 |
+
recommended_categories.append("social_skills")
|
| 347 |
+
|
| 348 |
+
# If we have liked resources, factor those in
|
| 349 |
+
if st.session_state.liked_resources:
|
| 350 |
+
liked_tags = []
|
| 351 |
+
for resource in st.session_state.liked_resources:
|
| 352 |
+
if "tags" in resource:
|
| 353 |
+
liked_tags.extend(resource["tags"])
|
| 354 |
+
|
| 355 |
+
# Count tag frequencies
|
| 356 |
+
tag_counts = {}
|
| 357 |
+
for tag in liked_tags:
|
| 358 |
+
if tag in tag_counts:
|
| 359 |
+
tag_counts[tag] += 1
|
| 360 |
+
else:
|
| 361 |
+
tag_counts[tag] = 1
|
| 362 |
+
|
| 363 |
+
# Add categories based on liked tags
|
| 364 |
+
if tag_counts:
|
| 365 |
+
top_tags = sorted(tag_counts.items(), key=lambda x: x[1], reverse=True)[:2]
|
| 366 |
+
for tag, _ in top_tags:
|
| 367 |
+
for category, resource_list in resources.items():
|
| 368 |
+
for resource in resource_list:
|
| 369 |
+
if "tags" in resource and tag in resource["tags"] and category not in recommended_categories:
|
| 370 |
+
recommended_categories.append(category)
|
| 371 |
+
break
|
| 372 |
+
|
| 373 |
+
# Ensure we have at least one category
|
| 374 |
+
if not recommended_categories:
|
| 375 |
+
recommended_categories = [random.choice(list(resources.keys()))]
|
| 376 |
+
|
| 377 |
+
# Get resources from recommended categories
|
| 378 |
+
st.session_state.personalized_resources = []
|
| 379 |
+
for category in recommended_categories:
|
| 380 |
+
if category in resources:
|
| 381 |
+
st.session_state.personalized_resources.extend(resources[category])
|
| 382 |
+
|
| 383 |
+
# Limit to 5 resources and shuffle for variety
|
| 384 |
+
if len(st.session_state.personalized_resources) > 5:
|
| 385 |
+
st.session_state.personalized_resources = random.sample(st.session_state.personalized_resources, 5)
|
| 386 |
+
else:
|
| 387 |
+
random.shuffle(st.session_state.personalized_resources)
|
| 388 |
+
|
| 389 |
+
st.markdown("<h3>Here are some resources tailored for you:</h3>", unsafe_allow_html=True)
|
| 390 |
+
|
| 391 |
+
# Display resources in an improved UI
|
| 392 |
+
for i, resource in enumerate(st.session_state.personalized_resources):
|
| 393 |
+
with st.container():
|
| 394 |
+
st.markdown(f"""
|
| 395 |
+
<div class="resource-card">
|
| 396 |
+
<div class="resource-title">{resource['title']}</div>
|
| 397 |
+
<div class="resource-type">{resource['type']}</div>
|
| 398 |
+
</div>
|
| 399 |
+
""", unsafe_allow_html=True)
|
| 400 |
+
|
| 401 |
+
st.markdown(f"**[{resource['title']}]({resource['link']})**")
|
| 402 |
+
st.write(resource.get("description", ""))
|
| 403 |
+
|
| 404 |
+
# Display tags
|
| 405 |
+
if "tags" in resource:
|
| 406 |
+
st.write("Tags: " + ", ".join([f"#{tag}" for tag in resource["tags"]]))
|
| 407 |
+
|
| 408 |
+
# Display difficulty
|
| 409 |
+
if "difficulty" in resource:
|
| 410 |
+
st.write(f"Difficulty: {resource['difficulty']}")
|
| 411 |
+
|
| 412 |
+
# Like, Comment, Share, and Bookmark buttons in a row
|
| 413 |
+
col1, col2, col3, col4 = st.columns(4)
|
| 414 |
+
|
| 415 |
+
with col1:
|
| 416 |
+
liked = any(r.get('title') == resource['title'] for r in st.session_state.liked_resources)
|
| 417 |
+
like_label = "👍 Liked" if liked else "👍 Like"
|
| 418 |
+
if st.button(like_label, key=f"like_{i}_{resource['title']}"):
|
| 419 |
+
like_resource(resource)
|
| 420 |
+
st.experimental_rerun()
|
| 421 |
+
|
| 422 |
+
with col2:
|
| 423 |
+
if st.button("💬 Comment", key=f"comment_btn_{i}_{resource['title']}"):
|
| 424 |
+
toggle_comment_input(resource['title'])
|
| 425 |
+
st.experimental_rerun()
|
| 426 |
+
|
| 427 |
+
with col3:
|
| 428 |
+
if st.button("🔗 Share", key=f"share_{i}_{resource['title']}"):
|
| 429 |
+
st.code(f"{resource['link']}", language="text")
|
| 430 |
+
|
| 431 |
+
with col4:
|
| 432 |
+
saved = any(r.get('title') == resource['title'] for r in st.session_state.saved_resources)
|
| 433 |
+
save_label = "🔖 Saved" if saved else "🔖 Save"
|
| 434 |
+
if st.button(save_label, key=f"save_{i}_{resource['title']}"):
|
| 435 |
+
bookmark_resource(resource)
|
| 436 |
+
st.experimental_rerun()
|
| 437 |
+
|
| 438 |
+
# Show comment input if toggled
|
| 439 |
+
if resource['title'] in st.session_state.show_comment_input and st.session_state.show_comment_input[resource['title']]:
|
| 440 |
+
comment = st.text_area(f"Add a comment for {resource['title']}:", key=f"comment_text_{i}_{resource['title']}")
|
| 441 |
+
if st.button("Submit Comment", key=f"submit_comment_{i}_{resource['title']}"):
|
| 442 |
+
if comment:
|
| 443 |
+
add_comment(resource['title'], comment)
|
| 444 |
+
st.success("Comment added!")
|
| 445 |
+
st.experimental_rerun()
|
| 446 |
+
|
| 447 |
+
# Show existing comments
|
| 448 |
+
if resource['title'] in st.session_state.comments and st.session_state.comments[resource['title']]:
|
| 449 |
+
with st.expander(f"View {len(st.session_state.comments[resource['title']])} comments"):
|
| 450 |
+
for comment in st.session_state.comments[resource['title']]:
|
| 451 |
+
st.write(f"- {comment}")
|
| 452 |
+
|
| 453 |
+
st.markdown("---")
|
| 454 |
+
|
| 455 |
+
# Function to display user profile with improved UI
|
| 456 |
+
def user_profile():
|
| 457 |
+
st.markdown("<h2 class='subheader'>Your Profile</h2>", unsafe_allow_html=True)
|
| 458 |
+
|
| 459 |
+
# User name input
|
| 460 |
+
if not st.session_state.user_name:
|
| 461 |
+
user_name = st.text_input("What should we call you?")
|
| 462 |
+
if user_name:
|
| 463 |
+
st.session_state.user_name = user_name
|
| 464 |
+
st.success(f"Welcome, {user_name}!")
|
| 465 |
+
st.experimental_rerun()
|
| 466 |
+
else:
|
| 467 |
+
st.write(f"Welcome back, {st.session_state.user_name}! 👋")
|
| 468 |
+
if st.button("Change name"):
|
| 469 |
+
st.session_state.user_name = ""
|
| 470 |
+
st.experimental_rerun()
|
| 471 |
+
|
| 472 |
+
# Create tabs for different sections of the profile
|
| 473 |
+
profile_tab1, profile_tab2, profile_tab3 = st.tabs(["Mood History", "Saved Resources", "Activity"])
|
| 474 |
+
|
| 475 |
+
with profile_tab1:
|
| 476 |
+
st.subheader("Mood History")
|
| 477 |
+
if st.session_state.mood_history:
|
| 478 |
+
# Create a dataframe for visualization
|
| 479 |
+
mood_df = pd.DataFrame(st.session_state.mood_history, columns=["timestamp", "mood"])
|
| 480 |
+
mood_df["date"] = pd.to_datetime(mood_df["timestamp"]).dt.date
|
| 481 |
+
mood_df["time"] = pd.to_datetime(mood_df["timestamp"]).dt.time
|
| 482 |
+
|
| 483 |
+
# Group by date and get the last mood for each day
|
| 484 |
+
daily_mood = mood_df.sort_values("timestamp").groupby("date").last().reset_index()
|
| 485 |
+
|
| 486 |
+
# Create a calendar heatmap-like visualization
|
| 487 |
+
if len(daily_mood) > 0:
|
| 488 |
+
# Create a color mapping for moods
|
| 489 |
+
color_scale = {
|
| 490 |
+
"happy": "#FFD700", # Gold
|
| 491 |
+
"calm": "#90EE90", # Light green
|
| 492 |
+
"anxious": "#ADD8E6", # Light blue
|
| 493 |
+
"sad": "#A9A9A9", # Dark gray
|
| 494 |
+
"angry": "#FF6347" # Tomato
|
| 495 |
+
}
|
| 496 |
+
|
| 497 |
+
daily_mood["color"] = daily_mood["mood"].map(color_scale)
|
| 498 |
+
|
| 499 |
+
# Create a chart
|
| 500 |
+
chart = alt.Chart(daily_mood).mark_rect().encode(
|
| 501 |
+
x=alt.X('date:T', title='Date', axis=alt.Axis(format='%b %d')),
|
| 502 |
+
color=alt.Color('mood:N', scale=alt.Scale(
|
| 503 |
+
domain=list(color_scale.keys()),
|
| 504 |
+
range=list(color_scale.values())
|
| 505 |
+
))
|
| 506 |
+
).properties(
|
| 507 |
+
title="Your Mood Calendar",
|
| 508 |
+
width=600,
|
| 509 |
+
height=100
|
| 510 |
+
)
|
| 511 |
+
|
| 512 |
+
st.altair_chart(chart, use_container_width=True)
|
| 513 |
+
|
| 514 |
+
# Show a table of recent moods
|
| 515 |
+
st.subheader("Recent Mood Entries")
|
| 516 |
+
recent_moods = mood_df.sort_values("timestamp", ascending=False).head(10)
|
| 517 |
+
for _, row in recent_moods.iterrows():
|
| 518 |
+
st.write(f"{row['timestamp']}: {row['mood']}")
|
| 519 |
+
else:
|
| 520 |
+
st.write("No mood history available.")
|
| 521 |
+
else:
|
| 522 |
+
st.write("No mood history available.")
|
| 523 |
+
|
| 524 |
+
with profile_tab2:
|
| 525 |
+
st.subheader("Saved Resources")
|
| 526 |
+
if st.session_state.saved_resources:
|
| 527 |
+
for i, resource in enumerate(st.session_state.saved_resources):
|
| 528 |
+
with st.container():
|
| 529 |
+
st.markdown(f"""
|
| 530 |
+
<div class="resource-card">
|
| 531 |
+
<div class="resource-title">{resource['title']}</div>
|
| 532 |
+
<div class="resource-type">{resource['type']}</div>
|
| 533 |
+
</div>
|
| 534 |
+
""", unsafe_allow_html=True)
|
| 535 |
+
|
| 536 |
+
st.markdown(f"**[{resource['title']}]({resource['link']})**")
|
| 537 |
+
|
| 538 |
+
if "description" in resource:
|
| 539 |
+
st.write(resource["description"])
|
| 540 |
+
|
| 541 |
+
# Remove from saved button
|
| 542 |
+
if st.button("❌ Remove", key=f"remove_saved_{i}"):
|
| 543 |
+
st.session_state.saved_resources.remove(resource)
|
| 544 |
+
st.experimental_rerun()
|
| 545 |
+
|
| 546 |
+
st.markdown("---")
|
| 547 |
+
else:
|
| 548 |
+
st.write("No saved resources yet.")
|
| 549 |
+
|
| 550 |
+
with profile_tab3:
|
| 551 |
+
col1, col2 = st.columns(2)
|
| 552 |
+
|
| 553 |
+
with col1:
|
| 554 |
+
st.subheader("Liked Resources")
|
| 555 |
+
if st.session_state.liked_resources:
|
| 556 |
+
for resource in st.session_state.liked_resources:
|
| 557 |
+
st.markdown(f"**[{resource['title']}]({resource['link']})**")
|
| 558 |
+
else:
|
| 559 |
+
st.write("No liked resources yet.")
|
| 560 |
+
|
| 561 |
+
with col2:
|
| 562 |
+
st.subheader("Your Comments")
|
| 563 |
+
if st.session_state.comments:
|
| 564 |
+
for resource_title, comments in st.session_state.comments.items():
|
| 565 |
+
with st.expander(f"{resource_title} ({len(comments)} comments)"):
|
| 566 |
+
for comment in comments:
|
| 567 |
+
st.write(f"- {comment}")
|
| 568 |
+
else:
|
| 569 |
+
st.write("No comments yet.")
|
| 570 |
+
|
| 571 |
+
# About Us section in the sidebar with improved content
|
| 572 |
+
def about_us():
|
| 573 |
+
st.sidebar.markdown("<h2>About Us</h2>", unsafe_allow_html=True)
|
| 574 |
+
st.sidebar.write("""
|
| 575 |
+
**AfyaMind Space** is a technology-driven mental health platform designed to empower adolescents in low-resource settings.
|
| 576 |
+
|
| 577 |
+
Our mission is to provide accessible, culturally adaptive, and engaging mental health resources to help young people
|
| 578 |
+
manage their emotional well-being and build resilience.
|
| 579 |
+
|
| 580 |
+
### Our Approach
|
| 581 |
+
- **Personalized Resources**: Get content tailored to your emotional needs
|
| 582 |
+
- **Mood Tracking**: Monitor your emotional patterns over time
|
| 583 |
+
- **Community Support**: Connect with peers in a safe environment
|
| 584 |
+
- **Offline Access**: Access key resources even without internet
|
| 585 |
+
""")
|
| 586 |
+
|
| 587 |
+
# FAQs section in the sidebar with improved content
|
| 588 |
+
def faqs():
|
| 589 |
+
st.sidebar.markdown("<h2>FAQs</h2>", unsafe_allow_html=True)
|
| 590 |
+
|
| 591 |
+
# Create expandable FAQ items
|
| 592 |
+
with st.sidebar.expander("What is AfyaMind Space?"):
|
| 593 |
+
st.write("AfyaMind Space is a digital platform offering AI-powered mental health resources, mood tracking, and peer support for adolescents.")
|
| 594 |
+
|
| 595 |
+
with st.sidebar.expander("Is AfyaMind free to use?"):
|
| 596 |
+
st.write("Yes, AfyaMind is completely free and accessible to all users.")
|
| 597 |
+
|
| 598 |
+
with st.sidebar.expander("How does AfyaMind protect my data?"):
|
| 599 |
+
st.write("We use strong encryption and comply with global data protection standards to ensure your privacy.")
|
| 600 |
+
|
| 601 |
+
with st.sidebar.expander("Can I use AfyaMind offline?"):
|
| 602 |
+
st.write("Yes, AfyaMind offers offline features like mood tracking and downloadable resources for users without internet access.")
|
| 603 |
+
|
| 604 |
+
with st.sidebar.expander("How can I get help in a crisis?"):
|
| 605 |
+
st.write("""
|
| 606 |
+
If you're experiencing a mental health crisis:
|
| 607 |
+
- Call your local emergency number
|
| 608 |
+
- Text a crisis helpline
|
| 609 |
+
- Reach out to a trusted adult
|
| 610 |
+
- Visit the nearest hospital emergency room
|
| 611 |
+
|
| 612 |
+
Remember, you're not alone and help is available.
|
| 613 |
+
""")
|
| 614 |
+
|
| 615 |
+
# Help resources in the sidebar
|
| 616 |
+
def help_resources():
|
| 617 |
+
st.sidebar.markdown("<h2>Help Resources</h2>", unsafe_allow_html=True)
|
| 618 |
+
st.sidebar.write("""
|
| 619 |
+
### Crisis Helplines
|
| 620 |
+
- **Kenya Mental Health Helpline**: 1199 (toll-free)
|
| 621 |
+
- **Befrienders Kenya**: 0722 178 177
|
| 622 |
+
|
| 623 |
+
### Online Resources
|
| 624 |
+
- [Mental Health Kenya](https://mentalhealthkenya.org/)
|
| 625 |
+
- [Chiromo Hospital Group](https://www.chiromohospital.org/)
|
| 626 |
+
""")
|
| 627 |
+
|
| 628 |
+
# Main app layout with improved UI
|
| 629 |
+
def main():
|
| 630 |
+
st.markdown("<h1 class='main-header'>AfyaMind Space: Your Mental Health Companion</h1>", unsafe_allow_html=True)
|
| 631 |
+
|
| 632 |
+
# Check if user is logged in
|
| 633 |
+
is_logged_in = auth_page()
|
| 634 |
+
|
| 635 |
+
# Sidebar with About Us, FAQs, and Help Resources
|
| 636 |
+
about_us()
|
| 637 |
+
faqs()
|
| 638 |
+
help_resources()
|
| 639 |
+
|
| 640 |
+
# Only show main content if user is logged in or authentication is not required for this section
|
| 641 |
+
if "require_auth" not in st.session_state:
|
| 642 |
+
st.session_state.require_auth = True
|
| 643 |
+
|
| 644 |
+
if is_logged_in or not st.session_state.require_auth:
|
| 645 |
+
# Tabs for different sections with better styling
|
| 646 |
+
tab1, tab2, tab3, tab4, tab5, tab6, tab7, tab8 = st.tabs(["🌡️ Track Mood", "📊 Analytics", "📚 Resources", "🧘 Meditation", "🎯 Goals", "💬 Community", "👤 Profile", "💬 Chat Assistant"])
|
| 647 |
+
|
| 648 |
+
with tab1:
|
| 649 |
+
track_mood()
|
| 650 |
+
|
| 651 |
+
with tab2:
|
| 652 |
+
mood_analytics_page()
|
| 653 |
+
|
| 654 |
+
with tab3:
|
| 655 |
+
recommend_resources()
|
| 656 |
+
|
| 657 |
+
with tab4:
|
| 658 |
+
meditation_page()
|
| 659 |
+
|
| 660 |
+
with tab5:
|
| 661 |
+
goals_page()
|
| 662 |
+
|
| 663 |
+
with tab6:
|
| 664 |
+
forum_page()
|
| 665 |
+
|
| 666 |
+
with tab7:
|
| 667 |
+
user_profile()
|
| 668 |
+
|
| 669 |
+
with tab8:
|
| 670 |
+
create_chat_interface()
|
| 671 |
+
|
| 672 |
+
# Footer
|
| 673 |
+
st.markdown("---")
|
| 674 |
+
st.markdown(
|
| 675 |
+
"""
|
| 676 |
+
<div style="text-align: center; color: #6C757D; font-size: 0.8rem;">
|
| 677 |
+
© 2025 AfyaMind Space. Made with ♥ for adolescent mental health.
|
| 678 |
+
</div>
|
| 679 |
+
""",
|
| 680 |
+
unsafe_allow_html=True
|
| 681 |
+
)
|
| 682 |
+
|
| 683 |
+
# Run the app
|
| 684 |
+
if __name__ == "__main__":
|
| 685 |
+
main()
|
AfyaMindSpace/app.py.new
ADDED
|
File without changes
|
AfyaMindSpace/auth.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import hashlib
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
|
| 7 |
+
# File to store user data
|
| 8 |
+
USER_DB_FILE = "user_data.json"
|
| 9 |
+
|
| 10 |
+
# Initialize user database
|
| 11 |
+
def initialize_user_db():
|
| 12 |
+
if not os.path.exists(USER_DB_FILE):
|
| 13 |
+
with open(USER_DB_FILE, "w") as f:
|
| 14 |
+
json.dump({"users": {}}, f)
|
| 15 |
+
return {"users": {}}
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
with open(USER_DB_FILE, "r") as f:
|
| 19 |
+
return json.load(f)
|
| 20 |
+
except json.JSONDecodeError:
|
| 21 |
+
# If file is corrupted, create a new one
|
| 22 |
+
with open(USER_DB_FILE, "w") as f:
|
| 23 |
+
json.dump({"users": {}}, f)
|
| 24 |
+
return {"users": {}}
|
| 25 |
+
|
| 26 |
+
# Save user database
|
| 27 |
+
def save_user_db(db):
|
| 28 |
+
with open(USER_DB_FILE, "w") as f:
|
| 29 |
+
json.dump(db, f)
|
| 30 |
+
|
| 31 |
+
# Hash password
|
| 32 |
+
def hash_password(password):
|
| 33 |
+
return hashlib.sha256(password.encode()).hexdigest()
|
| 34 |
+
|
| 35 |
+
# Register new user
|
| 36 |
+
def register_user(username, password, email):
|
| 37 |
+
db = initialize_user_db()
|
| 38 |
+
|
| 39 |
+
if username in db["users"]:
|
| 40 |
+
return False, "Username already exists"
|
| 41 |
+
|
| 42 |
+
# Check for email duplicates
|
| 43 |
+
for user in db["users"].values():
|
| 44 |
+
if user["email"] == email:
|
| 45 |
+
return False, "Email already registered"
|
| 46 |
+
|
| 47 |
+
# Create new user
|
| 48 |
+
db["users"][username] = {
|
| 49 |
+
"password_hash": hash_password(password),
|
| 50 |
+
"email": email,
|
| 51 |
+
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 52 |
+
"last_login": None,
|
| 53 |
+
"profile": {
|
| 54 |
+
"display_name": username,
|
| 55 |
+
"bio": "",
|
| 56 |
+
"preferences": {
|
| 57 |
+
"notifications": True,
|
| 58 |
+
"privacy": "public"
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
save_user_db(db)
|
| 64 |
+
return True, "Registration successful"
|
| 65 |
+
|
| 66 |
+
# Authenticate user
|
| 67 |
+
def authenticate_user(username, password):
|
| 68 |
+
db = initialize_user_db()
|
| 69 |
+
|
| 70 |
+
if username not in db["users"]:
|
| 71 |
+
return False, "Invalid username or password"
|
| 72 |
+
|
| 73 |
+
if db["users"][username]["password_hash"] != hash_password(password):
|
| 74 |
+
return False, "Invalid username or password"
|
| 75 |
+
|
| 76 |
+
# Update last login
|
| 77 |
+
db["users"][username]["last_login"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 78 |
+
save_user_db(db)
|
| 79 |
+
|
| 80 |
+
return True, "Login successful"
|
| 81 |
+
|
| 82 |
+
# Update user profile
|
| 83 |
+
def update_user_profile(username, profile_data):
|
| 84 |
+
db = initialize_user_db()
|
| 85 |
+
|
| 86 |
+
if username not in db["users"]:
|
| 87 |
+
return False, "User not found"
|
| 88 |
+
|
| 89 |
+
# Update profile fields
|
| 90 |
+
for key, value in profile_data.items():
|
| 91 |
+
if key in db["users"][username]["profile"]:
|
| 92 |
+
db["users"][username]["profile"][key] = value
|
| 93 |
+
|
| 94 |
+
save_user_db(db)
|
| 95 |
+
return True, "Profile updated successfully"
|
| 96 |
+
|
| 97 |
+
# Get user profile
|
| 98 |
+
def get_user_profile(username):
|
| 99 |
+
db = initialize_user_db()
|
| 100 |
+
|
| 101 |
+
if username not in db["users"]:
|
| 102 |
+
return None
|
| 103 |
+
|
| 104 |
+
return db["users"][username]["profile"]
|
| 105 |
+
|
| 106 |
+
# Change password
|
| 107 |
+
def change_password(username, old_password, new_password):
|
| 108 |
+
db = initialize_user_db()
|
| 109 |
+
|
| 110 |
+
if username not in db["users"]:
|
| 111 |
+
return False, "User not found"
|
| 112 |
+
|
| 113 |
+
if db["users"][username]["password_hash"] != hash_password(old_password):
|
| 114 |
+
return False, "Incorrect password"
|
| 115 |
+
|
| 116 |
+
db["users"][username]["password_hash"] = hash_password(new_password)
|
| 117 |
+
save_user_db(db)
|
| 118 |
+
|
| 119 |
+
return True, "Password changed successfully"
|
| 120 |
+
|
| 121 |
+
# Reset password (simplified version - in a real app, this would involve email verification)
|
| 122 |
+
def reset_password(email):
|
| 123 |
+
db = initialize_user_db()
|
| 124 |
+
|
| 125 |
+
# Find user with this email
|
| 126 |
+
username = None
|
| 127 |
+
for user, data in db["users"].items():
|
| 128 |
+
if data["email"] == email:
|
| 129 |
+
username = user
|
| 130 |
+
break
|
| 131 |
+
|
| 132 |
+
if not username:
|
| 133 |
+
return False, "Email not found"
|
| 134 |
+
|
| 135 |
+
# In a real app, we would send an email with a reset link
|
| 136 |
+
# For this demo, we'll just generate a temporary password
|
| 137 |
+
temp_password = "temp" + datetime.now().strftime("%H%M%S")
|
| 138 |
+
db["users"][username]["password_hash"] = hash_password(temp_password)
|
| 139 |
+
save_user_db(db)
|
| 140 |
+
|
| 141 |
+
return True, f"Password reset to: {temp_password}"
|
| 142 |
+
|
| 143 |
+
# Authentication UI components
|
| 144 |
+
def login_form():
|
| 145 |
+
st.subheader("Login")
|
| 146 |
+
|
| 147 |
+
username = st.text_input("Username", key="login_username")
|
| 148 |
+
password = st.text_input("Password", type="password", key="login_password")
|
| 149 |
+
|
| 150 |
+
if st.button("Login", key="login_button"):
|
| 151 |
+
if username and password:
|
| 152 |
+
success, message = authenticate_user(username, password)
|
| 153 |
+
if success:
|
| 154 |
+
st.session_state.logged_in = True
|
| 155 |
+
st.session_state.username = username
|
| 156 |
+
st.success(message)
|
| 157 |
+
st.rerun()
|
| 158 |
+
else:
|
| 159 |
+
st.error(message)
|
| 160 |
+
else:
|
| 161 |
+
st.warning("Please enter both username and password")
|
| 162 |
+
|
| 163 |
+
def register_form():
|
| 164 |
+
st.subheader("Register")
|
| 165 |
+
|
| 166 |
+
# Add asterisks to indicate required fields
|
| 167 |
+
username = st.text_input("Username *", key="register_username")
|
| 168 |
+
email = st.text_input("Email *", key="register_email")
|
| 169 |
+
password = st.text_input("Password *", type="password", key="register_password")
|
| 170 |
+
confirm_password = st.text_input("Confirm Password *", type="password", key="confirm_password")
|
| 171 |
+
|
| 172 |
+
# Add a note about required fields
|
| 173 |
+
st.markdown("<small>* Required fields</small>", unsafe_allow_html=True)
|
| 174 |
+
|
| 175 |
+
if st.button("Register", key="register_button"):
|
| 176 |
+
# Check each field individually and provide specific feedback
|
| 177 |
+
missing_fields = []
|
| 178 |
+
if not username:
|
| 179 |
+
missing_fields.append("Username")
|
| 180 |
+
if not email:
|
| 181 |
+
missing_fields.append("Email")
|
| 182 |
+
if not password:
|
| 183 |
+
missing_fields.append("Password")
|
| 184 |
+
if not confirm_password:
|
| 185 |
+
missing_fields.append("Confirm Password")
|
| 186 |
+
|
| 187 |
+
if missing_fields:
|
| 188 |
+
st.error(f"Please fill in the following required fields: {', '.join(missing_fields)}")
|
| 189 |
+
elif password != confirm_password:
|
| 190 |
+
st.error("Passwords do not match")
|
| 191 |
+
else:
|
| 192 |
+
success, message = register_user(username, password, email)
|
| 193 |
+
if success:
|
| 194 |
+
st.success(message)
|
| 195 |
+
st.info("Please login with your new account")
|
| 196 |
+
else:
|
| 197 |
+
st.error(message)
|
| 198 |
+
|
| 199 |
+
def forgot_password_form():
|
| 200 |
+
st.subheader("Reset Password")
|
| 201 |
+
|
| 202 |
+
email = st.text_input("Email", key="reset_email")
|
| 203 |
+
|
| 204 |
+
if st.button("Reset Password", key="reset_button"):
|
| 205 |
+
if email:
|
| 206 |
+
success, message = reset_password(email)
|
| 207 |
+
if success:
|
| 208 |
+
st.success("Password reset successful")
|
| 209 |
+
st.info(message)
|
| 210 |
+
else:
|
| 211 |
+
st.error(message)
|
| 212 |
+
else:
|
| 213 |
+
st.warning("Please enter your email")
|
| 214 |
+
|
| 215 |
+
def logout_user():
|
| 216 |
+
if st.sidebar.button("Logout"):
|
| 217 |
+
st.session_state.logged_in = False
|
| 218 |
+
st.session_state.username = None
|
| 219 |
+
st.rerun()
|
| 220 |
+
|
| 221 |
+
def auth_page():
|
| 222 |
+
if "logged_in" not in st.session_state:
|
| 223 |
+
st.session_state.logged_in = False
|
| 224 |
+
|
| 225 |
+
if "username" not in st.session_state:
|
| 226 |
+
st.session_state.username = None
|
| 227 |
+
|
| 228 |
+
if not st.session_state.logged_in:
|
| 229 |
+
tab1, tab2, tab3 = st.tabs(["Login", "Register", "Forgot Password"])
|
| 230 |
+
|
| 231 |
+
with tab1:
|
| 232 |
+
login_form()
|
| 233 |
+
|
| 234 |
+
with tab2:
|
| 235 |
+
register_form()
|
| 236 |
+
|
| 237 |
+
with tab3:
|
| 238 |
+
forgot_password_form()
|
| 239 |
+
|
| 240 |
+
return False
|
| 241 |
+
else:
|
| 242 |
+
# User is logged in
|
| 243 |
+
logout_user()
|
| 244 |
+
return True
|
AfyaMindSpace/chatbox.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import time
|
| 3 |
+
import json
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
|
| 6 |
+
class ChatMessage:
|
| 7 |
+
def __init__(self, content, is_user=True, timestamp=None):
|
| 8 |
+
self.content = content
|
| 9 |
+
self.is_user = is_user
|
| 10 |
+
self.timestamp = timestamp or datetime.now()
|
| 11 |
+
self.feedback = None
|
| 12 |
+
|
| 13 |
+
def to_dict(self):
|
| 14 |
+
return {
|
| 15 |
+
"content": self.content,
|
| 16 |
+
"is_user": self.is_user,
|
| 17 |
+
"timestamp": self.timestamp.isoformat(),
|
| 18 |
+
"feedback": self.feedback
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
@classmethod
|
| 22 |
+
def from_dict(cls, data):
|
| 23 |
+
msg = cls(
|
| 24 |
+
content=data["content"],
|
| 25 |
+
is_user=data["is_user"],
|
| 26 |
+
timestamp=datetime.fromisoformat(data["timestamp"])
|
| 27 |
+
)
|
| 28 |
+
msg.feedback = data.get("feedback")
|
| 29 |
+
return msg
|
| 30 |
+
|
| 31 |
+
class ChatBox:
|
| 32 |
+
def __init__(self, user_theme="green", assistant_theme="blue"):
|
| 33 |
+
self.user_theme = user_theme
|
| 34 |
+
self.assistant_theme = assistant_theme
|
| 35 |
+
self.init_session()
|
| 36 |
+
|
| 37 |
+
def init_session(self, clear=False):
|
| 38 |
+
"""Initialize or clear the chat session"""
|
| 39 |
+
if "chat_messages" not in st.session_state or clear:
|
| 40 |
+
st.session_state.chat_messages = []
|
| 41 |
+
|
| 42 |
+
def user_say(self, message):
|
| 43 |
+
"""Add a user message to the chat"""
|
| 44 |
+
if message and message.strip():
|
| 45 |
+
st.session_state.chat_messages.append(ChatMessage(message, is_user=True))
|
| 46 |
+
return True
|
| 47 |
+
return False
|
| 48 |
+
|
| 49 |
+
def ai_say(self, message, streaming=False):
|
| 50 |
+
"""Add an AI message to the chat"""
|
| 51 |
+
if message and message.strip():
|
| 52 |
+
st.session_state.chat_messages.append(ChatMessage(message, is_user=False))
|
| 53 |
+
return True
|
| 54 |
+
return False
|
| 55 |
+
|
| 56 |
+
def update_last_ai_message(self, message, streaming=False):
|
| 57 |
+
"""Update the last AI message in the chat"""
|
| 58 |
+
for i in range(len(st.session_state.chat_messages) - 1, -1, -1):
|
| 59 |
+
if not st.session_state.chat_messages[i].is_user:
|
| 60 |
+
st.session_state.chat_messages[i].content = message
|
| 61 |
+
return True
|
| 62 |
+
return False
|
| 63 |
+
|
| 64 |
+
def set_feedback(self, message_index, feedback):
|
| 65 |
+
"""Set feedback for a message"""
|
| 66 |
+
if 0 <= message_index < len(st.session_state.chat_messages):
|
| 67 |
+
st.session_state.chat_messages[message_index].feedback = feedback
|
| 68 |
+
return True
|
| 69 |
+
return False
|
| 70 |
+
|
| 71 |
+
def render_message(self, message, index):
|
| 72 |
+
"""Render a single message in the chat"""
|
| 73 |
+
if message.is_user:
|
| 74 |
+
with st.chat_message("user", avatar="👤"):
|
| 75 |
+
st.markdown(message.content)
|
| 76 |
+
else:
|
| 77 |
+
with st.chat_message("assistant", avatar="🧠"):
|
| 78 |
+
st.markdown(message.content)
|
| 79 |
+
|
| 80 |
+
# Add feedback buttons if needed
|
| 81 |
+
col1, col2, col3 = st.columns([1, 1, 8])
|
| 82 |
+
if col1.button("👍", key=f"like_{index}"):
|
| 83 |
+
self.set_feedback(index, "positive")
|
| 84 |
+
st.rerun()
|
| 85 |
+
if col2.button("👎", key=f"dislike_{index}"):
|
| 86 |
+
self.set_feedback(index, "negative")
|
| 87 |
+
st.rerun()
|
| 88 |
+
|
| 89 |
+
def render_chat(self):
|
| 90 |
+
"""Render the entire chat history"""
|
| 91 |
+
for i, message in enumerate(st.session_state.chat_messages):
|
| 92 |
+
self.render_message(message, i)
|
| 93 |
+
|
| 94 |
+
def to_json(self):
|
| 95 |
+
"""Export chat history to JSON"""
|
| 96 |
+
return json.dumps([msg.to_dict() for msg in st.session_state.chat_messages], indent=2)
|
| 97 |
+
|
| 98 |
+
def from_json(self, json_str):
|
| 99 |
+
"""Import chat history from JSON"""
|
| 100 |
+
try:
|
| 101 |
+
data = json.loads(json_str)
|
| 102 |
+
st.session_state.chat_messages = [ChatMessage.from_dict(msg) for msg in data]
|
| 103 |
+
return True
|
| 104 |
+
except Exception as e:
|
| 105 |
+
st.error(f"Error loading chat history: {e}")
|
| 106 |
+
return False
|
| 107 |
+
|
| 108 |
+
def clear_history(self):
|
| 109 |
+
"""Clear chat history"""
|
| 110 |
+
self.init_session(clear=True)
|
| 111 |
+
|
| 112 |
+
# Mock AI response generator for demonstration
|
| 113 |
+
class MockAI:
|
| 114 |
+
def __init__(self):
|
| 115 |
+
self.responses = {
|
| 116 |
+
"hello": "Hello! How can I help you with your mental health today?",
|
| 117 |
+
"how are you": "I'm just a program, but I'm here to support you. How are you feeling today?",
|
| 118 |
+
"feeling sad": "I'm sorry to hear you're feeling sad. Would you like to try a quick mood-lifting exercise or talk about what's bothering you?",
|
| 119 |
+
"feeling anxious": "Anxiety can be challenging. Have you tried any breathing exercises? Taking slow, deep breaths can help calm your nervous system.",
|
| 120 |
+
"meditation": "Meditation is a great practice for mental health. We have several guided meditations available in the Meditation tab. Would you like me to recommend one?",
|
| 121 |
+
"help": "I'm here to support your mental health journey. You can ask me about coping strategies, resources, or just chat about how you're feeling."
|
| 122 |
+
}
|
| 123 |
+
self.default_response = "I'm here to support you. Could you tell me more about what's on your mind?"
|
| 124 |
+
|
| 125 |
+
def get_response(self, query):
|
| 126 |
+
"""Get a response based on the user query"""
|
| 127 |
+
query = query.lower()
|
| 128 |
+
for key, response in self.responses.items():
|
| 129 |
+
if key in query:
|
| 130 |
+
return response
|
| 131 |
+
return self.default_response
|
| 132 |
+
|
| 133 |
+
def get_streaming_response(self, query):
|
| 134 |
+
"""Simulate a streaming response"""
|
| 135 |
+
response = self.get_response(query)
|
| 136 |
+
words = response.split()
|
| 137 |
+
result = ""
|
| 138 |
+
for word in words:
|
| 139 |
+
result += word + " "
|
| 140 |
+
yield result
|
| 141 |
+
time.sleep(0.1)
|
| 142 |
+
|
| 143 |
+
# Function to create a chat interface
|
| 144 |
+
def create_chat_interface():
|
| 145 |
+
st.markdown("<h2>Chat with AfyaMind Assistant</h2>", unsafe_allow_html=True)
|
| 146 |
+
|
| 147 |
+
# Initialize chat components
|
| 148 |
+
if "chatbox" not in st.session_state:
|
| 149 |
+
st.session_state.chatbox = ChatBox()
|
| 150 |
+
|
| 151 |
+
if "ai" not in st.session_state:
|
| 152 |
+
st.session_state.ai = MockAI()
|
| 153 |
+
|
| 154 |
+
# Chat settings
|
| 155 |
+
with st.expander("Chat Settings"):
|
| 156 |
+
col1, col2 = st.columns(2)
|
| 157 |
+
streaming = col1.checkbox("Enable streaming responses", value=True)
|
| 158 |
+
col2.download_button(
|
| 159 |
+
"Export Chat History",
|
| 160 |
+
st.session_state.chatbox.to_json(),
|
| 161 |
+
file_name="afyamind_chat_history.json",
|
| 162 |
+
mime="application/json"
|
| 163 |
+
)
|
| 164 |
+
if st.button("Clear Chat History"):
|
| 165 |
+
st.session_state.chatbox.clear_history()
|
| 166 |
+
st.rerun()
|
| 167 |
+
|
| 168 |
+
# Display chat history
|
| 169 |
+
st.session_state.chatbox.render_chat()
|
| 170 |
+
|
| 171 |
+
# Chat input
|
| 172 |
+
if prompt := st.chat_input("Type your message here..."):
|
| 173 |
+
# Add user message
|
| 174 |
+
st.session_state.chatbox.user_say(prompt)
|
| 175 |
+
|
| 176 |
+
# Generate AI response
|
| 177 |
+
if streaming:
|
| 178 |
+
# Initial placeholder for streaming response
|
| 179 |
+
st.session_state.chatbox.ai_say("Thinking...", streaming=True)
|
| 180 |
+
st.rerun()
|
| 181 |
+
|
| 182 |
+
# This part will run after the rerun
|
| 183 |
+
# In a real implementation, you would use a proper streaming API
|
| 184 |
+
# For now, we'll simulate streaming with our mock AI
|
| 185 |
+
else:
|
| 186 |
+
response = st.session_state.ai.get_response(prompt)
|
| 187 |
+
st.session_state.chatbox.ai_say(response)
|
| 188 |
+
st.rerun()
|
| 189 |
+
|
| 190 |
+
# For testing the module independently
|
| 191 |
+
if __name__ == "__main__":
|
| 192 |
+
st.set_page_config(page_title="AfyaMind Chat", page_icon="🧠")
|
| 193 |
+
create_chat_interface()
|
AfyaMindSpace/forum.py
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
from datetime import datetime
|
| 5 |
+
|
| 6 |
+
# File to store forum data
|
| 7 |
+
FORUM_DB_FILE = "forum_data.json"
|
| 8 |
+
|
| 9 |
+
# Initialize forum database
|
| 10 |
+
def initialize_forum_db():
|
| 11 |
+
if not os.path.exists(FORUM_DB_FILE):
|
| 12 |
+
forum_data = {
|
| 13 |
+
"categories": [
|
| 14 |
+
{
|
| 15 |
+
"id": "general",
|
| 16 |
+
"name": "General Discussion",
|
| 17 |
+
"description": "General topics related to mental health and wellbeing"
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"id": "stress",
|
| 21 |
+
"name": "Stress Management",
|
| 22 |
+
"description": "Discuss strategies for managing stress and anxiety"
|
| 23 |
+
},
|
| 24 |
+
{
|
| 25 |
+
"id": "mindfulness",
|
| 26 |
+
"name": "Mindfulness & Meditation",
|
| 27 |
+
"description": "Share experiences and tips about mindfulness practices"
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"id": "support",
|
| 31 |
+
"name": "Peer Support",
|
| 32 |
+
"description": "A safe space to seek support from others"
|
| 33 |
+
},
|
| 34 |
+
{
|
| 35 |
+
"id": "success",
|
| 36 |
+
"name": "Success Stories",
|
| 37 |
+
"description": "Share your mental health journey and achievements"
|
| 38 |
+
}
|
| 39 |
+
],
|
| 40 |
+
"topics": {},
|
| 41 |
+
"posts": {}
|
| 42 |
+
}
|
| 43 |
+
with open(FORUM_DB_FILE, "w") as f:
|
| 44 |
+
json.dump(forum_data, f)
|
| 45 |
+
return forum_data
|
| 46 |
+
|
| 47 |
+
try:
|
| 48 |
+
with open(FORUM_DB_FILE, "r") as f:
|
| 49 |
+
return json.load(f)
|
| 50 |
+
except json.JSONDecodeError:
|
| 51 |
+
# If file is corrupted, create a new one
|
| 52 |
+
forum_data = {
|
| 53 |
+
"categories": [
|
| 54 |
+
{
|
| 55 |
+
"id": "general",
|
| 56 |
+
"name": "General Discussion",
|
| 57 |
+
"description": "General topics related to mental health and wellbeing"
|
| 58 |
+
},
|
| 59 |
+
{
|
| 60 |
+
"id": "stress",
|
| 61 |
+
"name": "Stress Management",
|
| 62 |
+
"description": "Discuss strategies for managing stress and anxiety"
|
| 63 |
+
},
|
| 64 |
+
{
|
| 65 |
+
"id": "mindfulness",
|
| 66 |
+
"name": "Mindfulness & Meditation",
|
| 67 |
+
"description": "Share experiences and tips about mindfulness practices"
|
| 68 |
+
},
|
| 69 |
+
{
|
| 70 |
+
"id": "support",
|
| 71 |
+
"name": "Peer Support",
|
| 72 |
+
"description": "A safe space to seek support from others"
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
"id": "success",
|
| 76 |
+
"name": "Success Stories",
|
| 77 |
+
"description": "Share your mental health journey and achievements"
|
| 78 |
+
}
|
| 79 |
+
],
|
| 80 |
+
"topics": {},
|
| 81 |
+
"posts": {}
|
| 82 |
+
}
|
| 83 |
+
with open(FORUM_DB_FILE, "w") as f:
|
| 84 |
+
json.dump(forum_data, f)
|
| 85 |
+
return forum_data
|
| 86 |
+
|
| 87 |
+
# Save forum database
|
| 88 |
+
def save_forum_db(db):
|
| 89 |
+
with open(FORUM_DB_FILE, "w") as f:
|
| 90 |
+
json.dump(db, f)
|
| 91 |
+
|
| 92 |
+
# Create a new topic
|
| 93 |
+
def create_topic(category_id, title, content, author):
|
| 94 |
+
db = initialize_forum_db()
|
| 95 |
+
|
| 96 |
+
# Check if category exists
|
| 97 |
+
category_exists = False
|
| 98 |
+
for category in db["categories"]:
|
| 99 |
+
if category["id"] == category_id:
|
| 100 |
+
category_exists = True
|
| 101 |
+
break
|
| 102 |
+
|
| 103 |
+
if not category_exists:
|
| 104 |
+
return False, "Category does not exist"
|
| 105 |
+
|
| 106 |
+
# Generate a unique topic ID
|
| 107 |
+
topic_id = f"topic_{len(db['topics']) + 1}_{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
| 108 |
+
|
| 109 |
+
# Create the topic
|
| 110 |
+
db["topics"][topic_id] = {
|
| 111 |
+
"id": topic_id,
|
| 112 |
+
"category_id": category_id,
|
| 113 |
+
"title": title,
|
| 114 |
+
"author": author,
|
| 115 |
+
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 116 |
+
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 117 |
+
"views": 0,
|
| 118 |
+
"likes": 0,
|
| 119 |
+
"is_pinned": False,
|
| 120 |
+
"is_locked": False,
|
| 121 |
+
"tags": []
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
# Create the first post in the topic
|
| 125 |
+
post_id = f"post_{len(db['posts']) + 1}_{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
| 126 |
+
db["posts"][post_id] = {
|
| 127 |
+
"id": post_id,
|
| 128 |
+
"topic_id": topic_id,
|
| 129 |
+
"author": author,
|
| 130 |
+
"content": content,
|
| 131 |
+
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 132 |
+
"updated_at": None,
|
| 133 |
+
"likes": 0,
|
| 134 |
+
"is_solution": False,
|
| 135 |
+
"replies": []
|
| 136 |
+
}
|
| 137 |
+
|
| 138 |
+
save_forum_db(db)
|
| 139 |
+
return True, topic_id
|
| 140 |
+
|
| 141 |
+
# Get all categories
|
| 142 |
+
def get_categories():
|
| 143 |
+
db = initialize_forum_db()
|
| 144 |
+
return db["categories"]
|
| 145 |
+
|
| 146 |
+
# Get topics by category
|
| 147 |
+
def get_topics_by_category(category_id):
|
| 148 |
+
db = initialize_forum_db()
|
| 149 |
+
|
| 150 |
+
topics = []
|
| 151 |
+
for topic_id, topic in db["topics"].items():
|
| 152 |
+
if topic["category_id"] == category_id:
|
| 153 |
+
topics.append(topic)
|
| 154 |
+
|
| 155 |
+
# Sort topics by pinned status and then by creation date (newest first)
|
| 156 |
+
topics.sort(key=lambda x: (not x["is_pinned"], x["created_at"]), reverse=True)
|
| 157 |
+
|
| 158 |
+
return topics
|
| 159 |
+
|
| 160 |
+
# Get topic by ID
|
| 161 |
+
def get_topic(topic_id):
|
| 162 |
+
db = initialize_forum_db()
|
| 163 |
+
|
| 164 |
+
if topic_id in db["topics"]:
|
| 165 |
+
# Increment view count
|
| 166 |
+
db["topics"][topic_id]["views"] += 1
|
| 167 |
+
save_forum_db(db)
|
| 168 |
+
return db["topics"][topic_id]
|
| 169 |
+
|
| 170 |
+
return None
|
| 171 |
+
|
| 172 |
+
# Get posts by topic ID
|
| 173 |
+
def get_posts_by_topic(topic_id):
|
| 174 |
+
db = initialize_forum_db()
|
| 175 |
+
|
| 176 |
+
posts = []
|
| 177 |
+
for post_id, post in db["posts"].items():
|
| 178 |
+
if post["topic_id"] == topic_id:
|
| 179 |
+
posts.append(post)
|
| 180 |
+
|
| 181 |
+
# Sort posts by creation date
|
| 182 |
+
posts.sort(key=lambda x: x["created_at"])
|
| 183 |
+
|
| 184 |
+
return posts
|
| 185 |
+
|
| 186 |
+
# Create a reply to a post
|
| 187 |
+
def create_reply(post_id, content, author):
|
| 188 |
+
db = initialize_forum_db()
|
| 189 |
+
|
| 190 |
+
if post_id not in db["posts"]:
|
| 191 |
+
return False, "Post not found"
|
| 192 |
+
|
| 193 |
+
# Generate a unique reply ID
|
| 194 |
+
reply_id = f"reply_{len(db['posts'][post_id]['replies']) + 1}_{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
| 195 |
+
|
| 196 |
+
# Add the reply
|
| 197 |
+
db["posts"][post_id]["replies"].append({
|
| 198 |
+
"id": reply_id,
|
| 199 |
+
"author": author,
|
| 200 |
+
"content": content,
|
| 201 |
+
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 202 |
+
"updated_at": None,
|
| 203 |
+
"likes": 0
|
| 204 |
+
})
|
| 205 |
+
|
| 206 |
+
# Update the topic's updated_at timestamp
|
| 207 |
+
topic_id = db["posts"][post_id]["topic_id"]
|
| 208 |
+
db["topics"][topic_id]["updated_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 209 |
+
|
| 210 |
+
save_forum_db(db)
|
| 211 |
+
return True, reply_id
|
| 212 |
+
|
| 213 |
+
# Like a post
|
| 214 |
+
def like_post(post_id, username):
|
| 215 |
+
db = initialize_forum_db()
|
| 216 |
+
|
| 217 |
+
if post_id not in db["posts"]:
|
| 218 |
+
return False, "Post not found"
|
| 219 |
+
|
| 220 |
+
# In a real app, we would track which users liked which posts
|
| 221 |
+
# For simplicity, we'll just increment the like count
|
| 222 |
+
db["posts"][post_id]["likes"] += 1
|
| 223 |
+
|
| 224 |
+
save_forum_db(db)
|
| 225 |
+
return True, "Post liked"
|
| 226 |
+
|
| 227 |
+
# Like a topic
|
| 228 |
+
def like_topic(topic_id, username):
|
| 229 |
+
db = initialize_forum_db()
|
| 230 |
+
|
| 231 |
+
if topic_id not in db["topics"]:
|
| 232 |
+
return False, "Topic not found"
|
| 233 |
+
|
| 234 |
+
# In a real app, we would track which users liked which topics
|
| 235 |
+
# For simplicity, we'll just increment the like count
|
| 236 |
+
db["topics"][topic_id]["likes"] += 1
|
| 237 |
+
|
| 238 |
+
save_forum_db(db)
|
| 239 |
+
return True, "Topic liked"
|
| 240 |
+
|
| 241 |
+
# Search topics and posts
|
| 242 |
+
def search_forum(query):
|
| 243 |
+
db = initialize_forum_db()
|
| 244 |
+
|
| 245 |
+
results = {
|
| 246 |
+
"topics": [],
|
| 247 |
+
"posts": []
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
# Search in topics
|
| 251 |
+
for topic_id, topic in db["topics"].items():
|
| 252 |
+
if query.lower() in topic["title"].lower():
|
| 253 |
+
results["topics"].append(topic)
|
| 254 |
+
|
| 255 |
+
# Search in posts
|
| 256 |
+
for post_id, post in db["posts"].items():
|
| 257 |
+
if query.lower() in post["content"].lower():
|
| 258 |
+
# Add the associated topic
|
| 259 |
+
topic = db["topics"][post["topic_id"]]
|
| 260 |
+
if topic not in results["topics"]:
|
| 261 |
+
results["topics"].append(topic)
|
| 262 |
+
results["posts"].append(post)
|
| 263 |
+
|
| 264 |
+
return results
|
| 265 |
+
|
| 266 |
+
# Forum UI components
|
| 267 |
+
def forum_categories():
|
| 268 |
+
st.subheader("Community Forum")
|
| 269 |
+
st.write("Connect with others, share experiences, and find support in our community forums.")
|
| 270 |
+
|
| 271 |
+
categories = get_categories()
|
| 272 |
+
|
| 273 |
+
# Display categories in a grid
|
| 274 |
+
cols = st.columns(2)
|
| 275 |
+
for i, category in enumerate(categories):
|
| 276 |
+
with cols[i % 2]:
|
| 277 |
+
with st.container():
|
| 278 |
+
st.markdown(f"### {category['name']}")
|
| 279 |
+
st.write(category['description'])
|
| 280 |
+
if st.button(f"Browse {category['name']}", key=f"browse_{category['id']}"):
|
| 281 |
+
st.session_state.forum_view = "topics"
|
| 282 |
+
st.session_state.forum_category = category['id']
|
| 283 |
+
st.experimental_rerun()
|
| 284 |
+
|
| 285 |
+
def forum_topics(category_id):
|
| 286 |
+
db = initialize_forum_db()
|
| 287 |
+
|
| 288 |
+
# Find category name
|
| 289 |
+
category_name = "Unknown Category"
|
| 290 |
+
for category in db["categories"]:
|
| 291 |
+
if category["id"] == category_id:
|
| 292 |
+
category_name = category["name"]
|
| 293 |
+
break
|
| 294 |
+
|
| 295 |
+
st.subheader(f"{category_name} - Topics")
|
| 296 |
+
|
| 297 |
+
# Back button
|
| 298 |
+
if st.button("← Back to Categories"):
|
| 299 |
+
st.session_state.forum_view = "categories"
|
| 300 |
+
st.session_state.forum_category = None
|
| 301 |
+
st.experimental_rerun()
|
| 302 |
+
|
| 303 |
+
# New topic button
|
| 304 |
+
if st.session_state.get("logged_in", False):
|
| 305 |
+
if st.button("+ New Topic"):
|
| 306 |
+
st.session_state.forum_view = "new_topic"
|
| 307 |
+
st.experimental_rerun()
|
| 308 |
+
else:
|
| 309 |
+
st.info("Please log in to create a new topic")
|
| 310 |
+
|
| 311 |
+
# Get topics
|
| 312 |
+
topics = get_topics_by_category(category_id)
|
| 313 |
+
|
| 314 |
+
if not topics:
|
| 315 |
+
st.write("No topics in this category yet. Be the first to start a discussion!")
|
| 316 |
+
else:
|
| 317 |
+
# Display topics
|
| 318 |
+
for topic in topics:
|
| 319 |
+
with st.container():
|
| 320 |
+
col1, col2, col3 = st.columns([3, 1, 1])
|
| 321 |
+
|
| 322 |
+
with col1:
|
| 323 |
+
title = topic["title"]
|
| 324 |
+
if topic["is_pinned"]:
|
| 325 |
+
title = "📌 " + title
|
| 326 |
+
if topic["is_locked"]:
|
| 327 |
+
title = "🔒 " + title
|
| 328 |
+
|
| 329 |
+
if st.button(title, key=f"topic_{topic['id']}"):
|
| 330 |
+
st.session_state.forum_view = "topic"
|
| 331 |
+
st.session_state.forum_topic = topic['id']
|
| 332 |
+
st.experimental_rerun()
|
| 333 |
+
|
| 334 |
+
with col2:
|
| 335 |
+
st.write(f"👁️ {topic['views']} | ❤️ {topic['likes']}")
|
| 336 |
+
|
| 337 |
+
with col3:
|
| 338 |
+
st.write(f"by {topic['author']}")
|
| 339 |
+
st.write(f"{topic['created_at']}")
|
| 340 |
+
|
| 341 |
+
st.markdown("---")
|
| 342 |
+
|
| 343 |
+
def new_topic_form(category_id):
|
| 344 |
+
db = initialize_forum_db()
|
| 345 |
+
|
| 346 |
+
# Find category name
|
| 347 |
+
category_name = "Unknown Category"
|
| 348 |
+
for category in db["categories"]:
|
| 349 |
+
if category["id"] == category_id:
|
| 350 |
+
category_name = category["name"]
|
| 351 |
+
break
|
| 352 |
+
|
| 353 |
+
st.subheader(f"New Topic in {category_name}")
|
| 354 |
+
|
| 355 |
+
# Back button
|
| 356 |
+
if st.button("← Back to Topics"):
|
| 357 |
+
st.session_state.forum_view = "topics"
|
| 358 |
+
st.experimental_rerun()
|
| 359 |
+
|
| 360 |
+
# Topic form
|
| 361 |
+
title = st.text_input("Topic Title")
|
| 362 |
+
content = st.text_area("Content", height=200)
|
| 363 |
+
|
| 364 |
+
if st.button("Create Topic"):
|
| 365 |
+
if title and content:
|
| 366 |
+
success, result = create_topic(
|
| 367 |
+
category_id,
|
| 368 |
+
title,
|
| 369 |
+
content,
|
| 370 |
+
st.session_state.username
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
if success:
|
| 374 |
+
st.success("Topic created successfully!")
|
| 375 |
+
st.session_state.forum_view = "topic"
|
| 376 |
+
st.session_state.forum_topic = result
|
| 377 |
+
st.experimental_rerun()
|
| 378 |
+
else:
|
| 379 |
+
st.error(result)
|
| 380 |
+
else:
|
| 381 |
+
st.warning("Please fill in both title and content")
|
| 382 |
+
|
| 383 |
+
def view_topic(topic_id):
|
| 384 |
+
topic = get_topic(topic_id)
|
| 385 |
+
|
| 386 |
+
if not topic:
|
| 387 |
+
st.error("Topic not found")
|
| 388 |
+
if st.button("Back to Categories"):
|
| 389 |
+
st.session_state.forum_view = "categories"
|
| 390 |
+
st.session_state.forum_topic = None
|
| 391 |
+
st.experimental_rerun()
|
| 392 |
+
return
|
| 393 |
+
|
| 394 |
+
st.subheader(topic["title"])
|
| 395 |
+
|
| 396 |
+
# Back button
|
| 397 |
+
if st.button("← Back to Topics"):
|
| 398 |
+
st.session_state.forum_view = "topics"
|
| 399 |
+
st.session_state.forum_topic = None
|
| 400 |
+
st.experimental_rerun()
|
| 401 |
+
|
| 402 |
+
# Topic metadata
|
| 403 |
+
st.write(f"Started by {topic['author']} on {topic['created_at']}")
|
| 404 |
+
st.write(f"👁️ {topic['views']} views | ❤️ {topic['likes']} likes")
|
| 405 |
+
|
| 406 |
+
# Like button
|
| 407 |
+
if st.session_state.get("logged_in", False):
|
| 408 |
+
if st.button("❤️ Like", key="like_topic"):
|
| 409 |
+
like_topic(topic_id, st.session_state.username)
|
| 410 |
+
st.experimental_rerun()
|
| 411 |
+
|
| 412 |
+
st.markdown("---")
|
| 413 |
+
|
| 414 |
+
# Get posts
|
| 415 |
+
posts = get_posts_by_topic(topic_id)
|
| 416 |
+
|
| 417 |
+
# Display posts
|
| 418 |
+
for post in posts:
|
| 419 |
+
with st.container():
|
| 420 |
+
st.write(f"**{post['author']}** - {post['created_at']}")
|
| 421 |
+
st.write(post["content"])
|
| 422 |
+
|
| 423 |
+
# Like button for post
|
| 424 |
+
col1, col2 = st.columns([1, 10])
|
| 425 |
+
with col1:
|
| 426 |
+
if st.session_state.get("logged_in", False):
|
| 427 |
+
if st.button("👍", key=f"like_post_{post['id']}"):
|
| 428 |
+
like_post(post['id'], st.session_state.username)
|
| 429 |
+
st.experimental_rerun()
|
| 430 |
+
with col2:
|
| 431 |
+
st.write(f"{post['likes']} likes")
|
| 432 |
+
|
| 433 |
+
# Reply button
|
| 434 |
+
if st.session_state.get("logged_in", False) and not topic["is_locked"]:
|
| 435 |
+
if st.button("Reply", key=f"reply_button_{post['id']}"):
|
| 436 |
+
st.session_state.replying_to = post['id']
|
| 437 |
+
st.experimental_rerun()
|
| 438 |
+
|
| 439 |
+
# Reply form
|
| 440 |
+
if st.session_state.get("replying_to") == post['id']:
|
| 441 |
+
reply_content = st.text_area("Your reply", key=f"reply_{post['id']}", height=100)
|
| 442 |
+
|
| 443 |
+
col1, col2 = st.columns([1, 5])
|
| 444 |
+
with col1:
|
| 445 |
+
if st.button("Submit", key=f"submit_reply_{post['id']}"):
|
| 446 |
+
if reply_content:
|
| 447 |
+
success, _ = create_reply(
|
| 448 |
+
post['id'],
|
| 449 |
+
reply_content,
|
| 450 |
+
st.session_state.username
|
| 451 |
+
)
|
| 452 |
+
|
| 453 |
+
if success:
|
| 454 |
+
st.session_state.replying_to = None
|
| 455 |
+
st.experimental_rerun()
|
| 456 |
+
else:
|
| 457 |
+
st.warning("Please enter a reply")
|
| 458 |
+
with col2:
|
| 459 |
+
if st.button("Cancel", key=f"cancel_reply_{post['id']}"):
|
| 460 |
+
st.session_state.replying_to = None
|
| 461 |
+
st.experimental_rerun()
|
| 462 |
+
|
| 463 |
+
# Display replies
|
| 464 |
+
if post["replies"]:
|
| 465 |
+
with st.expander(f"View {len(post['replies'])} replies"):
|
| 466 |
+
for reply in post["replies"]:
|
| 467 |
+
st.write(f"**{reply['author']}** - {reply['created_at']}")
|
| 468 |
+
st.write(reply["content"])
|
| 469 |
+
st.write(f"{reply['likes']} likes")
|
| 470 |
+
st.markdown("---")
|
| 471 |
+
|
| 472 |
+
st.markdown("---")
|
| 473 |
+
|
| 474 |
+
# Reply to topic
|
| 475 |
+
if st.session_state.get("logged_in", False) and not topic["is_locked"]:
|
| 476 |
+
st.subheader("Add a Reply")
|
| 477 |
+
reply_content = st.text_area("Your reply", key="main_reply", height=150)
|
| 478 |
+
|
| 479 |
+
if st.button("Post Reply"):
|
| 480 |
+
if reply_content:
|
| 481 |
+
# Get the first post (original post)
|
| 482 |
+
original_post_id = posts[0]["id"] if posts else None
|
| 483 |
+
|
| 484 |
+
if original_post_id:
|
| 485 |
+
success, _ = create_reply(
|
| 486 |
+
original_post_id,
|
| 487 |
+
reply_content,
|
| 488 |
+
st.session_state.username
|
| 489 |
+
)
|
| 490 |
+
|
| 491 |
+
if success:
|
| 492 |
+
st.success("Reply posted successfully!")
|
| 493 |
+
st.experimental_rerun()
|
| 494 |
+
else:
|
| 495 |
+
st.error("Could not find the original post")
|
| 496 |
+
else:
|
| 497 |
+
st.warning("Please enter a reply")
|
| 498 |
+
elif not st.session_state.get("logged_in", False):
|
| 499 |
+
st.info("Please log in to reply")
|
| 500 |
+
elif topic["is_locked"]:
|
| 501 |
+
st.warning("This topic is locked and cannot be replied to")
|
| 502 |
+
|
| 503 |
+
def forum_search():
|
| 504 |
+
st.subheader("Search the Forum")
|
| 505 |
+
|
| 506 |
+
query = st.text_input("Search for topics and posts")
|
| 507 |
+
|
| 508 |
+
if query:
|
| 509 |
+
results = search_forum(query)
|
| 510 |
+
|
| 511 |
+
if not results["topics"] and not results["posts"]:
|
| 512 |
+
st.write("No results found")
|
| 513 |
+
else:
|
| 514 |
+
st.write(f"Found {len(results['topics'])} topics and {len(results['posts'])} posts")
|
| 515 |
+
|
| 516 |
+
# Display topic results
|
| 517 |
+
if results["topics"]:
|
| 518 |
+
st.subheader("Topics")
|
| 519 |
+
for topic in results["topics"]:
|
| 520 |
+
if st.button(topic["title"], key=f"search_topic_{topic['id']}"):
|
| 521 |
+
st.session_state.forum_view = "topic"
|
| 522 |
+
st.session_state.forum_topic = topic['id']
|
| 523 |
+
st.experimental_rerun()
|
| 524 |
+
st.write(f"by {topic['author']} on {topic['created_at']}")
|
| 525 |
+
st.markdown("---")
|
| 526 |
+
|
| 527 |
+
def forum_page():
|
| 528 |
+
# Initialize session state variables if they don't exist
|
| 529 |
+
if "forum_view" not in st.session_state:
|
| 530 |
+
st.session_state.forum_view = "categories"
|
| 531 |
+
|
| 532 |
+
if "forum_category" not in st.session_state:
|
| 533 |
+
st.session_state.forum_category = None
|
| 534 |
+
|
| 535 |
+
if "forum_topic" not in st.session_state:
|
| 536 |
+
st.session_state.forum_topic = None
|
| 537 |
+
|
| 538 |
+
if "replying_to" not in st.session_state:
|
| 539 |
+
st.session_state.replying_to = None
|
| 540 |
+
|
| 541 |
+
# Search box in sidebar
|
| 542 |
+
with st.sidebar:
|
| 543 |
+
st.subheader("Forum Search")
|
| 544 |
+
search_query = st.text_input("Search", key="forum_search")
|
| 545 |
+
if search_query:
|
| 546 |
+
st.session_state.forum_view = "search"
|
| 547 |
+
st.experimental_rerun()
|
| 548 |
+
|
| 549 |
+
# Display the appropriate view
|
| 550 |
+
if st.session_state.forum_view == "categories":
|
| 551 |
+
forum_categories()
|
| 552 |
+
elif st.session_state.forum_view == "topics" and st.session_state.forum_category:
|
| 553 |
+
forum_topics(st.session_state.forum_category)
|
| 554 |
+
elif st.session_state.forum_view == "topic" and st.session_state.forum_topic:
|
| 555 |
+
view_topic(st.session_state.forum_topic)
|
| 556 |
+
elif st.session_state.forum_view == "new_topic" and st.session_state.forum_category:
|
| 557 |
+
new_topic_form(st.session_state.forum_category)
|
| 558 |
+
elif st.session_state.forum_view == "search":
|
| 559 |
+
forum_search()
|
| 560 |
+
else:
|
| 561 |
+
# Default to categories view
|
| 562 |
+
st.session_state.forum_view = "categories"
|
| 563 |
+
forum_categories()
|
AfyaMindSpace/forum_data.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"categories": [{"id": "general", "name": "General Discussion", "description": "General topics related to mental health and wellbeing"}, {"id": "stress", "name": "Stress Management", "description": "Discuss strategies for managing stress and anxiety"}, {"id": "mindfulness", "name": "Mindfulness & Meditation", "description": "Share experiences and tips about mindfulness practices"}, {"id": "support", "name": "Peer Support", "description": "A safe space to seek support from others"}, {"id": "success", "name": "Success Stories", "description": "Share your mental health journey and achievements"}], "topics": {}, "posts": {}}
|
AfyaMindSpace/goals.py
ADDED
|
@@ -0,0 +1,778 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
from datetime import datetime, timedelta
|
| 5 |
+
import pandas as pd
|
| 6 |
+
import altair as alt
|
| 7 |
+
|
| 8 |
+
# File to store goals data
|
| 9 |
+
GOALS_DB_FILE = "goals_data.json"
|
| 10 |
+
|
| 11 |
+
# Initialize goals database
|
| 12 |
+
def initialize_goals_db():
|
| 13 |
+
if not os.path.exists(GOALS_DB_FILE):
|
| 14 |
+
goals_data = {
|
| 15 |
+
"goals": {},
|
| 16 |
+
"templates": [
|
| 17 |
+
{
|
| 18 |
+
"id": "stress_reduction",
|
| 19 |
+
"title": "Stress Reduction",
|
| 20 |
+
"description": "Develop habits to reduce and manage stress in your daily life.",
|
| 21 |
+
"suggested_activities": [
|
| 22 |
+
"Practice deep breathing for 5 minutes",
|
| 23 |
+
"Take a short walk outside",
|
| 24 |
+
"Write in a journal",
|
| 25 |
+
"Complete a guided meditation",
|
| 26 |
+
"Do a body scan relaxation exercise"
|
| 27 |
+
],
|
| 28 |
+
"duration_options": [7, 14, 30],
|
| 29 |
+
"category": "Stress Management",
|
| 30 |
+
"icon": "🧘"
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"id": "mood_tracking",
|
| 34 |
+
"title": "Consistent Mood Tracking",
|
| 35 |
+
"description": "Build a habit of tracking your mood daily to identify patterns.",
|
| 36 |
+
"suggested_activities": [
|
| 37 |
+
"Record your mood in the morning",
|
| 38 |
+
"Record your mood in the evening",
|
| 39 |
+
"Note what influenced your mood today",
|
| 40 |
+
"Reflect on mood patterns from the past week"
|
| 41 |
+
],
|
| 42 |
+
"duration_options": [7, 14, 30],
|
| 43 |
+
"category": "Self-Awareness",
|
| 44 |
+
"icon": "📊"
|
| 45 |
+
},
|
| 46 |
+
{
|
| 47 |
+
"id": "positive_thinking",
|
| 48 |
+
"title": "Positive Thinking Practice",
|
| 49 |
+
"description": "Train your mind to focus more on positive aspects of life.",
|
| 50 |
+
"suggested_activities": [
|
| 51 |
+
"Write down three things you're grateful for",
|
| 52 |
+
"Give someone a genuine compliment",
|
| 53 |
+
"Reframe a negative thought into a positive one",
|
| 54 |
+
"Practice positive affirmations",
|
| 55 |
+
"Notice and appreciate small joys"
|
| 56 |
+
],
|
| 57 |
+
"duration_options": [7, 14, 21],
|
| 58 |
+
"category": "Emotional Wellbeing",
|
| 59 |
+
"icon": "✨"
|
| 60 |
+
},
|
| 61 |
+
{
|
| 62 |
+
"id": "social_connection",
|
| 63 |
+
"title": "Strengthen Social Connections",
|
| 64 |
+
"description": "Build and maintain meaningful social relationships.",
|
| 65 |
+
"suggested_activities": [
|
| 66 |
+
"Reach out to a friend or family member",
|
| 67 |
+
"Have a meaningful conversation with someone",
|
| 68 |
+
"Participate in a group activity",
|
| 69 |
+
"Practice active listening",
|
| 70 |
+
"Express appreciation to someone important to you"
|
| 71 |
+
],
|
| 72 |
+
"duration_options": [7, 14, 30],
|
| 73 |
+
"category": "Social Skills",
|
| 74 |
+
"icon": "👥"
|
| 75 |
+
},
|
| 76 |
+
{
|
| 77 |
+
"id": "mindfulness",
|
| 78 |
+
"title": "Daily Mindfulness Practice",
|
| 79 |
+
"description": "Develop a regular mindfulness practice to stay present and aware.",
|
| 80 |
+
"suggested_activities": [
|
| 81 |
+
"Practice mindful eating for one meal",
|
| 82 |
+
"Do a 5-minute mindfulness meditation",
|
| 83 |
+
"Take a mindful walk focusing on your senses",
|
| 84 |
+
"Practice mindful breathing for 3 minutes",
|
| 85 |
+
"Do a mindful check-in during a stressful moment"
|
| 86 |
+
],
|
| 87 |
+
"duration_options": [7, 14, 30],
|
| 88 |
+
"category": "Mindfulness",
|
| 89 |
+
"icon": "🌿"
|
| 90 |
+
},
|
| 91 |
+
{
|
| 92 |
+
"id": "custom",
|
| 93 |
+
"title": "Custom Goal",
|
| 94 |
+
"description": "Create your own personalized mental health goal.",
|
| 95 |
+
"suggested_activities": [],
|
| 96 |
+
"duration_options": [7, 14, 21, 30],
|
| 97 |
+
"category": "Custom",
|
| 98 |
+
"icon": "🎯"
|
| 99 |
+
}
|
| 100 |
+
]
|
| 101 |
+
}
|
| 102 |
+
with open(GOALS_DB_FILE, "w") as f:
|
| 103 |
+
json.dump(goals_data, f)
|
| 104 |
+
return goals_data
|
| 105 |
+
|
| 106 |
+
try:
|
| 107 |
+
with open(GOALS_DB_FILE, "r") as f:
|
| 108 |
+
return json.load(f)
|
| 109 |
+
except json.JSONDecodeError:
|
| 110 |
+
# If file is corrupted, create a new one
|
| 111 |
+
goals_data = {
|
| 112 |
+
"goals": {},
|
| 113 |
+
"templates": [
|
| 114 |
+
{
|
| 115 |
+
"id": "stress_reduction",
|
| 116 |
+
"title": "Stress Reduction",
|
| 117 |
+
"description": "Develop habits to reduce and manage stress in your daily life.",
|
| 118 |
+
"suggested_activities": [
|
| 119 |
+
"Practice deep breathing for 5 minutes",
|
| 120 |
+
"Take a short walk outside",
|
| 121 |
+
"Write in a journal",
|
| 122 |
+
"Complete a guided meditation",
|
| 123 |
+
"Do a body scan relaxation exercise"
|
| 124 |
+
],
|
| 125 |
+
"duration_options": [7, 14, 30],
|
| 126 |
+
"category": "Stress Management",
|
| 127 |
+
"icon": "🧘"
|
| 128 |
+
},
|
| 129 |
+
{
|
| 130 |
+
"id": "mood_tracking",
|
| 131 |
+
"title": "Consistent Mood Tracking",
|
| 132 |
+
"description": "Build a habit of tracking your mood daily to identify patterns.",
|
| 133 |
+
"suggested_activities": [
|
| 134 |
+
"Record your mood in the morning",
|
| 135 |
+
"Record your mood in the evening",
|
| 136 |
+
"Note what influenced your mood today",
|
| 137 |
+
"Reflect on mood patterns from the past week"
|
| 138 |
+
],
|
| 139 |
+
"duration_options": [7, 14, 30],
|
| 140 |
+
"category": "Self-Awareness",
|
| 141 |
+
"icon": "📊"
|
| 142 |
+
},
|
| 143 |
+
{
|
| 144 |
+
"id": "positive_thinking",
|
| 145 |
+
"title": "Positive Thinking Practice",
|
| 146 |
+
"description": "Train your mind to focus more on positive aspects of life.",
|
| 147 |
+
"suggested_activities": [
|
| 148 |
+
"Write down three things you're grateful for",
|
| 149 |
+
"Give someone a genuine compliment",
|
| 150 |
+
"Reframe a negative thought into a positive one",
|
| 151 |
+
"Practice positive affirmations",
|
| 152 |
+
"Notice and appreciate small joys"
|
| 153 |
+
],
|
| 154 |
+
"duration_options": [7, 14, 21],
|
| 155 |
+
"category": "Emotional Wellbeing",
|
| 156 |
+
"icon": "✨"
|
| 157 |
+
},
|
| 158 |
+
{
|
| 159 |
+
"id": "social_connection",
|
| 160 |
+
"title": "Strengthen Social Connections",
|
| 161 |
+
"description": "Build and maintain meaningful social relationships.",
|
| 162 |
+
"suggested_activities": [
|
| 163 |
+
"Reach out to a friend or family member",
|
| 164 |
+
"Have a meaningful conversation with someone",
|
| 165 |
+
"Participate in a group activity",
|
| 166 |
+
"Practice active listening",
|
| 167 |
+
"Express appreciation to someone important to you"
|
| 168 |
+
],
|
| 169 |
+
"duration_options": [7, 14, 30],
|
| 170 |
+
"category": "Social Skills",
|
| 171 |
+
"icon": "👥"
|
| 172 |
+
},
|
| 173 |
+
{
|
| 174 |
+
"id": "mindfulness",
|
| 175 |
+
"title": "Daily Mindfulness Practice",
|
| 176 |
+
"description": "Develop a regular mindfulness practice to stay present and aware.",
|
| 177 |
+
"suggested_activities": [
|
| 178 |
+
"Practice mindful eating for one meal",
|
| 179 |
+
"Do a 5-minute mindfulness meditation",
|
| 180 |
+
"Take a mindful walk focusing on your senses",
|
| 181 |
+
"Practice mindful breathing for 3 minutes",
|
| 182 |
+
"Do a mindful check-in during a stressful moment"
|
| 183 |
+
],
|
| 184 |
+
"duration_options": [7, 14, 30],
|
| 185 |
+
"category": "Mindfulness",
|
| 186 |
+
"icon": "🌿"
|
| 187 |
+
},
|
| 188 |
+
{
|
| 189 |
+
"id": "custom",
|
| 190 |
+
"title": "Custom Goal",
|
| 191 |
+
"description": "Create your own personalized mental health goal.",
|
| 192 |
+
"suggested_activities": [],
|
| 193 |
+
"duration_options": [7, 14, 21, 30],
|
| 194 |
+
"category": "Custom",
|
| 195 |
+
"icon": "🎯"
|
| 196 |
+
}
|
| 197 |
+
]
|
| 198 |
+
}
|
| 199 |
+
with open(GOALS_DB_FILE, "w") as f:
|
| 200 |
+
json.dump(goals_data, f)
|
| 201 |
+
return goals_data
|
| 202 |
+
|
| 203 |
+
# Save goals database
|
| 204 |
+
def save_goals_db(db):
|
| 205 |
+
with open(GOALS_DB_FILE, "w") as f:
|
| 206 |
+
json.dump(db, f)
|
| 207 |
+
|
| 208 |
+
# Get all goal templates
|
| 209 |
+
def get_goal_templates():
|
| 210 |
+
db = initialize_goals_db()
|
| 211 |
+
return db["templates"]
|
| 212 |
+
|
| 213 |
+
# Get goal template by ID
|
| 214 |
+
def get_goal_template(template_id):
|
| 215 |
+
db = initialize_goals_db()
|
| 216 |
+
|
| 217 |
+
for template in db["templates"]:
|
| 218 |
+
if template["id"] == template_id:
|
| 219 |
+
return template
|
| 220 |
+
|
| 221 |
+
return None
|
| 222 |
+
|
| 223 |
+
# Create a new goal for a user
|
| 224 |
+
def create_goal(username, template_id, title, description, activities, duration, start_date=None):
|
| 225 |
+
if not username:
|
| 226 |
+
return False, "User not logged in"
|
| 227 |
+
|
| 228 |
+
db = initialize_goals_db()
|
| 229 |
+
|
| 230 |
+
# Generate a unique goal ID
|
| 231 |
+
goal_id = f"goal_{len(db['goals']) + 1}_{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
| 232 |
+
|
| 233 |
+
# Set start date to today if not provided
|
| 234 |
+
if not start_date:
|
| 235 |
+
start_date = datetime.now().strftime("%Y-%m-%d")
|
| 236 |
+
|
| 237 |
+
# Calculate end date
|
| 238 |
+
start_datetime = datetime.strptime(start_date, "%Y-%m-%d")
|
| 239 |
+
end_date = (start_datetime + timedelta(days=duration)).strftime("%Y-%m-%d")
|
| 240 |
+
|
| 241 |
+
# Create the goal
|
| 242 |
+
if username not in db["goals"]:
|
| 243 |
+
db["goals"][username] = {}
|
| 244 |
+
|
| 245 |
+
db["goals"][username][goal_id] = {
|
| 246 |
+
"id": goal_id,
|
| 247 |
+
"template_id": template_id,
|
| 248 |
+
"title": title,
|
| 249 |
+
"description": description,
|
| 250 |
+
"activities": activities,
|
| 251 |
+
"duration": duration,
|
| 252 |
+
"start_date": start_date,
|
| 253 |
+
"end_date": end_date,
|
| 254 |
+
"progress": {},
|
| 255 |
+
"completed": False,
|
| 256 |
+
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
save_goals_db(db)
|
| 260 |
+
return True, goal_id
|
| 261 |
+
|
| 262 |
+
# Get all goals for a user
|
| 263 |
+
def get_user_goals(username):
|
| 264 |
+
if not username:
|
| 265 |
+
return []
|
| 266 |
+
|
| 267 |
+
db = initialize_goals_db()
|
| 268 |
+
|
| 269 |
+
if username not in db["goals"]:
|
| 270 |
+
return []
|
| 271 |
+
|
| 272 |
+
# Convert to list and sort by creation date (newest first)
|
| 273 |
+
goals = list(db["goals"][username].values())
|
| 274 |
+
goals.sort(key=lambda x: x["created_at"], reverse=True)
|
| 275 |
+
|
| 276 |
+
return goals
|
| 277 |
+
|
| 278 |
+
# Get active goals for a user
|
| 279 |
+
def get_active_goals(username):
|
| 280 |
+
goals = get_user_goals(username)
|
| 281 |
+
|
| 282 |
+
# Filter for active goals (not completed and end date not passed)
|
| 283 |
+
today = datetime.now().strftime("%Y-%m-%d")
|
| 284 |
+
active_goals = [g for g in goals if not g["completed"] and g["end_date"] >= today]
|
| 285 |
+
|
| 286 |
+
return active_goals
|
| 287 |
+
|
| 288 |
+
# Get a specific goal
|
| 289 |
+
def get_goal(username, goal_id):
|
| 290 |
+
if not username:
|
| 291 |
+
return None
|
| 292 |
+
|
| 293 |
+
db = initialize_goals_db()
|
| 294 |
+
|
| 295 |
+
if username not in db["goals"] or goal_id not in db["goals"][username]:
|
| 296 |
+
return None
|
| 297 |
+
|
| 298 |
+
return db["goals"][username][goal_id]
|
| 299 |
+
|
| 300 |
+
# Update goal progress
|
| 301 |
+
def update_goal_progress(username, goal_id, activity, completed):
|
| 302 |
+
if not username:
|
| 303 |
+
return False, "User not logged in"
|
| 304 |
+
|
| 305 |
+
db = initialize_goals_db()
|
| 306 |
+
|
| 307 |
+
if username not in db["goals"] or goal_id not in db["goals"][username]:
|
| 308 |
+
return False, "Goal not found"
|
| 309 |
+
|
| 310 |
+
goal = db["goals"][username][goal_id]
|
| 311 |
+
|
| 312 |
+
# Check if activity exists in the goal
|
| 313 |
+
if activity not in goal["activities"]:
|
| 314 |
+
return False, "Activity not found in goal"
|
| 315 |
+
|
| 316 |
+
# Get today's date
|
| 317 |
+
today = datetime.now().strftime("%Y-%m-%d")
|
| 318 |
+
|
| 319 |
+
# Initialize progress for today if needed
|
| 320 |
+
if today not in goal["progress"]:
|
| 321 |
+
goal["progress"][today] = []
|
| 322 |
+
|
| 323 |
+
# Update progress
|
| 324 |
+
if completed and activity not in goal["progress"][today]:
|
| 325 |
+
goal["progress"][today].append(activity)
|
| 326 |
+
elif not completed and activity in goal["progress"][today]:
|
| 327 |
+
goal["progress"][today].remove(activity)
|
| 328 |
+
|
| 329 |
+
save_goals_db(db)
|
| 330 |
+
return True, "Progress updated"
|
| 331 |
+
|
| 332 |
+
# Mark goal as completed
|
| 333 |
+
def complete_goal(username, goal_id):
|
| 334 |
+
if not username:
|
| 335 |
+
return False, "User not logged in"
|
| 336 |
+
|
| 337 |
+
db = initialize_goals_db()
|
| 338 |
+
|
| 339 |
+
if username not in db["goals"] or goal_id not in db["goals"][username]:
|
| 340 |
+
return False, "Goal not found"
|
| 341 |
+
|
| 342 |
+
db["goals"][username][goal_id]["completed"] = True
|
| 343 |
+
|
| 344 |
+
save_goals_db(db)
|
| 345 |
+
return True, "Goal marked as completed"
|
| 346 |
+
|
| 347 |
+
# Calculate goal statistics
|
| 348 |
+
def calculate_goal_stats(username):
|
| 349 |
+
goals = get_user_goals(username)
|
| 350 |
+
|
| 351 |
+
if not goals:
|
| 352 |
+
return {
|
| 353 |
+
"total": 0,
|
| 354 |
+
"active": 0,
|
| 355 |
+
"completed": 0,
|
| 356 |
+
"expired": 0,
|
| 357 |
+
"streak": 0,
|
| 358 |
+
"completion_rate": 0
|
| 359 |
+
}
|
| 360 |
+
|
| 361 |
+
today = datetime.now().strftime("%Y-%m-%d")
|
| 362 |
+
yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
|
| 363 |
+
|
| 364 |
+
# Count goals by status
|
| 365 |
+
total = len(goals)
|
| 366 |
+
completed = sum(1 for g in goals if g["completed"])
|
| 367 |
+
expired = sum(1 for g in goals if not g["completed"] and g["end_date"] < today)
|
| 368 |
+
active = total - completed - expired
|
| 369 |
+
|
| 370 |
+
# Calculate completion rate
|
| 371 |
+
completion_rate = (completed / total) * 100 if total > 0 else 0
|
| 372 |
+
|
| 373 |
+
# Calculate activity streak
|
| 374 |
+
streak = 0
|
| 375 |
+
check_date = yesterday
|
| 376 |
+
had_activity = False
|
| 377 |
+
|
| 378 |
+
while True:
|
| 379 |
+
# Check if any goal had activity on this date
|
| 380 |
+
for goal in goals:
|
| 381 |
+
if check_date in goal["progress"] and goal["progress"][check_date]:
|
| 382 |
+
had_activity = True
|
| 383 |
+
break
|
| 384 |
+
|
| 385 |
+
if had_activity:
|
| 386 |
+
streak += 1
|
| 387 |
+
# Move to previous day
|
| 388 |
+
check_date = (datetime.strptime(check_date, "%Y-%m-%d") - timedelta(days=1)).strftime("%Y-%m-%d")
|
| 389 |
+
had_activity = False
|
| 390 |
+
else:
|
| 391 |
+
break
|
| 392 |
+
|
| 393 |
+
return {
|
| 394 |
+
"total": total,
|
| 395 |
+
"active": active,
|
| 396 |
+
"completed": completed,
|
| 397 |
+
"expired": expired,
|
| 398 |
+
"streak": streak,
|
| 399 |
+
"completion_rate": round(completion_rate, 1)
|
| 400 |
+
}
|
| 401 |
+
|
| 402 |
+
# Goals UI components
|
| 403 |
+
def goals_dashboard():
|
| 404 |
+
st.subheader("Your Mental Health Goals")
|
| 405 |
+
|
| 406 |
+
if not st.session_state.get("logged_in", False):
|
| 407 |
+
st.info("Please log in to set and track your mental health goals")
|
| 408 |
+
return
|
| 409 |
+
|
| 410 |
+
# Get user goals
|
| 411 |
+
active_goals = get_active_goals(st.session_state.username)
|
| 412 |
+
|
| 413 |
+
# Stats
|
| 414 |
+
stats = calculate_goal_stats(st.session_state.username)
|
| 415 |
+
|
| 416 |
+
# Display stats
|
| 417 |
+
col1, col2, col3, col4 = st.columns(4)
|
| 418 |
+
|
| 419 |
+
with col1:
|
| 420 |
+
st.metric("Active Goals", stats["active"])
|
| 421 |
+
|
| 422 |
+
with col2:
|
| 423 |
+
st.metric("Completed", stats["completed"])
|
| 424 |
+
|
| 425 |
+
with col3:
|
| 426 |
+
st.metric("Activity Streak", f"{stats['streak']} days")
|
| 427 |
+
|
| 428 |
+
with col4:
|
| 429 |
+
st.metric("Completion Rate", f"{stats['completion_rate']}%")
|
| 430 |
+
|
| 431 |
+
# Create new goal button
|
| 432 |
+
if st.button("+ Create New Goal"):
|
| 433 |
+
st.session_state.goals_view = "new_goal"
|
| 434 |
+
st.experimental_rerun()
|
| 435 |
+
|
| 436 |
+
# Display active goals
|
| 437 |
+
if not active_goals:
|
| 438 |
+
st.write("You don't have any active goals. Create one to get started!")
|
| 439 |
+
else:
|
| 440 |
+
st.subheader("Active Goals")
|
| 441 |
+
|
| 442 |
+
for goal in active_goals:
|
| 443 |
+
with st.container():
|
| 444 |
+
# Calculate days remaining
|
| 445 |
+
end_date = datetime.strptime(goal["end_date"], "%Y-%m-%d")
|
| 446 |
+
today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
| 447 |
+
days_remaining = (end_date - today).days
|
| 448 |
+
|
| 449 |
+
# Calculate progress percentage
|
| 450 |
+
total_days = goal["duration"]
|
| 451 |
+
days_passed = total_days - days_remaining
|
| 452 |
+
time_progress = min(100, max(0, (days_passed / total_days) * 100))
|
| 453 |
+
|
| 454 |
+
# Calculate activity completion
|
| 455 |
+
today_str = datetime.now().strftime("%Y-%m-%d")
|
| 456 |
+
activities_today = goal["progress"].get(today_str, [])
|
| 457 |
+
activities_completed = len(activities_today)
|
| 458 |
+
activities_total = len(goal["activities"])
|
| 459 |
+
activity_progress = (activities_completed / activities_total) * 100 if activities_total > 0 else 0
|
| 460 |
+
|
| 461 |
+
# Display goal card
|
| 462 |
+
st.markdown(f"### {goal['title']}")
|
| 463 |
+
st.write(goal["description"])
|
| 464 |
+
st.write(f"**Days remaining:** {days_remaining} of {goal['duration']}")
|
| 465 |
+
|
| 466 |
+
# Progress bars
|
| 467 |
+
st.write("**Time progress:**")
|
| 468 |
+
st.progress(time_progress / 100)
|
| 469 |
+
|
| 470 |
+
st.write(f"**Today's activities:** {activities_completed}/{activities_total}")
|
| 471 |
+
st.progress(activity_progress / 100)
|
| 472 |
+
|
| 473 |
+
# View details button
|
| 474 |
+
if st.button("View Details", key=f"view_{goal['id']}"):
|
| 475 |
+
st.session_state.goals_view = "view_goal"
|
| 476 |
+
st.session_state.current_goal = goal["id"]
|
| 477 |
+
st.experimental_rerun()
|
| 478 |
+
|
| 479 |
+
st.markdown("---")
|
| 480 |
+
|
| 481 |
+
def new_goal_form():
|
| 482 |
+
st.subheader("Create a New Mental Health Goal")
|
| 483 |
+
|
| 484 |
+
# Back button
|
| 485 |
+
if st.button("← Back to Dashboard"):
|
| 486 |
+
st.session_state.goals_view = "dashboard"
|
| 487 |
+
st.experimental_rerun()
|
| 488 |
+
|
| 489 |
+
# Get templates
|
| 490 |
+
templates = get_goal_templates()
|
| 491 |
+
|
| 492 |
+
# Step 1: Select template
|
| 493 |
+
if "goal_step" not in st.session_state:
|
| 494 |
+
st.session_state.goal_step = 1
|
| 495 |
+
|
| 496 |
+
if "selected_template" not in st.session_state:
|
| 497 |
+
st.session_state.selected_template = None
|
| 498 |
+
|
| 499 |
+
if st.session_state.goal_step == 1:
|
| 500 |
+
st.write("Step 1: Select a goal template")
|
| 501 |
+
|
| 502 |
+
# Display templates in a grid
|
| 503 |
+
cols = st.columns(2)
|
| 504 |
+
for i, template in enumerate(templates):
|
| 505 |
+
with cols[i % 2]:
|
| 506 |
+
with st.container():
|
| 507 |
+
st.markdown(f"### {template['icon']} {template['title']}")
|
| 508 |
+
st.write(template["description"])
|
| 509 |
+
st.write(f"Category: {template['category']}")
|
| 510 |
+
|
| 511 |
+
if st.button("Select", key=f"select_{template['id']}"):
|
| 512 |
+
st.session_state.selected_template = template["id"]
|
| 513 |
+
st.session_state.goal_step = 2
|
| 514 |
+
st.experimental_rerun()
|
| 515 |
+
|
| 516 |
+
# Step 2: Customize goal
|
| 517 |
+
elif st.session_state.goal_step == 2 and st.session_state.selected_template:
|
| 518 |
+
template = get_goal_template(st.session_state.selected_template)
|
| 519 |
+
|
| 520 |
+
if not template:
|
| 521 |
+
st.error("Template not found")
|
| 522 |
+
st.session_state.goal_step = 1
|
| 523 |
+
st.experimental_rerun()
|
| 524 |
+
return
|
| 525 |
+
|
| 526 |
+
st.write("Step 2: Customize your goal")
|
| 527 |
+
|
| 528 |
+
# Goal title and description
|
| 529 |
+
title = st.text_input("Goal Title", value=template["title"])
|
| 530 |
+
description = st.text_area("Goal Description", value=template["description"])
|
| 531 |
+
|
| 532 |
+
# Duration
|
| 533 |
+
duration = st.selectbox(
|
| 534 |
+
"Goal Duration (days)",
|
| 535 |
+
template["duration_options"],
|
| 536 |
+
index=0
|
| 537 |
+
)
|
| 538 |
+
|
| 539 |
+
# Start date
|
| 540 |
+
start_date = st.date_input(
|
| 541 |
+
"Start Date",
|
| 542 |
+
value=datetime.now()
|
| 543 |
+
).strftime("%Y-%m-%d")
|
| 544 |
+
|
| 545 |
+
# Activities
|
| 546 |
+
st.write("Activities to track:")
|
| 547 |
+
|
| 548 |
+
if template["id"] == "custom":
|
| 549 |
+
# For custom goals, let user add their own activities
|
| 550 |
+
if "custom_activities" not in st.session_state:
|
| 551 |
+
st.session_state.custom_activities = [""]
|
| 552 |
+
|
| 553 |
+
for i, activity in enumerate(st.session_state.custom_activities):
|
| 554 |
+
col1, col2 = st.columns([5, 1])
|
| 555 |
+
|
| 556 |
+
with col1:
|
| 557 |
+
st.session_state.custom_activities[i] = st.text_input(
|
| 558 |
+
f"Activity {i+1}",
|
| 559 |
+
value=activity,
|
| 560 |
+
key=f"activity_{i}"
|
| 561 |
+
)
|
| 562 |
+
|
| 563 |
+
with col2:
|
| 564 |
+
if i > 0 and st.button("Remove", key=f"remove_{i}"):
|
| 565 |
+
st.session_state.custom_activities.pop(i)
|
| 566 |
+
st.experimental_rerun()
|
| 567 |
+
|
| 568 |
+
if st.button("+ Add Activity"):
|
| 569 |
+
st.session_state.custom_activities.append("")
|
| 570 |
+
st.experimental_rerun()
|
| 571 |
+
|
| 572 |
+
activities = [a for a in st.session_state.custom_activities if a.strip()]
|
| 573 |
+
else:
|
| 574 |
+
# For predefined templates, let user select from suggested activities
|
| 575 |
+
activities = []
|
| 576 |
+
for activity in template["suggested_activities"]:
|
| 577 |
+
if st.checkbox(activity, value=True):
|
| 578 |
+
activities.append(activity)
|
| 579 |
+
|
| 580 |
+
# Option to add custom activities
|
| 581 |
+
if "additional_activities" not in st.session_state:
|
| 582 |
+
st.session_state.additional_activities = []
|
| 583 |
+
|
| 584 |
+
st.write("Add your own activities:")
|
| 585 |
+
|
| 586 |
+
if "new_activity" not in st.session_state:
|
| 587 |
+
st.session_state.new_activity = ""
|
| 588 |
+
|
| 589 |
+
col1, col2 = st.columns([3, 1])
|
| 590 |
+
|
| 591 |
+
with col1:
|
| 592 |
+
st.session_state.new_activity = st.text_input("New activity")
|
| 593 |
+
|
| 594 |
+
with col2:
|
| 595 |
+
if st.button("Add") and st.session_state.new_activity.strip():
|
| 596 |
+
st.session_state.additional_activities.append(st.session_state.new_activity)
|
| 597 |
+
st.session_state.new_activity = ""
|
| 598 |
+
st.experimental_rerun()
|
| 599 |
+
|
| 600 |
+
for i, activity in enumerate(st.session_state.additional_activities):
|
| 601 |
+
col1, col2 = st.columns([5, 1])
|
| 602 |
+
|
| 603 |
+
with col1:
|
| 604 |
+
if st.checkbox(activity, value=True, key=f"additional_{i}"):
|
| 605 |
+
activities.append(activity)
|
| 606 |
+
|
| 607 |
+
with col2:
|
| 608 |
+
if st.button("Remove", key=f"remove_additional_{i}"):
|
| 609 |
+
st.session_state.additional_activities.pop(i)
|
| 610 |
+
st.experimental_rerun()
|
| 611 |
+
|
| 612 |
+
# Create goal button
|
| 613 |
+
if st.button("Create Goal"):
|
| 614 |
+
if title and description and activities:
|
| 615 |
+
success, goal_id = create_goal(
|
| 616 |
+
st.session_state.username,
|
| 617 |
+
template["id"],
|
| 618 |
+
title,
|
| 619 |
+
description,
|
| 620 |
+
activities,
|
| 621 |
+
duration,
|
| 622 |
+
start_date
|
| 623 |
+
)
|
| 624 |
+
|
| 625 |
+
if success:
|
| 626 |
+
st.success("Goal created successfully!")
|
| 627 |
+
# Reset session state
|
| 628 |
+
st.session_state.goal_step = 1
|
| 629 |
+
st.session_state.selected_template = None
|
| 630 |
+
if "custom_activities" in st.session_state:
|
| 631 |
+
del st.session_state.custom_activities
|
| 632 |
+
if "additional_activities" in st.session_state:
|
| 633 |
+
del st.session_state.additional_activities
|
| 634 |
+
if "new_activity" in st.session_state:
|
| 635 |
+
del st.session_state.new_activity
|
| 636 |
+
|
| 637 |
+
# Go to view goal
|
| 638 |
+
st.session_state.goals_view = "view_goal"
|
| 639 |
+
st.session_state.current_goal = goal_id
|
| 640 |
+
st.experimental_rerun()
|
| 641 |
+
else:
|
| 642 |
+
st.error(goal_id) # Error message
|
| 643 |
+
else:
|
| 644 |
+
st.warning("Please fill in all required fields and add at least one activity")
|
| 645 |
+
|
| 646 |
+
def view_goal(goal_id):
|
| 647 |
+
goal = get_goal(st.session_state.username, goal_id)
|
| 648 |
+
|
| 649 |
+
if not goal:
|
| 650 |
+
st.error("Goal not found")
|
| 651 |
+
st.session_state.goals_view = "dashboard"
|
| 652 |
+
st.experimental_rerun()
|
| 653 |
+
return
|
| 654 |
+
|
| 655 |
+
# Back button
|
| 656 |
+
if st.button("← Back to Dashboard"):
|
| 657 |
+
st.session_state.goals_view = "dashboard"
|
| 658 |
+
st.experimental_rerun()
|
| 659 |
+
|
| 660 |
+
# Goal details
|
| 661 |
+
st.subheader(goal["title"])
|
| 662 |
+
st.write(goal["description"])
|
| 663 |
+
|
| 664 |
+
# Calculate days remaining
|
| 665 |
+
end_date = datetime.strptime(goal["end_date"], "%Y-%m-%d")
|
| 666 |
+
today = datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
| 667 |
+
days_remaining = (end_date - today).days
|
| 668 |
+
|
| 669 |
+
# Display dates and progress
|
| 670 |
+
col1, col2, col3 = st.columns(3)
|
| 671 |
+
|
| 672 |
+
with col1:
|
| 673 |
+
st.write(f"**Start Date:** {goal['start_date']}")
|
| 674 |
+
|
| 675 |
+
with col2:
|
| 676 |
+
st.write(f"**End Date:** {goal['end_date']}")
|
| 677 |
+
|
| 678 |
+
with col3:
|
| 679 |
+
if days_remaining > 0:
|
| 680 |
+
st.write(f"**Days Remaining:** {days_remaining}")
|
| 681 |
+
else:
|
| 682 |
+
st.write("**Status:** Ended")
|
| 683 |
+
|
| 684 |
+
# Calculate overall progress
|
| 685 |
+
total_days = goal["duration"]
|
| 686 |
+
days_passed = total_days - days_remaining
|
| 687 |
+
time_progress = min(100, max(0, (days_passed / total_days) * 100))
|
| 688 |
+
|
| 689 |
+
st.write("**Overall Progress:**")
|
| 690 |
+
st.progress(time_progress / 100)
|
| 691 |
+
|
| 692 |
+
# Today's activities
|
| 693 |
+
st.subheader("Today's Activities")
|
| 694 |
+
|
| 695 |
+
today_str = datetime.now().strftime("%Y-%m-%d")
|
| 696 |
+
today_activities = goal["progress"].get(today_str, [])
|
| 697 |
+
|
| 698 |
+
for activity in goal["activities"]:
|
| 699 |
+
completed = activity in today_activities
|
| 700 |
+
if st.checkbox(activity, value=completed, key=f"activity_{activity}"):
|
| 701 |
+
if not completed:
|
| 702 |
+
update_goal_progress(st.session_state.username, goal_id, activity, True)
|
| 703 |
+
st.experimental_rerun()
|
| 704 |
+
else:
|
| 705 |
+
if completed:
|
| 706 |
+
update_goal_progress(st.session_state.username, goal_id, activity, False)
|
| 707 |
+
st.experimental_rerun()
|
| 708 |
+
|
| 709 |
+
# Progress history
|
| 710 |
+
st.subheader("Progress History")
|
| 711 |
+
|
| 712 |
+
# Create a dataframe for visualization
|
| 713 |
+
history_data = []
|
| 714 |
+
|
| 715 |
+
start_date = datetime.strptime(goal["start_date"], "%Y-%m-%d")
|
| 716 |
+
end_date = datetime.strptime(goal["end_date"], "%Y-%m-%d")
|
| 717 |
+
current_date = min(end_date, datetime.now())
|
| 718 |
+
|
| 719 |
+
date_range = []
|
| 720 |
+
current = start_date
|
| 721 |
+
while current <= current_date:
|
| 722 |
+
date_range.append(current)
|
| 723 |
+
current += timedelta(days=1)
|
| 724 |
+
|
| 725 |
+
for date in date_range:
|
| 726 |
+
date_str = date.strftime("%Y-%m-%d")
|
| 727 |
+
activities_done = len(goal["progress"].get(date_str, []))
|
| 728 |
+
total_activities = len(goal["activities"])
|
| 729 |
+
completion_rate = (activities_done / total_activities) * 100 if total_activities > 0 else 0
|
| 730 |
+
|
| 731 |
+
history_data.append({
|
| 732 |
+
"date": date_str,
|
| 733 |
+
"completion_rate": completion_rate
|
| 734 |
+
})
|
| 735 |
+
|
| 736 |
+
if history_data:
|
| 737 |
+
history_df = pd.DataFrame(history_data)
|
| 738 |
+
|
| 739 |
+
# Create a chart
|
| 740 |
+
chart = alt.Chart(history_df).mark_bar().encode(
|
| 741 |
+
x=alt.X('date:T', title='Date'),
|
| 742 |
+
y=alt.Y('completion_rate:Q', title='Completion Rate (%)')
|
| 743 |
+
).properties(
|
| 744 |
+
title='Daily Activity Completion',
|
| 745 |
+
width=600,
|
| 746 |
+
height=200
|
| 747 |
+
)
|
| 748 |
+
|
| 749 |
+
st.altair_chart(chart, use_container_width=True)
|
| 750 |
+
else:
|
| 751 |
+
st.write("No progress data available yet.")
|
| 752 |
+
|
| 753 |
+
# Complete goal button
|
| 754 |
+
if not goal["completed"] and days_remaining <= 0:
|
| 755 |
+
if st.button("Mark Goal as Completed"):
|
| 756 |
+
complete_goal(st.session_state.username, goal_id)
|
| 757 |
+
st.success("Goal marked as completed!")
|
| 758 |
+
st.experimental_rerun()
|
| 759 |
+
|
| 760 |
+
def goals_page():
|
| 761 |
+
# Initialize session state variables if they don't exist
|
| 762 |
+
if "goals_view" not in st.session_state:
|
| 763 |
+
st.session_state.goals_view = "dashboard"
|
| 764 |
+
|
| 765 |
+
if "current_goal" not in st.session_state:
|
| 766 |
+
st.session_state.current_goal = None
|
| 767 |
+
|
| 768 |
+
# Display the appropriate view
|
| 769 |
+
if st.session_state.goals_view == "dashboard":
|
| 770 |
+
goals_dashboard()
|
| 771 |
+
elif st.session_state.goals_view == "new_goal":
|
| 772 |
+
new_goal_form()
|
| 773 |
+
elif st.session_state.goals_view == "view_goal" and st.session_state.current_goal:
|
| 774 |
+
view_goal(st.session_state.current_goal)
|
| 775 |
+
else:
|
| 776 |
+
# Default to dashboard view
|
| 777 |
+
st.session_state.goals_view = "dashboard"
|
| 778 |
+
goals_dashboard()
|
AfyaMindSpace/goals_data.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"goals": {}, "templates": [{"id": "stress_reduction", "title": "Stress Reduction", "description": "Develop habits to reduce and manage stress in your daily life.", "suggested_activities": ["Practice deep breathing for 5 minutes", "Take a short walk outside", "Write in a journal", "Complete a guided meditation", "Do a body scan relaxation exercise"], "duration_options": [7, 14, 30], "category": "Stress Management", "icon": "\ud83e\uddd8"}, {"id": "mood_tracking", "title": "Consistent Mood Tracking", "description": "Build a habit of tracking your mood daily to identify patterns.", "suggested_activities": ["Record your mood in the morning", "Record your mood in the evening", "Note what influenced your mood today", "Reflect on mood patterns from the past week"], "duration_options": [7, 14, 30], "category": "Self-Awareness", "icon": "\ud83d\udcca"}, {"id": "positive_thinking", "title": "Positive Thinking Practice", "description": "Train your mind to focus more on positive aspects of life.", "suggested_activities": ["Write down three things you're grateful for", "Give someone a genuine compliment", "Reframe a negative thought into a positive one", "Practice positive affirmations", "Notice and appreciate small joys"], "duration_options": [7, 14, 21], "category": "Emotional Wellbeing", "icon": "\u2728"}, {"id": "social_connection", "title": "Strengthen Social Connections", "description": "Build and maintain meaningful social relationships.", "suggested_activities": ["Reach out to a friend or family member", "Have a meaningful conversation with someone", "Participate in a group activity", "Practice active listening", "Express appreciation to someone important to you"], "duration_options": [7, 14, 30], "category": "Social Skills", "icon": "\ud83d\udc65"}, {"id": "mindfulness", "title": "Daily Mindfulness Practice", "description": "Develop a regular mindfulness practice to stay present and aware.", "suggested_activities": ["Practice mindful eating for one meal", "Do a 5-minute mindfulness meditation", "Take a mindful walk focusing on your senses", "Practice mindful breathing for 3 minutes", "Do a mindful check-in during a stressful moment"], "duration_options": [7, 14, 30], "category": "Mindfulness", "icon": "\ud83c\udf3f"}, {"id": "custom", "title": "Custom Goal", "description": "Create your own personalized mental health goal.", "suggested_activities": [], "duration_options": [7, 14, 21, 30], "category": "Custom", "icon": "\ud83c\udfaf"}]}
|
AfyaMindSpace/meditation.py
ADDED
|
@@ -0,0 +1,563 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import streamlit as st
|
| 2 |
+
import time
|
| 3 |
+
import json
|
| 4 |
+
import os
|
| 5 |
+
from datetime import datetime
|
| 6 |
+
|
| 7 |
+
# File to store meditation data
|
| 8 |
+
MEDITATION_DB_FILE = "meditation_data.json"
|
| 9 |
+
|
| 10 |
+
# Initialize meditation database
|
| 11 |
+
def initialize_meditation_db():
|
| 12 |
+
if not os.path.exists(MEDITATION_DB_FILE):
|
| 13 |
+
meditation_data = {
|
| 14 |
+
"sessions": [
|
| 15 |
+
{
|
| 16 |
+
"id": "breathing",
|
| 17 |
+
"title": "Deep Breathing Exercise",
|
| 18 |
+
"description": "A simple breathing exercise to help calm your mind and reduce stress.",
|
| 19 |
+
"duration": 5,
|
| 20 |
+
"difficulty": "Beginner",
|
| 21 |
+
"category": "Stress Relief",
|
| 22 |
+
"steps": [
|
| 23 |
+
"Find a comfortable position and close your eyes.",
|
| 24 |
+
"Take a deep breath in through your nose for 4 seconds.",
|
| 25 |
+
"Hold your breath for 2 seconds.",
|
| 26 |
+
"Exhale slowly through your mouth for 6 seconds.",
|
| 27 |
+
"Repeat this breathing pattern for the duration of the session."
|
| 28 |
+
],
|
| 29 |
+
"background": "#E6F7FF"
|
| 30 |
+
},
|
| 31 |
+
{
|
| 32 |
+
"id": "body_scan",
|
| 33 |
+
"title": "Body Scan Meditation",
|
| 34 |
+
"description": "A guided meditation to help you become aware of sensations throughout your body.",
|
| 35 |
+
"duration": 10,
|
| 36 |
+
"difficulty": "Intermediate",
|
| 37 |
+
"category": "Mindfulness",
|
| 38 |
+
"steps": [
|
| 39 |
+
"Lie down or sit in a comfortable position and close your eyes.",
|
| 40 |
+
"Begin by bringing awareness to your breath, noticing the natural rhythm.",
|
| 41 |
+
"Slowly shift your attention to your feet, noticing any sensations.",
|
| 42 |
+
"Gradually move your awareness up through your legs, torso, arms, and head.",
|
| 43 |
+
"If you notice any tension, breathe into that area and allow it to relax.",
|
| 44 |
+
"Complete the scan by bringing awareness to your body as a whole."
|
| 45 |
+
],
|
| 46 |
+
"background": "#F0E6FF"
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"id": "gratitude",
|
| 50 |
+
"title": "Gratitude Meditation",
|
| 51 |
+
"description": "A meditation practice focused on cultivating gratitude and positive emotions.",
|
| 52 |
+
"duration": 7,
|
| 53 |
+
"difficulty": "Beginner",
|
| 54 |
+
"category": "Emotional Wellbeing",
|
| 55 |
+
"steps": [
|
| 56 |
+
"Sit comfortably with your back straight and eyes closed.",
|
| 57 |
+
"Take a few deep breaths to center yourself.",
|
| 58 |
+
"Bring to mind something or someone you're grateful for.",
|
| 59 |
+
"Notice the feelings of appreciation and warmth that arise.",
|
| 60 |
+
"Acknowledge how this person or thing enriches your life.",
|
| 61 |
+
"Expand your awareness to include more things you're grateful for.",
|
| 62 |
+
"Conclude by setting an intention to notice moments of gratitude throughout your day."
|
| 63 |
+
],
|
| 64 |
+
"background": "#FFF0E6"
|
| 65 |
+
},
|
| 66 |
+
{
|
| 67 |
+
"id": "loving_kindness",
|
| 68 |
+
"title": "Loving-Kindness Meditation",
|
| 69 |
+
"description": "A practice to develop feelings of goodwill, kindness, and warmth towards others.",
|
| 70 |
+
"duration": 12,
|
| 71 |
+
"difficulty": "Intermediate",
|
| 72 |
+
"category": "Compassion",
|
| 73 |
+
"steps": [
|
| 74 |
+
"Sit in a comfortable position with your eyes closed.",
|
| 75 |
+
"Begin by focusing on your breath, allowing yourself to relax.",
|
| 76 |
+
"Bring to mind someone you care about deeply.",
|
| 77 |
+
"Silently repeat phrases like 'May you be happy. May you be healthy. May you be safe.'",
|
| 78 |
+
"Next, direct these wishes toward yourself: 'May I be happy. May I be healthy. May I be safe.'",
|
| 79 |
+
"Gradually extend these wishes to others: acquaintances, difficult people, and all beings.",
|
| 80 |
+
"End by taking a few deep breaths and slowly opening your eyes."
|
| 81 |
+
],
|
| 82 |
+
"background": "#E6FFE6"
|
| 83 |
+
},
|
| 84 |
+
{
|
| 85 |
+
"id": "visualization",
|
| 86 |
+
"title": "Peaceful Place Visualization",
|
| 87 |
+
"description": "A guided visualization to help you relax by imagining a peaceful, safe place.",
|
| 88 |
+
"duration": 8,
|
| 89 |
+
"difficulty": "Beginner",
|
| 90 |
+
"category": "Relaxation",
|
| 91 |
+
"steps": [
|
| 92 |
+
"Find a comfortable position and close your eyes.",
|
| 93 |
+
"Take several deep breaths, allowing your body to relax.",
|
| 94 |
+
"Imagine a place where you feel completely peaceful and safe.",
|
| 95 |
+
"Notice the details: What do you see? What sounds do you hear?",
|
| 96 |
+
"Experience the sensations: What do you feel? What scents are present?",
|
| 97 |
+
"Allow yourself to fully immerse in this peaceful place.",
|
| 98 |
+
"When ready, slowly bring your awareness back to the present moment."
|
| 99 |
+
],
|
| 100 |
+
"background": "#E6FFFF"
|
| 101 |
+
}
|
| 102 |
+
],
|
| 103 |
+
"user_progress": {}
|
| 104 |
+
}
|
| 105 |
+
with open(MEDITATION_DB_FILE, "w") as f:
|
| 106 |
+
json.dump(meditation_data, f)
|
| 107 |
+
return meditation_data
|
| 108 |
+
|
| 109 |
+
try:
|
| 110 |
+
with open(MEDITATION_DB_FILE, "r") as f:
|
| 111 |
+
return json.load(f)
|
| 112 |
+
except json.JSONDecodeError:
|
| 113 |
+
# If file is corrupted, create a new one
|
| 114 |
+
meditation_data = {
|
| 115 |
+
"sessions": [
|
| 116 |
+
{
|
| 117 |
+
"id": "breathing",
|
| 118 |
+
"title": "Deep Breathing Exercise",
|
| 119 |
+
"description": "A simple breathing exercise to help calm your mind and reduce stress.",
|
| 120 |
+
"duration": 5,
|
| 121 |
+
"difficulty": "Beginner",
|
| 122 |
+
"category": "Stress Relief",
|
| 123 |
+
"steps": [
|
| 124 |
+
"Find a comfortable position and close your eyes.",
|
| 125 |
+
"Take a deep breath in through your nose for 4 seconds.",
|
| 126 |
+
"Hold your breath for 2 seconds.",
|
| 127 |
+
"Exhale slowly through your mouth for 6 seconds.",
|
| 128 |
+
"Repeat this breathing pattern for the duration of the session."
|
| 129 |
+
],
|
| 130 |
+
"background": "#E6F7FF"
|
| 131 |
+
},
|
| 132 |
+
{
|
| 133 |
+
"id": "body_scan",
|
| 134 |
+
"title": "Body Scan Meditation",
|
| 135 |
+
"description": "A guided meditation to help you become aware of sensations throughout your body.",
|
| 136 |
+
"duration": 10,
|
| 137 |
+
"difficulty": "Intermediate",
|
| 138 |
+
"category": "Mindfulness",
|
| 139 |
+
"steps": [
|
| 140 |
+
"Lie down or sit in a comfortable position and close your eyes.",
|
| 141 |
+
"Begin by bringing awareness to your breath, noticing the natural rhythm.",
|
| 142 |
+
"Slowly shift your attention to your feet, noticing any sensations.",
|
| 143 |
+
"Gradually move your awareness up through your legs, torso, arms, and head.",
|
| 144 |
+
"If you notice any tension, breathe into that area and allow it to relax.",
|
| 145 |
+
"Complete the scan by bringing awareness to your body as a whole."
|
| 146 |
+
],
|
| 147 |
+
"background": "#F0E6FF"
|
| 148 |
+
},
|
| 149 |
+
{
|
| 150 |
+
"id": "gratitude",
|
| 151 |
+
"title": "Gratitude Meditation",
|
| 152 |
+
"description": "A meditation practice focused on cultivating gratitude and positive emotions.",
|
| 153 |
+
"duration": 7,
|
| 154 |
+
"difficulty": "Beginner",
|
| 155 |
+
"category": "Emotional Wellbeing",
|
| 156 |
+
"steps": [
|
| 157 |
+
"Sit comfortably with your back straight and eyes closed.",
|
| 158 |
+
"Take a few deep breaths to center yourself.",
|
| 159 |
+
"Bring to mind something or someone you're grateful for.",
|
| 160 |
+
"Notice the feelings of appreciation and warmth that arise.",
|
| 161 |
+
"Acknowledge how this person or thing enriches your life.",
|
| 162 |
+
"Expand your awareness to include more things you're grateful for.",
|
| 163 |
+
"Conclude by setting an intention to notice moments of gratitude throughout your day."
|
| 164 |
+
],
|
| 165 |
+
"background": "#FFF0E6"
|
| 166 |
+
},
|
| 167 |
+
{
|
| 168 |
+
"id": "loving_kindness",
|
| 169 |
+
"title": "Loving-Kindness Meditation",
|
| 170 |
+
"description": "A practice to develop feelings of goodwill, kindness, and warmth towards others.",
|
| 171 |
+
"duration": 12,
|
| 172 |
+
"difficulty": "Intermediate",
|
| 173 |
+
"category": "Compassion",
|
| 174 |
+
"steps": [
|
| 175 |
+
"Sit in a comfortable position with your eyes closed.",
|
| 176 |
+
"Begin by focusing on your breath, allowing yourself to relax.",
|
| 177 |
+
"Bring to mind someone you care about deeply.",
|
| 178 |
+
"Silently repeat phrases like 'May you be happy. May you be healthy. May you be safe.'",
|
| 179 |
+
"Next, direct these wishes toward yourself: 'May I be happy. May I be healthy. May I be safe.'",
|
| 180 |
+
"Gradually extend these wishes to others: acquaintances, difficult people, and all beings.",
|
| 181 |
+
"End by taking a few deep breaths and slowly opening your eyes."
|
| 182 |
+
],
|
| 183 |
+
"background": "#E6FFE6"
|
| 184 |
+
},
|
| 185 |
+
{
|
| 186 |
+
"id": "visualization",
|
| 187 |
+
"title": "Peaceful Place Visualization",
|
| 188 |
+
"description": "A guided visualization to help you relax by imagining a peaceful, safe place.",
|
| 189 |
+
"duration": 8,
|
| 190 |
+
"difficulty": "Beginner",
|
| 191 |
+
"category": "Relaxation",
|
| 192 |
+
"steps": [
|
| 193 |
+
"Find a comfortable position and close your eyes.",
|
| 194 |
+
"Take several deep breaths, allowing your body to relax.",
|
| 195 |
+
"Imagine a place where you feel completely peaceful and safe.",
|
| 196 |
+
"Notice the details: What do you see? What sounds do you hear?",
|
| 197 |
+
"Experience the sensations: What do you feel? What scents are present?",
|
| 198 |
+
"Allow yourself to fully immerse in this peaceful place.",
|
| 199 |
+
"When ready, slowly bring your awareness back to the present moment."
|
| 200 |
+
],
|
| 201 |
+
"background": "#E6FFFF"
|
| 202 |
+
}
|
| 203 |
+
],
|
| 204 |
+
"user_progress": {}
|
| 205 |
+
}
|
| 206 |
+
with open(MEDITATION_DB_FILE, "w") as f:
|
| 207 |
+
json.dump(meditation_data, f)
|
| 208 |
+
return meditation_data
|
| 209 |
+
|
| 210 |
+
# Save meditation database
|
| 211 |
+
def save_meditation_db(db):
|
| 212 |
+
with open(MEDITATION_DB_FILE, "w") as f:
|
| 213 |
+
json.dump(db, f)
|
| 214 |
+
|
| 215 |
+
# Get all meditation sessions
|
| 216 |
+
def get_meditation_sessions():
|
| 217 |
+
db = initialize_meditation_db()
|
| 218 |
+
return db["sessions"]
|
| 219 |
+
|
| 220 |
+
# Get meditation session by ID
|
| 221 |
+
def get_meditation_session(session_id):
|
| 222 |
+
db = initialize_meditation_db()
|
| 223 |
+
|
| 224 |
+
for session in db["sessions"]:
|
| 225 |
+
if session["id"] == session_id:
|
| 226 |
+
return session
|
| 227 |
+
|
| 228 |
+
return None
|
| 229 |
+
|
| 230 |
+
# Record completed meditation session
|
| 231 |
+
def record_meditation_completion(username, session_id, duration):
|
| 232 |
+
if not username:
|
| 233 |
+
return False, "User not logged in"
|
| 234 |
+
|
| 235 |
+
db = initialize_meditation_db()
|
| 236 |
+
|
| 237 |
+
# Check if session exists
|
| 238 |
+
session_exists = False
|
| 239 |
+
for session in db["sessions"]:
|
| 240 |
+
if session["id"] == session_id:
|
| 241 |
+
session_exists = True
|
| 242 |
+
break
|
| 243 |
+
|
| 244 |
+
if not session_exists:
|
| 245 |
+
return False, "Meditation session not found"
|
| 246 |
+
|
| 247 |
+
# Initialize user progress if needed
|
| 248 |
+
if username not in db["user_progress"]:
|
| 249 |
+
db["user_progress"][username] = {
|
| 250 |
+
"completed_sessions": [],
|
| 251 |
+
"total_minutes": 0,
|
| 252 |
+
"streak": 0,
|
| 253 |
+
"last_completed": None
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
# Check if this is a new day completion
|
| 257 |
+
today = datetime.now().strftime("%Y-%m-%d")
|
| 258 |
+
if db["user_progress"][username]["last_completed"] != today:
|
| 259 |
+
# Check if the streak should continue or reset
|
| 260 |
+
if db["user_progress"][username]["last_completed"] is None:
|
| 261 |
+
db["user_progress"][username]["streak"] = 1
|
| 262 |
+
else:
|
| 263 |
+
last_date = datetime.strptime(db["user_progress"][username]["last_completed"], "%Y-%m-%d")
|
| 264 |
+
today_date = datetime.strptime(today, "%Y-%m-%d")
|
| 265 |
+
days_diff = (today_date - last_date).days
|
| 266 |
+
|
| 267 |
+
if days_diff == 1:
|
| 268 |
+
# Consecutive day, increase streak
|
| 269 |
+
db["user_progress"][username]["streak"] += 1
|
| 270 |
+
elif days_diff > 1:
|
| 271 |
+
# Streak broken
|
| 272 |
+
db["user_progress"][username]["streak"] = 1
|
| 273 |
+
|
| 274 |
+
db["user_progress"][username]["last_completed"] = today
|
| 275 |
+
|
| 276 |
+
# Record the session
|
| 277 |
+
db["user_progress"][username]["completed_sessions"].append({
|
| 278 |
+
"session_id": session_id,
|
| 279 |
+
"completed_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 280 |
+
"duration": duration
|
| 281 |
+
})
|
| 282 |
+
|
| 283 |
+
# Update total minutes
|
| 284 |
+
db["user_progress"][username]["total_minutes"] += duration
|
| 285 |
+
|
| 286 |
+
save_meditation_db(db)
|
| 287 |
+
return True, "Meditation session recorded"
|
| 288 |
+
|
| 289 |
+
# Get user meditation stats
|
| 290 |
+
def get_user_meditation_stats(username):
|
| 291 |
+
if not username:
|
| 292 |
+
return None
|
| 293 |
+
|
| 294 |
+
db = initialize_meditation_db()
|
| 295 |
+
|
| 296 |
+
if username not in db["user_progress"]:
|
| 297 |
+
return {
|
| 298 |
+
"completed_sessions": 0,
|
| 299 |
+
"total_minutes": 0,
|
| 300 |
+
"streak": 0,
|
| 301 |
+
"last_completed": None
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
stats = db["user_progress"][username]
|
| 305 |
+
stats["completed_sessions"] = len(stats["completed_sessions"])
|
| 306 |
+
|
| 307 |
+
return stats
|
| 308 |
+
|
| 309 |
+
# Meditation UI components
|
| 310 |
+
def meditation_library():
|
| 311 |
+
st.subheader("Guided Meditation Library")
|
| 312 |
+
st.write("Explore our collection of guided meditations to help you relax, focus, and find peace.")
|
| 313 |
+
|
| 314 |
+
# Get all sessions
|
| 315 |
+
sessions = get_meditation_sessions()
|
| 316 |
+
|
| 317 |
+
# Filter options
|
| 318 |
+
col1, col2 = st.columns(2)
|
| 319 |
+
with col1:
|
| 320 |
+
difficulty_filter = st.selectbox(
|
| 321 |
+
"Filter by difficulty",
|
| 322 |
+
["All", "Beginner", "Intermediate"],
|
| 323 |
+
key="difficulty_filter"
|
| 324 |
+
)
|
| 325 |
+
|
| 326 |
+
with col2:
|
| 327 |
+
category_filter = st.selectbox(
|
| 328 |
+
"Filter by category",
|
| 329 |
+
["All", "Stress Relief", "Mindfulness", "Emotional Wellbeing", "Compassion", "Relaxation"],
|
| 330 |
+
key="category_filter"
|
| 331 |
+
)
|
| 332 |
+
|
| 333 |
+
# Apply filters
|
| 334 |
+
filtered_sessions = sessions
|
| 335 |
+
if difficulty_filter != "All":
|
| 336 |
+
filtered_sessions = [s for s in filtered_sessions if s["difficulty"] == difficulty_filter]
|
| 337 |
+
|
| 338 |
+
if category_filter != "All":
|
| 339 |
+
filtered_sessions = [s for s in filtered_sessions if s["category"] == category_filter]
|
| 340 |
+
|
| 341 |
+
# Display sessions in a grid
|
| 342 |
+
if not filtered_sessions:
|
| 343 |
+
st.write("No meditation sessions match your filters.")
|
| 344 |
+
else:
|
| 345 |
+
# Create rows with 2 sessions per row
|
| 346 |
+
for i in range(0, len(filtered_sessions), 2):
|
| 347 |
+
cols = st.columns(2)
|
| 348 |
+
|
| 349 |
+
# First session in the row
|
| 350 |
+
with cols[0]:
|
| 351 |
+
if i < len(filtered_sessions):
|
| 352 |
+
session = filtered_sessions[i]
|
| 353 |
+
with st.container():
|
| 354 |
+
st.markdown(f"""
|
| 355 |
+
<div style="padding: 1rem; border-radius: 0.5rem; background-color: {session['background']}; margin-bottom: 1rem;">
|
| 356 |
+
<h3>{session['title']}</h3>
|
| 357 |
+
<p><strong>{session['duration']} minutes</strong> | {session['difficulty']} | {session['category']}</p>
|
| 358 |
+
<p>{session['description']}</p>
|
| 359 |
+
</div>
|
| 360 |
+
""", unsafe_allow_html=True)
|
| 361 |
+
|
| 362 |
+
if st.button("Start Meditation", key=f"start_{session['id']}"):
|
| 363 |
+
st.session_state.meditation_view = "session"
|
| 364 |
+
st.session_state.meditation_session = session['id']
|
| 365 |
+
st.experimental_rerun()
|
| 366 |
+
|
| 367 |
+
# Second session in the row
|
| 368 |
+
with cols[1]:
|
| 369 |
+
if i + 1 < len(filtered_sessions):
|
| 370 |
+
session = filtered_sessions[i + 1]
|
| 371 |
+
with st.container():
|
| 372 |
+
st.markdown(f"""
|
| 373 |
+
<div style="padding: 1rem; border-radius: 0.5rem; background-color: {session['background']}; margin-bottom: 1rem;">
|
| 374 |
+
<h3>{session['title']}</h3>
|
| 375 |
+
<p><strong>{session['duration']} minutes</strong> | {session['difficulty']} | {session['category']}</p>
|
| 376 |
+
<p>{session['description']}</p>
|
| 377 |
+
</div>
|
| 378 |
+
""", unsafe_allow_html=True)
|
| 379 |
+
|
| 380 |
+
if st.button("Start Meditation", key=f"start_{session['id']}"):
|
| 381 |
+
st.session_state.meditation_view = "session"
|
| 382 |
+
st.session_state.meditation_session = session['id']
|
| 383 |
+
st.experimental_rerun()
|
| 384 |
+
|
| 385 |
+
def meditation_session(session_id):
|
| 386 |
+
session = get_meditation_session(session_id)
|
| 387 |
+
|
| 388 |
+
if not session:
|
| 389 |
+
st.error("Meditation session not found")
|
| 390 |
+
if st.button("Back to Library"):
|
| 391 |
+
st.session_state.meditation_view = "library"
|
| 392 |
+
st.session_state.meditation_session = None
|
| 393 |
+
st.experimental_rerun()
|
| 394 |
+
return
|
| 395 |
+
|
| 396 |
+
st.subheader(session["title"])
|
| 397 |
+
|
| 398 |
+
# Back button
|
| 399 |
+
if st.button("← Back to Library"):
|
| 400 |
+
st.session_state.meditation_view = "library"
|
| 401 |
+
st.session_state.meditation_session = None
|
| 402 |
+
st.experimental_rerun()
|
| 403 |
+
|
| 404 |
+
# Session details
|
| 405 |
+
st.write(f"**Duration:** {session['duration']} minutes")
|
| 406 |
+
st.write(f"**Difficulty:** {session['difficulty']}")
|
| 407 |
+
st.write(f"**Category:** {session['category']}")
|
| 408 |
+
st.write(session["description"])
|
| 409 |
+
|
| 410 |
+
# Start meditation button
|
| 411 |
+
if "meditation_in_progress" not in st.session_state or not st.session_state.meditation_in_progress:
|
| 412 |
+
if st.button("Begin Meditation"):
|
| 413 |
+
st.session_state.meditation_in_progress = True
|
| 414 |
+
st.session_state.meditation_step = 0
|
| 415 |
+
st.session_state.meditation_start_time = time.time()
|
| 416 |
+
st.experimental_rerun()
|
| 417 |
+
else:
|
| 418 |
+
# Display meditation steps
|
| 419 |
+
progress = st.progress(0)
|
| 420 |
+
step_duration = session["duration"] * 60 / len(session["steps"]) # seconds per step
|
| 421 |
+
|
| 422 |
+
# Calculate elapsed time and current step
|
| 423 |
+
elapsed = time.time() - st.session_state.meditation_start_time
|
| 424 |
+
total_duration = session["duration"] * 60 # total seconds
|
| 425 |
+
progress_value = min(elapsed / total_duration, 1.0)
|
| 426 |
+
progress.progress(progress_value)
|
| 427 |
+
|
| 428 |
+
current_step = min(int(elapsed / step_duration), len(session["steps"]) - 1)
|
| 429 |
+
|
| 430 |
+
# Update step if needed
|
| 431 |
+
if current_step != st.session_state.meditation_step:
|
| 432 |
+
st.session_state.meditation_step = current_step
|
| 433 |
+
st.experimental_rerun()
|
| 434 |
+
|
| 435 |
+
# Display current step
|
| 436 |
+
st.markdown(f"""
|
| 437 |
+
<div style="padding: 2rem; border-radius: 0.5rem; background-color: {session['background']};
|
| 438 |
+
text-align: center; font-size: 1.5rem; margin: 2rem 0;">
|
| 439 |
+
{session['steps'][current_step]}
|
| 440 |
+
</div>
|
| 441 |
+
""", unsafe_allow_html=True)
|
| 442 |
+
|
| 443 |
+
# Time remaining
|
| 444 |
+
remaining = max(0, total_duration - elapsed)
|
| 445 |
+
mins = int(remaining // 60)
|
| 446 |
+
secs = int(remaining % 60)
|
| 447 |
+
st.write(f"Time remaining: {mins}:{secs:02d}")
|
| 448 |
+
|
| 449 |
+
# End meditation button
|
| 450 |
+
if st.button("End Meditation Early"):
|
| 451 |
+
# Record partial completion
|
| 452 |
+
if st.session_state.get("logged_in", False):
|
| 453 |
+
actual_duration = min(session["duration"], elapsed / 60)
|
| 454 |
+
record_meditation_completion(
|
| 455 |
+
st.session_state.username,
|
| 456 |
+
session_id,
|
| 457 |
+
actual_duration
|
| 458 |
+
)
|
| 459 |
+
|
| 460 |
+
st.session_state.meditation_in_progress = False
|
| 461 |
+
st.session_state.meditation_step = 0
|
| 462 |
+
st.experimental_rerun()
|
| 463 |
+
|
| 464 |
+
# Check if meditation is complete
|
| 465 |
+
if elapsed >= total_duration:
|
| 466 |
+
# Record completion
|
| 467 |
+
if st.session_state.get("logged_in", False):
|
| 468 |
+
record_meditation_completion(
|
| 469 |
+
st.session_state.username,
|
| 470 |
+
session_id,
|
| 471 |
+
session["duration"]
|
| 472 |
+
)
|
| 473 |
+
|
| 474 |
+
st.success("Meditation complete! Take a moment to notice how you feel.")
|
| 475 |
+
|
| 476 |
+
if st.button("Return to Library"):
|
| 477 |
+
st.session_state.meditation_in_progress = False
|
| 478 |
+
st.session_state.meditation_step = 0
|
| 479 |
+
st.session_state.meditation_view = "library"
|
| 480 |
+
st.experimental_rerun()
|
| 481 |
+
|
| 482 |
+
def meditation_stats():
|
| 483 |
+
if not st.session_state.get("logged_in", False):
|
| 484 |
+
st.info("Please log in to view your meditation statistics")
|
| 485 |
+
return
|
| 486 |
+
|
| 487 |
+
stats = get_user_meditation_stats(st.session_state.username)
|
| 488 |
+
|
| 489 |
+
if not stats:
|
| 490 |
+
st.write("No meditation data available yet. Start your first session!")
|
| 491 |
+
return
|
| 492 |
+
|
| 493 |
+
# Display stats in a nice UI
|
| 494 |
+
col1, col2, col3 = st.columns(3)
|
| 495 |
+
|
| 496 |
+
with col1:
|
| 497 |
+
st.metric("Total Sessions", stats["completed_sessions"])
|
| 498 |
+
|
| 499 |
+
with col2:
|
| 500 |
+
st.metric("Total Minutes", stats["total_minutes"])
|
| 501 |
+
|
| 502 |
+
with col3:
|
| 503 |
+
st.metric("Current Streak", f"{stats['streak']} days")
|
| 504 |
+
|
| 505 |
+
# Last meditation
|
| 506 |
+
if stats["last_completed"]:
|
| 507 |
+
st.write(f"Last meditation: {stats['last_completed']}")
|
| 508 |
+
|
| 509 |
+
# Achievements
|
| 510 |
+
if stats["completed_sessions"] > 0:
|
| 511 |
+
st.subheader("Achievements")
|
| 512 |
+
|
| 513 |
+
achievements = []
|
| 514 |
+
|
| 515 |
+
if stats["completed_sessions"] >= 1:
|
| 516 |
+
achievements.append("🌱 First Step - Complete your first meditation")
|
| 517 |
+
|
| 518 |
+
if stats["completed_sessions"] >= 5:
|
| 519 |
+
achievements.append("🌿 Growing Practice - Complete 5 meditations")
|
| 520 |
+
|
| 521 |
+
if stats["completed_sessions"] >= 10:
|
| 522 |
+
achievements.append("🌳 Established Practice - Complete 10 meditations")
|
| 523 |
+
|
| 524 |
+
if stats["streak"] >= 3:
|
| 525 |
+
achievements.append("🔥 Momentum - 3 day meditation streak")
|
| 526 |
+
|
| 527 |
+
if stats["streak"] >= 7:
|
| 528 |
+
achievements.append("⚡ Consistency - 7 day meditation streak")
|
| 529 |
+
|
| 530 |
+
if stats["total_minutes"] >= 60:
|
| 531 |
+
achievements.append("⏱️ Hour of Peace - Meditate for 60 minutes total")
|
| 532 |
+
|
| 533 |
+
for achievement in achievements:
|
| 534 |
+
st.markdown(f"**{achievement}**")
|
| 535 |
+
|
| 536 |
+
def meditation_page():
|
| 537 |
+
# Initialize session state variables if they don't exist
|
| 538 |
+
if "meditation_view" not in st.session_state:
|
| 539 |
+
st.session_state.meditation_view = "library"
|
| 540 |
+
|
| 541 |
+
if "meditation_session" not in st.session_state:
|
| 542 |
+
st.session_state.meditation_session = None
|
| 543 |
+
|
| 544 |
+
if "meditation_in_progress" not in st.session_state:
|
| 545 |
+
st.session_state.meditation_in_progress = False
|
| 546 |
+
|
| 547 |
+
if "meditation_step" not in st.session_state:
|
| 548 |
+
st.session_state.meditation_step = 0
|
| 549 |
+
|
| 550 |
+
# Main content area
|
| 551 |
+
st.markdown("<h1 style='text-align: center;'>Guided Meditation</h1>", unsafe_allow_html=True)
|
| 552 |
+
|
| 553 |
+
# Tabs for different sections
|
| 554 |
+
tab1, tab2 = st.tabs(["Meditation Library", "Your Progress"])
|
| 555 |
+
|
| 556 |
+
with tab1:
|
| 557 |
+
if st.session_state.meditation_view == "library":
|
| 558 |
+
meditation_library()
|
| 559 |
+
elif st.session_state.meditation_view == "session" and st.session_state.meditation_session:
|
| 560 |
+
meditation_session(st.session_state.meditation_session)
|
| 561 |
+
|
| 562 |
+
with tab2:
|
| 563 |
+
meditation_stats()
|
AfyaMindSpace/meditation_data.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"sessions": [{"id": "breathing", "title": "Deep Breathing Exercise", "description": "A simple breathing exercise to help calm your mind and reduce stress.", "duration": 5, "difficulty": "Beginner", "category": "Stress Relief", "steps": ["Find a comfortable position and close your eyes.", "Take a deep breath in through your nose for 4 seconds.", "Hold your breath for 2 seconds.", "Exhale slowly through your mouth for 6 seconds.", "Repeat this breathing pattern for the duration of the session."], "background": "#E6F7FF"}, {"id": "body_scan", "title": "Body Scan Meditation", "description": "A guided meditation to help you become aware of sensations throughout your body.", "duration": 10, "difficulty": "Intermediate", "category": "Mindfulness", "steps": ["Lie down or sit in a comfortable position and close your eyes.", "Begin by bringing awareness to your breath, noticing the natural rhythm.", "Slowly shift your attention to your feet, noticing any sensations.", "Gradually move your awareness up through your legs, torso, arms, and head.", "If you notice any tension, breathe into that area and allow it to relax.", "Complete the scan by bringing awareness to your body as a whole."], "background": "#F0E6FF"}, {"id": "gratitude", "title": "Gratitude Meditation", "description": "A meditation practice focused on cultivating gratitude and positive emotions.", "duration": 7, "difficulty": "Beginner", "category": "Emotional Wellbeing", "steps": ["Sit comfortably with your back straight and eyes closed.", "Take a few deep breaths to center yourself.", "Bring to mind something or someone you're grateful for.", "Notice the feelings of appreciation and warmth that arise.", "Acknowledge how this person or thing enriches your life.", "Expand your awareness to include more things you're grateful for.", "Conclude by setting an intention to notice moments of gratitude throughout your day."], "background": "#FFF0E6"}, {"id": "loving_kindness", "title": "Loving-Kindness Meditation", "description": "A practice to develop feelings of goodwill, kindness, and warmth towards others.", "duration": 12, "difficulty": "Intermediate", "category": "Compassion", "steps": ["Sit in a comfortable position with your eyes closed.", "Begin by focusing on your breath, allowing yourself to relax.", "Bring to mind someone you care about deeply.", "Silently repeat phrases like 'May you be happy. May you be healthy. May you be safe.'", "Next, direct these wishes toward yourself: 'May I be happy. May I be healthy. May I be safe.'", "Gradually extend these wishes to others: acquaintances, difficult people, and all beings.", "End by taking a few deep breaths and slowly opening your eyes."], "background": "#E6FFE6"}, {"id": "visualization", "title": "Peaceful Place Visualization", "description": "A guided visualization to help you relax by imagining a peaceful, safe place.", "duration": 8, "difficulty": "Beginner", "category": "Relaxation", "steps": ["Find a comfortable position and close your eyes.", "Take several deep breaths, allowing your body to relax.", "Imagine a place where you feel completely peaceful and safe.", "Notice the details: What do you see? What sounds do you hear?", "Experience the sensations: What do you feel? What scents are present?", "Allow yourself to fully immerse in this peaceful place.", "When ready, slowly bring your awareness back to the present moment."], "background": "#E6FFFF"}], "user_progress": {}}
|
AfyaMindSpace/requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.41.1
|
| 2 |
+
pandas==2.0.0
|
| 3 |
+
altair==5.0.0
|
| 4 |
+
numpy==1.24.0
|
| 5 |
+
json5==0.9.14
|
AfyaMindSpace/user_data.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
{"users": {"Jnr": {"password_hash": "79db4572d62ee99cf63284fe62f9a60aae30322515511dd24cff728a8831c507", "email": "iammcqwory@gmail.com", "created_at": "2025-04-01 19:55:29", "last_login": "2025-04-01 20:33:14", "profile": {"display_name": "Jnr", "bio": "", "preferences": {"notifications": true, "privacy": "public"}}}}}
|