From 91b901ba607ec7d3675f864a9d92b0489f938ca0 Mon Sep 17 00:00:00 2001 From: Christophe Vila Date: Fri, 17 Jul 2026 19:29:02 +0200 Subject: [PATCH] fix: update seedsample for the fixed workout taxonomy and profile-based max HR Update workout kinds by fixed name (via ListWorkoutKinds/UpdateWorkoutKind) instead of creating new ones, since the taxonomy migration now seeds Easy Run/Tempo/Interval by unique name. Set profile.MaxHeartRate so avg_hr_pct_max is computed during classification, and drop the removed appsync.Config.MaxHR field. --- backend/cmd/seedsample/main.go | 56 +++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/backend/cmd/seedsample/main.go b/backend/cmd/seedsample/main.go index dbfd9f7..23b5444 100644 --- a/backend/cmd/seedsample/main.go +++ b/backend/cmd/seedsample/main.go @@ -29,39 +29,49 @@ func main() { } defer db.Close() - // Easy and Tempo's pace/HR ranges deliberately overlap a little (330-340 - // sec/km, 0.70-0.85 HR%max) so a run that lands in that overlap zone - // demonstrates the ambiguous-multi-match review path, not just a gap - // between disjoint ranges. - easyID, err := db.CreateWorkoutKind(ctx, store.WorkoutKind{ - Name: "Easy", Description: "Easy conversational runs", Color: "#22c55e", + // Set a max heart rate on the profile so avg_hr_pct_max is computed + // during classification below (it's nil/unset by default). + profile, err := db.GetProfile(ctx) + must(err) + maxHR := 190.0 + profile.MaxHeartRate = &maxHR + must(db.UpdateProfile(ctx, profile)) + + // Easy Run and Tempo's pace/HR ranges deliberately overlap a little + // (330-340 sec/km, 0.70-0.85 HR%max) so a run that lands in that overlap + // zone demonstrates the ambiguous-multi-match review path, not just a + // gap between disjoint ranges. The taxonomy migration already seeded + // these rows (by fixed name) -- update their rules in place rather than + // creating new ones, since names are unique. + easyID := mustFindKindID(ctx, db, "Easy Run") + must(db.UpdateWorkoutKind(ctx, store.WorkoutKind{ + ID: easyID, Name: "Easy Run", Description: "Easy conversational runs", Color: "#22c55e", RuleJSON: `{"match":"all","conditions":[ {"metric":"avg_pace_sec_per_km","op":"between","value":[330,420]}, {"metric":"avg_hr_pct_max","op":"<=","value":0.85} ]}`, IsActive: true, - }) - must(err) + })) - tempoID, err := db.CreateWorkoutKind(ctx, store.WorkoutKind{ - Name: "Tempo", Description: "Comfortably hard sustained effort", Color: "#f59e0b", + tempoID := mustFindKindID(ctx, db, "Tempo") + must(db.UpdateWorkoutKind(ctx, store.WorkoutKind{ + ID: tempoID, Name: "Tempo", Description: "Comfortably hard sustained effort", Color: "#f59e0b", RuleJSON: `{"match":"all","conditions":[ {"metric":"avg_pace_sec_per_km","op":"between","value":[300,340]}, {"metric":"avg_hr_pct_max","op":">=","value":0.70} ]}`, IsActive: true, - }) - must(err) + })) - _, err = db.CreateWorkoutKind(ctx, store.WorkoutKind{ - Name: "Interval", Description: "Structured work/rest intervals", Color: "#ef4444", + intervalID := mustFindKindID(ctx, db, "Interval") + must(db.UpdateWorkoutKind(ctx, store.WorkoutKind{ + ID: intervalID, Name: "Interval", Description: "Structured work/rest intervals", Color: "#ef4444", RuleJSON: `{"match":"all","conditions":[{"metric":"lap_interval_pattern","op":"==","value":true}]}`, IsActive: true, - }) - must(err) + })) m := &mock.Client{} - svc := appsync.NewService(m, db, appsync.Config{MinConfidence: 0.6, MaxHR: 190}, nil) + svc := appsync.NewService(m, db, appsync.Config{MinConfidence: 0.6}, nil) today := time.Now() activityIDs := []int64{} @@ -219,3 +229,15 @@ func must(err error) { log.Fatal(err) } } + +func mustFindKindID(ctx context.Context, db *store.DB, name string) int64 { + kinds, err := db.ListWorkoutKinds(ctx, false) + must(err) + for _, k := range kinds { + if k.Name == name { + return k.ID + } + } + log.Fatalf("seedsample: no workout kind named %q found (did migration 0004 run?)", name) + return 0 +}