Detect workout definition type automatically #42

master
mgifos 7 years ago
parent c8485b44a0
commit 22837ebc36
  1. 13
      README.md
  2. 11
      src/main/scala/com.github.mgifos.workouts/model/Workout.scala
  3. 7
      src/test/scala/com/github/mgifos/workouts/model/WorkoutSpec.scala

@ -71,21 +71,19 @@ quick-plan schedule -n 2018-04-29 -x -e your-mail-address@example.com ultra-80k-
## Workout notation
The reserved keywords of the notation are: workout, warmup, cooldown, run, bike, go, repeat, recover and lap-button.
**`<workout>`** := `<header><step>+`
**`<workout>`** := `<header>(<newline><step>)+`
**`<header>`** := `<sport>: <name>`
**`<sport>`** := (running | cycling | custom)
**`<header>`** := `[running | cycling | custom]: <name>`
**`<name>`** := `[\u0020-\u007F]+` (printable ascii characters)
**`<step>`** := `<newline>- <step-def>`
**`<step>`** := `<indent>- <step-def>`
**`<step-def>`** := `<simple-step> | <repetition-step>`
**`<simple-step>`** := `(warmup | cooldown | run | bike | go | recover): <duration> [@ <target>]`
**`<repetition-step>`** := `repeat: <count>(<newline> - <simple-step>)+`
**`<repetition-step>`** := `repeat: <count>(<newline><step>)+` (with each r. step, depth is increased by 1 - check `<indent>`)
**`<duration>`** := `<distance-duration> | <time-duration> | lap-button`
@ -117,6 +115,9 @@ The reserved keywords of the notation are: workout, warmup, cooldown, run, bike,
**`<newline>`** := `[\r\n]`
**`<indent>`** := `\s{depth * 2}` (depends on depth parameter related to the repetion step / starts from 0)
## Unit of measurements (metric vs imperial)
As Garmin supports metric and imperial measurement systems, quick-plan can do this as well. There are two ways of usage:

@ -41,9 +41,9 @@ case class WorkoutNote(note: String) extends Workout
object Workout {
private val WorkoutType = "(running|cycling|custom)"
private val WorkoutHeader = raw"""^$WorkoutType:\s([\u0020-\u007F]+)(([\r\n]+\s*\-\s[a-z]+:.*)*)$$""".r
private val WorkoutHeader = raw"""^$WorkoutType?:\s*([\u0020-\u007F]+)(([\r\n]+\s*\-\s[a-z]+:.*)*)$$""".r
private val NextStepRx = """^((-\s\w*:\s.*)(([\r\n]+\s{1,}-\s.*)*))(([\s].*)*)$""".r
private val PossibleWorkoutHeader = raw"""^\s*$WorkoutType\s*:\s*.*(([\r\n]+\s*.*)*)$$""".r
private val PossibleWorkoutHeader = raw"""^\s*$WorkoutType?\s*:\s*.*(([\r\n]+\s*.*)*)$$""".r
def parse(text: String)(implicit msys: MeasurementSystems.MeasurementSystem): Workout = {
def loop(w: WorkoutDef, steps: String): Workout = steps match {
@ -58,12 +58,19 @@ object Workout {
case _ => WorkoutStepFailure(text, steps.trim)
}
text match {
case WorkoutHeader(null, name, steps, _) => loop(WorkoutDef(detectSport(steps), name), steps.trim)
case WorkoutHeader(sport, name, steps, _) => loop(WorkoutDef(sport, name), steps.trim)
case PossibleWorkoutHeader(t, _, cause) => WorkoutDefFailure(`type` = t, text, if (cause == null) "" else cause.trim)
case _ => WorkoutNote(text)
}
}
def detectSport(steps: String): String = steps match {
case x if x.contains("- run") => "running"
case x if x.contains("- bike") => "cycling"
case _ => "custom"
}
def sportId(sport: String) = sport match {
case "running" => 1
case "cycling" => 2

@ -18,7 +18,7 @@ class WorkoutSpec extends WordSpec with Matchers {
*/
val testWO = "running: run-fast\n- warmup: 10:00\n- repeat: 2\n - run: 1500m @ 4:30-5:00\n - recover: 01:30 @ z2\n- cooldown: lap-button"
"Workout parser should" should {
"Workout parser" should {
"parse notes correctly" in {
Workout.parse("") shouldBe a[WorkoutNote]
@ -111,6 +111,11 @@ class WorkoutSpec extends WordSpec with Matchers {
)
)
}
"be able to detect workout type automatically" in {
val test = Seq("run", "bike", "go").map(step => s": detect\n- $step: 2km")
test.map(Workout.parse(_).asInstanceOf[WorkoutDef].sport) shouldBe Seq("running", "cycling", "custom")
}
}
"Workout should" should {

Loading…
Cancel
Save