-
Notifications
You must be signed in to change notification settings - Fork 4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CP leaderboard implementation #18
base: master
Are you sure you want to change the base?
Conversation
swayam-agrahari
commented
Nov 15, 2024
- Added scheduled tasks to update CP ratings and insert attendance records daily at midnight.
- Implemented API calls to fetch ratings from Codeforces and LeetCode.
- Updated update_cp_ratings function to handle dynamic ratings based on platform.
- Created placeholders for test configuration to avoid hardcoding sensitive data.
- Added tests to validate rating updates for members with Codeforces and LeetCode profiles.
@@ -14,4 +13,7 @@ pub struct Member { | |||
pub sex: String, | |||
pub year: i32, | |||
pub macaddress: String, | |||
pub leaderboard_id: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this used for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the username for their respective cp platform
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah gotcha, but they should be free to do both like i said not just 1 platform
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
alright so to implement this, we might need to create another leaderboard struct as this is one to many relation with foreign key as the member's id (primary key)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep that's right
@@ -14,4 +13,7 @@ pub struct Member { | |||
pub sex: String, | |||
pub year: i32, | |||
pub macaddress: String, | |||
pub leaderboard_id: String, | |||
pub cp_platform: String, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
aren't members restricted to only one platform if we do it like this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, they will be. I thought we were only allowed to choose one and given the current scenario, no one is trying for both.
mod tests { | ||
use super::*; | ||
|
||
// Mocking the PgPool for testing purposes (if necessary) | ||
use sqlx::PgPool; | ||
|
||
#[tokio::test] | ||
// Update these variables with the actual values before running the test | ||
|
||
async fn test_fetch_codeforces_rating() { | ||
let codeforces_username = ""; // Add your Codeforces username here | ||
let result = fetch_codeforces_rating(codeforces_username).await; | ||
assert!(result.is_ok()); | ||
let rating = result.unwrap(); | ||
assert!(rating.is_some()); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_fetch_leetcode_ranking() { | ||
let leetcode_username = ""; // Add your LeetCode username here | ||
let result = fetch_leetcode_ranking(leetcode_username).await; | ||
assert!(result.is_ok()); | ||
let ranking = result.unwrap(); | ||
assert!(ranking.is_some()); | ||
} | ||
|
||
#[tokio::test] | ||
async fn test_scheduled_task() { | ||
let database_url = ""; // Add your database URL here | ||
let pool = Arc::new(PgPool::connect_lazy(database_url).unwrap()); | ||
|
||
scheduled_task(pool).await; | ||
} | ||
|
||
// Test for update_cp_ratings | ||
#[tokio::test] | ||
async fn test_update_cp_ratings() { | ||
let database_url = ""; // Add your database URL here | ||
let pool = Arc::new(PgPool::connect(database_url).await.unwrap()); | ||
update_cp_ratings(pool).await; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good to add tests but should be in a separate directory not in main.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah sure will do it. I also thought about creating more tests for seeing members for dev purpose so this function can lie there.
username: &str, | ||
) -> Result<Option<i32>, Box<dyn std::error::Error>> { | ||
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)?; | ||
|
||
if data["status"] == "OK" { | ||
if let Some(results) = data["result"].as_array() { | ||
if let Some(last_contest) = results.last() { | ||
let new_rating = last_contest["newRating"].as_i64().unwrap_or_default() as i32; | ||
return Ok(Some(new_rating)); | ||
} | ||
} | ||
} | ||
Ok(None) | ||
} | ||
|
||
// Function to fetch LeetCode ranking | ||
async fn fetch_leetcode_ranking(username: &str) -> Result<Option<i32>, Box<dyn std::error::Error>> { | ||
let client = reqwest::Client::new(); | ||
let url = "https://leetcode.com/graphql"; | ||
let query = r#" | ||
query userPublicProfile($username: String!) { | ||
matchedUser(username: $username) { | ||
profile { | ||
ranking | ||
} | ||
} | ||
} | ||
"#; | ||
|
||
let response = client | ||
.post(url) | ||
.header("Content-Type", "application/json") | ||
.json(&serde_json::json!({ | ||
"query": query, | ||
"variables": { | ||
"username": username | ||
} | ||
})) | ||
.send() | ||
.await?; | ||
|
||
let data: Value = response.json().await?; | ||
let ranking = data["data"]["matchedUser"]["profile"]["ranking"] | ||
.as_i64() | ||
.map(|v| v as i32); | ||
|
||
Ok(ranking) | ||
} | ||
|
||
// Fetch and update CP ratings for all members | ||
async fn update_cp_ratings(pool: Arc<PgPool>) { | ||
let members: Result<Vec<Member>, sqlx::Error> = | ||
sqlx::query_as::<_, Member>("SELECT * FROM Member") | ||
.fetch_all(pool.as_ref()) | ||
.await; | ||
|
||
match members { | ||
Ok(members) => { | ||
for member in members { | ||
let rating = match member.cp_platform.as_str() { | ||
"Codeforces" => fetch_codeforces_rating(&member.leaderboard_id) | ||
.await | ||
.ok() | ||
.flatten(), | ||
"LeetCode" => fetch_leetcode_ranking(&member.leaderboard_id) | ||
.await | ||
.ok() | ||
.flatten(), | ||
_ => None, | ||
}; | ||
|
||
if let Some(rating) = rating { | ||
let update_result = sqlx::query("UPDATE Member SET rating = $1 WHERE id = $2") | ||
.bind(rating) | ||
.bind(member.id) | ||
.execute(pool.as_ref()) | ||
.await; | ||
|
||
match update_result { | ||
Ok(_) => println!("Updated rating for {}: {}", member.name, rating), | ||
Err(e) => eprintln!("Failed to update rating for {}: {:?}", member.name, e), | ||
} | ||
} | ||
} | ||
} | ||
Err(e) => eprintln!("Failed to fetch members: {:?}", e), | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Single Leaderboard with Weighted Scores:
Combines scores from LeetCode and Codeforces into a unified ranking.
-
Pros: Simple, gives a balanced view, and avoids splitting attention.
-
Cons: Needs agreement on weightage and a formula to merge scores fairly.
Separate Leaderboards:
Maintains distinct rankings for each platform.
-
Pros: Highlights specific performance on each platform.
-
Cons: Can feel redundant or less competitive if interest skews heavily toward one platform
can you ask in discord which is preferred? make sure to highlite these pros and cons
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes done : )