diff --git a/backend/src/controller/judger/mod.rs b/backend/src/controller/judger/mod.rs index 3707bccd..74f0f63c 100644 --- a/backend/src/controller/judger/mod.rs +++ b/backend/src/controller/judger/mod.rs @@ -324,7 +324,7 @@ impl JudgerController { .await { Ok(submit) => { - score::ScoreUpload::new(req.user, problem, submit.score, submit.accept) + score::ScoreUpload::new(req.user, problem, submit) .upload(&db) .await; } diff --git a/backend/src/controller/judger/score.rs b/backend/src/controller/judger/score.rs index 7e37b8b1..6a7f40d7 100644 --- a/backend/src/controller/judger/score.rs +++ b/backend/src/controller/judger/score.rs @@ -1,11 +1,14 @@ -use super::{user_contest, DebugName}; +use std::cmp; + +use super::{submit, user_contest, DebugName}; use crate::{ entity::{contest, problem, user}, util::error::Error, }; +use chrono::Local; use sea_orm::{ - ActiveModelTrait, ActiveValue, DatabaseConnection, EntityTrait, IntoActiveModel, ModelTrait, - TransactionTrait, + ActiveModelTrait, ActiveValue, ColumnTrait, DatabaseConnection, EntityTrait, IntoActiveModel, + ModelTrait, QueryFilter, QueryOrder, TransactionTrait, }; use tracing::instrument; @@ -14,18 +17,16 @@ const MAX_RETRY: usize = 32; pub struct ScoreUpload { user_id: i32, problem: problem::Model, - score: u32, - pass: bool, + submit: submit::Model, } impl ScoreUpload { #[instrument(skip(problem))] - pub fn new(user_id: i32, problem: problem::Model, score: u32, pass: bool) -> Self { + pub fn new(user_id: i32, problem: problem::Model, submit: submit::Model) -> Self { Self { user_id, problem, - score, - pass, + submit, } } #[instrument(skip(self))] @@ -58,7 +59,7 @@ impl ScoreUpload { let txn = db.begin().await?; let submit_count = model.submit_count.unwrap().saturating_add(1); - let accept_count = match self.pass { + let accept_count = match self.submit.accept { true => model.accept_count.unwrap().saturating_add(1), false => model.accept_count.unwrap(), }; @@ -71,15 +72,18 @@ impl ScoreUpload { txn.commit().await.map_err(Into::::into) } async fn upload_user(&self, db: &DatabaseConnection) -> Result<(), Error> { - if !self.pass { + if !self.submit.accept { + tracing::trace!(reason = "not acceptted", "score_user"); return Ok(()); } let txn = db.begin().await?; if self.user_id == self.problem.user_id { + tracing::trace!(reason = "problem owner score bypass", "score_user"); return Ok(()); } if !self.problem.public { + tracing::trace!(reason = "private problem score bypass", "score_user"); return Ok(()); } @@ -94,7 +98,7 @@ impl ScoreUpload { let user_score = user.score; let mut user = user.into_active_model(); - user.score = ActiveValue::Set(user_score.saturating_add_unsigned(self.score as u64)); + user.score = ActiveValue::Set(user_score.saturating_add_unsigned(self.submit.score as u64)); user.update(&txn).await.map_err(Into::::into)?; txn.commit().await.map_err(Into::::into) @@ -103,9 +107,16 @@ impl ScoreUpload { let txn = db.begin().await?; if self.user_id == self.problem.user_id { + tracing::trace!(reason = "problem owner score bypass", "score_contest"); return Ok(()); } if self.problem.contest_id.is_none() { + tracing::trace!(reason = "not under contest", "score_contest"); + return Ok(()); + } + + if self.submit.score == 0 { + tracing::trace!(reason = "no score to add", "score_contest"); return Ok(()); } @@ -118,7 +129,15 @@ impl ScoreUpload { .map_err(Into::::into)? .ok_or(Error::NotInDB(user::Entity::DEBUG_NAME))?; + let now = Local::now().naive_local(); + + if contest.end < now { + tracing::trace!(reason = "contest ended", "score_contest"); + return Ok(()); + } + if contest.hoster == self.user_id { + tracing::trace!(reason = "owner score bypass", "score_contest"); return Ok(()); } @@ -126,7 +145,20 @@ impl ScoreUpload { .ok_or(Error::Unreachable("user_contest should exist"))? .into_active_model(); - linker.score = ActiveValue::Set(self.score); + let mut score = linker.score.unwrap(); + + let submit = self + .problem + .find_related(submit::Entity) + .filter(submit::Column::UserId.eq(self.user_id)) + .order_by_desc(submit::Column::Score) + .one(&txn) + .await?; + + score = score.saturating_sub(submit.map(|x| x.score).unwrap_or_default()); + score = score.saturating_add(self.submit.score); + + linker.score = ActiveValue::Set(score); linker.update(&txn).await.map_err(Into::::into)?; txn.commit().await.map_err(Into::::into) diff --git a/backend/src/endpoint/contest.rs b/backend/src/endpoint/contest.rs index 5492af53..4e880026 100644 --- a/backend/src/endpoint/contest.rs +++ b/backend/src/endpoint/contest.rs @@ -316,24 +316,4 @@ impl ContestSet for Arc { Ok(Response::new(())) } - - #[doc = " return up to 10 user"] - #[instrument(skip_all, level = "debug")] - async fn rank(&self, req: Request) -> Result, Status> { - let (auth, req) = self.parse_request(req).await?; - - let contest: IdModel = Entity::related_read_by_id(&auth, req.id, &self.db).await?; - - let list = user_contest::Entity::find() - .filter(user_contest::Column::ContestId.eq(contest.id)) - .order_by_desc(user_contest::Column::Score) - .limit(10) - .all(self.db.deref()) - .await - .map_err(Into::::into)?; - - let list: Vec = list.into_iter().map(|x| x.into()).collect(); - - Ok(Response::new(Users { list })) - } } diff --git a/backend/src/endpoint/problem.rs b/backend/src/endpoint/problem.rs index 22b1eed4..a7f39576 100644 --- a/backend/src/endpoint/problem.rs +++ b/backend/src/endpoint/problem.rs @@ -24,6 +24,7 @@ impl From for ProblemInfo { title: value.title, submit_count: value.submit_count, ac_rate: value.ac_rate, + difficulty: value.difficulty, } } } @@ -42,6 +43,7 @@ impl From for ProblemFullInfo { title: value.title, submit_count: value.submit_count, ac_rate: value.ac_rate, + difficulty: value.difficulty, }, author: value.user_id.into(), } diff --git a/backend/src/endpoint/user.rs b/backend/src/endpoint/user.rs index e9f1790b..f7052045 100644 --- a/backend/src/endpoint/user.rs +++ b/backend/src/endpoint/user.rs @@ -91,6 +91,39 @@ impl UserSet for Arc { Ok(Response::new(ListUserResponse { list, next_session })) } + async fn list_by_contest( + &self, + req: Request, + ) -> Result, Status> { + let (auth, req) = self.parse_request(req).await?; + + let (rev, size) = split_rev(req.size); + let size = bound!(size, 64); + let offset = bound!(req.offset(), 1024); + + let (pager, models) = match req.request.ok_or(Error::NotInPayload("request"))? { + list_by_request::Request::Create(create) => { + ParentPaginator::new_fetch( + (0, create.parent_id), + &auth, + size, + offset, + create.start_from_end, + &self.db, + ) + .await + } + list_by_request::Request::Pager(old) => { + let pager: ParentPaginator = self.crypto.decode(old.session)?; + pager.fetch(&auth, size, offset, rev, &self.db).await + } + }?; + + let next_session = self.crypto.encode(pager)?; + let list = models.into_iter().map(|x| x.into()).collect(); + + Ok(Response::new(ListUserResponse { list, next_session })) + } #[instrument(skip_all, level = "debug")] async fn full_info(&self, _req: Request) -> Result, Status> { Err(Status::cancelled("deprecated")) diff --git a/backend/src/entity/announcement.rs b/backend/src/entity/announcement.rs index e6fb3899..86d538fc 100644 --- a/backend/src/entity/announcement.rs +++ b/backend/src/entity/announcement.rs @@ -108,14 +108,14 @@ impl PagerReflect for PartialModel { pub struct PagerTrait; +impl PagerData for PagerTrait { + type Data = (); +} + #[async_trait] impl PagerSource for PagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (); - const TYPE_NUMBER: u8 = 4; async fn filter( @@ -131,14 +131,14 @@ pub type Paginator = PkPager; pub struct TextPagerTrait; +impl PagerData for TextPagerTrait { + type Data = String; +} + #[async_trait] impl PagerSource for TextPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = String; - const TYPE_NUMBER: u8 = 4; async fn filter( @@ -154,14 +154,14 @@ pub type TextPaginator = PkPager; pub struct ParentPagerTrait; +impl PagerData for ParentPagerTrait { + type Data = (i32, chrono::NaiveDateTime); +} + #[async_trait] impl PagerSource for ParentPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (i32, chrono::NaiveDateTime); - const TYPE_NUMBER: u8 = 8; async fn filter( @@ -193,14 +193,14 @@ pub type ParentPaginator = ColPager; pub struct ColPagerTrait; +impl PagerData for ColPagerTrait { + type Data = (AnnouncementSortBy, String); +} + #[async_trait] impl PagerSource for ColPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (AnnouncementSortBy, String); - const TYPE_NUMBER: u8 = 8; async fn filter( diff --git a/backend/src/entity/chat.rs b/backend/src/entity/chat.rs index d57919a6..ee7941d3 100644 --- a/backend/src/entity/chat.rs +++ b/backend/src/entity/chat.rs @@ -77,14 +77,14 @@ impl PagerReflect for Model { pub struct ParentPagerTrait; +impl PagerData for ParentPagerTrait { + type Data = (i32, chrono::NaiveDateTime); +} + #[async_trait] impl PagerSource for ParentPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (i32, chrono::NaiveDateTime); - const TYPE_NUMBER: u8 = 8; async fn filter( diff --git a/backend/src/entity/contest.rs b/backend/src/entity/contest.rs index 74273bd0..1e8890f0 100644 --- a/backend/src/entity/contest.rs +++ b/backend/src/entity/contest.rs @@ -1,5 +1,6 @@ use std::ops::Deref; +use chrono::Local; use sea_orm::{DatabaseBackend, Statement}; use crate::{grpc::backend::ContestSortBy, partial_union}; @@ -129,22 +130,22 @@ impl super::ParentalTrait for Entity { ) -> Result { match user::Model::new_with_auth(auth) { Some(user) => { - // user.find_related(Entity).select_only().columns(col); let (query, param) = { let builder = db.get_database_backend().get_query_builder(); + let now = Local::now().naive_local(); partial_union!( - [Column::Id, Column::Hoster, Column::Public], + [Column::Id, Column::Hoster, Column::Public, Column::Begin], user.find_related(Entity), - Entity::find().filter(Column::Public.eq(true)), + Entity::find() + .filter(Column::Public.eq(true)) + .filter(Column::Begin.lte(now)), Entity::find().filter(Column::Hoster.eq(user.id)) ) .and_where(Column::Id.eq(id)) .build_any(builder.deref()) }; - // user.find_related(Entity).into_query() - IdModel::find_by_statement(Statement::from_sql_and_values( DatabaseBackend::Sqlite, query, @@ -203,14 +204,14 @@ impl PagerReflect for PartialModel { pub struct TextPagerTrait; +impl PagerData for TextPagerTrait { + type Data = String; +} + #[async_trait] impl PagerSource for TextPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = String; - const TYPE_NUMBER: u8 = 4; async fn filter( @@ -226,15 +227,14 @@ pub type TextPaginator = PkPager; pub struct ColPagerTrait; +impl PagerData for ColPagerTrait { + type Data = (ContestSortBy, String); +} + #[async_trait] impl PagerSource for ColPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - // FIXME: we need optional support - type Data = (ContestSortBy, String); - const TYPE_NUMBER: u8 = 8; async fn filter( diff --git a/backend/src/entity/education.rs b/backend/src/entity/education.rs index 0af3e33d..6cec6590 100644 --- a/backend/src/entity/education.rs +++ b/backend/src/entity/education.rs @@ -100,14 +100,14 @@ impl PagerReflect for PartialModel { pub struct PagerTrait; +impl PagerData for PagerTrait { + type Data = (); +} + #[async_trait] impl PagerSource for PagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (); - const TYPE_NUMBER: u8 = 4; async fn filter( @@ -123,14 +123,14 @@ pub type Paginator = PkPager; pub struct TextPagerTrait; +impl PagerData for TextPagerTrait { + type Data = String; +} + #[async_trait] impl PagerSource for TextPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = String; - const TYPE_NUMBER: u8 = 4; async fn filter( @@ -146,14 +146,14 @@ pub type TextPaginator = PkPager; pub struct ParentPagerTrait; +impl PagerData for ParentPagerTrait { + type Data = i32; +} + #[async_trait] impl PagerSource for ParentPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = i32; - const TYPE_NUMBER: u8 = 8; async fn filter( diff --git a/backend/src/entity/mod.rs b/backend/src/entity/mod.rs index 5b486777..6e714a86 100644 --- a/backend/src/entity/mod.rs +++ b/backend/src/entity/mod.rs @@ -21,7 +21,7 @@ pub mod util; use crate::util::{auth::Auth, error::Error}; use tonic::async_trait; -use util::paginator::{PagerReflect, PagerSource, PkPager}; +use util::paginator::{PagerData, PagerReflect, PagerSource, PkPager}; pub trait DebugName { const DEBUG_NAME: &'static str = "TEMPLATE_DEBUG_NAME"; diff --git a/backend/src/entity/problem.rs b/backend/src/entity/problem.rs index 2abcb386..3f73f26d 100644 --- a/backend/src/entity/problem.rs +++ b/backend/src/entity/problem.rs @@ -242,14 +242,14 @@ pub struct PagerTrait; pub struct TextPagerTrait; +impl PagerData for TextPagerTrait { + type Data = String; +} + #[async_trait] impl PagerSource for TextPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = String; - const TYPE_NUMBER: u8 = 4; async fn filter( @@ -266,14 +266,14 @@ pub type TextPaginator = PkPager; pub struct ParentPagerTrait; +impl PagerData for ParentPagerTrait { + type Data = (i32, f32); +} + #[async_trait] impl PagerSource for ParentPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (i32, f32); - const TYPE_NUMBER: u8 = 8; async fn filter( @@ -305,14 +305,14 @@ pub type ParentPaginator = ColPager; pub struct ColPagerTrait; +impl PagerData for ColPagerTrait { + type Data = (ProblemSortBy, String); +} + #[async_trait] impl PagerSource for ColPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (ProblemSortBy, String); - const TYPE_NUMBER: u8 = 8; async fn filter( diff --git a/backend/src/entity/submit.rs b/backend/src/entity/submit.rs index 9305328d..217fb58a 100644 --- a/backend/src/entity/submit.rs +++ b/backend/src/entity/submit.rs @@ -118,14 +118,14 @@ impl PagerReflect for PartialModel { pub struct ParentPagerTrait; +impl PagerData for ParentPagerTrait { + type Data = (i32, chrono::NaiveDateTime); +} + #[async_trait] impl PagerSource for ParentPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (i32, chrono::NaiveDateTime); - const TYPE_NUMBER: u8 = 8; async fn filter( @@ -156,14 +156,14 @@ pub type ParentPaginator = ColPager; pub struct ColPagerTrait; +impl PagerData for ColPagerTrait { + type Data = chrono::NaiveDateTime; +} + #[async_trait] impl PagerSource for ColPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = chrono::NaiveDateTime; - const TYPE_NUMBER: u8 = 8; async fn filter( diff --git a/backend/src/entity/test.rs b/backend/src/entity/test.rs index 9fad56cf..b8b8ded8 100644 --- a/backend/src/entity/test.rs +++ b/backend/src/entity/test.rs @@ -92,14 +92,14 @@ impl PagerReflect for Model { pub struct ParentPagerTrait; +impl PagerData for ParentPagerTrait { + type Data = (i32, u32); +} + #[async_trait] impl PagerSource for ParentPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (i32, u32); - const TYPE_NUMBER: u8 = 8; async fn filter( @@ -130,14 +130,14 @@ pub type ParentPaginator = ColPager; pub struct ColPagerTrait; +impl PagerData for ColPagerTrait { + type Data = u32; +} + #[async_trait] impl PagerSource for ColPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = u32; - const TYPE_NUMBER: u8 = 8; async fn filter( diff --git a/backend/src/entity/user.rs b/backend/src/entity/user.rs index 0abd61a1..9a492468 100644 --- a/backend/src/entity/user.rs +++ b/backend/src/entity/user.rs @@ -1,3 +1,5 @@ +use sea_orm::{QueryOrder, QuerySelect}; + use crate::grpc::backend::UserSortBy; use super::*; @@ -176,14 +178,14 @@ impl PagerReflect for Model { pub struct TextPagerTrait; +impl PagerData for TextPagerTrait { + type Data = String; +} + #[async_trait] impl PagerSource for TextPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = String; - const TYPE_NUMBER: u8 = 4; async fn filter( @@ -199,14 +201,14 @@ pub type TextPaginator = PkPager; pub struct ColPagerTrait; +impl PagerData for ColPagerTrait { + type Data = (UserSortBy, String); +} + #[async_trait] impl PagerSource for ColPagerTrait { const ID: ::Column = Column::Id; - type Entity = Entity; - - type Data = (UserSortBy, String); - const TYPE_NUMBER: u8 = 8; async fn filter( @@ -238,3 +240,107 @@ impl PagerSortSource for ColPagerTrait { } pub type ColPaginator = ColPager; + +/// ParentPaginator (offset base) +#[derive(serde::Serialize, serde::Deserialize)] +pub struct ParentPaginator { + pub offset: u64, + pub ppk: i32, + pub start_from_end: bool, +} + +pub struct ParentSource; + +impl PagerData for ParentSource { + type Data = (u64, i32); +} + +fn to_order(raw: bool) -> sea_query::Order { + match raw { + true => sea_query::Order::Desc, + false => sea_query::Order::Asc, + } +} + +#[async_trait] +impl util::paginator::Pager for ParentPaginator { + type Source = ParentSource; + type Reflect = Model; + + async fn fetch( + mut self, + auth: &Auth, + size: u64, + offset: u64, + rel_dir: bool, + db: &DatabaseConnection, + ) -> Result<(Self, Vec), Error> { + // check user is in contest(or admin) + contest::Entity::read_by_id(self.ppk, auth)? + .one(db) + .await? + .ok_or(Error::NotInDB(contest::Entity::DEBUG_NAME))?; + + let result = user_contest::Entity::find() + .filter(user_contest::Column::ContestId.eq(self.ppk)) + .order_by( + user_contest::Column::Score, + to_order(self.start_from_end ^ rel_dir), + ) + .offset(self.offset + offset) + .limit(size) + .all(db) + .await?; + + let result: Vec<_> = result + .load_one(Entity, db) + .await? + .into_iter() + .flatten() + .collect(); + + self.offset += result.len() as u64; + Ok((self, result)) + } + async fn new_fetch( + data: ::Data, + auth: &Auth, + size: u64, + offset: u64, + abs_dir: bool, + db: &DatabaseConnection, + ) -> Result<(Self, Vec), Error> { + let ppk = data.1; + // check user is in contest(or admin) + contest::Entity::read_by_id(ppk, auth)? + .one(db) + .await? + .ok_or(Error::NotInDB(contest::Entity::DEBUG_NAME))?; + + let result = user_contest::Entity::find() + .filter(user_contest::Column::ContestId.eq(data.0)) + .order_by(user_contest::Column::Score, to_order(abs_dir)) + .offset(offset) + .limit(size) + .all(db) + .await?; + + let result: Vec<_> = result + .load_one(Entity, db) + .await? + .into_iter() + .flatten() + .collect(); + + let offset = offset + result.len() as u64; + + Ok(( + ParentPaginator { + ppk, + offset, + start_from_end: abs_dir, + }, + result, + )) + } +} diff --git a/backend/src/entity/util/paginator.rs b/backend/src/entity/util/paginator.rs index c850b538..efd9980a 100644 --- a/backend/src/entity/util/paginator.rs +++ b/backend/src/entity/util/paginator.rs @@ -21,6 +21,34 @@ use sea_orm::{ColumnTrait, EntityTrait, QuerySelect, Select}; use crate::util::error::Error; +#[async_trait] +pub trait Pager +where + Self: Sized + Serialize + DeserializeOwned, +{ + type Source: PagerData; + type Reflect: Send; + async fn fetch( + self, + auth: &Auth, + size: u64, + offset: u64, + rel_dir: bool, + db: &DatabaseConnection, + ) -> Result<(Self, Vec), Error>; + async fn new_fetch( + data: ::Data, + auth: &Auth, + size: u64, + offset: u64, + abs_dir: bool, + db: &DatabaseConnection, + ) -> Result<(Self, Vec), Error>; +} + +pub trait PagerData { + type Data: Send + Sized + Serialize + DeserializeOwned; +} /// indicate foreign object is ready for page source /// /// In `Education` example, we expect ::entity::education::Entity @@ -28,11 +56,10 @@ use crate::util::error::Error; #[async_trait] pub trait PagerSource where - Self: Send, + Self: Send + PagerData, { const ID: ::Column; type Entity: EntityTrait + DebugName; - type Data: Send + Sized + Serialize + DeserializeOwned; const TYPE_NUMBER: u8; /// filter reconstruction async fn filter( @@ -56,31 +83,6 @@ where async fn all(query: Select, db: &DatabaseConnection) -> Result, Error>; } -#[async_trait] -pub trait Pager -where - Self: Sized + Serialize + DeserializeOwned, -{ - type Source: PagerSource; - type Reflect: PagerReflect<::Entity> + Send; - async fn fetch( - self, - auth: &Auth, - size: u64, - offset: u64, - rel_dir: bool, - db: &DatabaseConnection, - ) -> Result<(Self, Vec), Error>; - async fn new_fetch( - data: ::Data, - auth: &Auth, - size: u64, - offset: u64, - abs_dir: bool, - db: &DatabaseConnection, - ) -> Result<(Self, Vec), Error>; -} - /// compact primary key pager #[derive(Serialize, Deserialize)] pub struct PkPager> { @@ -91,13 +93,9 @@ pub struct PkPager> { /// original direction direction: bool, /// data - #[serde(bound( - deserialize = "< as Pager>::Source as PagerSource>::Data: DeserializeOwned" - ))] - #[serde(bound( - serialize = "< as Pager>::Source as PagerSource>::Data: Serialize" - ))] - data: < as Pager>::Source as PagerSource>::Data, + #[serde(bound(deserialize = "S::Data: DeserializeOwned"))] + #[serde(bound(serialize = "S::Data: Serialize"))] + data: S::Data, #[serde(bound(serialize = ""))] #[serde(bound(deserialize = ""))] source: PhantomData, @@ -118,7 +116,7 @@ impl> Pager for PkPager { offset: u64, rel_dir: bool, db: &DatabaseConnection, - ) -> Result<(Self, Vec), Error> { + ) -> Result<(Self, Vec), Error> { let paginator = PaginatePkBuilder::default() .pk(::ID) .include(self.last_direction ^ rel_dir) @@ -150,7 +148,7 @@ impl> Pager for PkPager { _offset: u64, abs_dir: bool, db: &DatabaseConnection, - ) -> Result<(Self, Vec), Error> { + ) -> Result<(Self, Vec), Error> { let query = order_by_bool( S::filter(auth, &data, db).await?, ::ID, @@ -222,7 +220,7 @@ impl, R: PagerReflect> Pager for ColPager offset: u64, rel_dir: bool, db: &DatabaseConnection, - ) -> Result<(Self, Vec), Error> { + ) -> Result<(Self, Vec), Error> { let col = S::sort_col(&self.data); let val = S::get_val(&self.data); @@ -264,7 +262,7 @@ impl, R: PagerReflect> Pager for ColPager offset: u64, abs_dir: bool, db: &DatabaseConnection, - ) -> Result<(Self, Vec), Error> { + ) -> Result<(Self, Vec), Error> { let col = S::sort_col(&data); let query = order_by_bool( diff --git a/proto/backend.proto b/proto/backend.proto index dfdf955c..caee0dbd 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -342,6 +342,7 @@ message ProblemInfo { required ProblemId id = 1; required string title = 2; required uint32 submit_count = 3; + required uint32 difficulty = 5; required float ac_rate = 4; } @@ -614,9 +615,6 @@ service ContestSet { rpc Join(JoinContestRequest) returns (google.protobuf.Empty); rpc Exit(ContestId) returns (google.protobuf.Empty); - - // return up to 10 user - rpc Rank(ContestId) returns (Users); } // User @@ -694,6 +692,7 @@ message ListUserRequest { service UserSet { rpc List(ListUserRequest) returns (ListUserResponse); rpc SearchByText(TextSearchRequest) returns (ListUserResponse); + rpc ListByContest(ListByRequest) returns (ListUserResponse); rpc FullInfo(UserId) returns (UserFullInfo); rpc Create(CreateUserRequest) returns (UserId);