v2: Program research, Groq extraction, and workout tracking (REVIEW — not deployed)

Backend:
- New models: ProgramCatalog, CatalogWorkout, CrawlQueue, WorkoutLog
- Program.py: added catalog_id, current_week, current_day columns
- program_research.py: ttp-crawler integration, Groq Llama 3.3 70B extraction, known sources map, validation
- crawl_scheduler.py: priority queue with off-peak/weekend scheduling
- catalog.py router: research, catalog browse, adopt, schedule, complete, progress endpoints
- Points: 75pts/workout, 50pts weekly bonus, 200pts completion bonus
- config.py: added groq_api_key setting
- main.py: catalog router, background scheduler, v2 migration

Frontend:
- ProgramSearch.jsx: search, research submission, catalog browsing, adopt programs
- ProgramDetail.jsx: schedule view, today's workout, completion modal, progress tracking
- App.jsx: Programs nav tab, new routes
- api.js: catalog and tracking API functions

Config:
- docker-compose.yml: GROQ_API_KEY env var

NOT DEPLOYED — requires Groq API key and Scot review
This commit is contained in:
claude 2026-04-03 16:22:53 +00:00
parent a81ca9c275
commit 93f24fff83
13 changed files with 1738 additions and 9 deletions

View file

@ -64,11 +64,30 @@ export const api = {
createReward: (data) => request('/rewards', { method: 'POST', body: JSON.stringify(data) }),
redeemReward: (id, date) => request(`/rewards/${id}/redeem`, { method: 'POST', body: JSON.stringify({ date }) }),
// Programs
// Programs (v1 — existing)
listPrograms: () => request('/programs'),
createProgram: (data) => request('/programs', { method: 'POST', body: JSON.stringify(data) }),
getProgram: (id) => request(`/programs/${id}`),
// Program Catalog (v2 — new)
searchCatalog: (q) => request(`/programs/catalog${q ? `?q=${encodeURIComponent(q)}` : ''}`),
getCatalogProgram: (id) => request(`/programs/catalog/${id}`),
researchProgram: (name) =>
request('/programs/research', { method: 'POST', body: JSON.stringify({ name }) }),
adoptProgram: (catalogId, startDate) =>
request(`/programs/catalog/${catalogId}/adopt`, {
method: 'POST', body: JSON.stringify({ start_date: startDate }),
}),
// Program Tracking (v2 — new)
getProgramSchedule: (id) => request(`/programs/${id}/schedule`),
getWorkoutDetail: (progId, workoutId) => request(`/programs/${progId}/workout/${workoutId}`),
completeWorkout: (progId, workoutId, data) =>
request(`/programs/${progId}/workout/${workoutId}/complete`, {
method: 'POST', body: JSON.stringify(data),
}),
getProgramProgress: (id) => request(`/programs/${id}/progress`),
// Integrations
integrationStatus: () => request('/integrations'),
stravaAuth: () => request('/integrations/strava/auth'),
@ -78,7 +97,7 @@ export const api = {
disconnect: (provider) => request(`/integrations/${provider}`, { method: 'DELETE' }),
// Resources
getRecommendations: () => request('/onboarding/recommendations'),
getResourceRecommendations: () => request('/onboarding/recommendations'),
};
export function setToken(token) {