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
18 changes: 10 additions & 8 deletions api/v1_events_remix_contests.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ type GetRemixContestsParams struct {

// v1EventsRemixContests returns remix-contest events from the events table.
// Sort priority:
// 1. Open contests whose host is followed by the Audius account.
// 2. Open contests whose host is not followed by the Audius account.
// 3. Ended contests whose host is followed by the Audius account.
// 4. Ended contests whose host is not followed by the Audius account.
// 1. Open contests with a featured host.
// 2. Open contests with any other host.
// 3. Ended contests with a featured host.
// 4. Ended contests with any other host.
//
// The Audius account is config.Cfg.FeaturedAudienceUserID; 0 disables the
// follow-based tiebreak (groups collapse to open-vs-ended only).
// A featured host is verified, is the Audius account, or is followed by the
// Audius account. The Audius account is config.Cfg.FeaturedAudienceUserID;
// 0 leaves verified as the only signal.
// Within each group, results are sorted by entry count descending, then by
// the active-first / soonest-ending sort, then event_id ASC as a stable tiebreak.
// Supports pagination and an optional `status` filter (active | ended | all).
Expand Down Expand Up @@ -132,9 +133,10 @@ func (app *ApiServer) v1EventsRemixContests(c *fiber.Ctx) error {
ORDER BY
-- Primary: open (0) before ended (1).
CASE WHEN e.end_date IS NULL OR e.end_date > NOW() THEN 0 ELSE 1 END ASC,
-- Secondary: Audius-followed hosts (0) before unfollowed (1).
-- When @audius_user_id is 0 the tiebreak collapses (all rows score 1).
-- Secondary: featured hosts (0) before the rest (1).
CASE
WHEN u.is_verified THEN 0
WHEN @audius_user_id::int4 != 0 AND e.user_id = @audius_user_id::int4 THEN 0
WHEN @audius_user_id::int4 != 0
AND EXISTS (
SELECT 1 FROM follows f
Expand Down
72 changes: 72 additions & 0 deletions api/v1_events_remix_contests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,78 @@ func TestRemixContestsFollowFilterIgnoresStaleRows(t *testing.T) {
})
}

// TestRemixContestsFeaturesAudiusAndVerifiedHosts: a contest hosted by the
// Audius account or by a verified host sorts ahead of other open contests,
// even with fewer entries.
func TestRemixContestsFeaturesAudiusAndVerifiedHosts(t *testing.T) {
app := emptyTestApp(t)

audiusUserID := 9300
verifiedHostID := 9301
plainHostID := 9302
remixerID := 9303

audiusTrackID := 8601
verifiedTrackID := 8602
plainTrackID := 8603

start := parseTime(t, "2024-01-02")
inWindow := parseTime(t, "2024-01-03")
end := parseTime(t, "2099-01-01")

fixtures := database.FixtureMap{
"events": []map[string]any{
{
"event_id": 801, "event_type": "remix_contest", "entity_type": "track",
"entity_id": plainTrackID, "user_id": plainHostID,
"created_at": start, "end_date": end,
},
{
"event_id": 802, "event_type": "remix_contest", "entity_type": "track",
"entity_id": verifiedTrackID, "user_id": verifiedHostID,
"created_at": start, "end_date": end,
},
{
"event_id": 803, "event_type": "remix_contest", "entity_type": "track",
"entity_id": audiusTrackID, "user_id": audiusUserID,
"created_at": start, "end_date": end,
},
},
"users": []map[string]any{
{"user_id": audiusUserID, "handle": "audius"},
{"user_id": verifiedHostID, "handle": "verified_host", "is_verified": true},
{"user_id": plainHostID, "handle": "plain_host"},
{"user_id": remixerID, "handle": "remixer"},
},
"tracks": []map[string]any{
{"track_id": audiusTrackID, "owner_id": audiusUserID, "created_at": start},
{"track_id": verifiedTrackID, "owner_id": verifiedHostID, "created_at": start},
{"track_id": plainTrackID, "owner_id": plainHostID, "created_at": start},
{"track_id": 8611, "owner_id": remixerID, "created_at": inWindow},
{"track_id": 8612, "owner_id": remixerID, "created_at": inWindow},
},
"remixes": []map[string]any{
{"parent_track_id": plainTrackID, "child_track_id": 8611},
{"parent_track_id": plainTrackID, "child_track_id": 8612},
},
}
database.Seed(app.pool.Replicas[0], fixtures)

prev := config.Cfg.FeaturedAudienceUserID
config.Cfg.FeaturedAudienceUserID = int32(audiusUserID)
t.Cleanup(func() { config.Cfg.FeaturedAudienceUserID = prev })

status, body := testGet(t, app, "/v1/events/remix-contests")
assert.Equal(t, 200, status)

jsonAssert(t, body, map[string]any{
"data.#": 3,
"data.0.event_id": trashid.MustEncodeHashID(802), // verified host, 0 entries
"data.1.event_id": trashid.MustEncodeHashID(803), // Audius account, 0 entries
"data.2.event_id": trashid.MustEncodeHashID(801), // 2 entries
})
}

// TestRemixContestsExcludesUnavailableContent covers server-side filtering
// of contests whose track or host is not in a publishable state. The
// frontend used to drop these on the client (the "deleted accounts surface
Expand Down
9 changes: 5 additions & 4 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ type Config struct {
AudiusApiSecret string
// Shared secret for notifications-dashboard (or other internal jobs) to read notification campaign push open counts
NotificationCampaignOpenMetricsSecret string
// Audius account user id. In the public remix-contests list, hosts followed
// by this account sort ahead of other hosts within both the open and ended
// groups. Zero (the default when the env var is unset) disables follow-based
// prioritization (the list reduces to open-before-ended).
// Audius account user id. In the public remix-contests list, this account
// and the hosts it follows sort ahead of other hosts within both the open
// and ended groups. Defaults to the Audius account in prod; zero elsewhere
// unless featuredAudienceUserId is set.
FeaturedAudienceUserID int32

// Genesis migration dual-write queue.
Expand Down Expand Up @@ -270,6 +270,7 @@ func init() {
case "prod":
fallthrough
case "production":
Cfg.FeaturedAudienceUserID = 51
Cfg.OpenAudioURLs = []string{
"rpc.audius.co",
}
Expand Down
Loading