package main
import (
"fmt"
"os"
"strconv"
"time"
"git.sr.ht/~hokiegeek/fitstrava"
)
// https://developers.strava.com/docs/reference/#api-models-ActivityType
var fitbitActivityTypeToStrava = map[string]string{
"Yoga": "Yoga",
"Bike": "Ride",
"Elliptical": "Elliptical",
"Hike": "Hike",
"Rowing Machine": "Rowing",
"Walk": "Walk",
"Weights": "WeightTraining",
"Workout": "Workout",
"Sport": "Workout",
"Jumping rope": "Workout",
}
func stravaActivityTypeFromFitbit(fitbitName string) string {
return ""
}
func main() {
fitbitClientID := os.Getenv("FITBIT_CLIENT_ID")
fitbitClientSecret := os.Getenv("FITBIT_CLIENT_SECRET")
stravaClientID := os.Getenv("STRAVA_CLIENT_ID")
stravaClientSecret := os.Getenv("STRAVA_CLIENT_SECRET")
fmt.Println("- Authorizing clients (check browser)")
fitbit, err := fitstrava.NewFitbitClient(fitbitClientID, fitbitClientSecret)
if err != nil {
panic(err)
}
strava, err := fitstrava.NewStravaClient(stravaClientID, stravaClientSecret)
if err != nil {
panic(err)
}
// fmt.Printf("fitbit token: %v\n", fitbit.Token.AccessToken)
// fmt.Printf("strava token: %v\n", strava.Token.AccessToken)
fmt.Println("- Retrieving activities")
after, err := time.Parse("2006-Jan-02", "2021-Dec-31")
if err != nil {
panic(err)
}
fitbitActivities, err := fitstrava.FitbitGetActivities(fitbit, after)
if err != nil {
panic(err)
}
fmt.Printf("... found %d Fitbit activities in 2022\n", len(fitbitActivities))
stravaActivities, err := fitstrava.StravaGetActivities(strava, after)
if err != nil {
panic(err)
}
fmt.Printf("... found %d Strava activities in 2022\n", len(stravaActivities))
alreadyInStrava := make(map[string]bool)
for _, a := range stravaActivities {
/*
start, err := time.Parse(time.RFC3339, a.StartDate)
if err != nil {
fmt.Printf("error understanding StartDate for Strava activity %d\n", a.ID)
}
key := fmt.Sprintf("%s_%s", start.Format(time.RFC3339Nano), a.Type)
if a.ElapsedTime == 1 {
fmt.Printf("found in strava: %s,%s,%s,%s\n", key, a.StartDate, a.Name, a.ExternalID)
}
*/
// map strava external ids to identify those already uploaded by Fitbit
if a.ExternalID != "" {
alreadyInStrava[a.ExternalID[:len(a.ExternalID)-4]] = true
// } else {
// alreadyInStrava[fmt.Sprintf("%s_%s", start.Format(time.RFC3339Nano), a.Type)] = true
// alreadyInStrava[key] = true
}
// pull in ids from the description from those this app synched
// TODO: read description
// if a.Description != "" {
// fmt.Println("woo!", a.Description)
// }
}
synchedToStrava := func(fa fitstrava.FitbitActivity) (bool, fitstrava.StravaActivity) {
fbStart, err := time.Parse(time.RFC3339, fa.StartTime)
if err != nil {
fmt.Printf("error understanding StartDate for Fitbit activity %d\n", fa.LogID)
return false, fitstrava.StravaActivity{}
}
for _, sa := range stravaActivities {
if fitbitActivityTypeToStrava[fa.ActivityTypeName(fitbit)] != sa.Type {
continue
}
svStart, err := time.Parse(time.RFC3339, sa.StartDate)
if err != nil {
fmt.Printf("error understanding StartDate for Strava activity %d\n", sa.ID)
return false, fitstrava.StravaActivity{}
}
if !svStart.Equal(fbStart) {
continue
}
return true, sa
}
return false, fitstrava.StravaActivity{}
}
unsynched := make([]fitstrava.FitbitActivity, 0)
for _, fa := range fitbitActivities {
if _, inStrava := alreadyInStrava[strconv.FormatInt(fa.LogID, 10)]; inStrava {
continue
}
if synched, _ := synchedToStrava(fa); synched {
// fmt.Printf("already synched:\n\thttps://www.fitbit.com/activities/exercise/%d\n\thttps://www.strava.com/activities/%d\n", fa.LogID, sa.ID)
continue
}
/*
specialstart, err := time.Parse("2006-01-02T15:04:05.999-07:00", "2021-12-31T05:39:28.000-05:00")
if err != nil {
panic(err)
}
key := fmt.Sprintf("%s_%s", start.Format(time.RFC3339Nano), fitbitActivityTypeToStrava[a.ActivityTypeName(fitbit)])
if start.Equal(specialstart) {
fmt.Printf("found in fitbit: %s,%s,%s\n", key, a.StartTime, a.ActivityName)
}
if _, inStrava := alreadyInStrava[key]; inStrava {
// if _, inStrava := alreadyInStrava[fmt.Sprintf("%s_%s", start.Format(time.RFC3339Nano), fitbitActivityTypeToStrava[a.ActivityTypeName(fitbit)])]; inStrava {
continue
}
*/
unsynched = append(unsynched, fa)
}
fmt.Printf("... %d Fitbit activities not in Strava\n", len(unsynched))
/*
if starttime, err := time.Parse("2006-01-02T15:04:05.999-07:00", "2021-12-31T05:39:28.000-05:00"); err == nil {
if err := fitstrava.StravaCreateActivity(strava, fitstrava.StravaNewActivity{
Name: "Elliptical",
Type: "Elliptical",
// Description: fmt.Sprintf(`{"fitbit_id": %d}`, a.LogID),
StartDate: starttime,
ElapsedSeconds: 1,
}); err != nil {
panic(err)
}
}
*/
// os.Exit(42)
type syncError struct {
err error
activity fitstrava.FitbitActivity
}
fmt.Println("- Synching")
errors := make([]syncError, 0)
for _, a := range unsynched {
fmt.Printf("... %d,%s,%s\n", a.LogID, a.ActivityName, a.StartTime)
if starttime, err := time.Parse("2006-01-02T15:04:05.999-07:00", a.StartTime); err == nil {
/*
fmt.Printf(" %+v\n",
fitstrava.StravaNewActivity{
Name: a.ActivityName,
Type: fitbitActivityTypeToStrava[a.ActivityTypeName(fitbit)],
StartDate: starttime,
ElapsedSeconds: a.Duration / 1000,
})
*/
if err := fitstrava.StravaCreateActivity(strava, fitstrava.StravaNewActivity{
Name: a.ActivityName,
Type: fitbitActivityTypeToStrava[a.ActivityTypeName(fitbit)],
StartDate: starttime,
ElapsedSeconds: a.Duration / 1000,
}); err != nil {
errors = append(errors, syncError{err: err, activity: a})
}
/*
*/
} else {
errors = append(errors, syncError{err: fmt.Errorf("could not parse StartTime: %v", err), activity: a})
}
}
if len(errors) > 0 {
fmt.Println("- Errors")
for _, e := range errors {
fmt.Printf(" %s %s: %s", e.activity.ActivityName, e.activity.StartTime, e.err)
}
}
/*
if err := fitstrava.StravaCreateActivity(strava, fitstrava.StravaNewActivity{
Name: "testing (42)",
Type: "Workout",
Description: fmt.Sprintf(`{"fitbit_id": %d}`, 42),
StartDate: time.Now(),
ElapsedSeconds: 1,
}); err != nil {
errors = append(errors, syncError{err: err, activity: fitstrava.FitbitActivity{}})
}
*/
}