From d87ed2eb978e70a9d12458b3694c57ce7b569797 Mon Sep 17 00:00:00 2001 From: Gabe Farrell <90876006+gabehf@users.noreply.github.com> Date: Wed, 14 Jan 2026 21:35:01 -0500 Subject: [PATCH] fix: ensure listen activity correctly sums listen activity in step (#139) * remove impossible nil check * fix listen activity not correctly aggregating step * remove stray log * fix test --- engine/handlers/get_listen_activity.go | 41 ++++++++++++++++++------ internal/catalog/associate_track.go | 3 -- internal/db/period.go | 4 ++- internal/db/psql/listen_activity_test.go | 19 ++++++----- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/engine/handlers/get_listen_activity.go b/engine/handlers/get_listen_activity.go index 22d23fa..c11ed3e 100644 --- a/engine/handlers/get_listen_activity.go +++ b/engine/handlers/get_listen_activity.go @@ -106,7 +106,7 @@ func GetListenActivityHandler(store db.DB) func(w http.ResponseWriter, r *http.R return } - activity = fillMissingActivity(activity, opts) + activity = processActivity(activity, opts) l.Debug().Msg("GetListenActivityHandler: Successfully retrieved listen activity") utils.WriteJSON(w, http.StatusOK, activity) @@ -114,34 +114,55 @@ func GetListenActivityHandler(store db.DB) func(w http.ResponseWriter, r *http.R } // ngl i hate this -func fillMissingActivity( +func processActivity( items []db.ListenActivityItem, opts db.ListenActivityOpts, ) []db.ListenActivityItem { from, to := db.ListenActivityOptsToTimes(opts) - existing := make(map[string]int64, len(items)) + buckets := make(map[string]int64) + for _, item := range items { - existing[item.Start.Format("2006-01-02")] = item.Listens + bucketStart := normalizeToStep(item.Start, opts.Step) + key := bucketStart.Format("2006-01-02") + buckets[key] += item.Listens } var result []db.ListenActivityItem - for t := from; t.Before(to); t = addStep(t, opts.Step) { - listens := int64(0) - if v, ok := existing[t.Format("2006-01-02")]; ok { - listens = v - } + for t := normalizeToStep(from, opts.Step); t.Before(to); t = addStep(t, opts.Step) { + key := t.Format("2006-01-02") result = append(result, db.ListenActivityItem{ Start: t, - Listens: int64(listens), + Listens: buckets[key], }) } return result } +func normalizeToStep(t time.Time, step db.StepInterval) time.Time { + switch step { + case db.StepDay: + return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) + + case db.StepWeek: + weekday := int(t.Weekday()) + if weekday == 0 { + weekday = 7 + } + start := t.AddDate(0, 0, -(weekday - 1)) + return time.Date(start.Year(), start.Month(), start.Day(), 0, 0, 0, 0, t.Location()) + + case db.StepMonth: + return time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, t.Location()) + + default: + return t + } +} + func addStep(t time.Time, step db.StepInterval) time.Time { switch step { case db.StepDay: diff --git a/internal/catalog/associate_track.go b/internal/catalog/associate_track.go index bb8ebc7..3fa1fbc 100644 --- a/internal/catalog/associate_track.go +++ b/internal/catalog/associate_track.go @@ -74,9 +74,6 @@ func matchTrackByMbzID(ctx context.Context, d db.DB, opts AssociateTrackOpts) (* } else { l.Warn().Msgf("Attempted to update track %s with MusicBrainz ID, but an existing ID was already found", track.Title) } - if err != nil { - return nil, fmt.Errorf("matchTrackByMbzID: %w", err) - } track.MbzID = &opts.TrackMbzID return track, nil } diff --git a/internal/db/period.go b/internal/db/period.go index d28f59a..f13d321 100644 --- a/internal/db/period.go +++ b/internal/db/period.go @@ -91,7 +91,9 @@ func ListenActivityOptsToTimes(opts ListenActivityOpts) (start, end time.Time) { // Align to most recent Sunday weekday := int(now.Weekday()) // Sunday = 0 startOfThisWeek := time.Date(now.Year(), now.Month(), now.Day()-weekday, 0, 0, 0, 0, loc) - start = startOfThisWeek.AddDate(0, 0, -7*opts.Range) + // need to subtract 1 from range for week because we are going back from the beginning of this + // week, so we sort of already went back a week + start = startOfThisWeek.AddDate(0, 0, -7*(opts.Range-1)) end = startOfThisWeek.AddDate(0, 0, 7).Add(-time.Nanosecond) case StepMonth: diff --git a/internal/db/psql/listen_activity_test.go b/internal/db/psql/listen_activity_test.go index 9b277ff..b2da07f 100644 --- a/internal/db/psql/listen_activity_test.go +++ b/internal/db/psql/listen_activity_test.go @@ -97,20 +97,19 @@ func TestListenActivity(t *testing.T) { err = store.Exec(context.Background(), `INSERT INTO listens (user_id, track_id, listened_at) - VALUES (1, 1, NOW() - INTERVAL '1 month'), - (1, 1, NOW() - INTERVAL '2 months'), - (1, 1, NOW() - INTERVAL '3 months'), - (1, 2, NOW() - INTERVAL '1 month'), - (1, 2, NOW() - INTERVAL '2 months')`) + VALUES (1, 1, NOW() - INTERVAL '1 month 1 day'), + (1, 1, NOW() - INTERVAL '2 months 1 day'), + (1, 1, NOW() - INTERVAL '3 months 1 day'), + (1, 2, NOW() - INTERVAL '1 month 1 day'), + (1, 2, NOW() - INTERVAL '1 hour'), + (1, 2, NOW() - INTERVAL '1 minute'), + (1, 2, NOW() - INTERVAL '2 months 1 day')`) require.NoError(t, err) - // This test is bad, and I think it's because of daylight savings. - // I need to find a better test. - activity, err = store.GetListenActivity(ctx, db.ListenActivityOpts{Step: db.StepMonth, Range: 8}) require.NoError(t, err) - // require.Len(t, activity, 8) - // assert.Equal(t, []int64{0, 0, 0, 0, 1, 2, 2, 0}, flattenListenCounts(activity)) + require.Len(t, activity, 4) + assert.Equal(t, []int64{1, 2, 2, 2}, flattenListenCounts(activity)) // Truncate listens table and insert specific dates for testing opts.Step = db.StepYear err = store.Exec(context.Background(), `TRUNCATE TABLE listens RESTART IDENTITY`)