From bfd73f2a3d80ed356d6de08b4f63a113fcab4bfa Mon Sep 17 00:00:00 2001 From: K0nnyaku Date: Sat, 30 Nov 2024 12:26:37 +0800 Subject: [PATCH] refector: submission (#23) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refector: submission * chore: format the code * feat: remove `status` field * feat: get by id --------- Co-authored-by: 苏向夜 <46275354+fu050409@users.noreply.github.com> --- Cargo.lock | 18 +----- Cargo.toml | 2 +- src/lib.rs | 2 + src/models/mod.rs | 1 + src/models/submission.rs | 33 +++++++++++ src/routes/problem.rs | 118 +-------------------------------------- src/routes/submission.rs | 70 +++++++++++++++++++++++ src/utils/submission.rs | 30 ++++++++++ 8 files changed, 142 insertions(+), 132 deletions(-) create mode 100644 src/models/submission.rs create mode 100644 src/routes/submission.rs create mode 100644 src/utils/submission.rs diff --git a/Cargo.lock b/Cargo.lock index efbeaf8..bc2a920 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1000,13 +1000,13 @@ dependencies = [ [[package]] name = "eval-stack" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c9d45b30cbca6cacd3632308c99919af682246286e5006594d7ec72c7d9b8a" +checksum = "9125fb2a805170969cfa4ba8138589dc57d8b3a64e7bf7e600f8d1cee6494940" dependencies = [ "anyhow", "libc", - "nix", + "serde", "tokio", ] @@ -2180,18 +2180,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "noisy_float" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index f6b912a..9bd0dcb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ license = "AGPL-3.0-only" [dependencies] anyhow = "1.0.92" chrono = { version = "0.4.38", features = ["serde"] } -eval-stack = "0.1.0" +eval-stack = { version = "0.1.1", features = ["serde"] } rocket = { version = "0.5.1", features = ["json"] } serde = { version = "1.0.214", features = ["derive"] } surrealdb = "2.1.0" diff --git a/src/lib.rs b/src/lib.rs index 120b4d8..febc4a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ pub mod utils { pub mod organization; pub mod problem; pub mod session; + pub mod submission; } pub mod routes { @@ -15,6 +16,7 @@ pub mod routes { pub mod contest; pub mod index; pub mod organization; + pub mod submission; pub mod problem; } diff --git a/src/models/mod.rs b/src/models/mod.rs index c9f24c4..bc95eb8 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -6,5 +6,6 @@ pub mod organization; pub mod problem; pub mod response; pub mod shared; +pub mod submission; pub use shared::*; diff --git a/src/models/submission.rs b/src/models/submission.rs new file mode 100644 index 0000000..28e4c4e --- /dev/null +++ b/src/models/submission.rs @@ -0,0 +1,33 @@ +use eval_stack::{compile::Language, judge::JudgeResult}; +use serde::{Deserialize, Serialize}; +use surrealdb::sql::Thing; + +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(untagged, rename_all = "snake_case")] +pub enum Status { + InQueue, + Judging, + Ready, +} + +#[derive(Serialize, Deserialize)] +pub struct Submission { + pub id: Option, + + pub lang: Language, + pub problem_id: String, + pub code: String, + pub status: Status, + pub results: Vec, + pub creator: Thing, + + pub created_at: chrono::NaiveDateTime, + pub updated_at: chrono::NaiveDateTime, +} + +#[derive(Serialize, Deserialize)] +pub struct UserSubmission<'r> { + pub lang: Language, + pub problem_id: &'r str, + pub code: String, +} diff --git a/src/routes/problem.rs b/src/routes/problem.rs index ba4daf7..5b07221 100644 --- a/src/routes/problem.rs +++ b/src/routes/problem.rs @@ -1,14 +1,4 @@ -use std::time::Duration; - -use eval_stack::{compile::Language, config::JudgeOptions, judge::JudgeStatus}; -use rocket::{ - serde::json::Json, - tokio::{ - fs::{create_dir_all, File}, - io::AsyncWriteExt, - }, - State, -}; +use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; use surrealdb::{engine::remote::ws::Client, Surreal}; @@ -178,111 +168,7 @@ pub async fn list( })) } -#[derive(Serialize, Deserialize)] -pub struct SubmitData<'r> { - pub id: &'r str, - pub token: &'r str, - pub language: &'r str, - pub code: String, -} - -#[derive(Serialize, Deserialize)] -pub struct SubmitResponse { - pub status: String, -} - -#[post("/submit/", data = "")] -pub async fn submit( - db: &State>, - id: &str, - data: Json>, -) -> Result { - if !session::verify(db, data.id, data.token).await { - return Err(Error::Unauthorized(Json("Invalid token".into()))); - } - - let problem = problem::get::(db, id) - .await - .map_err(|e| Error::ServerError(Json(e.to_string().into())))? - .ok_or(Error::NotFound(Json( - "Problem with specified id not found".into(), - )))?; - - let options = JudgeOptions { - time_limit: Duration::from_secs(problem.time_limit), - memory_limit: problem.memory_limit, - }; - - let workspace = std::env::current_dir() - .unwrap() - .join("content") - .join(data.id) - .join("workspace") - .join(uuid::Uuid::new_v4().to_string()); - - if !workspace.exists() { - create_dir_all(&workspace) - .await - .map_err(|e| Error::ServerError(Json(e.to_string().into())))?; - } - - let language = match data.language { - "rust" => Language::Rust, - "c" => Language::C, - "cpp" => Language::CPP, - "python" => Language::Python, - _ => Language::Rust, - }; - - let source_file_path = workspace.join("source.code"); - File::create(&source_file_path) - .await - .map_err(|e| Error::ServerError(Json(e.to_string().into())))? - .write_all(data.code.as_bytes()) - .await - .unwrap(); - - println!("{:?}", problem.test_cases); - let result = eval_stack::case::run_test_cases( - language, - workspace, - source_file_path, - options, - problem - .test_cases - .iter() - .map(|s| (s.input.clone(), s.output.clone())) - .collect(), - true, - ) - .await - .map_err(|e| Error::ServerError(Json(e.to_string().into())))?; - println!("{:?}", result); - - let err = result - .iter() - .find(|&r| !matches!(r.status, JudgeStatus::Accepted)); - - if let Some(_e) = err { - Ok(Json(Response { - success: true, - message: "Unaccepted".to_string(), - data: Some(SubmitResponse { - status: "Wrong Answer".to_string(), - }), - })) - } else { - Ok(Json(Response { - success: true, - message: "Accepted".to_string(), - data: Some(SubmitResponse { - status: "Accepted".to_string(), - }), - })) - } -} - pub fn routes() -> Vec { use rocket::routes; - routes![create, get, submit, list] + routes![create, get, list] } diff --git a/src/routes/submission.rs b/src/routes/submission.rs new file mode 100644 index 0000000..e48f0ed --- /dev/null +++ b/src/routes/submission.rs @@ -0,0 +1,70 @@ +use rocket::{serde::json::Json, State}; +use serde::{Deserialize, Serialize}; +use surrealdb::{engine::remote::ws::Client, Surreal}; + +use crate::{ + models::{ + error::Error, + response::Response, + submission::{Submission, UserSubmission}, + Credentials, + }, + utils::{session, submission}, + Result, +}; + +#[derive(Serialize, Deserialize)] +pub struct CreateSubmission<'r> { + pub id: &'r str, + pub token: &'r str, + + pub sub: UserSubmission<'r>, +} + +#[derive(Serialize, Deserialize)] +pub struct SubmitResponse { + pub id: String, +} + +#[post("/submit", data = "")] +pub async fn submit( + db: &State>, + data: Json>, +) -> Result { + if !session::verify(db, data.id, data.token).await { + return Err(Error::Unauthorized(Json("Invalid token".into()))); + } + + let submission = submission::create(db, data.id, data.into_inner().sub) + .await + .map_err(|e| Error::ServerError(Json(e.to_string().into())))? + .ok_or(Error::ServerError(Json( + "Failed to submit, please try again later.".into(), + )))?; + + Ok(Json(Response { + success: true, + message: "Submission created successfully".to_string(), + data: Some(SubmitResponse { + id: submission.id.unwrap().id.to_string(), + }), + })) +} + +#[post("/get/", data = "<_auth>")] +pub async fn get( + db: &State>, + id: &str, + _auth: Json>, +) -> Result { + let submission = submission::get_by_id(db, id) + .await + .map_err(|e| Error::ServerError(Json(e.to_string().into())))? + .ok_or(Error::NotFound(Json("Submission not found".into())))?; + + Ok(Json(Response { + success: true, + message: "Submission fetched successfully".to_string(), + data: Some(submission.into()), + })) +} diff --git a/src/utils/submission.rs b/src/utils/submission.rs new file mode 100644 index 0000000..30d3a33 --- /dev/null +++ b/src/utils/submission.rs @@ -0,0 +1,30 @@ +use crate::models::submission::{Submission, UserSubmission}; +use anyhow::Result; +use surrealdb::{engine::remote::ws::Client, Surreal}; + +pub async fn create<'a>( + db: &'a Surreal, + id: &str, + submission: UserSubmission<'a>, +) -> Result> { + Ok(db + .create("submission") + .content(Submission { + id: None, + lang: submission.lang, + code: submission.code, + problem_id: submission.problem_id.to_string(), + status: crate::models::submission::Status::InQueue, + + creator: ("account", id).into(), + results: vec![], + + created_at: chrono::Local::now().naive_local(), + updated_at: chrono::Local::now().naive_local(), + }) + .await?) +} + +pub async fn get_by_id(db: &Surreal, id: &str) -> Result> { + Ok(db.select(("submission", id)).await?) +}