Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions api/v1_playlists_new_releases.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"github.com/jackc/pgx/v5"
)

const minNewAlbumValidationScore = 5

type GetPlaylistsNewReleasesParams struct {
Limit int `query:"limit" default:"10" validate:"min=1,max=100"`
Offset int `query:"offset" default:"0" validate:"min=0"`
Expand All @@ -19,25 +21,43 @@ func (app *ApiServer) v1PlaylistsNewReleases(c *fiber.Ctx) error {
}

isAlbum := params.Type == "album"
joinClause := ""
filterClause := ""
orderClause := "COALESCE(p.release_date, p.created_at) DESC, p.playlist_id DESC"

if isAlbum {
joinClause = "LEFT JOIN aggregate_playlist ap ON p.playlist_id = ap.playlist_id"
filterClause = `
AND COALESCE(ap.save_count, 0) + COALESCE(ap.repost_count, 0) >= @min_validation_score
`
orderClause = `
COALESCE(ap.save_count, 0) + COALESCE(ap.repost_count, 0) DESC,
COALESCE(p.release_date, p.created_at) DESC,
p.playlist_id DESC
`
}

sql := `
SELECT playlist_id
FROM playlists
WHERE is_delete = false
AND is_current = true
AND is_private = false
AND is_album = @is_album
AND COALESCE(release_date, created_at) <= NOW()
AND COALESCE(release_date, created_at) > NOW() - INTERVAL '90 days'
ORDER BY COALESCE(release_date, created_at) DESC, playlist_id DESC
SELECT p.playlist_id
FROM playlists p
` + joinClause + `
WHERE p.is_delete = false
AND p.is_current = true
AND p.is_private = false
AND p.is_album = @is_album
AND COALESCE(p.release_date, p.created_at) <= NOW()
AND COALESCE(p.release_date, p.created_at) > NOW() - INTERVAL '90 days'
` + filterClause + `
ORDER BY ` + orderClause + `
LIMIT @limit
OFFSET @offset
`

rows, err := app.pool.Query(c.Context(), sql, pgx.NamedArgs{
"is_album": isAlbum,
"limit": params.Limit,
"offset": params.Offset,
"is_album": isAlbum,
"limit": params.Limit,
"offset": params.Offset,
"min_validation_score": minNewAlbumValidationScore,
})
if err != nil {
return err
Expand Down
58 changes: 57 additions & 1 deletion api/v1_playlists_new_releases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestGetPlaylistsNewReleases_Albums(t *testing.T) {
"created_at": now.AddDate(0, 0, -1),
"playlist_name": "one",
},
// older album
// older album with stronger engagement
{
"playlist_id": 2,
"playlist_owner_id": 1,
Expand All @@ -36,6 +36,16 @@ func TestGetPlaylistsNewReleases_Albums(t *testing.T) {
"created_at": now.AddDate(0, 0, -10),
"playlist_name": "two",
},
// recent album without enough social proof (excluded)
{
"playlist_id": 7,
"playlist_owner_id": 1,
"is_album": true,
"is_private": false,
"release_date": now.AddDate(0, 0, -2),
"created_at": now.AddDate(0, 0, -2),
"playlist_name": "seven",
},
// non-album playlist (excluded when type=album)
{
"playlist_id": 3,
Expand Down Expand Up @@ -77,6 +87,11 @@ func TestGetPlaylistsNewReleases_Albums(t *testing.T) {
"playlist_name": "six",
},
},
"aggregate_playlist": {
{"playlist_id": 1, "is_album": true, "save_count": 3, "repost_count": 2},
{"playlist_id": 2, "is_album": true, "save_count": 6, "repost_count": 2},
{"playlist_id": 7, "is_album": true, "save_count": 2, "repost_count": 2},
},
}
database.Seed(app.pool.Replicas[0], fixtures)

Expand All @@ -86,6 +101,47 @@ func TestGetPlaylistsNewReleases_Albums(t *testing.T) {

status, body := testGet(t, app, "/v1/playlists/new-releases?type=album", &resp)
assert.Equal(t, 200, status)
jsonAssert(t, body, map[string]any{
"data.0.id": trashid.MustEncodeHashID(2),
"data.1.id": trashid.MustEncodeHashID(1),
})
assert.Len(t, resp.Data, 2)
}

func TestGetPlaylistsNewReleases_PlaylistsRemainChronological(t *testing.T) {
app := emptyTestApp(t)
now := time.Now()
fixtures := database.FixtureMap{
"users": {{"user_id": 1, "handle_lc": "one"}},
"playlists": {
{
"playlist_id": 1,
"playlist_owner_id": 1,
"is_album": false,
"is_private": false,
"release_date": now.AddDate(0, 0, -1),
"created_at": now.AddDate(0, 0, -1),
"playlist_name": "newer playlist",
},
{
"playlist_id": 2,
"playlist_owner_id": 1,
"is_album": false,
"is_private": false,
"release_date": now.AddDate(0, 0, -10),
"created_at": now.AddDate(0, 0, -10),
"playlist_name": "older playlist",
},
},
}
database.Seed(app.pool.Replicas[0], fixtures)

var resp struct {
Data []dbv1.Playlist
}

status, body := testGet(t, app, "/v1/playlists/new-releases?type=playlist", &resp)
assert.Equal(t, 200, status)
jsonAssert(t, body, map[string]any{
"data.0.id": trashid.MustEncodeHashID(1),
"data.1.id": trashid.MustEncodeHashID(2),
Expand Down
Loading