From 0d059f6ed6b1d496f9ad579f014cb2bd483256ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=8F=E5=90=91=E5=A4=9C?= <46275354+fu050409@users.noreply.github.com> Date: Sat, 7 Dec 2024 05:00:56 +0800 Subject: [PATCH] fix(index): fix index page (#56) * feat(contest): support list contest * chore: bump version * feat(contest): support for contest * chore: fix code lint * feat(rank): support rank --- .changes/contest.md | 5 +++++ .changes/create-contest.md | 5 +++++ .changes/index.md | 5 +++++ .changes/rank.md | 5 +++++ .gitignore | 3 +++ Cargo.lock | 2 +- src/models/contest.rs | 9 +++++++++ src/routes/contest.rs | 21 ++++++++++++++++++++- src/routes/index.rs | 9 +++++++-- src/utils/contest.rs | 34 +++++++++++++++++++++++++++++++++- 10 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 .changes/contest.md create mode 100644 .changes/create-contest.md create mode 100644 .changes/index.md create mode 100644 .changes/rank.md diff --git a/.changes/contest.md b/.changes/contest.md new file mode 100644 index 0000000..e18d029 --- /dev/null +++ b/.changes/contest.md @@ -0,0 +1,5 @@ +--- +"algohub-server": patch:feat +--- + +Support create and list all contests. diff --git a/.changes/create-contest.md b/.changes/create-contest.md new file mode 100644 index 0000000..7ae0fc9 --- /dev/null +++ b/.changes/create-contest.md @@ -0,0 +1,5 @@ +--- +"algohub-server": patch:feat +--- + +Support `ContestProblem` model to display submits and acceptations. diff --git a/.changes/index.md b/.changes/index.md new file mode 100644 index 0000000..3ec940b --- /dev/null +++ b/.changes/index.md @@ -0,0 +1,5 @@ +--- +"algohub-server": patch:fix +--- + +Fixed index pages cannot be displayed correctly. diff --git a/.changes/rank.md b/.changes/rank.md new file mode 100644 index 0000000..3b82eca --- /dev/null +++ b/.changes/rank.md @@ -0,0 +1,5 @@ +--- +"algohub-server": patch:feat +--- + +Support enpoints for getting rank list. diff --git a/.gitignore b/.gitignore index 045c110..faf75ff 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ # Node.js /node_modules + +# Assets +/dist diff --git a/Cargo.lock b/Cargo.lock index 8950848..324eefd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ [[package]] name = "algohub-server" -version = "0.1.13" +version = "0.1.14" dependencies = [ "anyhow", "chrono", diff --git a/src/models/contest.rs b/src/models/contest.rs index c447ced..d5c50e0 100644 --- a/src/models/contest.rs +++ b/src/models/contest.rs @@ -127,3 +127,12 @@ pub struct ContestProblem { pub submitted_count: u32, pub accepted_count: u32, } + +#[derive(Serialize, Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct ContestRank { + name: String, + problem_id: String, + accepted: bool, + wrongs: u32, +} diff --git a/src/routes/contest.rs b/src/routes/contest.rs index 143ff0a..40554ba 100644 --- a/src/routes/contest.rs +++ b/src/routes/contest.rs @@ -3,7 +3,7 @@ use surrealdb::{engine::remote::ws::Client, sql::Thing, Surreal}; use crate::{ models::{ - contest::{AddProblems, ContestProblem, CreateContest, UserContest}, + contest::{AddProblems, ContestProblem, ContestRank, CreateContest, UserContest}, error::Error, response::{Empty, Response}, Credentials, OwnedId, @@ -119,6 +119,25 @@ pub async fn get( })) } +#[post("/rank/", data = "")] +pub async fn rank( + db: &State>, + id: &str, + auth: Json>, +) -> Result> { + if !session::verify(db, auth.id, auth.token).await { + return Err(Error::Unauthorized(Json("Invalid credentials".into()))); + } + + let rank = contest::rank(db, id).await?; + + Ok(Json(Response { + success: true, + message: "Contest rank retrieved successfully".into(), + data: Some(rank), + })) +} + pub fn routes() -> Vec { use rocket::routes; routes![create, get, add_problems, list_problems, list_all] diff --git a/src/routes/index.rs b/src/routes/index.rs index ac03cdb..ee7638b 100644 --- a/src/routes/index.rs +++ b/src/routes/index.rs @@ -12,8 +12,13 @@ use rocket::fs::NamedFile; use surrealdb::engine::remote::ws::Client; use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, Surreal}; +#[get("/")] +async fn index() -> Result { + NamedFile::open("dist/index.html").await +} + #[get("/", rank = 1)] -async fn index(file: PathBuf) -> Option { +async fn files(file: PathBuf) -> Option { NamedFile::open(Path::new("dist/").join(file)).await.ok() } @@ -39,7 +44,7 @@ pub async fn init_db(db_addr: &str) -> Result> { pub async fn rocket(db: Surreal) -> rocket::Rocket { rocket::build() .attach(CORS) - .mount("/", routes![index]) + .mount("/", routes![index, files]) .mount("/account", account::routes()) .mount("/asset", asset::routes()) .mount("/problem", problem::routes()) diff --git a/src/utils/contest.rs b/src/utils/contest.rs index ed5897c..5025cfc 100644 --- a/src/utils/contest.rs +++ b/src/utils/contest.rs @@ -1,7 +1,7 @@ use anyhow::Result; use surrealdb::{engine::remote::ws::Client, sql::Thing, Surreal}; -use crate::models::contest::{Contest, ContestData, ContestProblem}; +use crate::models::contest::{Contest, ContestData, ContestProblem, ContestRank}; pub async fn create( db: &Surreal, @@ -106,3 +106,35 @@ pub async fn remove_problem( .await? .take(0)?) } + +const RANK_QUERY: &str = r#" +SELECT VALUE array::map((SELECT VALUE id FROM type::thing("contest", $id).problems), |$problem| { + LET $submissions = SELECT judge_result.status.type AS status, created_at + FROM submission WHERE problem.id == $problem AND $parent.id == creator ORDER BY created_at ASC; + LET $first_accepted = array::find_index($submissions, |$submission| { + RETURN $submission.status == "accepted" + }); + RETURN IF $first_accepted { + RETURN { + name: $problem.title, + problem_id: record::id($problem), + accepted: true, + wrongs: count($submissions) - $first_accepted - 1, + } + } ELSE { + RETURN { + name: $problem.title, + problem_id: record::id($problem), + accepted: false, + wrongs: count($submissions), + } + } +}) FROM array::distinct(SELECT VALUE creator FROM submission) +"#; +pub async fn rank(db: &Surreal, id: &str) -> Result> { + Ok(db + .query(RANK_QUERY) + .bind(("id", id.to_string())) + .await? + .take(0)?) +}