147 lines
5.0 KiB
JSON
147 lines
5.0 KiB
JSON
{
|
||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||
"$id": "weekplan-import",
|
||
"title": "WeekPlanImportRequest",
|
||
"description": "AI-generated week plan import format for peakUp",
|
||
"type": "object",
|
||
"required": ["year", "calendarWeek", "workouts"],
|
||
"additionalProperties": false,
|
||
"properties": {
|
||
"year": {
|
||
"type": "integer",
|
||
"minimum": 2000,
|
||
"maximum": 2100,
|
||
"description": "ISO week-based year"
|
||
},
|
||
"calendarWeek": {
|
||
"type": "integer",
|
||
"minimum": 1,
|
||
"maximum": 53,
|
||
"description": "ISO calendar week number (1–53)"
|
||
},
|
||
"weekLabel": {
|
||
"type": "string",
|
||
"description": "Short label for the week, e.g. 'Race Prep Week 3'"
|
||
},
|
||
"weekDescription": {
|
||
"type": "string",
|
||
"description": "Longer description of the week's focus and goals"
|
||
},
|
||
"workouts": {
|
||
"type": "array",
|
||
"minItems": 1,
|
||
"description": "Planned workouts. Omit rest days — do not add empty entries.",
|
||
"items": { "$ref": "#/$defs/WorkoutImportItem" }
|
||
}
|
||
},
|
||
"$defs": {
|
||
"WorkoutImportItem": {
|
||
"type": "object",
|
||
"required": ["title", "day", "type"],
|
||
"additionalProperties": false,
|
||
"properties": {
|
||
"title": {
|
||
"type": "string",
|
||
"description": "Short name of the workout, e.g. 'Zone 2 Ride'"
|
||
},
|
||
"day": {
|
||
"type": "string",
|
||
"enum": ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"],
|
||
"description": "Day of the week (lowercase)"
|
||
},
|
||
"time": {
|
||
"type": "string",
|
||
"pattern": "^\\d{2}:\\d{2}$",
|
||
"description": "Start time in HH:mm format (24h), e.g. '07:30'. Optional — omit if no fixed start time."
|
||
},
|
||
"type": {
|
||
"type": "string",
|
||
"enum": ["BIKE", "RUN", "SWIM", "STRENGTH", "OTHER"],
|
||
"description": "Sport type"
|
||
},
|
||
"estimatedTimeSeconds": {
|
||
"type": "integer",
|
||
"minimum": 1,
|
||
"description": "Total workout duration in seconds. Must equal the sum of all step durations when steps are provided."
|
||
},
|
||
"estimatedTss": {
|
||
"type": "integer",
|
||
"minimum": 1,
|
||
"description": "Estimated Training Stress Score. Formula: IF² × (estimatedTimeSeconds / 3600) × 100, where IF = avg intensity / threshold."
|
||
},
|
||
"description": {
|
||
"type": "string",
|
||
"description": "Optional longer description / coaching notes for the workout"
|
||
},
|
||
"statusNote": {
|
||
"type": "string",
|
||
"description": "Optional pre-populated status note"
|
||
},
|
||
"steps": {
|
||
"type": "array",
|
||
"description": "Ordered workout steps. If omitted the workout has no structured intervals.",
|
||
"items": { "$ref": "#/$defs/StepImportItem" }
|
||
}
|
||
}
|
||
},
|
||
"StepImportItem": {
|
||
"type": "object",
|
||
"required": ["label", "stepType", "durationSeconds", "description", "intensityReference"],
|
||
"additionalProperties": false,
|
||
"if": {
|
||
"properties": { "stepType": { "const": "RAMP" } },
|
||
"required": ["stepType"]
|
||
},
|
||
"then": {
|
||
"required": ["targetValuePctStart", "targetValuePctEnd"]
|
||
},
|
||
"else": {
|
||
"required": ["targetValuePct"]
|
||
},
|
||
"properties": {
|
||
"label": {
|
||
"type": "string",
|
||
"description": "Short name, e.g. 'Warm Up', '4×8 min threshold', 'Recovery'"
|
||
},
|
||
"stepType": {
|
||
"type": "string",
|
||
"enum": ["WARM_UP", "WORK", "RAMP", "RECOVERY", "REST", "COOL_DOWN"],
|
||
"description": "Step category. RAMP = progressive intensity change (requires targetValuePctStart + targetValuePctEnd)."
|
||
},
|
||
"durationSeconds": {
|
||
"type": "integer",
|
||
"minimum": 1,
|
||
"description": "Duration of this step in seconds"
|
||
},
|
||
"description": {
|
||
"type": "string",
|
||
"description": "Optional coaching cue for this step, e.g. 'Keep cadence above 90 rpm'"
|
||
},
|
||
"intensityReference": {
|
||
"type": "string",
|
||
"enum": ["FTP", "LTHR", "THRESHOLD_PACE", "MAX_HR"],
|
||
"description": "Reference metric for target intensity. FTP = cycling power, LTHR = lactate-threshold HR, THRESHOLD_PACE = run threshold pace, MAX_HR = maximum HR."
|
||
},
|
||
"targetValuePct": {
|
||
"type": "integer",
|
||
"minimum": 0,
|
||
"maximum": 200,
|
||
"description": "Target intensity as % of intensityReference. Use for all stepTypes except RAMP."
|
||
},
|
||
"targetValuePctStart": {
|
||
"type": "integer",
|
||
"minimum": 0,
|
||
"maximum": 200,
|
||
"description": "Ramp start intensity (% of intensityReference). RAMP steps only."
|
||
},
|
||
"targetValuePctEnd": {
|
||
"type": "integer",
|
||
"minimum": 0,
|
||
"maximum": 200,
|
||
"description": "Ramp end intensity (% of intensityReference). RAMP steps only."
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|