aboutsummaryrefslogtreecommitdiff
path: root/ride/store.go
diff options
context:
space:
mode:
Diffstat (limited to 'ride/store.go')
-rw-r--r--ride/store.go49
1 files changed, 44 insertions, 5 deletions
diff --git a/ride/store.go b/ride/store.go
index b906b42..f6de535 100644
--- a/ride/store.go
+++ b/ride/store.go
@@ -4,6 +4,8 @@ import (
"database/sql"
"fmt"
"log/slog"
+ "os"
+ "path/filepath"
"strings"
"time"
@@ -15,6 +17,7 @@ type StoredRide struct {
ID int64
ExternalID string
GPXPath string
+ FitPath string
Name string
Type string
StartDate time.Time
@@ -46,7 +49,7 @@ const (
SortCotacolKm SortColumn = "cotacol_100km"
)
-const columns = "id, external_id, gpx_path, name, type, start_date, distance_m, external_moving_time_s, moving_time_s, moving_time_algo_version, elapsed_time_s, total_elevation_gain_m, average_speed_mps, cotacol_score, cotacol_algo_version, bike_type, ride_type, created_at, updated_at"
+const columns = "id, external_id, gpx_path, fit_path, name, type, start_date, distance_m, external_moving_time_s, moving_time_s, moving_time_algo_version, elapsed_time_s, total_elevation_gain_m, average_speed_mps, cotacol_score, cotacol_algo_version, bike_type, ride_type, created_at, updated_at"
func Save(db *sql.DB, r StoredRide) error {
var cotacolScore any
@@ -70,10 +73,11 @@ func Save(db *sql.DB, r StoredRide) error {
return fmt.Errorf("invalid ride_type %q", r.RideType)
}
_, err := db.Exec(`
- INSERT INTO rides (external_id, gpx_path, name, type, start_date, distance_m, external_moving_time_s, moving_time_s, moving_time_algo_version, elapsed_time_s, total_elevation_gain_m, average_speed_mps, cotacol_score, cotacol_algo_version, bike_type, ride_type)
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ INSERT INTO rides (external_id, gpx_path, fit_path, name, type, start_date, distance_m, external_moving_time_s, moving_time_s, moving_time_algo_version, elapsed_time_s, total_elevation_gain_m, average_speed_mps, cotacol_score, cotacol_algo_version, bike_type, ride_type)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(external_id) DO UPDATE SET
gpx_path = excluded.gpx_path,
+ fit_path = excluded.fit_path,
name = excluded.name,
type = excluded.type,
start_date = excluded.start_date,
@@ -89,7 +93,7 @@ func Save(db *sql.DB, r StoredRide) error {
bike_type = CASE WHEN excluded.bike_type = '' THEN rides.bike_type ELSE excluded.bike_type END,
ride_type = CASE WHEN excluded.ride_type = '' THEN rides.ride_type ELSE excluded.ride_type END,
updated_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
- `, r.ExternalID, r.GPXPath, r.Name, r.Type, r.StartDate.UTC().Format(time.RFC3339), r.DistanceM, r.ExternalMovingTimeS, movingTimeS, movingTimeAlgorithmVersion, r.ElapsedTimeS, r.TotalElevationGainM, r.AverageSpeedMps, cotacolScore, cotacolAlgorithmVersion, string(r.BikeType), string(r.RideType))
+ `, r.ExternalID, r.GPXPath, r.FitPath, r.Name, r.Type, r.StartDate.UTC().Format(time.RFC3339), r.DistanceM, r.ExternalMovingTimeS, movingTimeS, movingTimeAlgorithmVersion, r.ElapsedTimeS, r.TotalElevationGainM, r.AverageSpeedMps, cotacolScore, cotacolAlgorithmVersion, string(r.BikeType), string(r.RideType))
return err
}
@@ -124,6 +128,41 @@ func Backfill(db *sql.DB, reporters ...func(current, total int)) (int, error) {
}
for i, item := range pending {
report(i+1, len(pending))
+ if item.GPXPath != "" {
+ fitPath := item.FitPath
+ if fitPath == "" {
+ ext := filepath.Ext(item.GPXPath)
+ if ext == "" {
+ fitPath = item.GPXPath + ".fit"
+ } else {
+ fitPath = strings.TrimSuffix(item.GPXPath, ext) + ".fit"
+ }
+ }
+ if _, err := os.Stat(fitPath); os.IsNotExist(err) {
+ parsed, err := ParseFile(GPXRideParser{}, item.GPXPath)
+ if err != nil {
+ slog.Warn("Failed to backfill FIT", "ride", item.ExternalID, "file", item.GPXPath, "error", err)
+ } else {
+ f, err := os.Create(fitPath)
+ if err != nil {
+ slog.Warn("Failed to create FIT file", "ride", item.ExternalID, "file", fitPath, "error", err)
+ } else {
+ if err := EncodeFIT(parsed, f); err != nil {
+ _ = f.Close()
+ _ = os.Remove(fitPath)
+ slog.Warn("Failed to encode FIT", "ride", item.ExternalID, "file", fitPath, "error", err)
+ } else {
+ _ = f.Close()
+ item.FitPath = fitPath
+ }
+ }
+ }
+ } else if err == nil && item.FitPath == "" {
+ item.FitPath = fitPath
+ } else if err != nil && !os.IsNotExist(err) {
+ slog.Warn("Failed to stat FIT file", "ride", item.ExternalID, "file", fitPath, "error", err)
+ }
+ }
if err := Save(db, item); err != nil {
slog.Warn("Failed to backfill ride values", "ride", item.ExternalID, "file", item.GPXPath, "error", err)
continue
@@ -227,7 +266,7 @@ func scanRide(s scanner) (StoredRide, error) {
createdAt string
updatedAt string
)
- err := s.Scan(&ride.ID, &ride.ExternalID, &ride.GPXPath, &ride.Name, &ride.Type, &startDate, &ride.DistanceM, &ride.ExternalMovingTimeS, &movingTimeS, &movingTimeAlgoVer, &ride.ElapsedTimeS, &ride.TotalElevationGainM, &ride.AverageSpeedMps, &cotacolScore, &cotacolAlgoVersion, &bikeType, &rideType, &createdAt, &updatedAt)
+ err := s.Scan(&ride.ID, &ride.ExternalID, &ride.GPXPath, &ride.FitPath, &ride.Name, &ride.Type, &startDate, &ride.DistanceM, &ride.ExternalMovingTimeS, &movingTimeS, &movingTimeAlgoVer, &ride.ElapsedTimeS, &ride.TotalElevationGainM, &ride.AverageSpeedMps, &cotacolScore, &cotacolAlgoVersion, &bikeType, &rideType, &createdAt, &updatedAt)
if err != nil {
return StoredRide{}, err
}