mirror of
https://github.com/danbulant/Portfolio
synced 2026-07-06 19:40:49 +00:00
feat!: candidate list field of study filtering, pagination
This commit is contained in:
parent
51d86ff507
commit
7a9910c9d7
10 changed files with 67 additions and 17 deletions
|
|
@ -123,10 +123,9 @@ pub async fn list_candidates(
|
||||||
if !(field == "KB".to_string() || field == "IT".to_string() || field == "G") {
|
if !(field == "KB".to_string() || field == "IT".to_string() || field == "G") {
|
||||||
return Err(Custom(Status::BadRequest, "Invalid field of study".to_string()));
|
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)?;
|
.await.map_err(to_custom_error)?;
|
||||||
|
|
||||||
Ok(
|
Ok(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use ::entity::application;
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
use sea_orm::{DbConn, DbErr, Set, ActiveModelTrait, IntoActiveModel, DeleteResult, ModelTrait};
|
use sea_orm::{DbConn, DbErr, Set, ActiveModelTrait, IntoActiveModel, DeleteResult, ModelTrait};
|
||||||
|
|
||||||
use crate::Mutation;
|
use crate::{Mutation, models::candidate::FieldOfStudy};
|
||||||
|
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
pub async fn create_application(
|
pub async fn create_application(
|
||||||
|
|
@ -14,8 +14,10 @@ impl Mutation {
|
||||||
pubkey: String,
|
pubkey: String,
|
||||||
encrypted_priv_key: String,
|
encrypted_priv_key: String,
|
||||||
) -> Result<application::Model, DbErr> {
|
) -> Result<application::Model, DbErr> {
|
||||||
|
let field_of_study = FieldOfStudy::from(application_id);
|
||||||
let insert = application::ActiveModel {
|
let insert = application::ActiveModel {
|
||||||
id: Set(application_id),
|
id: Set(application_id),
|
||||||
|
field_of_study: Set(field_of_study.into()),
|
||||||
personal_id_number: Set(enc_personal_id_number),
|
personal_id_number: Set(enc_personal_id_number),
|
||||||
password: Set(hashed_password),
|
password: Set(hashed_password),
|
||||||
candidate_id: Set(candidate_id),
|
candidate_id: Set(candidate_id),
|
||||||
|
|
@ -23,7 +25,6 @@ impl Mutation {
|
||||||
private_key: Set(encrypted_priv_key),
|
private_key: Set(encrypted_priv_key),
|
||||||
created_at: Set(chrono::offset::Local::now().naive_local()),
|
created_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
updated_at: Set(chrono::offset::Local::now().naive_local()),
|
||||||
..Default::default()
|
|
||||||
}
|
}
|
||||||
.insert(db)
|
.insert(db)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
use entity::{application, candidate};
|
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)]
|
#[derive(FromQueryResult, Clone)]
|
||||||
pub struct ApplicationCandidateJoin {
|
pub struct ApplicationCandidateJoin {
|
||||||
|
|
@ -12,7 +14,7 @@ pub struct ApplicationCandidateJoin {
|
||||||
pub telephone: Option<String>,
|
pub telephone: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::Query;
|
use crate::{Query};
|
||||||
|
|
||||||
impl Query {
|
impl Query {
|
||||||
pub async fn find_application_by_id(
|
pub async fn find_application_by_id(
|
||||||
|
|
@ -36,9 +38,16 @@ impl Query {
|
||||||
|
|
||||||
pub async fn list_applications(
|
pub async fn list_applications(
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
|
field_of_study: Option<String>,
|
||||||
|
page: Option<u64>,
|
||||||
) -> Result<Vec<ApplicationCandidateJoin>, DbErr> {
|
) -> Result<Vec<ApplicationCandidateJoin>, DbErr> {
|
||||||
application::Entity::find()
|
let select = application::Entity::find();
|
||||||
// .column_as(application::Column::Id, "application_id")
|
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())
|
.join(JoinType::InnerJoin, application::Relation::Candidate.def())
|
||||||
.column_as(application::Column::Id, "application_id")
|
.column_as(application::Column::Id, "application_id")
|
||||||
.column_as(candidate::Column::Id, "candidate_id")
|
.column_as(candidate::Column::Id, "candidate_id")
|
||||||
|
|
@ -46,9 +55,16 @@ impl Query {
|
||||||
.column_as(candidate::Column::Surname, "surname")
|
.column_as(candidate::Column::Surname, "surname")
|
||||||
.column_as(candidate::Column::Email, "email")
|
.column_as(candidate::Column::Email, "email")
|
||||||
.column_as(candidate::Column::Telephone, "telephone")
|
.column_as(candidate::Column::Telephone, "telephone")
|
||||||
.into_model::<ApplicationCandidateJoin>()
|
.into_model::<ApplicationCandidateJoin>();
|
||||||
.all(db)
|
|
||||||
.await
|
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(
|
pub async fn list_applications_compact(
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,33 @@ use crate::{
|
||||||
|
|
||||||
use super::candidate_details::{EncryptedString, EncryptedCandidateDetails};
|
use super::candidate_details::{EncryptedString, EncryptedCandidateDetails};
|
||||||
|
|
||||||
|
pub enum FieldOfStudy {
|
||||||
|
G,
|
||||||
|
IT,
|
||||||
|
KB,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> 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<i32> 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
|
/// Minimal candidate response containing database only not null fields
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,11 @@ impl ApplicationService {
|
||||||
.filter(|(_, id)| id == &personal_id_number)
|
.filter(|(_, id)| id == &personal_id_number)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if found_ids.iter().any(|(_, personal_id)| personal_id == &personal_id_number) {
|
if let Some((candidate_id, _)) = found_ids.first() {
|
||||||
Ok(
|
Ok(
|
||||||
Self::find_linkable_candidate(db,
|
Self::find_linkable_candidate(db,
|
||||||
application_id,
|
application_id,
|
||||||
found_ids[0].0,
|
*candidate_id,
|
||||||
personal_id_number
|
personal_id_number
|
||||||
).await?
|
).await?
|
||||||
)
|
)
|
||||||
|
|
@ -230,8 +230,11 @@ impl ApplicationService {
|
||||||
pub async fn list_applications(
|
pub async fn list_applications(
|
||||||
private_key: &String,
|
private_key: &String,
|
||||||
db: &DbConn,
|
db: &DbConn,
|
||||||
|
field_of_study: Option<String>,
|
||||||
|
page: Option<u64>,
|
||||||
|
|
||||||
) -> Result<Vec<ApplicationResponse>, ServiceError> {
|
) -> Result<Vec<ApplicationResponse>, ServiceError> {
|
||||||
let applications = Query::list_applications(db).await?;
|
let applications = Query::list_applications(db, field_of_study, page).await?;
|
||||||
|
|
||||||
futures::future::try_join_all(
|
futures::future::try_join_all(
|
||||||
applications
|
applications
|
||||||
|
|
|
||||||
|
|
@ -79,12 +79,12 @@ pub mod tests {
|
||||||
let db = get_memory_sqlite_connection().await;
|
let db = get_memory_sqlite_connection().await;
|
||||||
let admin = create_admin(&db).await;
|
let admin = create_admin(&db).await;
|
||||||
let private_key = crypto::decrypt_password(admin.private_key, "admin".to_string()).await.unwrap();
|
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);
|
assert_eq!(candidates.len(), 0);
|
||||||
|
|
||||||
put_user_data(&db).await;
|
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);
|
assert_eq!(candidates.len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
0
core/src/utils/field_of_study.rs
Normal file
0
core/src/utils/field_of_study.rs
Normal file
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod csv;
|
pub mod csv;
|
||||||
pub mod filetype;
|
pub mod filetype;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod date;
|
pub mod date;
|
||||||
|
pub mod field_of_study;
|
||||||
|
|
@ -8,6 +8,7 @@ pub struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub candidate_id: i32,
|
pub candidate_id: i32,
|
||||||
|
pub field_of_study: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
pub public_key: String,
|
pub public_key: String,
|
||||||
pub private_key: String,
|
pub private_key: String,
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ impl MigrationTrait for Migration {
|
||||||
.not_null()
|
.not_null()
|
||||||
.primary_key(),
|
.primary_key(),
|
||||||
)
|
)
|
||||||
|
.col(ColumnDef::new(Application::FieldOfStudy).string().not_null())
|
||||||
.col(ColumnDef::new(Application::CandidateId).integer().not_null())
|
.col(ColumnDef::new(Application::CandidateId).integer().not_null())
|
||||||
.col(ColumnDef::new(Application::Password).string().not_null())
|
.col(ColumnDef::new(Application::Password).string().not_null())
|
||||||
.col(ColumnDef::new(Application::PublicKey).string().not_null())
|
.col(ColumnDef::new(Application::PublicKey).string().not_null())
|
||||||
|
|
@ -51,6 +52,7 @@ impl MigrationTrait for Migration {
|
||||||
pub enum Application {
|
pub enum Application {
|
||||||
Table,
|
Table,
|
||||||
Id,
|
Id,
|
||||||
|
FieldOfStudy,
|
||||||
Password,
|
Password,
|
||||||
PersonalIdNumber,
|
PersonalIdNumber,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue