Skip to content

Commit

Permalink
feat: hall-of-fame entry point (#201)
Browse files Browse the repository at this point in the history
  • Loading branch information
akorchyn authored Nov 7, 2024
1 parent 0dbb222 commit 33c6d7a
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 16 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions server/sql/get_hall_of_fame.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
SELECT
u.id,
u.login,
u.full_name,
u.permanent_bonus,
upd.total_rating
FROM
users AS u
JOIN user_period_data upd ON upd.user_id = u.id
AND upd.period_type = $1
WHERE
u.permanent_bonus > 0
ORDER BY
u.permanent_bonus desc,
upd.total_rating desc
LIMIT
$2 OFFSET $3
30 changes: 29 additions & 1 deletion server/src/db/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub struct DB(PgPool);

pub mod types;

use types::{LeaderboardRecord, Statistics};
use types::{HallOfFameRecord, LeaderboardRecord, Statistics};

use self::types::{
RepoLeaderboardRecord, RepoRecord, StreakRecord, User, UserCachedMetadata,
Expand Down Expand Up @@ -893,6 +893,34 @@ impl DB {

Ok(rec)
}

pub async fn get_hall_of_fame(
&self,
period: &str,
page: i64,
limit: i64,
) -> anyhow::Result<(Vec<HallOfFameRecord>, u64)> {
let rec: Vec<HallOfFameRecord> = sqlx::query_file_as!(
HallOfFameRecord,
"./sql/get_hall_of_fame.sql",
period,
limit,
page * limit
)
.fetch_all(&self.0)
.await?;

let total = sqlx::query!(
r#"SELECT COUNT(DISTINCT(login)) as id
FROM users
WHERE permanent_bonus > 0
"#,
)
.fetch_one(&self.0)
.await?;

Ok((rec, total.id.unwrap_or_default() as u64))
}
}

async fn run_migrations(rocket: Rocket<Build>) -> fairing::Result {
Expand Down
9 changes: 9 additions & 0 deletions server/src/db/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,12 @@ pub struct Statistics {
// CSV
pub hall_of_fame: Option<String>,
}

#[derive(Debug, Clone, sqlx::FromRow)]
pub struct HallOfFameRecord {
pub id: i32,
pub login: String,
pub full_name: Option<String>,
pub permanent_bonus: i32,
pub total_rating: i32,
}
43 changes: 41 additions & 2 deletions server/src/entrypoints/leaderboards.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;

use race_of_sloths_server::db::DB;
use race_of_sloths_server::{db::DB, types::HallOfFameResponse};
use rocket::{serde::json::Json, State};
use shared::{telegram, TimePeriod};

Expand Down Expand Up @@ -37,6 +37,40 @@ async fn get_leaderboard(
total as u64,
)))
}
#[utoipa::path(context_path = "/leaderboard", responses(
(status = 200, description = "Get hall of fame records", body = PaginatedHallOfFameResponse)
))]
#[get("/users/hall_of_fame?<period>&<page>&<limit>")]
async fn get_hall_of_fame(
db: &State<DB>,
telegram: &State<Arc<telegram::TelegramSubscriber>>,
period: Option<String>,
page: Option<u64>,
limit: Option<u64>,
) -> Option<Json<PaginatedResponse<HallOfFameResponse>>> {
let page = page.unwrap_or(0);
let limit = limit.unwrap_or(50);
let period = period.unwrap_or(TimePeriod::AllTime.time_string(0));
let (records, total) = match db
.get_hall_of_fame(&period, page as i64, limit as i64)
.await
{
Err(e) => {
race_of_sloths_server::error(
telegram,
&format!("Failed to get leaderboard: {period}: {e}"),
);
return None;
}
Ok(value) => value,
};
Some(Json(PaginatedResponse::new(
records.into_iter().map(Into::into).collect(),
page + 1,
limit,
total,
)))
}

#[utoipa::path(context_path = "/repos", responses(
(status = 200, description = "Get repo leaderboard", body = PaginatedRepoResponse)
Expand Down Expand Up @@ -90,7 +124,12 @@ pub fn stage() -> rocket::fairing::AdHoc {
rocket::fairing::AdHoc::on_ignite("Installing entrypoints", |rocket| async {
rocket.mount(
"/leaderboard",
rocket::routes![get_repos, get_leaderboard, get_potential_repos],
rocket::routes![
get_repos,
get_leaderboard,
get_potential_repos,
get_hall_of_fame
],
)
})
}
54 changes: 41 additions & 13 deletions server/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
use std::collections::HashMap;

use crate::db::types::{
LeaderboardRecord, RepoLeaderboardRecord, UserContributionRecord, UserRecord,
HallOfFameRecord, LeaderboardRecord, RepoLeaderboardRecord, UserContributionRecord, UserRecord,
};
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};
use shared::TimePeriod;
use utoipa::ToSchema;

#[derive(Clone, Debug, Serialize, Deserialize, Default, ToSchema)]
#[aliases(PaginatedLeaderboardResponse = PaginatedResponse<LeaderboardResponse>, PaginatedRepoResponse = PaginatedResponse<RepoResponse>, PaginatedUserContributionResponse = PaginatedResponse<UserContributionResponse>)]
#[aliases(
PaginatedLeaderboardResponse = PaginatedResponse<LeaderboardResponse>,
PaginatedHallOfFameResponse = PaginatedResponse<HallOfFameResponse>,
PaginatedRepoResponse = PaginatedResponse<RepoResponse>,
PaginatedUserContributionResponse = PaginatedResponse<UserContributionResponse>
)]
pub struct PaginatedResponse<T: Serialize> {
pub records: Vec<T>,
pub page: u64,
Expand Down Expand Up @@ -94,18 +99,20 @@ pub struct LeaderboardResponse {
pub scored_prs: u32,
}

fn get_rank(permanent_bonus: u32) -> String {
match permanent_bonus {
a if a >= 25 => "Rust",
a if a >= 20 => "Platinum",
a if a >= 15 => "Gold",
a if a >= 10 => "Silver",
a if a >= 5 => "Bronze",
_ => "Unranked",
}
.to_string()
}

impl From<LeaderboardRecord> for LeaderboardResponse {
fn from(record: LeaderboardRecord) -> Self {
let rank = match record.permanent_bonus {
a if a >= 25 => "Rust",
a if a >= 20 => "Platinum",
a if a >= 15 => "Gold",
a if a >= 10 => "Silver",
a if a >= 5 => "Bronze",
_ => "Unranked",
}
.to_string();

let streak = Streak::new(
"Weekly".to_string(),
record.weekly_streak_amount as u32,
Expand All @@ -132,7 +139,7 @@ impl From<LeaderboardRecord> for LeaderboardResponse {
merged_prs: record.prs_merged as u32,
score: record.total_score as u32,
place: record.place as u32,
rank,
rank: get_rank(record.permanent_bonus as u32),
scored_prs: record.prs_scored as u32,
}
}
Expand Down Expand Up @@ -355,3 +362,24 @@ impl From<crate::db::types::Statistics> for Statistics {
}
}
}

#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
pub struct HallOfFameResponse {
pub user_id: u32,
pub user: GithubMeta,
pub sloths_points: u32,
pub permanent_bonus: u32,
pub rank: String,
}

impl From<HallOfFameRecord> for HallOfFameResponse {
fn from(record: HallOfFameRecord) -> Self {
Self {
user_id: record.id as u32,
user: GithubMeta::new(record.login, record.full_name),
sloths_points: record.total_rating as u32,
permanent_bonus: record.permanent_bonus as u32,
rank: get_rank(record.permanent_bonus as u32),
}
}
}

0 comments on commit 33c6d7a

Please sign in to comment.