# Database Schema SQLite database at `data/sneakyswole.db`, managed by SQLModel ORM with Alembic migrations. --- ## Tables ### `users` User profiles (admin and regular). Admin has login credentials; regular profiles are managed by the admin. | Column | Type | Constraints | Description | |--------|------|------------|-------------| | `id` | INTEGER | PK, auto-increment | | | `username` | VARCHAR | UNIQUE, indexed | Login identifier | | `password_hash` | VARCHAR | default="" | bcrypt hash (admin only) | | `display_name` | VARCHAR | default="" | Name shown in UI | | `height` | VARCHAR | nullable | e.g., "6'0\"" | | `weight` | VARCHAR | nullable | e.g., "260 lbs" | | `goals` | VARCHAR | nullable | Free-text training goals | | `is_admin` | BOOLEAN | default=False | Admin privileges flag | | `created_at` | DATETIME | auto | Record creation time | | `updated_at` | DATETIME | auto | Last update time | **Model:** `app/models/user.py:User` --- ### `exercises` Exercise library catalog. Each exercise belongs to a workout day. | Column | Type | Constraints | Description | |--------|------|------------|-------------| | `id` | INTEGER | PK, auto-increment | | | `name` | VARCHAR | indexed | e.g., "DB Chest Press (Floor)" | | `muscle_group` | VARCHAR | default="" | e.g., "Chest", "Shoulders" | | `workout_day` | VARCHAR | indexed | "Push", "Pull", "Lower", "Full Body" | | `sets` | INTEGER | default=3 | Default number of sets | | `tempo` | VARCHAR | default="" | e.g., "3-1-2" (eccentric-pause-concentric) | | `form_cues` | VARCHAR | default="" | Detailed form instructions | | `created_at` | DATETIME | auto | Record creation time | **Model:** `app/models/exercise.py:Exercise` --- ### `warmups` Standardized warmup routine displayed before every workout. | Column | Type | Constraints | Description | |--------|------|------------|-------------| | `id` | INTEGER | PK, auto-increment | | | `name` | VARCHAR | indexed | e.g., "Cat / Cow" | | `type` | VARCHAR | default="" | Category: "Thoracic Mob", "Hip Mobility", etc. | | `reps` | VARCHAR | default="" | e.g., "8 reps", "8 each side" | | `form_cues` | VARCHAR | default="" | Detailed form instructions | | `sort_order` | INTEGER | default=0 | Display order in warmup sequence | | `created_at` | DATETIME | auto | Record creation time | **Model:** `app/models/warmup.py:Warmup` --- ### `workout_days` The 4-day training split definition. | Column | Type | Constraints | Description | |--------|------|------------|-------------| | `id` | INTEGER | PK, auto-increment | | | `name` | VARCHAR | UNIQUE, indexed | "Push", "Pull", "Lower", "Full Body" | | `day_number` | INTEGER | UNIQUE | Order in rotation (1-4) | | `description` | VARCHAR | default="" | Brief focus description | **Model:** `app/models/workout_day.py:WorkoutDay` --- ### `user_exercise_programs` Per-user programming targets linking users to exercises with week 1/4 goals. | Column | Type | Constraints | Description | |--------|------|------------|-------------| | `id` | INTEGER | PK, auto-increment | | | `user_id` | INTEGER | FK → `users.id`, indexed | Profile this applies to | | `exercise_id` | INTEGER | FK → `exercises.id`, indexed | Exercise being programmed | | `wk1_reps` | VARCHAR | default="" | Week 1 target reps (e.g., "10", "30 sec") | | `wk4_reps` | VARCHAR | default="" | Week 4 target reps | | `wk1_weight` | VARCHAR | default="" | Week 1 target weight (e.g., "30 lbs", "BW") | | `wk4_weight` | VARCHAR | default="" | Week 4 target weight | | `created_at` | DATETIME | auto | Record creation time | | `updated_at` | DATETIME | auto | Last update time | **Model:** `app/models/user_exercise_program.py:UserExerciseProgram` --- ### `workout_sessions` A completed workout session — ties a user to a workout day on a date. | Column | Type | Constraints | Description | |--------|------|------------|-------------| | `id` | INTEGER | PK, auto-increment | | | `user_id` | INTEGER | FK → `users.id`, indexed | Who did the workout | | `workout_day_id` | INTEGER | FK → `workout_days.id` | Which day was trained | | `date` | DATE | default=today | Date the workout was performed | | `notes` | VARCHAR | nullable | Free-text session notes | | `created_at` | DATETIME | auto | Record creation time | **Model:** `app/models/workout_session.py:WorkoutSession` --- ### `workout_logs` Individual set logs within a session. Each row = one set of one exercise. | Column | Type | Constraints | Description | |--------|------|------------|-------------| | `id` | INTEGER | PK, auto-increment | | | `session_id` | INTEGER | FK → `workout_sessions.id`, indexed | Parent session | | `exercise_id` | INTEGER | FK → `exercises.id` | Which exercise | | `set_number` | INTEGER | default=1 | Set number (1, 2, 3...) | | `reps_completed` | INTEGER | default=0 | Actual reps performed | | `weight_used` | VARCHAR | default="" | Weight used (e.g., "30 lbs", "BW") | | `felt_easy` | BOOLEAN | default=False | Progression signal | | `notes` | VARCHAR | nullable | Per-set notes | | `created_at` | DATETIME | auto | Record creation time | **Model:** `app/models/workout_log.py:WorkoutLog` --- ### `progress_log` Progression tracking — what the engine suggested vs what the user actually did. | Column | Type | Constraints | Description | |--------|------|------------|-------------| | `id` | INTEGER | PK, auto-increment | | | `user_id` | INTEGER | FK → `users.id`, indexed | Profile being tracked | | `exercise_id` | INTEGER | FK → `exercises.id` | Exercise being tracked | | `date` | DATE | default=today | Date of progression entry | | `suggested_reps` | INTEGER | nullable | Engine recommendation | | `suggested_weight` | VARCHAR | nullable | Engine recommendation | | `actual_reps` | INTEGER | nullable | What user actually did | | `actual_weight` | VARCHAR | nullable | What user actually used | | `progression_applied` | VARCHAR | nullable | Type: "reps_increase", "weight_increase", "deload" | | `created_at` | DATETIME | auto | Record creation time | **Model:** `app/models/progress_log.py:ProgressLog` --- ## Relationships ``` users ├── user_exercise_programs (1:N via user_id) ├── workout_sessions (1:N via user_id) └── progress_log (1:N via user_id) exercises ├── user_exercise_programs (1:N via exercise_id) ├── workout_logs (1:N via exercise_id) └── progress_log (1:N via exercise_id) workout_days └── workout_sessions (1:N via workout_day_id) workout_sessions └── workout_logs (1:N via session_id) ``` --- ## Migrations Managed by Alembic. Config at `alembic.ini`, migration scripts at `alembic/versions/`. - **Initial migration:** `1855836abf6c_initial_schema_8_tables.py` — creates all 8 tables To create a new migration: ```bash alembic revision --autogenerate -m "description" alembic upgrade head ``` --- ## Seeding On first startup, `SeedService.seed_all()` reads: - `config/exercises.yaml` — exercise catalog + warmups + workout days - `config/user_programs.yaml` — per-user week 1/4 targets Admin user is created from `ADMIN_USERNAME` / `ADMIN_PASSWORD` env vars with bcrypt hash. Seeding is skipped if data already exists.