# Homeschool Dashboard A self-hosted web app for managing homeschool schedules, tracking daily learning sessions, and logging activities. Features a full-screen TV dashboard with live timers and real-time updates via WebSockets. --- ## Features - **TV Dashboard** — Full-screen display for the living room TV. Shows the current subject, countdown timer, day progress, and upcoming schedule blocks. Updates live without page refresh. - **Schedule Builder** — Create named schedule templates with time blocks assigned to subjects. Assign templates per-child or share across all children. - **Daily Sessions** — Start a school day against a schedule template. Track which blocks are active, paused, or complete. Timer state (including paused) is preserved when navigating between pages. - **Activity Log** — Automatically records every timer event (day started, block start/pause/resume/complete/skip) as a timeline, including which schedule template was used. Supports manual notes with subject, duration, and free text. Browse and filter history by child and date. - **Behavior Tracking (Strikes)** — Issue up to 3 strikes per child from the Dashboard. Strike count is shown on the TV dashboard and resets automatically when a new school day begins. - **Timezone Support** — Set your local timezone in Admin → Settings. All activity log timestamps display in your timezone, including the TV dashboard clock. - **Multi-Child Support** — Manage multiple students under one parent account, each with their own color, schedule, and history. - **JWT Authentication** — Secure parent login with access tokens and httpOnly refresh cookies. TV dashboard is public (no login required). --- ## Tech Stack | Layer | Technology | |-------|-----------| | Frontend | Vue 3 + Vite + Pinia + Vue Router | | Frontend server | nginx (Docker) | | Backend API | FastAPI (Python 3.12) | | Real-time | WebSockets via FastAPI | | Database | MySQL 8 | | ORM | SQLAlchemy 2.0 (async) | | Migrations | Alembic | | Auth | JWT — python-jose + passlib/bcrypt | | Orchestration | Docker Compose | --- ## Project Structure ``` homeschool/ ├── docker-compose.yml # Production stack (3 services) ├── docker-compose.override.yml # Dev overrides (hot reload) ├── .env.example # Environment variable template │ ├── backend/ │ ├── Dockerfile │ ├── requirements.txt │ ├── alembic/ # Database migrations │ └── app/ │ ├── main.py # FastAPI app entry point │ ├── config.py # Settings (reads from .env) │ ├── database.py # Async SQLAlchemy engine │ ├── dependencies.py # Auth dependencies (get_current_user) │ ├── auth/jwt.py # Token creation, password hashing │ ├── models/ # SQLAlchemy ORM models │ ├── schemas/ # Pydantic request/response schemas │ ├── routers/ # API route handlers │ └── websocket/manager.py # WebSocket connection manager │ └── frontend/ ├── Dockerfile # Multi-stage: Node build → nginx serve ├── nginx.conf # Proxy /api/ and /ws/ to backend └── src/ ├── composables/ │ ├── useApi.js # Axios with auto token-refresh │ └── useWebSocket.js # Auto-reconnecting WebSocket ├── stores/ # Pinia: auth, children, schedule ├── views/ # LoginView, TVView, DashboardView, etc. └── components/ # TimerDisplay, ScheduleBlock, NavBar, etc. ``` --- ## Getting Started ### Prerequisites - [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/) installed - Port `8054` available on the host ### 1. Clone the repo ```bash git clone https://git.chns.tech/CooperandGoodman/homeschool.git cd homeschool ``` ### 2. Create your `.env` file ```bash cp .env.example .env ``` Open `.env` and fill in the values: ```env MYSQL_ROOT_PASSWORD=your_secure_root_password MYSQL_DATABASE=homeschool MYSQL_USER=homeschool MYSQL_PASSWORD=your_secure_db_password # Generate with: openssl rand -hex 32 SECRET_KEY=your_generated_secret_key ALGORITHM=HS256 ACCESS_TOKEN_EXPIRE_MINUTES=30 REFRESH_TOKEN_EXPIRE_DAYS=30 # Your host IP or domain (no trailing slash) CORS_ORIGINS=http://localhost:8054 ``` ### 3. Build and start ```bash docker compose up --build ``` The first build takes a few minutes (npm install + pip install). On subsequent starts it's fast. ### 4. Register your parent account Open **http://localhost:8054/login** and register. This creates your admin account. ### 5. Set up your data (in order) 1. **Admin** (`/admin`) → Add each child, pick a color 2. **Admin** → Add subjects (Math, Reading, Science, etc.) with emoji icons and colors 3. **Admin** → Scroll to **Settings** and select your local timezone — this ensures activity log times and the TV clock display correctly 4. **Schedules** (`/schedules`) → Create a schedule template, add time blocks assigned to subjects 5. **Dashboard** (`/dashboard`) → Click "Start Day", choose a template 6. **TV** → Open `http://your-lan-ip:8054/tv/1` on the living room TV (replace `1` with the child's ID) --- ## Usage ### Parent Views (require login) | URL | Description | |-----|-------------| | `/dashboard` | Overview, start/stop sessions, timer controls, issue behavior strikes | | `/schedules` | Create and edit schedule templates and time blocks | | `/logs` | Browse timer event history and manual activity notes; filter by child and date | | `/admin` | Manage children, subjects, schedule templates, and account settings (timezone) | ### TV Dashboard (no login) | URL | Description | |-----|-------------| | `/tv/:childId` | Full-screen display — current block, countdown timer, day progress | Point a browser on the living room TV at `http://your-lan-ip:8054/tv/1`. The page connects via WebSocket and updates automatically when a parent starts/stops/advances the timer from the Dashboard. ### API Documentation FastAPI auto-generates interactive API docs: - **Swagger UI** — `http://localhost:8054/api/docs` - **ReDoc** — `http://localhost:8054/api/redoc` --- ## Development Mode The `docker-compose.override.yml` file enables hot reload automatically when running locally: - **Backend** — uvicorn `--reload` watches `backend/app/` for changes - **Frontend** — Vite HMR on port `5173` (mapped to `8054` in dev mode) ```bash docker compose up --build # override is applied automatically in dev ``` To run production mode explicitly (no hot reload): ```bash docker compose -f docker-compose.yml up --build ``` --- ## Database Migrations Alembic is configured for async SQLAlchemy migrations. ```bash # Generate a new migration after changing models docker compose exec backend alembic revision --autogenerate -m "description" # Apply pending migrations docker compose exec backend alembic upgrade head # Roll back one migration docker compose exec backend alembic downgrade -1 ``` > **Note:** On first startup the app auto-creates all tables via SQLAlchemy's `create_all`. Alembic is used for schema changes after the initial setup. --- ## WebSocket Events The TV dashboard connects to `ws://host/ws/{child_id}` and receives JSON events: | Event | Triggered by | Payload | |-------|-------------|---------| | `session_update` | Session start/end | Full session snapshot | | `start` | Timer started | `session_id`, `block_id`, `current_block_id` | | `pause` | Timer paused | `session_id`, `block_id` | | `resume` | Timer resumed | `session_id`, `block_id` | | `complete` | Block completed | `session_id`, `block_id` | | `skip` | Block skipped | `session_id`, `block_id` | | `strikes_update` | Strike issued/cleared | `child_id`, `strikes` | --- ## Environment Variables Reference | Variable | Required | Description | |----------|----------|-------------| | `MYSQL_ROOT_PASSWORD` | Yes | MySQL root password | | `MYSQL_DATABASE` | Yes | Database name (default: `homeschool`) | | `MYSQL_USER` | Yes | App database user | | `MYSQL_PASSWORD` | Yes | App database password | | `SECRET_KEY` | Yes | JWT signing key — generate with `openssl rand -hex 32` | | `ALGORITHM` | No | JWT algorithm (default: `HS256`) | | `ACCESS_TOKEN_EXPIRE_MINUTES` | No | Access token lifetime (default: `30`) | | `REFRESH_TOKEN_EXPIRE_DAYS` | No | Refresh token lifetime (default: `30`) | | `CORS_ORIGINS` | No | Comma-separated allowed origins (default: `http://localhost:8054`) | --- ## Stopping and Restarting ```bash # Stop containers (data preserved in Docker volume) docker compose down # Stop and wipe the database volume (full reset) docker compose down -v # Restart without rebuilding docker compose up ```