diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index f74fb80..8794a20 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -123,10 +123,9 @@ pub async fn list_candidates( if !(field == "KB".to_string() || field == "IT".to_string() || field == "G") { return Err(Custom(Status::BadRequest, "Invalid field of study".to_string())); } - } - let candidates = ApplicationService::list_applications(&private_key, db) + let candidates = ApplicationService::list_applications(&private_key, db, field, page) .await.map_err(to_custom_error)?; Ok( diff --git a/core/src/database/mutation/application.rs b/core/src/database/mutation/application.rs index 87b086e..115aac3 100644 --- a/core/src/database/mutation/application.rs +++ b/core/src/database/mutation/application.rs @@ -2,7 +2,7 @@ use ::entity::application; use log::{info, warn}; use sea_orm::{DbConn, DbErr, Set, ActiveModelTrait, IntoActiveModel, DeleteResult, ModelTrait}; -use crate::Mutation; +use crate::{Mutation, models::candidate::FieldOfStudy}; impl Mutation { pub async fn create_application( @@ -14,8 +14,10 @@ impl Mutation { pubkey: String, encrypted_priv_key: String, ) -> Result { + let field_of_study = FieldOfStudy::from(application_id); let insert = application::ActiveModel { id: Set(application_id), + field_of_study: Set(field_of_study.into()), personal_id_number: Set(enc_personal_id_number), password: Set(hashed_password), candidate_id: Set(candidate_id), @@ -23,7 +25,6 @@ impl Mutation { private_key: Set(encrypted_priv_key), created_at: Set(chrono::offset::Local::now().naive_local()), updated_at: Set(chrono::offset::Local::now().naive_local()), - ..Default::default() } .insert(db) .await?; diff --git a/core/src/database/query/application.rs b/core/src/database/query/application.rs index a7b904e..1f5334e 100644 --- a/core/src/database/query/application.rs +++ b/core/src/database/query/application.rs @@ -1,5 +1,7 @@ use entity::{application, candidate}; -use sea_orm::{EntityTrait, DbErr, DbConn, ModelTrait, FromQueryResult, QuerySelect, JoinType, RelationTrait, QueryFilter, ColumnTrait}; +use sea_orm::{EntityTrait, DbErr, DbConn, ModelTrait, FromQueryResult, QuerySelect, JoinType, RelationTrait, QueryFilter, ColumnTrait, QueryOrder, PaginatorTrait}; + +const PAGE_SIZE: u64 = 20; #[derive(FromQueryResult, Clone)] pub struct ApplicationCandidateJoin { @@ -12,7 +14,7 @@ pub struct ApplicationCandidateJoin { pub telephone: Option, } -use crate::Query; +use crate::{Query}; impl Query { pub async fn find_application_by_id( @@ -36,9 +38,16 @@ impl Query { pub async fn list_applications( db: &DbConn, + field_of_study: Option, + page: Option, ) -> Result, DbErr> { - application::Entity::find() - // .column_as(application::Column::Id, "application_id") + let select = application::Entity::find(); + let query = if let Some(field) = field_of_study { + select.filter(application::Column::FieldOfStudy.eq(field)) + } else { + select + } + .order_by(application::Column::Id, sea_orm::Order::Asc) .join(JoinType::InnerJoin, application::Relation::Candidate.def()) .column_as(application::Column::Id, "application_id") .column_as(candidate::Column::Id, "candidate_id") @@ -46,9 +55,16 @@ impl Query { .column_as(candidate::Column::Surname, "surname") .column_as(candidate::Column::Email, "email") .column_as(candidate::Column::Telephone, "telephone") - .into_model::() - .all(db) - .await + .into_model::(); + + if let Some(page) = page { + query + .paginate(db, PAGE_SIZE) + .fetch_page(page).await + } else { + query + .all(db).await + } } pub async fn list_applications_compact( diff --git a/core/src/models/candidate.rs b/core/src/models/candidate.rs index 7fb684d..6b8fce4 100644 --- a/core/src/models/candidate.rs +++ b/core/src/models/candidate.rs @@ -8,6 +8,33 @@ use crate::{ use super::candidate_details::{EncryptedString, EncryptedCandidateDetails}; +pub enum FieldOfStudy { + G, + IT, + KB, +} + +impl Into for FieldOfStudy { + fn into(self) -> String { + match self { + FieldOfStudy::G => "G".to_string(), + FieldOfStudy::IT => "IT".to_string(), + FieldOfStudy::KB => "KB".to_string(), + } + } +} + +impl From for FieldOfStudy { + fn from(id: i32) -> Self { + match &id.to_string().as_str()[0..3] { + "101" => FieldOfStudy::G, + "102" => FieldOfStudy::IT, + "103" => FieldOfStudy::KB, + _ => panic!("Invalid field of study id"), // TODO: handle using TryFrom + } + } +} + /// Minimal candidate response containing database only not null fields #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index eff1b64..1848ea1 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -100,11 +100,11 @@ impl ApplicationService { .filter(|(_, id)| id == &personal_id_number) .collect(); - if found_ids.iter().any(|(_, personal_id)| personal_id == &personal_id_number) { + if let Some((candidate_id, _)) = found_ids.first() { Ok( Self::find_linkable_candidate(db, application_id, - found_ids[0].0, + *candidate_id, personal_id_number ).await? ) @@ -230,8 +230,11 @@ impl ApplicationService { pub async fn list_applications( private_key: &String, db: &DbConn, + field_of_study: Option, + page: Option, + ) -> Result, ServiceError> { - let applications = Query::list_applications(db).await?; + let applications = Query::list_applications(db, field_of_study, page).await?; futures::future::try_join_all( applications diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index bd0dc2b..661d614 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -79,12 +79,12 @@ pub mod tests { let db = get_memory_sqlite_connection().await; let admin = create_admin(&db).await; let private_key = crypto::decrypt_password(admin.private_key, "admin".to_string()).await.unwrap(); - let candidates = ApplicationService::list_applications(&private_key, &db).await.unwrap(); + let candidates = ApplicationService::list_applications(&private_key, &db, None, None).await.unwrap(); assert_eq!(candidates.len(), 0); put_user_data(&db).await; - let candidates = ApplicationService::list_applications(&private_key, &db).await.unwrap(); + let candidates = ApplicationService::list_applications(&private_key, &db, None, None).await.unwrap(); assert_eq!(candidates.len(), 1); } diff --git a/core/src/utils/field_of_study.rs b/core/src/utils/field_of_study.rs new file mode 100644 index 0000000..e69de29 diff --git a/core/src/utils/mod.rs b/core/src/utils/mod.rs index 8205ec2..7065490 100644 --- a/core/src/utils/mod.rs +++ b/core/src/utils/mod.rs @@ -1,4 +1,5 @@ pub mod csv; pub mod filetype; pub mod db; -pub mod date; \ No newline at end of file +pub mod date; +pub mod field_of_study; \ No newline at end of file diff --git a/entity/src/application.rs b/entity/src/application.rs index 6f82e4b..22dbd9c 100644 --- a/entity/src/application.rs +++ b/entity/src/application.rs @@ -8,6 +8,7 @@ pub struct Model { #[sea_orm(primary_key, auto_increment = false)] pub id: i32, pub candidate_id: i32, + pub field_of_study: String, pub password: String, pub public_key: String, pub private_key: String, diff --git a/migration/src/m20230114_114628_create_application.rs b/migration/src/m20230114_114628_create_application.rs index 004dbd3..6ab88ba 100644 --- a/migration/src/m20230114_114628_create_application.rs +++ b/migration/src/m20230114_114628_create_application.rs @@ -17,6 +17,7 @@ impl MigrationTrait for Migration { .not_null() .primary_key(), ) + .col(ColumnDef::new(Application::FieldOfStudy).string().not_null()) .col(ColumnDef::new(Application::CandidateId).integer().not_null()) .col(ColumnDef::new(Application::Password).string().not_null()) .col(ColumnDef::new(Application::PublicKey).string().not_null()) @@ -51,6 +52,7 @@ impl MigrationTrait for Migration { pub enum Application { Table, Id, + FieldOfStudy, Password, PersonalIdNumber, PublicKey,