A website inspired by Last.fm that will keep track of your listening statistics
lastfm music statistics
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add user ID filter to artist scrobble queries

Refactor artist scrobble repository to support filtering by user ID and
time range in a more maintainable way with helper functions

Add new GetCount method to retrieve total scrobble count for an artist

Update service and handler layers to use the new repository methods

oscar345 4fc4662d e462b830

+79 -22
+1
internal/filters/catalog.go
··· 10 10 Pagination pagination.Filter 11 11 From time.Time 12 12 To time.Time 13 + UserID int 13 14 }
+67 -15
internal/repo/db/artist_scrobble.go
··· 4 4 "context" 5 5 "database/sql" 6 6 "fmt" 7 + "strings" 7 8 8 9 "github.com/oscar345/keeptrack/internal/filters" 9 10 "github.com/oscar345/keeptrack/internal/models" ··· 22 23 return &ArtistScrobbleRepoDB{duck: duck} 23 24 } 24 25 25 - func (repo *ArtistScrobbleRepoDB) ListCountByUser( 26 - ctx context.Context, userID int, filter filters.Count, 27 - ) ([]models.CountMBID, pagination.Page, error) { 26 + func artistScrobbleCountWhere(filter filters.Count, wheres []string, args []any) ([]string, []any) { 27 + if filter.UserID != 0 { 28 + wheres = append(wheres, "scrobble.user_id = ?") 29 + args = append(args, filter.UserID) 30 + } 31 + 32 + if !filter.From.IsZero() { 33 + wheres = append(wheres, "scrobble.played_at >= ?") 34 + args = append(args, filter.From.UnixMicro()) 35 + } 36 + 37 + if !filter.To.IsZero() { 38 + wheres = append(wheres, "scrobble.played_at <= ?") 39 + args = append(args, filter.To.UnixMicro()) 40 + } 41 + 42 + return wheres, args 43 + } 44 + 45 + func (repo *ArtistScrobbleRepoDB) ListCount(ctx context.Context, filter filters.Count) ([]models.CountMBID, pagination.Page, error) { 28 46 var statement = /*sql*/ ` 29 47 WITH scrobbles AS ( 30 48 SELECT 31 - recording_mbid__artist_mbid.artist_mbid as mbid, 32 - count(scrobble) as amount 49 + recording_mbid__artist_mbid.artist_mbid AS mbid, 50 + count(scrobble) AS amount 33 51 FROM scrobble 34 52 INNER JOIN recording_mbid__artist_mbid 35 53 ON recording_mbid__artist_mbid.recording_mbid = scrobble.recording_mbid 36 - WHERE scrobble.user_id = ? %s 54 + WHERE %s 37 55 GROUP BY recording_mbid__artist_mbid.artist_mbid 38 56 ) 39 57 SELECT ··· 46 64 LIMIT ?; 47 65 ` 48 66 49 - where := "" 50 - args := []any{userID} 67 + wheres := []string{} 68 + args := []any{} 51 69 52 - if !filter.From.IsZero() { 53 - where += " AND scrobble.played_at >= ?" 54 - args = append(args, filter.From.UnixMicro()) 55 - } 70 + wheres, args = artistScrobbleCountWhere(filter, wheres, args) 56 71 57 - if !filter.To.IsZero() { 58 - where += " AND scrobble.played_at <= ?" 59 - args = append(args, filter.To.UnixMicro()) 72 + where := "TRUE" 73 + if len(wheres) > 0 { 74 + where = strings.Join(wheres, " AND ") 60 75 } 61 76 62 77 statement = fmt.Sprintf(statement, where) ··· 77 92 78 93 return items, pagination.New(filter.Pagination, total), nil 79 94 } 95 + 96 + func (repo *ArtistScrobbleRepoDB) GetCount(ctx context.Context, mbid string, filter filters.Count) (int, error) { 97 + var statement = /*sql*/ ` 98 + WITH scrobbles AS ( 99 + SELECT 100 + recording_mbid__artist_mbid.artist_mbid AS mbid, 101 + count(scrobble) AS amount 102 + FROM scrobble 103 + INNER JOIN recording_mbid__artist_mbid 104 + ON recording_mbid__artist_mbid.recording_mbid = scrobble.recording_mbid 105 + WHERE recording_mbid__artist_mbid.artist_mbid = ? AND %s 106 + GROUP BY recording_mbid__artist_mbid.artist_mbid 107 + ) 108 + SELECT sum(scrobbles.amount) AS amount 109 + FROM scrobbles 110 + ` 111 + 112 + wheres := []string{} 113 + args := []any{mbid} 114 + 115 + wheres, args = artistScrobbleCountWhere(filter, wheres, args) 116 + 117 + where := "TRUE" 118 + if len(wheres) > 0 { 119 + where = strings.Join(wheres, " AND ") 120 + } 121 + 122 + statement = fmt.Sprintf(statement, where) 123 + 124 + return database.QueryOne(ctx, repo.duck, statement, args, func(r *sql.Rows) (int, error) { 125 + var amount int 126 + if err := r.Scan(&amount); err != nil { 127 + return 0, err 128 + } 129 + return amount, nil 130 + }) 131 + }
+2 -1
internal/repo/repo.go
··· 14 14 } 15 15 16 16 type ArtistScrobbleRepo interface { 17 - ListCountByUser(ctx context.Context, userID int, filter filters.Count) ([]models.CountMBID, pagination.Page, error) 17 + ListCount(ctx context.Context, filter filters.Count) ([]models.CountMBID, pagination.Page, error) 18 + GetCount(ctx context.Context, mbid string, filter filters.Count) (int, error) 18 19 } 19 20 20 21 type RecordingRepo interface {
+2 -4
internal/services/catalog.go
··· 37 37 } 38 38 } 39 39 40 - func (as *ArtistService) ListArtistByUserCount( 41 - ctx context.Context, id int, filter filters.Count, 42 - ) ([]models.Artist, pagination.Page, error) { 43 - counts, page, err := as.artistScrobbleRepo.ListCountByUser(ctx, id, filter) 40 + func (as *ArtistService) ListArtistByCount(ctx context.Context, filter filters.Count) ([]models.Artist, pagination.Page, error) { 41 + counts, page, err := as.artistScrobbleRepo.ListCount(ctx, filter) 44 42 if err != nil { 45 43 return nil, page, err 46 44 }
+7 -2
internal/web/handlers/index.go
··· 31 31 } 32 32 33 33 filter, err := r.Context().Value(httpin.Input).(*requests.Count).Filter() 34 + if err != nil { 35 + http.Error(w, err.Error(), http.StatusBadRequest) 36 + return 37 + } 38 + filter.UserID = id 34 39 40 + items, page, err := handler.artistService.ListArtistByCount(r.Context(), filter) 35 41 if err != nil { 36 - http.Error(w, err.Error(), http.StatusBadRequest) 42 + http.Error(w, err.Error(), http.StatusInternalServerError) 37 43 return 38 44 } 39 45 40 - items, page, err := handler.artistService.ListArtistByUserCount(r.Context(), id, filter) 41 46 artists := enum.Map(items, responses.NewArtistFromModel) 42 47 43 48 w.Header().Set("Content-Type", "application/json")