mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-19 06:21:15 +00:00
feat: csv export
This commit is contained in:
parent
b3e2b38b5f
commit
71428a72d5
5 changed files with 64 additions and 56 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue