feat: csv export

This commit is contained in:
Sebastian Pravda 2023-01-15 18:22:07 +01:00
parent b3e2b38b5f
commit 71428a72d5
No known key found for this signature in database
GPG key ID: F3BC84F08EFA3F57
5 changed files with 64 additions and 56 deletions

View file

@ -51,6 +51,15 @@ impl Query {
.await .await
} }
pub async fn list_applications_compact(
db: &DbConn,
) -> Result<Vec<application::Model>, DbErr> {
application::Entity::find()
.join(JoinType::InnerJoin, application::Relation::Candidate.def())
.all(db)
.await
}
pub async fn find_applications_by_candidate_id( pub async fn find_applications_by_candidate_id(
db: &DbConn, db: &DbConn,
candidate_id: i32, candidate_id: i32,

View file

@ -38,3 +38,32 @@ impl ApplicationResponse {
) )
} }
} }
/// CSV export (admin endpoint)
#[derive(Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ApplicationRow {
pub application: i32,
pub name: Option<String>,
pub surname: Option<String>,
pub birthplace: Option<String>,
pub birthdate: Option<String>,
pub address: Option<String>,
pub telephone: Option<String>,
pub citizenship: Option<String>,
pub email: Option<String>,
pub sex: Option<String>,
pub personal_identification_number: Option<String>,
pub school_name: Option<String>,
pub health_insurance: Option<String>,
pub parent_name: Option<String>,
pub parent_surname: Option<String>,
pub parent_telephone: Option<String>,
pub parent_email: Option<String>,
pub second_parent_name: Option<String>,
pub second_parent_surname: Option<String>,
pub second_parent_telephone: Option<String>,
pub second_parent_email: Option<String>,
}

View file

@ -65,36 +65,6 @@ pub struct ApplicationDetails {
pub parents: Vec<ParentDetails>, pub parents: Vec<ParentDetails>,
} }
/// CSV export (admin endpoint)
#[derive(FromQueryResult, Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct Row {
pub application: i32,
pub name: Option<String>,
pub surname: Option<String>,
pub birthplace: Option<String>,
pub birthdate: Option<String>,
pub address: Option<String>,
pub telephone: Option<String>,
pub citizenship: Option<String>,
pub email: Option<String>,
pub sex: Option<String>,
pub study: Option<String>,
pub personal_identification_number: Option<String>,
pub school_name: Option<String>,
pub health_insurance: Option<String>,
pub parent_name: Option<String>,
pub parent_surname: Option<String>,
pub parent_telephone: Option<String>,
pub parent_email: Option<String>,
pub second_parent_name: Option<String>,
pub second_parent_surname: Option<String>,
pub second_parent_telephone: Option<String>,
pub second_parent_email: Option<String>,
}
impl NewCandidateResponse { impl NewCandidateResponse {
pub async fn from_encrypted( pub async fn from_encrypted(
current_application: i32, current_application: i32,

View file

@ -3,9 +3,9 @@ use chrono::NaiveDate;
use entity::{candidate, parent}; use entity::{candidate, parent};
use futures::future; use futures::future;
use crate::{crypto, models::candidate::{Row, ApplicationDetails}, error::ServiceError}; use crate::{crypto, models::candidate::{ApplicationDetails}, error::ServiceError};
use super::candidate::{CandidateDetails, ParentDetails}; use super::{candidate::{CandidateDetails, ParentDetails}, application::ApplicationRow};
pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d"; pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d";
@ -331,11 +331,11 @@ impl From<(&candidate::Model, Vec<parent::Model>)> for EncryptedApplicationDetai
} }
} }
impl TryFrom<Row> for EncryptedApplicationDetails { impl TryFrom<ApplicationRow> for EncryptedApplicationDetails {
type Error = ServiceError; type Error = ServiceError;
fn try_from( fn try_from(
cp: Row, cp: ApplicationRow,
) -> Result<Self, Self::Error> { ) -> Result<Self, Self::Error> {
Ok(EncryptedApplicationDetails { Ok(EncryptedApplicationDetails {
candidate: EncryptedCandidateDetails { candidate: EncryptedCandidateDetails {

View file

@ -1,7 +1,12 @@
use sea_orm::{DbConn}; use crate::{
use crate::{error::ServiceError, models::candidate_details::{EncryptedApplicationDetails}, Query, models::candidate::{Row, ApplicationDetails}}; error::ServiceError,
models::candidate_details::EncryptedApplicationDetails,
models::{application::ApplicationRow, candidate::ApplicationDetails},
Query, services::application_service::ApplicationService,
};
use sea_orm::DbConn;
impl From<(i32, ApplicationDetails)> for Row { impl From<(i32, ApplicationDetails)> for ApplicationRow {
fn from((application, d): (i32, ApplicationDetails)) -> Self { fn from((application, d): (i32, ApplicationDetails)) -> Self {
let c = d.candidate; let c = d.candidate;
Self { Self {
@ -15,7 +20,6 @@ impl From<(i32, ApplicationDetails)> for Row {
citizenship: Some(c.citizenship), citizenship: Some(c.citizenship),
email: Some(c.email), email: Some(c.email),
sex: Some(c.sex), sex: Some(c.sex),
study: Some("TODO".to_string()),
health_insurance: Some(c.health_insurance), health_insurance: Some(c.health_insurance),
school_name: Some(c.school_name), school_name: Some(c.school_name),
personal_identification_number: Some(c.personal_id_number), personal_identification_number: Some(c.personal_id_number),
@ -33,33 +37,29 @@ impl From<(i32, ApplicationDetails)> for Row {
} }
} }
pub async fn export( pub async fn export(db: &DbConn, private_key: String) -> Result<Vec<u8>, ServiceError> {
db: &DbConn,
private_key: String,
) -> Result<Vec<u8>, ServiceError> {
let mut wtr = csv::Writer::from_writer(vec![]); let mut wtr = csv::Writer::from_writer(vec![]);
let candidates_with_parents = Query::list_candidates_full(&db).await?; let applications = Query::list_applications_compact(&db).await?;
for candidate in candidates_with_parents { for application in applications {
let application = candidate.id; let candidate = ApplicationService::find_related_candidate(db, &application).await?;
let parents = Query::find_candidate_parents(db, &candidate).await?; let parents = Query::find_candidate_parents(db, &candidate).await?;
let row: Row = match EncryptedApplicationDetails::try_from((&candidate, parents)) { let row: ApplicationRow = match EncryptedApplicationDetails::try_from((&candidate, parents))
Ok(d) => Row::from( {
d Ok(d) => ApplicationRow::from(
.decrypt(private_key.to_string()) d.decrypt(private_key.to_string())
.await .await
.map(|d| (application, d))? .map(|d| (application.id, d))?,
), ),
Err(_) => Row { Err(_) => ApplicationRow {
application, application: application.id,
..Default::default() ..Default::default()
} },
}; };
wtr.serialize(row)?; wtr.serialize(row)?;
} }
wtr wtr.into_inner()
.into_inner()
.map_err(|_| ServiceError::CsvIntoInnerError) .map_err(|_| ServiceError::CsvIntoInnerError)
} }