aboutsummaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2026-08-27 19:07:33 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2026-08-27 19:07:33 +0200
commit4a61f3eb3103aa512bef1b0d82295403bc4bc977 (patch)
tree73eb78c50518f129d5e6aaa86153a1b4c4637bf5 /web
parentd7b93325d4f70eeab369fa8a3c4cce9f0c4e0081 (diff)
Swap ride read path from GPX to FIT for performance
Always use FIT for detail, official climb creation and backtest; keep GPX fallback for legacy rides pending backfill. FIT is 10-20x smaller and parses without XML.
Diffstat (limited to 'web')
-rw-r--r--web/rides.go33
-rw-r--r--web/server_test.go11
2 files changed, 37 insertions, 7 deletions
diff --git a/web/rides.go b/web/rides.go
index d3f5634..f59c0a4 100644
--- a/web/rides.go
+++ b/web/rides.go
@@ -51,14 +51,25 @@ func (s *Server) handleRide(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
return
}
- if item.GPXPath == "" {
+ if item.FitPath == "" && item.GPXPath == "" {
slog.Info("Loaded ride detail without route", "ride_id", id)
kcore.RenderPage(r.Context(), RideDetailPage(RideDetailView{RideView: buildRideView(item), Notice: rideDetailNotice(r)}), w)
return
}
- parsed, err := ride.ParseFile(ride.GPXRideParser{}, item.GPXPath)
+ // Prefer FIT; fallback to GPX for legacy rides pending backfill.
+ var parsed ride.Ride
+ if item.FitPath != "" {
+ parsed, err = ride.ParseFile(ride.FITRideParser{}, item.FitPath)
+ if err != nil {
+ slog.Error("Failed to load ride route", "ride_id", id, "file", item.FitPath, "error", err)
+ }
+ } else {
+ parsed, err = ride.ParseFile(ride.GPXRideParser{}, item.GPXPath)
+ if err != nil {
+ slog.Error("Failed to load ride route", "ride_id", id, "file", item.GPXPath, "error", err)
+ }
+ }
if err != nil {
- slog.Error("Failed to load ride route", "ride_id", id, "file", item.GPXPath, "error", err)
kcore.RenderPage(r.Context(), RideDetailPage(RideDetailView{
RideView: buildRideView(item),
RouteError: "The recorded route is unavailable.",
@@ -121,7 +132,7 @@ func (s *Server) handleCreateOfficialClimb(w http.ResponseWriter, r *http.Reques
http.NotFound(w, r)
return
}
- if item.GPXPath == "" {
+ if item.FitPath == "" && item.GPXPath == "" {
http.Error(w, "a recorded route is required to create an official climb", http.StatusBadRequest)
return
}
@@ -139,9 +150,19 @@ func (s *Server) handleCreateOfficialClimb(w http.ResponseWriter, r *http.Reques
http.Error(w, "a valid end route point is required", http.StatusBadRequest)
return
}
- parsed, err := ride.ParseFile(ride.GPXRideParser{}, item.GPXPath)
+ var parsed ride.Ride
+ if item.FitPath != "" {
+ parsed, err = ride.ParseFile(ride.FITRideParser{}, item.FitPath)
+ if err != nil {
+ slog.Error("Failed to load ride route for official climb creation", "ride_id", id, "file", item.FitPath, "error", err)
+ }
+ } else {
+ parsed, err = ride.ParseFile(ride.GPXRideParser{}, item.GPXPath)
+ if err != nil {
+ slog.Error("Failed to load ride route for official climb creation", "ride_id", id, "file", item.GPXPath, "error", err)
+ }
+ }
if err != nil {
- slog.Error("Failed to load ride route for official climb creation", "ride_id", id, "file", item.GPXPath, "error", err)
http.Error(w, "the recorded route is unavailable", http.StatusBadRequest)
return
}
diff --git a/web/server_test.go b/web/server_test.go
index e9444c7..d75628d 100644
--- a/web/server_test.go
+++ b/web/server_test.go
@@ -66,9 +66,18 @@ func (c fakeSyncClient) Get(id int64) (strava.Activity, []byte, []byte, error) {
if !ok {
return strava.Activity{}, nil, nil, fmt.Errorf("missing fake activity %d", id)
}
+ if result.err != nil {
+ return result.activity, nil, nil, result.err
+ }
fitData := result.fitData
if fitData == nil && result.data != nil {
- fitData = result.data
+ parsed, err := (ride.GPXRideParser{}).Parse(bytes.NewReader(result.data))
+ if err == nil {
+ var buf bytes.Buffer
+ if err := ride.EncodeFIT(parsed, &buf); err == nil {
+ fitData = buf.Bytes()
+ }
+ }
}
return result.activity, result.data, fitData, result.err
}