- Auto-create a locked "Meeting" subject for every user on registration and seed it for all existing users on startup - Meeting subject cannot be deleted or renamed (is_system flag) - 5-minute corner toast warning on Dashboard and TV with live countdown, dismiss button, and 1-minute re-notify if dismissed - At start time: full-screen TV overlay with 30-second auto-dismiss, automatic pause of running block, switch to Meeting block, and auto-start of Meeting timer - Web Audio API chimes: rising on warnings, falling at meeting start - Update README with Meeting subject and notification system docs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
304 lines
18 KiB
Markdown
304 lines
18 KiB
Markdown
# 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 bar, activity options, and the schedule block list. Updates live without page refresh via WebSocket.
|
|
- **Morning Routine** — Define a list of morning routine items in Admin. They appear in the TV dashboard Activities panel during the "Good Morning" greeting before the first block starts, then switch to subject-specific activities once a block begins.
|
|
- **Break Time** — Each schedule block can optionally include a break at the end. Enable the checkbox and set a duration (in minutes) when building a block in Admin. Once the block's main timer is done, a **Break Time** section appears on the Dashboard with its own **Start / Pause / Resume / Reset** controls — the break does not start automatically. While break is active the TV left column switches to an amber break badge and countdown timer, and the center column shows the configurable **Break Activities** list instead of subject options.
|
|
- **Break Activities** — A global list of break-time activities (e.g. "Get a snack", "Go outside") managed in Admin → Break Activities, using the same add/edit/delete interface as Morning Routine. These items are shown on the TV during any active break.
|
|
- **Day Progress Bar** — Both the TV dashboard and the parent dashboard display a progress bar showing how far through the day the child is. Progress is calculated from total scheduled block time vs. remaining block time — not wall-clock time — so it advances only as blocks are actively worked. On the TV the bar is labeled **🟢 Start** and **Finish 🏁**. On the parent dashboard the left label shows the scheduled start time of the first block and the right label shows a live-updating **estimated finish time** computed as the current time plus all remaining block time and break time for incomplete blocks.
|
|
- **Schedule Builder** — Create named schedule templates with time blocks assigned to subjects. Each block supports an optional custom duration override, label, and break time setting. Managed inside the Admin page.
|
|
- **Daily Sessions** — Start a school day against a schedule template. Click any block in the list to select it as the current block. Use the **Start** button to begin timing, **Pause** to stop, **Resume** to continue from where you left off, and **Reset** to clear the elapsed time and restart the timer from zero. Elapsed time per block is remembered across switches, so returning to a block picks up where it left off.
|
|
- **Block Timer Remaining** — Each block in the schedule list shows time remaining (allocated duration minus elapsed), counting down live on both the parent dashboard and the TV sidebar. Shows "< 1 min" when under a minute, and "Done!" when the full duration is elapsed.
|
|
- **Activity Log** — Automatically records every timer event (day started, block start/pause/resume/complete/skip/reset, break start/pause/resume/reset) and every strike change as a timestamped timeline. Includes which schedule template was used. Supports manual notes with free text. Browse and filter history by child and date.
|
|
- **Behavior Tracking (Strikes)** — Issue up to 3 strikes per child from the Dashboard. Strike additions and removals are logged in the activity log with a timestamp. 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).
|
|
- **Super Admin Panel** — A separate admin interface (at `/super-admin`) for site-wide management. Log in with a dedicated admin username and password (set in `.env`). Lists all registered parent accounts and allows impersonating any user — switching into their session to view and manage their data. An impersonation banner is shown at the top of the screen with a one-click "Exit to Admin Panel" button.
|
|
- **Meeting Subject** — A system subject called "Meeting" (📅) is automatically created for every user and cannot be deleted or renamed. Add it to any schedule block like a normal subject and assign activity options (e.g. agenda items) that will display on the TV during the meeting.
|
|
- **Meeting Notifications** — When a schedule block assigned to the Meeting subject is approaching, the app automatically alerts everyone:
|
|
- **5-minute warning** — An amber corner toast appears on both the parent Dashboard and the TV with the meeting name and a live countdown. Tap ✕ to dismiss.
|
|
- **1-minute re-notify** — If the 5-minute toast was dismissed, it reappears at the 1-minute mark.
|
|
- **At start time** — A full-screen overlay fires on the TV with the meeting name and a 30-second auto-dismiss countdown (tap anywhere to dismiss early). Simultaneously, the currently running block timer is paused, the schedule switches to the Meeting block, and its timer starts automatically. The TV center panel switches to show the meeting's activity options.
|
|
- **Chime sounds** — A rising three-note chime plays on warnings; a falling chime plays at meeting start. Generated via the Web Audio API — no audio files 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) |
|
|
| Auth | JWT — python-jose + passlib/bcrypt |
|
|
| Orchestration | Docker Compose |
|
|
|
|
---
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
homeschool/
|
|
├── docker-compose.yml # Stack definition (3 services: db, backend, frontend)
|
|
├── .env.example # Environment variable template
|
|
│
|
|
├── backend/
|
|
│ ├── Dockerfile
|
|
│ ├── requirements.txt
|
|
│ └── app/
|
|
│ ├── main.py # FastAPI app entry point + table auto-creation
|
|
│ ├── 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
|
|
│ │ ├── child.py
|
|
│ │ ├── subject.py # Subject + SubjectOption
|
|
│ │ ├── schedule.py # ScheduleTemplate + ScheduleBlock (incl. break fields)
|
|
│ │ ├── session.py # DailySession + TimerEvent
|
|
│ │ ├── activity.py # ActivityLog (manual notes)
|
|
│ │ ├── morning_routine.py# MorningRoutineItem
|
|
│ │ ├── break_activity.py # BreakActivityItem
|
|
│ │ └── strike.py # StrikeEvent (strike history)
|
|
│ ├── schemas/ # Pydantic request/response schemas
|
|
│ ├── routers/ # API route handlers
|
|
│ │ ├── auth.py
|
|
│ │ ├── children.py
|
|
│ │ ├── subjects.py
|
|
│ │ ├── schedules.py
|
|
│ │ ├── sessions.py # Timer actions + break timer events
|
|
│ │ ├── logs.py # Timeline + strike events
|
|
│ │ ├── morning_routine.py
|
|
│ │ ├── break_activity.py # Break activities CRUD
|
|
│ │ ├── dashboard.py # Public snapshot endpoint (TV)
|
|
│ │ ├── admin.py # Super admin: login, user list, impersonation
|
|
│ │ └── users.py
|
|
│ ├── utils/
|
|
│ │ └── timer.py # Elapsed-time computation for block and break timers
|
|
│ └── 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
|
|
│ └── useMeetingAlerts.js # Meeting countdown, corner toasts, TV overlay, chimes
|
|
├── stores/ # Pinia: auth, children, schedule, superAdmin
|
|
├── views/ # LoginView, TVView, DashboardView, LogView, AdminView,
|
|
│ # SuperAdminLoginView, SuperAdminView
|
|
└── 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 (or change it in `docker-compose.yml`)
|
|
|
|
### 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
|
|
|
|
# Super admin credentials (for /super-admin)
|
|
ADMIN_USERNAME=admin
|
|
ADMIN_PASSWORD=change_me_admin_password
|
|
```
|
|
|
|
### 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. Add activity options to each subject — they appear on the TV dashboard during that block. The **Meeting** subject is created automatically and cannot be deleted or renamed, but you can add activity options (agenda items) to it.
|
|
3. **Admin** → Add **Morning Routine** items — these show on the TV during the greeting before the first block starts.
|
|
4. **Admin** → Add **Break Activities** items — these show on the TV center panel whenever a break is active.
|
|
5. **Admin** → Scroll to **Settings** and select your local timezone
|
|
6. **Admin** → Scroll to **Schedules** → Create a schedule template, set school day hours, add time blocks assigned to subjects. For any block that should include a break, check **Break** and enter the break duration in minutes.
|
|
6. **Dashboard** (`/dashboard`) → Click "Start Day", choose a template
|
|
7. **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, select and time blocks, issue behavior strikes |
|
|
| `/logs` | Browse timer and strike event history and manual notes; filter by child and date |
|
|
| `/admin` | Manage children, subjects (with activity options), morning routine, break activities, schedule templates, and account settings |
|
|
|
|
### Super Admin Views
|
|
|
|
| URL | Description |
|
|
|-----|-------------|
|
|
| `/super-admin/login` | Log in with the `ADMIN_USERNAME` / `ADMIN_PASSWORD` from `.env` |
|
|
| `/super-admin` | List all registered parent accounts and impersonate any user |
|
|
|
|
While impersonating, a yellow banner appears at the top of every page showing who you're viewing as, with an **Exit to Admin Panel** button to return.
|
|
|
|
### Dashboard Controls
|
|
|
|
While a session is active, clicking a block in the schedule list **selects** it as the current block without starting the timer. The action buttons then provide explicit control:
|
|
|
|
**Main block timer:**
|
|
|
|
| Button | Condition | Action |
|
|
|--------|-----------|--------|
|
|
| **Start** | Block selected, never timed | Begin counting from zero |
|
|
| **Resume** | Block was previously paused | Continue from saved elapsed time |
|
|
| **Pause** | Timer is running | Stop counting, save elapsed time |
|
|
| **Reset** | Any current block | Clear elapsed to zero and restart timer immediately |
|
|
| **End Day** | Session active | Mark the session complete |
|
|
|
|
**Break timer** (shown when the current block has Break Time enabled):
|
|
|
|
| Button | Condition | Action |
|
|
|--------|-----------|--------|
|
|
| **Start Break** | Break not yet started | Begin break countdown from zero |
|
|
| **Pause** | Break timer running | Stop break countdown |
|
|
| **Resume** | Break timer paused | Continue break countdown |
|
|
| **Reset** | Break in progress | Clear break elapsed to zero and restart immediately |
|
|
|
|
### TV Dashboard (no login)
|
|
|
|
| URL | Description |
|
|
|-----|-------------|
|
|
| `/tv/:childId` | Full-screen display — greeting + morning routine, current block timer with subject activities, break timer with break activities, day progress bar (🟢 Start → Finish 🏁), schedule sidebar, meeting warning toasts, meeting start overlay |
|
|
|
|
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`
|
|
|
|
---
|
|
|
|
## Schema Migrations
|
|
|
|
The app automatically creates all database tables on startup via SQLAlchemy's `create_all`. Additive column changes are applied with idempotent `ALTER TABLE` statements in the startup lifespan, so upgrading to a new version is safe — just rebuild and restart:
|
|
|
|
```bash
|
|
docker compose build
|
|
docker compose up -d
|
|
```
|
|
|
|
No separate migration tool or manual steps are required.
|
|
|
|
---
|
|
|
|
## WebSocket Events
|
|
|
|
The TV dashboard connects to `ws://host/ws/{child_id}` and receives JSON events:
|
|
|
|
| Event | Triggered by | Key payload fields |
|
|
|-------|-------------|---------|
|
|
| `session_update` | Session start | Full session snapshot including blocks, morning routine, break activities, and day times |
|
|
| `start` | Block timer started | `block_id`, `current_block_id`, `block_elapsed_seconds`, `prev_block_id`, `prev_block_elapsed_seconds` |
|
|
| `pause` | Block timer paused | `block_id`, `current_block_id` |
|
|
| `resume` | Block timer resumed | `block_id`, `current_block_id` |
|
|
| `select` | Block selected (not started) | `block_id`, `current_block_id`, `block_elapsed_seconds`, `prev_block_id`, `prev_block_elapsed_seconds` |
|
|
| `reset` | Block timer reset to zero | `block_id`, `current_block_id`, `block_elapsed_seconds` (always 0) |
|
|
| `break_start` | Break timer started | `block_id`, `current_block_id`, `break_elapsed_seconds` |
|
|
| `break_pause` | Break timer paused | `block_id`, `current_block_id` |
|
|
| `break_resume` | Break timer resumed | `block_id`, `current_block_id` |
|
|
| `break_reset` | Break timer reset to zero | `block_id`, `current_block_id`, `break_elapsed_seconds` (always 0) |
|
|
| `complete` | Session ended | `is_active: false` |
|
|
| `strikes_update` | Strike issued/cleared | `strikes` |
|
|
|
|
`block_elapsed_seconds` on `start` and `select` events carries the authoritative accumulated elapsed time for that block (all previous intervals, respecting any prior resets), so every client — including the TV — can restore the correct timer offset without a local cache.
|
|
|
|
`prev_block_id` and `prev_block_elapsed_seconds` on `start` and `select` events carry the saved elapsed for the block being left, so the TV sidebar immediately shows the correct remaining time for that block.
|
|
|
|
Break timer events (`break_*`) are stored as `TimerEvent` records alongside regular timer events but are computed and broadcast independently — they do not affect block selection, implicit pauses, or elapsed time for the main block timer.
|
|
|
|
---
|
|
|
|
## 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`) |
|
|
| `ADMIN_USERNAME` | No | Super admin login username (default: `admin`) |
|
|
| `ADMIN_PASSWORD` | No | Super admin login password (default: `change_me_admin_password`) |
|
|
|
|
> **Note:** `ADMIN_USERNAME` and `ADMIN_PASSWORD` must be set in `.env` **and** listed in the `backend` service's `environment` block in `docker-compose.yml`. Changing them in `.env` alone is not sufficient — the backend container reads them as environment variables, not from the file directly.
|
|
|
|
---
|
|
|
|
## 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
|
|
```
|