Move backfill horizon from a startup env var into the editable profile
SMARTRUN_BACKFILL_HORIZON_DAYS was a server-startup-only env var with no UI, defaulting to 3 years -- so editing the unrelated "Rolling window" profile field (for classification, not sync) had no effect on how far back Sync Now reached. Backfill horizon is now Profile.BackfillHorizonDays, read fresh on every Backfill call, with its own field on the Profile page.
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
-- Backfill horizon used to be a startup-time env var
|
||||
-- (SMARTRUN_BACKFILL_HORIZON_DAYS) with no UI at all -- exactly the kind of
|
||||
-- "tunable analysis-engine parameter" this profile table exists for.
|
||||
-- Default matches the old env var's default (3 years) so existing
|
||||
-- deployments keep their current behavior until the user changes it.
|
||||
ALTER TABLE profile ADD COLUMN backfill_horizon_days INTEGER NOT NULL DEFAULT 1095;
|
||||
@@ -11,8 +11,14 @@ type Profile struct {
|
||||
GarminEmail string
|
||||
GarminPassword string
|
||||
RollingWindowDays int
|
||||
MaxHeartRate *float64
|
||||
RestingHeartRate *float64
|
||||
// BackfillHorizonDays bounds how far back "Sync now" reaches when
|
||||
// walking backward from today; it's read fresh on every sync (not fixed
|
||||
// at server startup), so changing it here takes effect on the next
|
||||
// click. Unrelated to RollingWindowDays, which is for the (not yet
|
||||
// implemented) relative classification window.
|
||||
BackfillHorizonDays int
|
||||
MaxHeartRate *float64
|
||||
RestingHeartRate *float64
|
||||
|
||||
HRZone1MinPct, HRZone1MaxPct float64
|
||||
HRZone2MinPct, HRZone2MaxPct float64
|
||||
@@ -30,7 +36,7 @@ type Profile struct {
|
||||
}
|
||||
|
||||
const profileColumns = `
|
||||
garmin_email, garmin_password, rolling_window_days, max_heart_rate, resting_heart_rate,
|
||||
garmin_email, garmin_password, rolling_window_days, backfill_horizon_days, max_heart_rate, resting_heart_rate,
|
||||
hr_zone1_min_pct, hr_zone1_max_pct, hr_zone2_min_pct, hr_zone2_max_pct,
|
||||
hr_zone3_min_pct, hr_zone3_max_pct, hr_zone4_min_pct, hr_zone4_max_pct,
|
||||
hr_zone5_min_pct, hr_zone5_max_pct,
|
||||
@@ -42,7 +48,7 @@ const profileColumns = `
|
||||
func (db *DB) GetProfile(ctx context.Context) (Profile, error) {
|
||||
var p Profile
|
||||
err := db.QueryRowContext(ctx, `SELECT `+profileColumns+` FROM profile WHERE id = 1`).Scan(
|
||||
&p.GarminEmail, &p.GarminPassword, &p.RollingWindowDays, &p.MaxHeartRate, &p.RestingHeartRate,
|
||||
&p.GarminEmail, &p.GarminPassword, &p.RollingWindowDays, &p.BackfillHorizonDays, &p.MaxHeartRate, &p.RestingHeartRate,
|
||||
&p.HRZone1MinPct, &p.HRZone1MaxPct, &p.HRZone2MinPct, &p.HRZone2MaxPct,
|
||||
&p.HRZone3MinPct, &p.HRZone3MaxPct, &p.HRZone4MinPct, &p.HRZone4MaxPct,
|
||||
&p.HRZone5MinPct, &p.HRZone5MaxPct,
|
||||
@@ -61,14 +67,14 @@ func (db *DB) GetProfile(ctx context.Context) (Profile, error) {
|
||||
func (db *DB) UpdateProfile(ctx context.Context, p Profile) error {
|
||||
_, err := db.ExecContext(ctx, `
|
||||
UPDATE profile SET
|
||||
garmin_email=?, garmin_password=?, rolling_window_days=?, max_heart_rate=?, resting_heart_rate=?,
|
||||
garmin_email=?, garmin_password=?, rolling_window_days=?, backfill_horizon_days=?, max_heart_rate=?, resting_heart_rate=?,
|
||||
hr_zone1_min_pct=?, hr_zone1_max_pct=?, hr_zone2_min_pct=?, hr_zone2_max_pct=?,
|
||||
hr_zone3_min_pct=?, hr_zone3_max_pct=?, hr_zone4_min_pct=?, hr_zone4_max_pct=?,
|
||||
hr_zone5_min_pct=?, hr_zone5_max_pct=?,
|
||||
warmup_minutes=?, cooldown_minutes=?,
|
||||
updated_at=datetime('now')
|
||||
WHERE id = 1`,
|
||||
p.GarminEmail, p.GarminPassword, p.RollingWindowDays, p.MaxHeartRate, p.RestingHeartRate,
|
||||
p.GarminEmail, p.GarminPassword, p.RollingWindowDays, p.BackfillHorizonDays, p.MaxHeartRate, p.RestingHeartRate,
|
||||
p.HRZone1MinPct, p.HRZone1MaxPct, p.HRZone2MinPct, p.HRZone2MaxPct,
|
||||
p.HRZone3MinPct, p.HRZone3MaxPct, p.HRZone4MinPct, p.HRZone4MaxPct,
|
||||
p.HRZone5MinPct, p.HRZone5MaxPct,
|
||||
|
||||
@@ -22,6 +22,9 @@ func TestProfile_DefaultsThenUpdate(t *testing.T) {
|
||||
if p.WarmupMinutes != 10 || p.CooldownMinutes != 5 {
|
||||
t.Errorf("phase-minute defaults = %+v, want warmup=10, cooldown=5", p)
|
||||
}
|
||||
if p.BackfillHorizonDays != 1095 {
|
||||
t.Errorf("BackfillHorizonDays = %d, want 1095 (migration default)", p.BackfillHorizonDays)
|
||||
}
|
||||
|
||||
maxHR, restingHR := 190.0, 50.0
|
||||
p.GarminEmail = "runner@example.com"
|
||||
@@ -30,6 +33,7 @@ func TestProfile_DefaultsThenUpdate(t *testing.T) {
|
||||
p.MaxHeartRate = &maxHR
|
||||
p.RestingHeartRate = &restingHR
|
||||
p.WarmupMinutes = 8
|
||||
p.BackfillHorizonDays = 14
|
||||
|
||||
if err := db.UpdateProfile(ctx, p); err != nil {
|
||||
t.Fatalf("UpdateProfile: %v", err)
|
||||
@@ -48,4 +52,7 @@ func TestProfile_DefaultsThenUpdate(t *testing.T) {
|
||||
if got.WarmupMinutes != 8 {
|
||||
t.Errorf("WarmupMinutes = %v, want 8", got.WarmupMinutes)
|
||||
}
|
||||
if got.BackfillHorizonDays != 14 {
|
||||
t.Errorf("BackfillHorizonDays = %v, want 14", got.BackfillHorizonDays)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user