diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000000000000000000000000000000..eaced8b7188bd1ae6296d9b0f49e37af2d64f106 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,19 @@ +__pycache__/ +*.py[cod] +*.pyo +*.pyd +.Python +venv/ +.venv/ +env/ +*.egg-info/ +dist/ +build/ +*.db +*.sqlite3 +*.log +instance/ +node_modules/ +frontend/node_modules/ +.DS_Store +.env diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000000000000000000000000000000000..da5f8b31afd5afe62b9e6d81246ffb974447aaa5 --- /dev/null +++ b/.env.example @@ -0,0 +1,40 @@ +# ════════════════════════════════════════════════ +# BREATHE — Sample Environment File +# Copy this file to .env and fill in your values. +# NEVER commit the real .env to version control. +# ════════════════════════════════════════════════ + +# ── Flask ──────────────────────────────────────── +FLASK_APP=app.py +FLASK_DEBUG=true # set to false in production + +# A long random string — generate with: python -c "import secrets; print(secrets.token_hex(32))" +SECRET_KEY=change-me-to-a-very-long-random-secret-key + +# ── Database ───────────────────────────────────── +# SQLite (default, zero-config) +DATABASE_URL=sqlite:///breathe.db + +# PostgreSQL (recommended for production) +# DATABASE_URL=postgresql://user:password@localhost:5432/breathe + +# ── ML Model Paths ─────────────────────────────── +# Directory containing the saved psychometric model artefacts: +# base_scaler.pkl, final_scaler.pkl, le_dict.pkl, le_target.pkl, +# selected_cols.pkl, poly.pkl, top_num.pkl, +# lightgbm_best_model.pkl (or another *_best_model.pkl) +PSYCHO_MODEL_DIR=models/psychometric + +# Path to the RoBERTa .pt checkpoint file +ROBERTA_CKPT=models/text/roberta-model.pt + +# ── Server ─────────────────────────────────────── +HOST=0.0.0.0 +PORT=5000 + +# ── CORS (comma-separated origins) ────────────── +# Leave as * for development, restrict in production +CORS_ORIGINS=* + +# ── W&B (only needed if you re-train models) ───── +# WANDB_API_KEY=your_wandb_api_key_here diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fd322bde4d79f44bad2513231fc610bafe933875 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +# Local environment secrets +.env +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +venv/ +.venv/ +env/ +*.egg-info/ +dist/ +build/ +*.db +*.sqlite3 +# Exclude large model weights from git (store separately or use Git LFS) +models/ +*.pt +*.pkl +*.log +instance/ +.DS_Store +node_modules/ diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000000000000000000000000000000000000..e6ea852787001a4bcb1cdb8ee9dea69af25a6afb --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +python 3.11.3 diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md new file mode 100644 index 0000000000000000000000000000000000000000..3ec9519adcdc0a2c399d8302b0febe3479081afd --- /dev/null +++ b/DEPLOYMENT.md @@ -0,0 +1,169 @@ +# Deploying BREATHE — Completely Free + +This guide deploys the app using: +| Layer | Service | Cost | +|---|---|---| +| Frontend (React/Vite) | **Vercel** | Free forever | +| Backend (Flask API) | **Render** | Free tier | +| Database | **Supabase** | Free tier (500MB) | + +> **Note on ML models:** PyTorch and the transformer models require ~1–2 GB RAM. Render's free tier provides only 512 MB. The app will run without the ML features on the free tier — assessments and auth still work. To enable ML inference for free, see [Option B: Hugging Face Spaces](#option-b-ml-on-hugging-face-spaces-free) below. + +--- + +## Prerequisites + +- A [GitHub](https://github.com) account (push your project there first) +- Accounts on [Vercel](https://vercel.com), [Render](https://render.com), [Supabase](https://supabase.com) — all free to sign up + +--- + +## Step 1 — Push to GitHub + +```bash +cd breathe-app +git init +git add . +git commit -m "Initial commit" +# Create a repo on github.com, then: +git remote add origin https://github.com/YOUR_USERNAME/breathe-app.git +git push -u origin main +``` + +Make sure `.env` is in `.gitignore` (never commit secrets). + +--- + +## Step 2 — Set up the Database on Supabase (free) + +1. Go to [supabase.com](https://supabase.com) → **New Project** +2. Give it a name (e.g. `breathe`) and set a database password +3. Once created, go to **Project Settings → Database** +4. Copy the **Connection string (URI)** — it looks like: + ``` + postgresql://postgres:[PASSWORD]@db.xxxx.supabase.co:5432/postgres + ``` +5. Save this — you'll need it in Steps 3 and 4. + +--- + +## Step 3 — Deploy the Backend on Render (free) + +1. Go to [render.com](https://render.com) → **New → Web Service** +2. Connect your GitHub repo +3. Configure the service: + | Setting | Value | + |---|---| + | **Root Directory** | `breathe-app` | + | **Runtime** | Python 3 | + | **Build Command** | `pip install -r requirements.txt` | + | **Start Command** | `gunicorn app:app` | + | **Instance Type** | Free | + +4. Under **Environment Variables**, add: + ``` + FLASK_APP=app.py + FLASK_DEBUG=false + SECRET_KEY= + DATABASE_URL= + ``` + +5. Click **Create Web Service** — Render will build and deploy. +6. Your backend URL will be: `https://your-service-name.onrender.com` + +> ⚠️ Free tier services spin down after 15 minutes of inactivity and take ~30s to wake up on the next request. This is normal. + +--- + +## Step 4 — Deploy the Frontend on Vercel (free) + +1. Go to [vercel.com](https://vercel.com) → **Add New Project** +2. Import your GitHub repo +3. Configure the project: + | Setting | Value | + |---|---| + | **Root Directory** | `breathe-app/frontend` | + | **Framework Preset** | Vite | + | **Build Command** | `npm run build` | + | **Output Directory** | `dist` | + +4. Under **Environment Variables**, add: + ``` + VITE_API_URL=https://your-service-name.onrender.com + ``` + +5. Click **Deploy** — Vercel builds and publishes instantly. +6. Your app will be live at: `https://your-project.vercel.app` + +--- + +## Option C — Deploy on Hugging Face Spaces with Docker + +This repository now includes a root-level `Dockerfile` so you can deploy the full app as one Docker Space. + +1. Go to [huggingface.co/spaces](https://huggingface.co/spaces) → **Create new Space** +2. Choose **Docker** as the SDK +3. Push or upload this repository to the Space +4. In Space settings, add environment variables: + ``` + SECRET_KEY= + DATABASE_URL= + PSYCHO_MODEL_DIR=models/psychometric + ROBERTA_CKPT=models/text/roberta-model.pt + ``` + +5. Build the Space. The app will start on port `7860` and serve both the React frontend and Flask API from the same domain. + +> Note: If model files are missing, the app falls back to demo mode for assessments. + +--- + +## Step 5 — Update the API Client + +In `frontend/src/api/client.js`, make sure the base URL reads from the env variable: + +```js +const BASE_URL = import.meta.env.VITE_API_URL || '' +``` + +Redeploy the frontend after making this change. + +--- + +## Option B: ML on Hugging Face Spaces (free) + +If you want the full ML-powered assessments for free: + +1. Go to [huggingface.co/spaces](https://huggingface.co/spaces) → **Create new Space** +2. Choose **Docker** as the SDK +3. Upload: + - `app.py`, `backend/`, `models/`, `requirements.txt` + - A `Dockerfile` (see below) +4. Add the same environment variables in the Space settings + +**Dockerfile for Hugging Face:** +```dockerfile +FROM python:3.11-slim +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt +COPY . . +EXPOSE 7860 +CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"] +``` + +HF Spaces provides 2 CPU cores and 16 GB RAM on the free tier — enough for the ML models. + +--- + +## Summary + +| Step | Action | Time | +|---|---|---| +| 1 | Push code to GitHub | 5 min | +| 2 | Create Supabase database, copy connection string | 5 min | +| 3 | Deploy Flask backend on Render | 10 min | +| 4 | Deploy React frontend on Vercel | 5 min | +| 5 | Set `VITE_API_URL` and redeploy frontend | 2 min | + +Total: ~30 minutes, $0. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..f9bb380b7b90d0ba5999be3606a95d357d27875f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM node:20-bullseye AS frontend-build + +WORKDIR /app/frontend +COPY frontend/package*.json ./ +RUN npm install +COPY frontend/ . +RUN npm run build + +FROM python:3.11-slim + +WORKDIR /app +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . +COPY --from=frontend-build /app/frontend/dist ./frontend/dist + +EXPOSE 7860 +CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"] diff --git a/FUTURE_ENHANCEMENTS.md b/FUTURE_ENHANCEMENTS.md new file mode 100644 index 0000000000000000000000000000000000000000..60ba714bebe5411769d6c07970d5294b59990831 --- /dev/null +++ b/FUTURE_ENHANCEMENTS.md @@ -0,0 +1,555 @@ +# 🚀 BREATHE — Future Enhancements + +A living document of planned and possible improvements to the BREATHE platform. + +--- + +## 1. PostgreSQL — Persistent Production Database + +SQLite is fine for development but it resets or corrupts when the server restarts on many cloud platforms. PostgreSQL is a proper production-grade database where your data persists independently of the server. + +### Why switch? +- Data survives server restarts, crashes, and re-deployments +- Multiple workers can read/write simultaneously (SQLite locks) +- You can inspect, back up, and query data directly from any Postgres client +- Required by most cloud platforms (Railway, Render, Supabase, AWS RDS, etc.) + +### Step-by-step setup (local) + +**1. Install PostgreSQL** +```bash +# macOS (Homebrew) +brew install postgresql@16 +brew services start postgresql@16 + +# Ubuntu / Debian +sudo apt install postgresql postgresql-contrib +sudo systemctl start postgresql +``` + +**2. Create the database and user** +```bash +psql postgres +``` +```sql +CREATE USER breathe_user WITH PASSWORD 'your_strong_password'; +CREATE DATABASE breathe_db OWNER breathe_user; +GRANT ALL PRIVILEGES ON DATABASE breathe_db TO breathe_user; +\q +``` + +**3. Install the Python driver** +```bash +source venv/bin/activate +pip install psycopg2-binary +``` + +**4. Update `.env`** +``` +DATABASE_URL=postgresql://breathe_user:your_strong_password@localhost:5432/breathe_db +``` + +**5. Run the app — tables are created automatically** +```bash +python app.py +``` +Flask's `db.create_all()` will create all tables in Postgres on first run. + +**6. Verify** +```bash +psql -U breathe_user -d breathe_db -c "\dt" +# Should list: users, assessments, gratitude_entries +``` + +### Step-by-step setup (cloud — Supabase, free tier) + +1. Go to [supabase.com](https://supabase.com) → New project +2. In **Settings → Database** copy the **Connection string** (URI format) +3. Paste it into `.env` as `DATABASE_URL` +4. The app connects and creates tables on next start — no other changes needed + +### Step-by-step setup (cloud — Railway) + +1. Go to [railway.app](https://railway.app) → New project → Add PostgreSQL +2. Click the Postgres service → **Connect** tab → copy the `DATABASE_URL` +3. Add it as an environment variable in your BREATHE service on Railway +4. Re-deploy — done + +### Keeping data safe with backups +```bash +# Dump the entire database +pg_dump -U breathe_user breathe_db > backup_$(date +%Y%m%d).sql + +# Restore from a dump +psql -U breathe_user breathe_db < backup_20260502.sql +``` + +--- + +## 2. Sign Up / Log In with Google (OAuth 2.0) + +Let users authenticate with their Google account — no password to remember. + +### How it works +1. User clicks "Continue with Google" +2. Browser redirects to Google's OAuth consent screen +3. Google returns an authorization code to your callback URL +4. Flask exchanges the code for an access token and reads the user's profile (name, email, avatar) +5. Your app creates or logs in the user automatically + +### Backend — Flask-Dance (simplest approach) + +**Install** +```bash +pip install flask-dance[sqla] +``` + +**Register a Google OAuth app** +1. Go to [console.cloud.google.com](https://console.cloud.google.com) +2. Create a new project → **APIs & Services → Credentials** +3. Click **Create Credentials → OAuth client ID** +4. Application type: **Web application** +5. Authorised redirect URI: `http://localhost:5000/login/google/authorized` +6. Copy the **Client ID** and **Client Secret** into `.env`: + +``` +GOOGLE_CLIENT_ID=your_client_id +GOOGLE_CLIENT_SECRET=your_client_secret +``` + +**Add to `backend/__init__.py`** +```python +from flask_dance.contrib.google import make_google_blueprint, google + +google_bp = make_google_blueprint( + client_id=os.environ.get("GOOGLE_CLIENT_ID"), + client_secret=os.environ.get("GOOGLE_CLIENT_SECRET"), + scope=["openid", "email", "profile"], + redirect_url="/api/auth/google/callback", +) +app.register_blueprint(google_bp, url_prefix="/login") +``` + +**Add a callback route in `auth.py`** +```python +from flask_dance.contrib.google import google + +@auth_bp.route("/google/callback") +def google_callback(): + if not google.authorized: + return jsonify({"error": "Not authorized"}), 401 + resp = google.get("/oauth2/v2/userinfo") + info = resp.json() + user = User.query.filter_by(email=info["email"]).first() + if not user: + user = User(username=info["name"], email=info["email"]) + user.avatar = info.get("picture") + db.session.add(user) + db.session.commit() + session["user_id"] = user.id + return redirect("http://localhost:5173/app/breathe") +``` + +**Frontend — add a Google button to `AuthPage.jsx`** +```jsx + + Continue with Google + +``` + +--- + +## 3. Improved UI & UX + +### Ideas to implement + +| Area | Enhancement | +|------|-------------| +| Onboarding | First-time walkthrough tooltip tour (using `driver.js` or `intro.js`) | +| Themes | Light mode toggle + system preference detection | +| Animations | Framer Motion page transitions and micro-interactions | +| Mobile | Full PWA support — installable on phone home screen | +| Accessibility | ARIA labels, keyboard navigation, high-contrast mode | +| Charts | Hover annotations, zoom on timeline, weekly/monthly toggle | +| Notifications | Browser push notifications for daily assessment reminders | +| Streaks | Gamification — show journaling streak counter on dashboard | +| Data export | Download your assessment + journal history as CSV or PDF | + +### Quick win — dark/light theme toggle +```js +// in index.css: add a [data-theme="light"] block overriding --bg, --surface, etc. +// in a ThemeToggle component: +document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light') +localStorage.setItem('theme', isDark ? 'dark' : 'light') +``` + +--- + +## 4. Calm Background Music + +Play ambient/calming audio tracks inside the app to accompany breathing and journaling sessions. + +### Option A — Self-hosted audio files (simplest) + +1. Download royalty-free tracks from [freemusicarchive.org](https://freemusicarchive.org) or [pixabay.com/music](https://pixabay.com/music/) +2. Place `.mp3` files in `frontend/public/audio/` +3. Build a `MusicPlayer` component: + +```jsx +// frontend/src/components/MusicPlayer.jsx +import { useState, useRef } from 'react' + +const TRACKS = [ + { title: 'Forest Rain', src: '/audio/forest-rain.mp3' }, + { title: 'Ocean Waves', src: '/audio/ocean-waves.mp3' }, + { title: 'Tibetan Bowls', src: '/audio/tibetan-bowls.mp3' }, +] + +export default function MusicPlayer() { + const [playing, setPlaying] = useState(false) + const [track, setTrack] = useState(0) + const audioRef = useRef(null) + + function togglePlay() { + if (playing) { audioRef.current.pause() } + else { audioRef.current.play() } + setPlaying(!playing) + } + + function changeTrack(i) { + setTrack(i) + setPlaying(false) + setTimeout(() => { audioRef.current.load(); audioRef.current.play(); setPlaying(true) }, 50) + } + + return ( +
+
+ ) +} +``` + +4. Add `` to `BreathePage.jsx` or the guided exercise modal + +### Option B — Streaming via YouTube IFrame API (no file hosting needed) + +```jsx +// Embed a YouTube ambient playlist +