Skip to content

Commit

Permalink
feat(contest): support list contest (#52)
Browse files Browse the repository at this point in the history
* feat(contest): support list contest

* chore: bump version
  • Loading branch information
fu050409 authored Dec 6, 2024
1 parent be8a9d6 commit 63283e7
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 59 deletions.
5 changes: 5 additions & 0 deletions .changes/contest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"algohub-server": patch:feat
---

Support create and list all contests.
2 changes: 1 addition & 1 deletion Cargo.lock

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

3 changes: 2 additions & 1 deletion src/models/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub struct Login<'r> {
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(crate = "rocket::serde")]
pub struct Profile {
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
Expand All @@ -61,7 +62,7 @@ pub struct Profile {
#[serde(skip_serializing_if = "Option::is_none")]
pub sex: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub birthday: Option<String>,
pub birthday: Option<chrono::NaiveDateTime>,

#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
Expand Down
48 changes: 47 additions & 1 deletion src/models/contest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ pub struct Contest {

pub start_time: chrono::NaiveDateTime,
pub end_time: chrono::NaiveDateTime,
pub problems: Vec<Thing>,

pub owner: Thing,
pub creator: Thing,
Expand Down Expand Up @@ -71,3 +70,50 @@ pub struct RemoveProblem {
pub contest_id: Thing,
pub problem_id: Thing,
}

#[derive(Serialize, Deserialize)]
pub struct UserContest {
pub id: String,

pub name: String,
pub mode: Mode,
pub visibility: Visibility,
pub description: String,
pub announcement: Option<String>,

pub start_time: chrono::NaiveDateTime,
pub end_time: chrono::NaiveDateTime,

pub owner: UserRecordId,
pub creator: String,
pub updaters: Vec<String>,
pub participants: Vec<String>,

pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
}

impl From<Contest> for UserContest {
fn from(value: Contest) -> Self {
UserContest {
id: value.id.unwrap().id.to_string(),
name: value.name,
mode: value.mode,
visibility: value.visibility,
description: value.description,
announcement: value.announcement,
start_time: value.start_time,
end_time: value.end_time,
owner: value.owner.into(),
creator: value.creator.to_string(),
updaters: value.updaters.iter().map(|x| x.id.to_string()).collect(),
participants: value
.participants
.iter()
.map(|x| x.id.to_string())
.collect(),
created_at: value.created_at,
updated_at: value.updated_at,
}
}
}
2 changes: 2 additions & 0 deletions src/models/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub struct Problem {
pub tags: Vec<String>,

pub visibility: ProblemVisibility,
pub contest: Option<Thing>,

pub created_at: chrono::NaiveDateTime,
pub updated_at: chrono::NaiveDateTime,
Expand Down Expand Up @@ -110,6 +111,7 @@ impl From<CreateProblem<'_>> for Problem {
categories: val.categories,
tags: val.tags,
visibility: val.visibility,
contest: None,
created_at: chrono::Local::now().naive_local(),
updated_at: chrono::Local::now().naive_local(),
}
Expand Down
20 changes: 6 additions & 14 deletions src/routes/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,30 +55,26 @@ pub async fn profile(
profile: Json<MergeProfile<'_>>,
) -> Result<Empty> {
account::get_by_id::<Record>(db, profile.id)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?
.await?
.ok_or(Error::NotFound(Json("Account not found".into())))?;

if session::verify(db, profile.id, profile.token).await {
account::merge_profile(db, profile.id, profile.profile.clone())
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?;
account::merge_profile(db, profile.id, profile.profile.clone()).await?;
Ok(Response {
success: true,
message: "Profile updated successfully".into(),
data: None,
}
.into())
} else {
Err(Error::Unauthorized(Json("Invalid token".into())))
Err(Error::Unauthorized(Json("Invalid credentials".into())))
}
}

#[get("/profile/<id>")]
pub async fn get_profile(db: &State<Surreal<Client>>, id: &str) -> Result<Profile> {
let profile = account::get_by_identity::<Profile>(db, id)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?
.await?
.ok_or(Error::NotFound(Json("Account not found".into())))?;

Ok(Response {
Expand All @@ -97,13 +93,9 @@ pub async fn delete(db: &State<Surreal<Client>>, id: &str, auth: Json<Token<'_>>
)));
}

account::delete(db, id)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?;
account::delete(db, id).await?;

remove_dir_all(Path::new("content/").join(id))
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?;
remove_dir_all(Path::new("content/").join(id)).await?;

Ok(Response {
success: true,
Expand Down
44 changes: 24 additions & 20 deletions src/routes/contest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use surrealdb::{engine::remote::ws::Client, sql::Thing, Surreal};

use crate::{
models::{
contest::{AddProblems, CreateContest},
contest::{AddProblems, CreateContest, UserContest},
error::Error,
response::{Empty, Response},
OwnedId,
Credentials, OwnedId,
},
utils::{contest, session},
Result,
Expand All @@ -15,13 +15,12 @@ use crate::{
#[post("/create", data = "<contest>")]
pub async fn create(db: &State<Surreal<Client>>, contest: Json<CreateContest>) -> Result<OwnedId> {
if !session::verify(db, &contest.auth.id, &contest.auth.token).await {
return Err(Error::Unauthorized(Json("Invalid session".into())));
return Err(Error::Unauthorized(Json("Invalid credentials".into())));
}

let contest = contest.into_inner();
let contest = contest::create(db, &contest.auth.id, contest.data)
.await
.map_err(|e| Error::ServerError(Json(e.into())))?
.await?
.ok_or(Error::ServerError(Json("Failed to create contest".into())))?;

Ok(Json(Response {
Expand All @@ -34,27 +33,15 @@ pub async fn create(db: &State<Surreal<Client>>, contest: Json<CreateContest>) -
}

#[post("/problems/add", data = "<data>")]
pub async fn add_problem(
pub async fn add_problems(
db: &State<Surreal<Client>>,
data: Json<AddProblems<'_>>,
) -> Result<Empty> {
if !session::verify(db, &data.auth.id, &data.auth.token).await {
return Err(Error::Unauthorized(Json("Invalid session".into())));
return Err(Error::Unauthorized(Json("Invalid credentials".into())));
}

let problem = data.into_inner();
contest::add_problems(
db,
problem.contest_id,
&problem
.problem_ids
.iter()
.map(|&p| Thing::from(("problem", p)))
.collect::<Vec<Thing>>(),
)
.await
.map_err(|e| Error::ServerError(Json(e.into())))?
.ok_or(Error::NotFound(Json("Contest not found".into())))?;

Ok(Json(Response {
success: true,
Expand All @@ -63,7 +50,24 @@ pub async fn add_problem(
}))
}

#[post("/list/all", data = "<auth>")]
pub async fn list_all(
db: &State<Surreal<Client>>,
auth: Json<Credentials<'_>>,
) -> Result<Vec<UserContest>> {
if !session::verify(db, &auth.id, &auth.token).await {
return Err(Error::Unauthorized(Json("Invalid credentials".into())));
}

let contests = contest::list_all(db).await?;
Ok(Json(Response {
success: true,
message: "Contests listed successfully".into(),
data: Some(contests.into_iter().map(|c| c.into()).collect()),
}))
}

pub fn routes() -> Vec<rocket::Route> {
use rocket::routes;
routes![create, add_problem]
routes![create, add_problems, list_all]
}
10 changes: 4 additions & 6 deletions src/routes/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ pub async fn list(
) -> Result<Vec<UserProblem>> {
let authed_id = if let Some(auth) = &data.auth {
if !session::verify(db, &auth.id, &auth.token).await {
return Err(Error::Unauthorized(Json("Invalid token".into())));
return Err(Error::Unauthorized(Json("Invalid credentials".into())));
};
Some(auth.id.clone())
} else {
Expand All @@ -125,8 +125,7 @@ pub async fn list(
let account_id = if let Some(identity) = data.identity.clone() {
Some(
account::get_by_identity::<Account>(db, &identity)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?
.await?
.ok_or(Error::Unauthorized(Json("Invalid identity".into())))?
.id
.unwrap()
Expand All @@ -137,9 +136,8 @@ pub async fn list(
None
};

let problems = problem::list_for_account::<Problem>(db, account_id, authed_id, data.limit)
.await
.map_err(|e| Error::ServerError(Json(e.to_string().into())))?;
let problems =
problem::list_for_account::<Problem>(db, account_id, authed_id, data.limit).await?;

Ok(Json(Response {
success: true,
Expand Down
24 changes: 8 additions & 16 deletions src/utils/contest.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use anyhow::Result;
use surrealdb::{engine::remote::ws::Client, opt::PatchOp, sql::Thing, Surreal};
use surrealdb::{engine::remote::ws::Client, sql::Thing, Surreal};

use crate::models::contest::{Contest, ContestData};

Expand All @@ -19,7 +19,6 @@ pub async fn create(
announcement: None,
start_time: contest.start_time,
end_time: contest.end_time,
problems: vec![],
owner: contest.owner.clone().into(),
creator: ("account", creator_id).into(),
updaters: vec![("account", creator_id).into()],
Expand All @@ -34,23 +33,16 @@ pub async fn get(db: &Surreal<Client>, id: &str) -> Result<Option<Contest>> {
Ok(db.select(("contest", id)).await?)
}

pub async fn list(db: &Surreal<Client>, id: Thing) -> Result<Vec<Contest>> {
Ok(db
.query("SELECT * FROM contest WHERE owner = $id")
.bind(("id", id))
.await?
.take(0)?)
pub async fn list_all(db: &Surreal<Client>) -> Result<Vec<Contest>> {
Ok(db.query("SELECT * FROM contest").await?.take(0)?)
}

pub async fn add_problems(
db: &Surreal<Client>,
id: &str,
problems: &[Thing],
) -> Result<Option<Contest>> {
pub async fn list_by_owner(db: &Surreal<Client>, id: &str) -> Result<Vec<Contest>> {
Ok(db
.update(("contest", id))
.patch(PatchOp::add("/problems", problems))
.await?)
.query("SELECT * FROM contest WHERE record::id(owner) = $id")
.bind(("id", id.to_string()))
.await?
.take(0)?)
}

const REMOVE_PROBLEM: &str =
Expand Down

0 comments on commit 63283e7

Please sign in to comment.