Program Catalog & User Enrollment with Intelligent Scheduling
OpenLift empowers users with structured, adaptable training through a robust Program Catalog and an intelligent enrollment and scheduling system. This document outlines how ProgramPlan
entities are managed in the catalog and how users like Sam enroll in these plans, benefit from personalized snapshots, and receive dynamically generated workout schedules.
Overview
- For Administrators/Content Creators: You build and manage a library of high-quality
ProgramPlan
s. These plans define the structure, goals, workout templates, and progression for various training objectives. - For End-Users (like Sam): Sam discovers these
ProgramPlan
s in the app. When he enrolls:- A personal snapshot of the chosen plan is created just for him, ensuring his program remains consistent.
- He specifies his preferred training days.
- Our Intelligent Scheduling Service immediately populates his in-app calendar with workouts for the upcoming weeks, based on his plan snapshot and preferences.
- This schedule is dynamic, featuring smart catch-up for missed sessions and the flexibility for Sam to skip workouts if needed, with the system adjusting his progression accordingly.
Why This System Matters
For Administrators/Content Creators:
- 📚 Curate Quality Content: Build a rich library of effective, structured training programs.
- 🛠️ Flexible Program Design: Define comprehensive plan parameters, workout sequences, and weekly adjustments.
- 👁️ Controlled Publishing: Manage the lifecycle of programs from draft to public release.
For End-Users (like Sam):
- ✅ Access to Expert Plans: Benefit from well-designed programs.
- 🎯 Goal-Oriented & Personalized: Find plans matching his goals, and the schedule adapts to his life.
- 🚀 Clear, Structured Progression: Follow an intelligent path designed for improvement, with flexibility.
- 🧘 Reduced Planning Overhead: Focus on training, not on manually figuring out what to do next.
- 💪 Empowerment & Control: The ability to skip workouts gives Sam agency over his training load.
Core Concepts
- Catalog Components
- User Enrollment & Scheduling Components
ProgramPlan
: The central entity in the catalog. Represents a complete, structured training plan.- Contains: Name, description,
TrainingGoal
,TrainingLevel
,durationWeeks
,daysPerWeek
, defaultProgramTrainingFocus
,Visibility
,isPublished
. - Relations: Links to an ordered sequence of
ProgramWorkoutTemplate
s viaProgramPlanTemplateLink
. Can haveProgramPlanWeeklyModifier
s.
- Contains: Name, description,
ProgramWorkoutTemplate
(PWT): A specific workout routine (e.g., "Day 1 - Upper Body Strength").ProgramPlanTemplateLink
: Join table linkingProgramPlan
toProgramWorkoutTemplate
, defining theorder
of templates within the plan's cycle.ProgramTrainingFocus
: Defines default training parameters (reps, sets, RPE, rest) for a plan or template.ProgramPlanWeeklyModifier
: Allows for adjustments to training parameters for specific weeks within a masterProgramPlan
(e.g., "Deload Week").ProgramDefaultWeeklyModifier
: Reusable templates for weekly modifiers.
ProgramUserInstance
(PUI): Sam's personal enrollment in aProgramPlan
.- Tracks:
status
(Active, Paused, etc.),currentPlanWeekNumber
,lastCompletedPwtOrderInWeek
,preferredTrainingDays
,lastActivityDate
. - Links to his unique
UserProgramPlanSnapshot
.
- Tracks:
- Snapshot System:
UserProgramPlanSnapshot
: An immutable copy of theProgramPlan
's core details at Sam's enrollment.UserProgramPWTSnapshot
: Snapshots of eachProgramWorkoutTemplate
from the plan.UserProgramPWESnapshot
: Snapshots of each exercise within each PWT snapshot, capturing base parameters.UserProgramWeeklyModifierSnapshot
: Snapshots of any weekly modifiers. This snapshot system ensures Sam's program structure doesn't change if the master catalog plan is updated.
UserProgramScheduledWeekWorkout
(SWW): A specificUserProgramPWTSnapshot
scheduled for Sam on a particularyear
,isoWeek
, anddayOfWeek
.- Tracks
status
(SCHEDULED
,COMPLETED
,SKIPPED_USER
,MISSED_SYSTEM
) and theplanWeekNumber
(logical week of the PWT content).
- Tracks
EffectiveWorkoutService
: Calculates the actual exercises and targets for a session by merging snapshot data, coach overrides (PUEOs/PUAEs), and weekly modifiers.UserProgramSchedulingService
: Orchestrates enrollment, dynamic schedule generation (with catch-up and skip logic), and PUI state progression.
Key Workflows & API Interactions
1. Catalog Plan Management (Admin Perspective)
Administrators use dedicated (typically admin-restricted) API endpoints (e.g., POST /api/catalog/
, PUT /api/catalog/:id
) to:
- Create new
ProgramPlan
s with all their details, linkingProgramWorkoutTemplate
s and definingProgramPlanWeeklyModifier
s. - View, update, publish, or soft-delete these catalog plans.
- (Refer to specific admin documentation for detailed catalog CUD operations).
2. User Program Enrollment & Initial Scheduling (Sam's Experience)
- A. Sam Enrolls
- B. Sam Views His Weekly Schedule
- Sam Explores: Sam uses the app to view available (public, published) catalog plans.
- API Interaction (Frontend):
GET /api/plans
(REST) orquery { plans { ... } }
(GraphQL) to list plans. May callGET /api/plans/:id
orquery { plan(id: ...) { ... } }
for details.
- API Interaction (Frontend):
- Sam Enrolls: Sam selects a plan (e.g., "Beginner Full Body Blast") and provides his preferred training days (e.g., Mon, Wed, Fri).
- API Interaction (Frontend):
POST /api/user-programs
(REST) ormutation { enrollInProgram(...) }
(GraphQL).- Body:
{ "programPlanId": "plan-bfb4w-001", "preferredTrainingDays": [1,3,5] }
- Body:
- Backend (
UserProgramSchedulingService.enrollUser
):- Validates the request.
- Creates the
ProgramUserInstance
(PUI) for Sam. - Creates all associated snapshots (Plan, PWTs, PWEs, Weekly Modifiers) linked to this PUI.
- Calls
_generateScheduleForWindow
to populate Sam's initialUserProgramScheduledWeekWorkout
entries for the first few weeks.
- Response: The newly created
ProgramUserInstance
DTO. - Frontend: Stores active PUI details, navigates Sam to his program dashboard.
- API Interaction (Frontend):
- Sam Opens App/Schedule View:
- API Interaction (Frontend):
- First, potentially
GET /api/user-programs/active
(REST) orquery { activeProgram { id ... } }
(GraphQL) to get Sam's active PUI ID. - Then,
GET /api/user-programs/active/schedule/week?year=2025&isoweek=20
(REST) orquery { weeklySchedule(puiId: "sam-pui-id", year: 2025, isoWeek: 20) { ... } }
(GraphQL).
- First, potentially
- Backend (
UserProgramSchedulingService.getOrGenerateScheduledWorkoutsForWeek
):- Authenticates/Authorizes Sam.
- Fetches Sam's PUI and its complete snapshot data.
- Calls
_generateScheduleForWindow
to ensure the schedule for the requested week (and a buffer) is populated, applying catch-up logic if needed. - Returns the list of
UserProgramScheduledWeekWorkout
DTOs for the target week.
- Frontend: Displays the workouts.
- API Interaction (Frontend):
3. Interacting with a Scheduled Workout (Sam's Experience)
- A. Sam Starts a Workout
- B. Sam Completes a Workout
- C. Sam Skips a Workout
- Sam Sees "Leg Day" scheduled for today and decides to start it. Let's say its
scheduledWorkoutId
is "sww-legday-123".- API Interaction (Frontend):
POST /api/user-programs/scheduled-workouts/sww-legday-123/start
(REST) ormutation { startScheduledWorkout(scheduledWorkoutId: "sww-legday-123") { ... } }
(GraphQL). - Backend (
UserProgramController.startWorkout
callingUserProgramSchedulingService.startScheduledWorkoutSession
):- Validates the SWW and PUI state.
- Checks for an existing unfinished
WorkoutHistoryEntry
for this specific SWW. - If none, calls
EffectiveWorkoutService.getEffectiveWorkout
to get exercises and targets (based on PWE snapshots, PUI/coach overrides, weekly modifiers). - Creates a new "skeleton"
WorkoutHistoryEntry
(WHE) withendTime: null
, linking it to the SWW. - Populates
WorkoutCompletedExercise
records with calculated targets and emptyExerciseSet
s. - Updates
PUI.lastActivityDate
.
- Response:
WorkoutHistorySkeletonResponse
(the new or existing WHE skeleton). - Frontend: Opens the workout logging screen, pre-filled from the skeleton. Sam performs his workout, logging sets/reps/RPE into this WHE via other workout history endpoints (e.g.,
PUT /api/history/me/:wheId
).
- API Interaction (Frontend):
- Sam finishes logging all sets for "Leg Day" and hits "Finish Workout." The WHE now has an
endTime
.- API Interaction (Frontend - Step 1: Save WHE): (Covered by Workout History feature)
PUT /api/history/me/{wheId}
with all details includingendTime
.
- API Interaction (Frontend - Step 1: Save WHE): (Covered by Workout History feature)
- Frontend then informs the Program system the scheduled item is done.
- API Interaction (Frontend - Step 2: Mark SWW Complete):
POST /api/user-programs/scheduled-workouts/sww-legday-123/complete
(REST) ormutation { completeScheduledWorkout(...) }
(GraphQL).- Body:
{ "workoutHistoryEntryId": "whe-id-of-legday" }
- Body:
- Backend (
UserProgramSchedulingService.markWorkoutAsCompleted
):- Validates.
- Updates SWW status to
COMPLETED
and links theworkoutHistoryEntryId
. - Updates
PUI.lastActivityDate
,PUI.lastCompletedPwtOrderInWeek
. - If the logical week is now finished, advances
PUI.currentPlanWeekNumber
and resetslastCompletedPwtOrderInWeek
. - If the entire plan is finished, sets PUI status to
COMPLETED_PLAN
. - Triggers
_generateScheduleForWindow
to refresh the schedule.
- Response: Updated
ProgramUserInstance
DTO. - Frontend: Updates UI to show "Leg Day" as completed, may refresh weekly schedule view.
- API Interaction (Frontend - Step 2: Mark SWW Complete):
- Sam sees "Workout X (Catch-Up)" scheduled for Monday. He decides to skip it.
scheduledWorkoutId
is "sww-catchup-x".- API Interaction (Frontend):
POST /api/user-programs/scheduled-workouts/sww-catchup-x/skip
(REST) ormutation { skipScheduledWorkout(...) }
(GraphQL). - Backend (
UserProgramSchedulingService.markWorkoutAsSkipped
):- Validates.
- Updates SWW status to
SKIPPED_USER
. - Updates PUI progression as if the workout was notionally done for sequencing (advances
lastCompletedPwtOrderInWeek
, potentiallycurrentPlanWeekNumber
). - Updates
PUI.lastActivityDate
. - Triggers
_generateScheduleForWindow
.
- Response: Updated
ProgramUserInstance
DTO. - Frontend: Updates UI for "Workout X" to "Skipped." Refreshes schedule to show the next actual workout (e.g., what was originally planned for Monday or the next available slot).
- API Interaction (Frontend):
4. Program State Management (Pause, Resume, End, Change Preferred Days)
User-initiated actions via specific API endpoints:
PATCH /api/user-programs/:id/pause
PATCH /api/user-programs/:id/resume
PATCH /api/user-programs/:id/end
POST /api/user-programs/:id/preferred-training-days
These call corresponding methods in UserProgramSchedulingService
which:
- Update the PUI
status
,pauseDate
,actualProgramEndDate
, orpreferredTrainingDays
. - Update
PUI.lastActivityDate
. - For
resume
andupdatePreferredTrainingDays
(andpause
/end
), they clear relevant futureSCHEDULED
SWWs and call_generateScheduleForWindow
to rebuild the schedule. - All return the updated
ProgramUserInstance
DTO.
Technical Deep Dive (For Developers)
Primary Data Models (User Program Context)
ProgramUserInstance
(PUI): Core user enrollment record.- Snapshot Models:
UserProgramPlanSnapshot
,UserProgramPWTSnapshot
,UserProgramPWESnapshot
,UserProgramWeeklyModifierSnapshot
. These form the immutable basis of the user's specific program. UserProgramScheduledWeekWorkout
(SWW): Dynamically generated schedule entries, linking a PUI to aUserProgramPWTSnapshot
for a specific calendar day.WorkoutHistoryEntry
(WHE): Linked to an SWW viacompletedWorkoutHistoryEntryId
(on SWW) if started from the schedule.WorkoutCompletedExercise
(WCE): Now containstargetSets
,targetRepsMin
,targetRepsMax
,targetRestSeconds
calculated by theEffectiveWorkoutService
at the time ofstartWorkout
.
Key Services & Responsibilities
UserProgramSchedulingService
:enrollUser
: Handles PUI and all snapshot creation, initial schedule generation.getOrGenerateScheduledWorkoutsForWeek
: Main entry point for fetching weekly schedules; triggers generation if needed._generateScheduleForWindow
(private): Core intelligent scheduling logic (catch-up, progression-aware population of SWWs).markWorkoutAsCompleted
: Updates SWW & PUI progression post-workout.markWorkoutAsSkipped
: Updates SWW & PUI progression for skipped workouts.- PUI state management methods (
pauseProgramInstance
, etc.).
EffectiveWorkoutService
:getEffectiveWorkout
: Calculates session-specific exercises and targets by combining PWE snapshot data, PUEOs, PUAEs, weekly modifiers, and training focus overrides. Called byUserProgramController.startWorkout
(or its underlying service).
Key API Endpoints (User-Facing Program Interaction)
Endpoints are primarily under /api/user-programs/
.
Method | Endpoint | Controller Function | Description |
---|---|---|---|
POST | / | enrollInProgram | Enrolls user in a plan, creates snapshots, initial schedule. |
GET | /active | getActiveProgram | Retrieves user's active/paused PUI. |
GET | /active/schedule/week (with query params) | getActiveScheduledWorkoutsForWeek | Gets (and generates if needed) weekly schedule. |
PATCH | /:id/pause | pauseProgram | Pauses the PUI, clears future schedule. |
PATCH | /:id/resume | resumeProgram | Resumes PUI, regenerates schedule. |
PATCH | /:id/end | endProgram | Ends PUI by user, clears future schedule. |
POST | /:id/preferred-training-days | updatePreferredTrainingDays | Updates preferred days, clears and regenerates future schedule. |
POST | /scheduled-workouts/:scheduledWorkoutId/start | startWorkout | Initiates a workout session for a specific SWW, creates WHE skeleton with effective targets. |
POST | /scheduled-workouts/:scheduledWorkoutId/complete | completeScheduledWorkout | Marks SWW complete, updates PUI progression, regenerates schedule. |
POST | /scheduled-workouts/:scheduledWorkoutId/skip | skipScheduledWorkout | Marks SWW skipped, updates PUI progression (pseudo-complete), regenerates schedule. |
Important Technical Considerations
- Database Transactions: Enrollment (
enrollUser
), workout completion (markWorkoutAsCompleted
), skipping (markWorkoutAsSkipped
), and other state changes that involve multiple DB writes are (or should be) wrapped inprisma.$transaction
to ensure atomicity. - Idempotency:
startWorkout
is idempotent for a specific SWW (returns existing unfinished WHE).markWorkoutAsCompleted
andmarkWorkoutAsSkipped
are idempotent for already processed SWWs.
- Data Flow for "Start Workout":
- Client calls
POST .../:scheduledWorkoutId/start
. - Controller fetches SWW (with PUI & PWT Snapshot). Validates.
- Controller calls
EffectiveWorkoutService.getEffectiveWorkout(puiId, sww.planWeekNumber, sww.userProgramPwtSnapshotId)
. - Controller (within a transaction):
- Creates
WorkoutHistoryEntry
(linking to SWW, PUI, PWT Snapshot, master PWT). - Creates
WorkoutCompletedExercise
s based onEffectiveWorkout
data (includingtarget*
fields). - Creates empty
ExerciseSet
s for each WCE. - Updates
PUI.lastActivityDate
.
- Creates
- Controller returns the
WorkoutHistorySkeletonResponse
.
- Client calls
- Data Flow for "Complete/Skip Workout":
- Client calls
POST .../:scheduledWorkoutId/complete
(withworkoutHistoryEntryId
) or/skip
. - Controller calls respective
UserProgramSchedulingService
method. - Service (within a transaction):
- Validates.
- Updates
UserProgramScheduledWeekWorkout.status
(and links WHE for complete). - Updates
ProgramUserInstance
progression fields. - Calls
_generateScheduleForWindow
(which performs its own DB reads/writes for SWWs).
- Service returns updated PUI (with snapshot details for DTO).
- Controller uses
mapPuiToResponseDto
and sends response.
- Client calls
Future Enhancements (Potential)
- Storing a "reason" for skipping workouts.
- More sophisticated rules for when/how "catch-up" workouts are presented or automatically aged out.
- UI/UX for managing the schedule more directly (e.g., drag-and-drop rescheduling, though this adds complexity).
This updated page now integrates the user enrollment/scheduling features directly with the concept of the Program Catalog, providing a more unified view of how users interact with predefined plans. Remember to replace placeholders and add visuals!