Skip to content

Conversation

@AadarshM07
Copy link

This adds the necessary queries and mutations required for the cp leaderboard . It includes:

  • Schema definitions for leaderboard data
  • Structs to represent leaderboard entries
  • Mutations to add or update CP handles and unified leaderboard
  • Queries to fetch leaderboard information

All functionalities have been tested using the GraphiQL playground

@ivinjabraham
Copy link
Member

Your commit messages could use some work; refer https://www.conventionalcommits.org/en/v1.0.0/ and https://www.simplethread.com/what-makes-a-good-git-commit/.

@ivinjabraham
Copy link
Member

Rewrite the commits and fix the failing workflow and you're good to go.

@AadarshM07
Copy link
Author

@ivinjabraham ok thanks👍

@AadarshM07 AadarshM07 force-pushed the master branch 2 times, most recently from 02b2cd0 to 9ca8e7e Compare July 16, 2025 06:07
@AadarshM07
Copy link
Author

@ivinjabraham I have updated the PR with the requested changes


CREATE TABLE IF NOT EXISTS codeforces_stats (
id SERIAL PRIMARY KEY,
member_id INT NOT NULL,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unique

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L

Ok(members) => {
for member in members {
// Fetch LeetCode username
let leetcode_username = sqlx::query_as::<_, LeetCodeStats>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the variable name, the SQL will return an entire row from the leetcode table

}

// Fetch Codeforces username
let codeforces_username = sqlx::query_as::<_, CodeforcesStats>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the variable name, the SQL will return an entire row from the codeforces table

@AadarshM07 AadarshM07 force-pushed the master branch 2 times, most recently from d691046 to 8770b04 Compare September 21, 2025 16:29
@AadarshM07
Copy link
Author

@hrideshmg I hope this fixes all that you mentioned

@hrideshmg
Copy link
Member

That's a lotta commits😳

Will take a look later this week

@KKSurendran06 KKSurendran06 requested a review from Copilot October 7, 2025 08:23
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Adds competitive programming (CP) leaderboard functionality: data models, migrations, external API integration (LeetCode/Codeforces), GraphQL queries/mutations, daily task updates, and seed data to populate and compute scores.

  • Introduces new tables and models for leaderboard, LeetCode, and Codeforces stats.
  • Adds GraphQL queries/mutations and background task to fetch/update stats and recompute unified scores.
  • Implements external API calls and scoring logic but introduces several mapping, ordering, and robustness issues.

Reviewed Changes

Copilot reviewed 15 out of 20 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
src/models/mod.rs Exposes new leaderboard module.
src/models/leaderboard.rs Defines leaderboard and CP stats GraphQL/DB models.
src/graphql/queries/mod.rs Registers leaderboard queries.
src/graphql/queries/leaderboard_queries.rs Adds queries for unified leaderboard and platform-specific stats.
src/graphql/mutations/mod.rs Registers new fetch mutations for CP stats.
src/graphql/mutations/leetcode_status.rs Adds mutation to fetch/update LeetCode stats.
src/graphql/mutations/leaderboard_mutation.rs Adds mutation for adding/updating CP handles (not wired into schema).
src/graphql/mutations/codeforces_status.rs Adds mutation to fetch/update Codeforces stats.
src/graphql/mod.rs Integrates leaderboard queries and fetch mutations into schema.
src/graphql/api/mod.rs Exposes leaderboard API helper functions.
src/graphql/api/leaderboard_api.rs Implements external API integration and scoring logic.
src/database_seeder/seed.sql Adds seed data and initial leaderboard computations.
src/daily_task/mod.rs Extends daily task to refresh CP stats and leaderboard.
package.json Adds client-side GraphQL tooling dependencies.
migrations/20250312124630_add_leaderboard_tables.sql Creates leaderboard and stats tables.
Files not reviewed (4)
  • .sqlx/query-00eed700b05fd13ddbbe88daed394e408acdc61d36618361f302bf25ca3f15a4.json: Language not supported
  • .sqlx/query-1697cc6c5888d46b40bde260b125576e3b31d93275b267b5f2c8f39e4b7fe2ba.json: Language not supported
  • .sqlx/query-8fe3e023fde759a78e0c4071f658cc4ed4cf29158360ce3f17b87b403dcf9f15.json: Language not supported
  • .sqlx/query-d1a8d44f4608a7ca73e7e96d88678b0afdd72f2b91332a47f7f41627230dd989.json: Language not supported

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +21 to +25
let leaderboard = sqlx::query_as::<_, LeaderboardWithMember>(
"SELECT l.*, m.name AS member_name
FROM leaderboard l
JOIN member m ON l.member_id = m.member_id
ORDER BY unified_score DESC",
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The struct LeaderboardWithMember places member_name as the third field, but this query selects l.* (which yields member_name last) and appends member_name at the end, causing column-to-field misalignment and a runtime row decoding error. Explicitly list columns in the correct order, e.g. SELECT l.id, l.member_id, m.name AS member_name, l.leetcode_score, l.codeforces_score, l.unified_score, l.last_updated ....

Copilot uses AI. Check for mistakes.
Comment on lines +39 to +44
let leetcode_stats = sqlx::query_as::<_, LeetCodeStatsWithName>(
"SELECT l.*, m.name AS member_name
FROM leetcode_stats l
JOIN member m ON l.member_id = m.member_id
ORDER BY best_rank",
)
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LeetCodeStatsWithName expects member_name as the third field but l.* places leetcode_username third; this will mis-map fields and likely fail decoding. Replace l.* with an ordered column list including m.name in the third position.

Copilot uses AI. Check for mistakes.
Comment on lines +57 to +62
let codeforces_stats = sqlx::query_as::<_, CodeforcesStatsWithName>(
"SELECT c.*, m.name AS member_name
FROM codeforces_stats c
JOIN member m ON c.member_id = m.member_id
ORDER BY max_rating DESC",
)
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c.* includes the id column (first) which is not present in CodeforcesStatsWithName and also places member_name last, causing column/field mismatch; decoding will fail. Select only the needed columns in correct order: c.member_id, m.name AS member_name, c.codeforces_handle, c.codeforces_rating, c.max_rating, c.contests_participated.

Copilot uses AI. Check for mistakes.
Comment on lines +117 to +122
let rating_val = rating.max(&0);
let max_rating_val = max_rating.max(&0);
let contests_val = contests.max(&0);

// Codeforces scoring
let rating_points = (*rating_val as f64 * 0.8).round() as i32;
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rating_val, max_rating_val, and contests_val are &i32 references (from .max(&0)); subtracting references (max_rating_val - rating_val) does not compile. Convert them to owned i32 values (e.g. let rating_val = (*rating).max(0);) and adjust subsequent arithmetic.

Suggested change
let rating_val = rating.max(&0);
let max_rating_val = max_rating.max(&0);
let contests_val = contests.max(&0);
// Codeforces scoring
let rating_points = (*rating_val as f64 * 0.8).round() as i32;
let rating_val = *rating.max(&0);
let max_rating_val = *max_rating.max(&0);
let contests_val = *contests.max(&0);
// Codeforces scoring
let rating_points = (rating_val as f64 * 0.8).round() as i32;

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,303 @@
use reqwest;
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This top-level use brings the crate name into scope redundantly since you already refer to reqwest::get and create Client via reqwest::Client; the crate name is available without a use statement. Remove this unused import to reduce noise.

Suggested change
use reqwest;

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +85
if let Ok(Some(leetcode_stats)) = sqlx::query_as::<_, LeetCodeStats>(
"SELECT leetcode_username FROM leetcode_stats WHERE member_id = $1 AND leetcode_username IS NOT NULL AND leetcode_username != ''",
)
.bind(member.member_id)
.fetch_optional(pool.as_ref())
.await {
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The query returns a single column (leetcode_username) but LeetCodeStats expects all columns defined in the model; this will cause a decode error. Use a lightweight struct with only leetcode_username or select all required columns matching LeetCodeStats.

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +99
if let Ok(Some(codeforces_stats)) = sqlx::query_as::<_, CodeforcesStats>(
"SELECT codeforces_handle FROM codeforces_stats WHERE member_id = $1 AND codeforces_handle IS NOT NULL AND codeforces_handle != ''",
)
.bind(member.member_id)
.fetch_optional(pool.as_ref())
.await {
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to the LeetCode query, only codeforces_handle is selected while CodeforcesStats expects multiple columns; this will fail row decoding. Either select all columns or map to a minimal handle-only struct.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +107
for member in &members {
// Update LeetCode stats
if let Ok(Some(leetcode_stats)) = sqlx::query_as::<_, LeetCodeStats>(
"SELECT leetcode_username FROM leetcode_stats WHERE member_id = $1 AND leetcode_username IS NOT NULL AND leetcode_username != ''",
)
.bind(member.member_id)
.fetch_optional(pool.as_ref())
.await {
let username = leetcode_stats.leetcode_username.clone();

match fetch_and_update_leetcode(pool.clone(), member.member_id, &username).await {
Ok(_) => debug!("LeetCode stats updated for member ID: {}", member.member_id),
Err(e) => error!("Failed to update LeetCode stats for member ID {}: {:?}", member.member_id, e),
}
}

if let Ok(Some(codeforces_stats)) = sqlx::query_as::<_, CodeforcesStats>(
"SELECT codeforces_handle FROM codeforces_stats WHERE member_id = $1 AND codeforces_handle IS NOT NULL AND codeforces_handle != ''",
)
.bind(member.member_id)
.fetch_optional(pool.as_ref())
.await {
let username = codeforces_stats.codeforces_handle.clone();

match fetch_and_update_codeforces_stats(pool.clone(), member.member_id, &username).await {
Ok(_) => debug!("Codeforces stats updated for member ID: {}", member.member_id),
Err(e) => error!("Failed to update Codeforces stats for member ID {}: {:?}", member.member_id, e),
}
}
}
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This performs up to two separate queries per member (N+1 pattern) and sequential external API calls, which will not scale; batch fetch existing usernames/handles first and parallelize external calls with bounded concurrency (e.g. FuturesUnordered + semaphore).

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +16
let url = format!("https://codeforces.com/api/user.rating?handle={username}");
let response = reqwest::get(&url).await?.text().await?;
let data: Value = serde_json::from_str(&response)?;
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

External HTTP call lacks an explicit timeout; a hanging upstream request could stall the task—configure a Client with a timeout (e.g. Client::builder().timeout(...).build()) and propagate errors.

Copilot uses AI. Check for mistakes.
Comment on lines +232 to +240
let response = client
.post(url)
.header("Content-Type", "application/json")
.json(&serde_json::json!({
"query": query,
"variables": { "username": username }
}))
.send()
.await?;
Copy link

Copilot AI Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LeetCode request also lacks a timeout; set a client-level timeout to avoid indefinite waits and consider retry/backoff for transient failures.

Copilot uses AI. Check for mistakes.
@ivinjabraham
Copy link
Member

Don't know if this is still planned, if it is please create an issue for it too @AadarshM07. Thanks.

@hrideshmg
Copy link
Member

Don't know if this is still planned, if it is please create an issue for it too @AadarshM07. Thanks.

It is, waiting for @AadarshM07 to resolve the copilot reviews and rebase with the refactored version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants