From c9149b0a68a467aafbe0c5506e783fb97df04f01 Mon Sep 17 00:00:00 2001 From: Barrett Langton Date: Thu, 1 Jan 2026 07:25:11 -0700 Subject: [PATCH 1/2] Convert RPE workouts to zone based --- .../workout/structure/WorkoutStructure.kt | 1 + .../workout/IntervalsWorkoutDocDTO.kt | 1 + .../workout/ToIntervalsStructureConverter.kt | 46 +++++++++++++++++-- .../workout/structure/TPTargetMapper.kt | 1 + 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/boot/src/main/kotlin/org/freekode/tp2intervals/domain/workout/structure/WorkoutStructure.kt b/boot/src/main/kotlin/org/freekode/tp2intervals/domain/workout/structure/WorkoutStructure.kt index c443f183..162dda29 100644 --- a/boot/src/main/kotlin/org/freekode/tp2intervals/domain/workout/structure/WorkoutStructure.kt +++ b/boot/src/main/kotlin/org/freekode/tp2intervals/domain/workout/structure/WorkoutStructure.kt @@ -19,5 +19,6 @@ data class WorkoutStructure( FTP_PERCENTAGE, LTHR_PERCENTAGE, PACE_PERCENTAGE, + RELATIVE_PERCEIVED_EFFORT, } } diff --git a/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/IntervalsWorkoutDocDTO.kt b/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/IntervalsWorkoutDocDTO.kt index 19f265af..5a1de149 100644 --- a/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/IntervalsWorkoutDocDTO.kt +++ b/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/IntervalsWorkoutDocDTO.kt @@ -17,6 +17,7 @@ class IntervalsWorkoutDocDTO( "POWER" to WorkoutStructure.TargetUnit.FTP_PERCENTAGE, "HR" to WorkoutStructure.TargetUnit.LTHR_PERCENTAGE, "PACE" to WorkoutStructure.TargetUnit.PACE_PERCENTAGE, + "RPE" to WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT, ) fun mapTarget(): WorkoutStructure.TargetUnit { diff --git a/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/ToIntervalsStructureConverter.kt b/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/ToIntervalsStructureConverter.kt index 56d65033..4249a7d6 100644 --- a/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/ToIntervalsStructureConverter.kt +++ b/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/ToIntervalsStructureConverter.kt @@ -11,6 +11,7 @@ class ToIntervalsStructureConverter( WorkoutStructure.TargetUnit.FTP_PERCENTAGE to "%", WorkoutStructure.TargetUnit.LTHR_PERCENTAGE to "% LTHR", WorkoutStructure.TargetUnit.PACE_PERCENTAGE to "% Pace", + WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT to " HR", ) fun toIntervalsStructureStr(): String { @@ -34,11 +35,7 @@ class ToIntervalsStructureConverter( val name = workoutStep.name.orEmpty().replace("\\", "/") val length = toStepLength(workoutStep.length) val targetUnitStr = targetTypeMap[structure.target]!! - val target: String = if (workoutStep.target.isSingleValue()) { - "${workoutStep.target.start}" - } else { - "${workoutStep.target.start}-${workoutStep.target.end}" - } + val target = toTarget(structure.target, workoutStep.target) val cadence = workoutStep.cadence?.let { if (it.isSingleValue()) { "${it.start}rpm" @@ -54,4 +51,43 @@ class ToIntervalsStructureConverter( LengthUnit.SECONDS -> Duration.ofSeconds(length.value).toString().substring(2).lowercase() LengthUnit.METERS -> (length.value / 1000.0).toString() + "km" } + + private fun toTarget(targetUnit: WorkoutStructure.TargetUnit, target: StepTarget) : String { + val targetVal = + if (target.isSingleValue()) { + if (targetUnit == WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT) { + rpeToTarget(target.start) + } else { + "${target.start}" + } + } else { + if (targetUnit == WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT) { + if (rpeToTarget(target.start) == rpeToTarget(target.end)) { + rpeToTarget(target.start) + } else { + "${rpeToTarget(target.start)}-${rpeToTarget(target.end)}" + } + } else { + "${target.start}-${target.end}" + } + } + + return targetVal + } + + private fun rpeToTarget(value: Int) : String { + if (value <= 3) { + return "Z1" + } else if (value <= 6) { + return "Z2" + } else if (value <= 8) { + return "Z3" + } else if (value == 9) { + return "Z4" + } else if (value == 10) { + return "Z5" + } + + return "Z2" + } } diff --git a/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/trainingpeaks/workout/structure/TPTargetMapper.kt b/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/trainingpeaks/workout/structure/TPTargetMapper.kt index b945912e..08f8c1fc 100644 --- a/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/trainingpeaks/workout/structure/TPTargetMapper.kt +++ b/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/trainingpeaks/workout/structure/TPTargetMapper.kt @@ -8,6 +8,7 @@ class TPTargetMapper { WorkoutStructure.TargetUnit.FTP_PERCENTAGE to "percentOfFtp", WorkoutStructure.TargetUnit.LTHR_PERCENTAGE to "percentOfThresholdHr", WorkoutStructure.TargetUnit.PACE_PERCENTAGE to "percentOfThresholdPace", + WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT to "rpe", ) fun getByIntensity(intensity: String): WorkoutStructure.TargetUnit = From 371c83808492caec92e3e210b4236545ff610138 Mon Sep 17 00:00:00 2001 From: Barrett Langton Date: Thu, 1 Jan 2026 14:05:15 -0700 Subject: [PATCH 2/2] Change rpe to show as description so it'll be text on workout steps on my watch --- .../workout/ToIntervalsStructureConverter.kt | 44 +++++++------------ 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/ToIntervalsStructureConverter.kt b/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/ToIntervalsStructureConverter.kt index 4249a7d6..01ab5a65 100644 --- a/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/ToIntervalsStructureConverter.kt +++ b/boot/src/main/kotlin/org/freekode/tp2intervals/infrastructure/platform/intervalsicu/workout/ToIntervalsStructureConverter.kt @@ -11,7 +11,7 @@ class ToIntervalsStructureConverter( WorkoutStructure.TargetUnit.FTP_PERCENTAGE to "%", WorkoutStructure.TargetUnit.LTHR_PERCENTAGE to "% LTHR", WorkoutStructure.TargetUnit.PACE_PERCENTAGE to "% Pace", - WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT to " HR", + WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT to "", ) fun toIntervalsStructureStr(): String { @@ -32,7 +32,7 @@ class ToIntervalsStructureConverter( } private fun getStepString(workoutStep: SingleStep): String { - val name = workoutStep.name.orEmpty().replace("\\", "/") + val description = getDescription(structure.target, workoutStep) val length = toStepLength(workoutStep.length) val targetUnitStr = targetTypeMap[structure.target]!! val target = toTarget(structure.target, workoutStep.target) @@ -44,7 +44,7 @@ class ToIntervalsStructureConverter( } } ?: "" - return "- $name $length $target$targetUnitStr ${structure.modifier.value} $cadence" + return "- $description $length $target$targetUnitStr ${structure.modifier.value} $cadence" } private fun toStepLength(length: StepLength) = when (length.unit) { @@ -54,19 +54,11 @@ class ToIntervalsStructureConverter( private fun toTarget(targetUnit: WorkoutStructure.TargetUnit, target: StepTarget) : String { val targetVal = - if (target.isSingleValue()) { - if (targetUnit == WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT) { - rpeToTarget(target.start) - } else { - "${target.start}" - } + if (targetUnit == WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT) { + "" } else { - if (targetUnit == WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT) { - if (rpeToTarget(target.start) == rpeToTarget(target.end)) { - rpeToTarget(target.start) - } else { - "${rpeToTarget(target.start)}-${rpeToTarget(target.end)}" - } + if (target.isSingleValue()) { + "${target.start}" } else { "${target.start}-${target.end}" } @@ -75,19 +67,17 @@ class ToIntervalsStructureConverter( return targetVal } - private fun rpeToTarget(value: Int) : String { - if (value <= 3) { - return "Z1" - } else if (value <= 6) { - return "Z2" - } else if (value <= 8) { - return "Z3" - } else if (value == 9) { - return "Z4" - } else if (value == 10) { - return "Z5" + private fun getDescription(targetUnit: WorkoutStructure.TargetUnit, workoutStep: SingleStep) : String { + var description = workoutStep.name.orEmpty().replace("\\", "/") + + if (targetUnit == WorkoutStructure.TargetUnit.RELATIVE_PERCEIVED_EFFORT) { + if (workoutStep.target.isSingleValue()) { + description += " RPE ${workoutStep.target.start}" + } else { + description += " RPE ${workoutStep.target.start}-${workoutStep.target.end}" + } } - return "Z2" + return description.trim() } }