Skip to content

Commit

Permalink
refector: submission (#23)
Browse files Browse the repository at this point in the history
* refector: submission

* chore: format the code

* feat: remove `status` field

* feat: get by id

---------

Co-authored-by: 苏向夜 <[email protected]>
  • Loading branch information
K0nnyaku and fu050409 authored Nov 30, 2024
1 parent 5809662 commit bfd73f2
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 132 deletions.
18 changes: 3 additions & 15 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ pub mod utils {
pub mod organization;
pub mod problem;
pub mod session;
pub mod submission;
}

pub mod routes {
pub mod account;
pub mod contest;
pub mod index;
pub mod organization;
pub mod submission;
pub mod problem;
}

Expand Down
1 change: 1 addition & 0 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ pub mod organization;
pub mod problem;
pub mod response;
pub mod shared;
pub mod submission;

pub use shared::*;
33 changes: 33 additions & 0 deletions src/models/submission.rs
Original file line number Diff line number Diff line change
@@ -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<Thing>,

pub lang: Language,
pub problem_id: String,
pub code: String,
pub status: Status,
pub results: Vec<JudgeResult>,
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,
}
118 changes: 2 additions & 116 deletions src/routes/problem.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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/<id>", data = "<data>")]
pub async fn submit(
db: &State<Surreal<Client>>,
id: &str,
data: Json<SubmitData<'_>>,
) -> Result<SubmitResponse> {
if !session::verify(db, data.id, data.token).await {
return Err(Error::Unauthorized(Json("Invalid token".into())));
}

let problem = problem::get::<Problem>(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<rocket::Route> {
use rocket::routes;
routes![create, get, submit, list]
routes![create, get, list]
}
70 changes: 70 additions & 0 deletions src/routes/submission.rs
Original file line number Diff line number Diff line change
@@ -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 = "<data>")]
pub async fn submit(
db: &State<Surreal<Client>>,
data: Json<CreateSubmission<'_>>,
) -> Result<SubmitResponse> {
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/<id>", data = "<_auth>")]
pub async fn get(
db: &State<Surreal<Client>>,
id: &str,
_auth: Json<Credentials<'_>>,
) -> Result<Submission> {
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()),
}))
}
30 changes: 30 additions & 0 deletions src/utils/submission.rs
Original file line number Diff line number Diff line change
@@ -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<Client>,
id: &str,
submission: UserSubmission<'a>,
) -> Result<Option<Submission>> {
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<Client>, id: &str) -> Result<Option<Submission>> {
Ok(db.select(("submission", id)).await?)
}

0 comments on commit bfd73f2

Please sign in to comment.