mirror of
https://github.com/danbulant/Portfolio
synced 2026-07-05 19:11:06 +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
|
.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,
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
}
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue