@ -1,18 +1,18 @@
package com.github.mgifos.workouts
import java.nio.file. { Files , Paths }
import java.nio.file. { Files , Paths }
import java.time.LocalDate
import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.stream.ActorMaterializer
import com.github.mgifos.workouts.model. { WeeklyPlan , _ }
import com.github.mgifos.workouts.model. { WeeklyPlan , _ }
import com.typesafe.scalalogging.Logger
import scopt.OptionParser
import scala.collection.immutable.Seq
import scala.concurrent.duration._
import scala.concurrent. { Await , ExecutionContextExecutor , Future }
import scala.concurrent. { Await , ExecutionContextExecutor , Future }
object Modes extends Enumeration {
type Mode = Value
@ -20,8 +20,7 @@ object Modes extends Enumeration {
val schedule : Mode = Value ( "schedule" )
}
case class Config (
mode : Option [ Modes . Mode ] = None ,
case class Config ( mode : Option [ Modes . Mode ] = None ,
system : MeasurementSystems . MeasurementSystem = MeasurementSystems . metric ,
csv : String = "" ,
delete : Boolean = false ,
@ -59,10 +58,13 @@ object Main extends App {
opt [ String ] ( 'p' , "password" ) . action ( ( x , c ) => c . copy ( password = x ) ) . text ( "Password to login to Garmin Connect" )
opt [ MeasurementSystems . MeasurementSystem ] ( 'm' , "measurement_system" ) . action ( ( x , c ) => c . copy ( system = x ) ) . text ( """"metric" (default) or "imperial" (miles, inches, ...) measurement system choice.""" )
opt [ MeasurementSystems . MeasurementSystem ] ( 'm' , "measurement_system" )
. action ( ( x , c ) => c . copy ( system = x ) )
. text ( """"metric" (default) or "imperial" (miles, inches, ...) measurement system choice.""" )
opt [ Unit ] ( 'x' , "delete" ) . action ( ( _ , c ) => c . copy ( delete = true ) ) . text (
"Delete all existing workouts with same names as the ones contained within the file. In case of import/schedule commands, " +
opt [ Unit ] ( 'x' , "delete" )
. action ( ( _ , c ) => c . copy ( delete = true ) )
. text ( "Delete all existing workouts with same names as the ones contained within the file. In case of import/schedule commands, " +
"this will be done before the actual action." )
help ( "help" ) . text ( "prints this usage text" )
@ -73,13 +75,15 @@ object Main extends App {
note ( "\n" )
cmd ( "import" ) .
action ( ( _ , c ) => c . copy ( mode = Some ( Modes . `import` ) ) ) . text (
"Imports all workout definitions from CSV file. If it's omitted, it is will be on by default." )
cmd ( "import" )
. action ( ( _ , c ) => c . copy ( mode = Some ( Modes . `import` ) ) )
. text ( "Imports all workout definitions from CSV file. If it's omitted, it is will be on by default." )
note ( "" )
cmd ( "schedule" ) . action ( ( _ , c ) => c . copy ( mode = Some ( Modes . schedule ) ) ) . text (
cmd ( "schedule" )
. action ( ( _ , c ) => c . copy ( mode = Some ( Modes . schedule ) ) )
. text (
"Schedules your weekly plan defined in CSV in Garmin Connect calendar, starting from the first day of first week or" +
" ending on the last day of the last week. Either start or end date must be entered so the scheduling can be done" +
" properly. In case both are entered, start date has priority. All dates have to be entered in ISO date format" +
@ -87,12 +91,15 @@ object Main extends App {
. children (
opt [ String ] ( 's' , "start" ) . action ( ( x , c ) => c . copy ( start = LocalDate . parse ( x ) ) ) . text ( "Date of the first day of the first week of the plan" ) ,
opt [ String ] ( 'n' , "end" ) . action ( ( x , c ) => c . copy ( end = LocalDate . parse ( x ) ) ) . text ( "Date of the last day of the last week of the plan\n" ) ,
checkConfig ( c =>
checkConfig (
c =>
if ( c . mode . contains ( Modes . schedule ) && c . start . isEqual ( LocalDate . MIN ) && c . end . isEqual ( LocalDate . MIN ) )
failure ( "Either start or end date must be entered!" )
else success ) )
else success )
)
note ( "EXAMPLES" ) . text ( "EXAMPLES\n\nSchedules ultra 80k plan targeting 28-4-2018 for a race day (also deletes existing workouts with the same names)" +
note ( "EXAMPLES" ) . text (
"EXAMPLES\n\nSchedules ultra 80k plan targeting 28-4-2018 for a race day (also deletes existing workouts with the same names)" +
"\n\nquick-plan schedule -n 2018-04-29 -x -e your-mail-address@example.com ultra-80k-runnersworld.csv" )
note ( "" )
@ -107,11 +114,15 @@ object Main extends App {
val console = System . console ( )
def proceedToGarmin ( ) = {
val email = if ( config . email . nonEmpty ) config . email else {
val email =
if ( config . email . nonEmpty ) config . email
else {
print ( "Please enter your email address to login to Garmin Connect: " )
console . readLine ( )
}
val password = if ( config . password . nonEmpty ) config . password else {
val password =
if ( config . password . nonEmpty ) config . password
else {
print ( "Password: " )
new String ( console . readPassword ( ) )
}
@ -154,21 +165,26 @@ object Main extends App {
/* *
* Deletes existing workouts with the same names or not
*/
private def deleteWorkoutsTask ( workouts : Seq [ String ] ) ( implicit config : Config , garmin : GarminConnect , session : GarminSession ) : Future [ Option [ String ] ] = {
private def deleteWorkoutsTask (
workouts : Seq [ String ] ) ( implicit config : Config , garmin : GarminConnect , session : GarminSession ) : Future [ Option [ String ] ] = {
if ( config . delete )
garmin . deleteWorkouts ( workouts ) . map ( c => Some ( s" $c deleted " ) )
else
Future . successful ( None )
}
private def createWorkoutsTask ( workouts : Seq [ WorkoutDef ] ) ( implicit config : Config , garmin : GarminConnect , session : GarminSession ) : Future [ Option [ Seq [ GarminWorkout ] ] ] = {
private def createWorkoutsTask (
workouts : Seq [ WorkoutDef ] ) ( implicit config : Config , garmin : GarminConnect , session : GarminSession ) : Future [ Option [ Seq [ GarminWorkout ] ] ] = {
if ( config . mode . exists ( Seq ( Modes . `import` , Modes . schedule ) . contains ) )
garmin . createWorkouts ( workouts ) . map ( Option . apply )
else
Future . successful ( None )
}
private def scheduleTask ( workouts : Seq [ GarminWorkout ] ) ( implicit config : Config , garmin : GarminConnect , plan : WeeklyPlan , session : GarminSession ) : Future [ Option [ String ] ] = {
private def scheduleTask ( workouts : Seq [ GarminWorkout ] ) ( implicit config : Config ,
garmin : GarminConnect ,
plan : WeeklyPlan ,
session : GarminSession ) : Future [ Option [ String ] ] = {
if ( config . mode . contains ( Modes . schedule ) ) {
@ -178,9 +194,13 @@ object Main extends App {
}
val woMap : Map [ String , GarminWorkout ] = Map ( workouts . map ( ga => ga . name -> ga ) : _ * )
val spec = plan . get ( ) . zipWithIndex . collect {
val spec = plan
. get ( )
. zipWithIndex
. collect {
case ( Some ( ref ) , day ) if ! start . plusDays ( day ) . isBefore ( LocalDate . now ( ) ) => start . plusDays ( day ) -> woMap ( ref . name )
} . to [ Seq ]
}
. to [ Seq ]
garmin . schedule ( spec ) . map ( c => Some ( s" $c scheduled " ) )
} else
Future . successful ( None )