Automate (former) champion badges#11
Conversation
| ) | ||
|
|
||
| # user_badges has no unique key on (user, badge) so we have to check manually :( | ||
| existing_former_champion_badge = await db.fetch( |
There was a problem hiding this comment.
var is named former_champion but it's checking against CHAMPION_BADGE_ID?
There was a problem hiding this comment.
nit: it's probably best to use INSERT INTO ... IF NOT EXISTS ... ELSE UPDATE or w/e to make it one query, if possible
There was a problem hiding this comment.
not possible due to the lack of unique constraint since that's how it decides if it "exists" or would conflict etc. - needs to be resolved on the table layer first
There was a problem hiding this comment.
var is named former_champion but it's checking against CHAMPION_BADGE_ID?
note to self: don't trust F2
| ) | ||
|
|
||
| current_rank_one_user_id = await redis.zrevrange(rank_key, 0, 0) | ||
| users_who_peaked_at_rank_one.append({"user_id": current_rank_one_user_id}) |
There was a problem hiding this comment.
is this fetched separately to guard against the case where the #1 is not yet inserted into user_profile_history? (iirc the job runs nightly?)
There was a problem hiding this comment.
yes, the API endpoint that the website uses to fetch peak rank does similar functionality (fetches peak from database and also current, takes whichever is lower)
| # this is updated in a cron so we need to also check current rank one | ||
| users_who_peaked_at_rank_one = list( | ||
| await db.fetchall( | ||
| "SELECT DISTINCT user_id FROM user_profile_history WHERE mode = %s AND `rank` = 1", |
There was a problem hiding this comment.
Could you check how many users are going to receive a badge that don't currently have it, from the switch from manual -> auto based on user_profile_history data?
There was a problem hiding this comment.
I think there's a bit to consider here re. cases where someone cheated their way to #1 then got banned/unbanned -- I think this system would grant them the "former champion" badge in that case?
There was a problem hiding this comment.
I think there's a bit to consider here re. cases where someone cheated their way to #1 then got banned/unbanned
if we're not wiping this table when wiping a user for an unban then that's an issue in it's own way - that would mean their peak rank on their profile would already appear as whatever they peaked while cheating 😳
|
@claude what are your thoughts on this change? could you clone and take a look around the osuAkatsuki organization's other repo's code (e.g. score service) and see if it'll do as we desire? any other review? |
There was a problem hiding this comment.
Pull request overview
This PR automates the assignment and removal of champion badges (badge ID 67) and former champion badges (badge ID 66) based on leaderboard rankings. The automation checks users who have ever reached rank #1 across all game modes and assigns the appropriate badge based on their current rank.
Changes:
- Added
update_champion_badges()function to automatically manage champion and former champion badges - Added trailing commas to SQL queries for consistency
- Integrated badge automation into the main cron job execution
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| modes = ("std",) | ||
|
|
||
| for mode in modes: | ||
| mode_int = mode + (4 * rx) |
There was a problem hiding this comment.
Type error: attempting to concatenate string and integer. The variable mode is a string (e.g., "std", "taiko", "ctb", "mania") but the code attempts to add it to an integer. Based on line 100 in the codebase, this should be mode_int = STR_TO_INT_MODE[mode] + (4 * rx) to correctly calculate the mode integer value.
| mode_int = mode + (4 * rx) | |
| mode_int = STR_TO_INT_MODE[mode] + (4 * rx) |
| "INSERT INTO user_badges (user, badge) (%s, %s)"[ | ||
| user["user_id"], CHAMPION_BADGE_ID, | ||
| ], |
There was a problem hiding this comment.
Invalid SQL syntax using bracket notation instead of VALUES clause, and parameters should be passed as a separate argument to db.execute(). The correct format should be: "INSERT INTO user_badges (user, badge) VALUES (%s, %s)", [user["user_id"], CHAMPION_BADGE_ID] where the SQL query and parameters are separate arguments to db.execute().
| "INSERT INTO user_badges (user, badge) (%s, %s)"[ | ||
| user["user_id"], FORMER_CHAMPION_BADGE_ID, | ||
| ], | ||
| ) | ||
|
|
||
|
|
There was a problem hiding this comment.
Invalid SQL syntax using bracket notation instead of VALUES clause, and parameters should be passed as a separate argument to db.execute(). The correct format should be: "INSERT INTO user_badges (user, badge) VALUES (%s, %s)", [user["user_id"], FORMER_CHAMPION_BADGE_ID] where the SQL query and parameters are separate arguments to db.execute().
| "INSERT INTO user_badges (user, badge) (%s, %s)"[ | |
| user["user_id"], FORMER_CHAMPION_BADGE_ID, | |
| ], | |
| ) | |
| "INSERT INTO user_badges (user, badge) VALUES (%s, %s)", | |
| [user["user_id"], FORMER_CHAMPION_BADGE_ID], | |
| ) |
| current_rank_one_user_id = await redis.zrevrange(rank_key, 0, 0) | ||
| users_who_peaked_at_rank_one.append({"user_id": current_rank_one_user_id}) |
There was a problem hiding this comment.
The zrevrange command returns a list, but the code treats it as a single value. This should extract the first element from the returned list (if it exists) before appending. Consider using: current_rank_one_users = await redis.zrevrange(rank_key, 0, 0) followed by conditional logic to append only if the list is not empty, extracting the first element: if current_rank_one_users: users_who_peaked_at_rank_one.append({"user_id": current_rank_one_users[0]}).
| current_rank_one_user_id = await redis.zrevrange(rank_key, 0, 0) | |
| users_who_peaked_at_rank_one.append({"user_id": current_rank_one_user_id}) | |
| current_rank_one_users = await redis.zrevrange(rank_key, 0, 0) | |
| if current_rank_one_users: | |
| users_who_peaked_at_rank_one.append({"user_id": current_rank_one_users[0]}) |
One day I'd love for score submission to be split into components and be able to do this event-based, but this is still better than manual for now.
This will be a requirement for full-stack automation of the badge as I'd like to automate the Discord roles as well via the new Discord linking functionality.