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
}
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(
db: &DbConn,
candidate_id: i32,

View file

@ -37,4 +37,33 @@ 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>,
}
/// 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 {
pub async fn from_encrypted(
current_application: i32,

View file

@ -3,9 +3,9 @@ use chrono::NaiveDate;
use entity::{candidate, parent};
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";
@ -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;
fn try_from(
cp: Row,
cp: ApplicationRow,
) -> Result<Self, Self::Error> {
Ok(EncryptedApplicationDetails {
candidate: EncryptedCandidateDetails {

View file

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