feat: 2/9 semester support

This commit is contained in:
Sebastian Pravda 2023-01-27 23:26:20 +01:00
parent 54cdca8a11
commit 88febd5e20
No known key found for this signature in database
GPG key ID: F3BC84F08EFA3F57
5 changed files with 90 additions and 12 deletions

View file

@ -78,6 +78,8 @@ pub enum ServiceError {
CsvError(#[from] csv::Error), CsvError(#[from] csv::Error),
#[error("Csv into inner error")] #[error("Csv into inner error")]
CsvIntoInnerError, CsvIntoInnerError,
#[error("Format error")]
FormatError,
} }
impl ServiceError { impl ServiceError {
@ -122,6 +124,7 @@ impl ServiceError {
ServiceError::ZipError(_) => 500, ServiceError::ZipError(_) => 500,
ServiceError::CsvError(_) => 500, ServiceError::CsvError(_) => 500,
ServiceError::CsvIntoInnerError => 500, ServiceError::CsvIntoInnerError => 500,
ServiceError::FormatError => 500,
} }
} }

View file

@ -42,40 +42,71 @@ impl ApplicationResponse {
/// CSV export (admin endpoint) /// CSV export (admin endpoint)
#[derive(Serialize, Default)] #[derive(Serialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct ApplicationRow { pub struct ApplicationRow {
#[serde(rename = "Ev. č. přihlášky")]
pub application: i32, pub application: i32,
#[serde(rename = "Jméno")]
pub name: Option<String>, pub name: Option<String>,
#[serde(rename = "Příjmení")]
pub surname: Option<String>, pub surname: Option<String>,
#[serde(rename = "Rodné příjmení (pokud odlišné)")]
pub birth_surname: Option<String>, pub birth_surname: Option<String>,
#[serde(rename = "Místo narození")]
pub birthplace: Option<String>, pub birthplace: Option<String>,
#[serde(rename = "Datum narození")]
pub birthdate: Option<String>, pub birthdate: Option<String>,
#[serde(rename = "Adresa trvalého pobytu")]
pub address: Option<String>, pub address: Option<String>,
#[serde(rename = "Adresa pro doručování písemností (pokud odlišné)")]
pub letter_address: Option<String>, pub letter_address: Option<String>,
#[serde(rename = "Telefon")]
pub telephone: Option<String>, pub telephone: Option<String>,
#[serde(rename = "Státní občanství")]
pub citizenship: Option<String>, pub citizenship: Option<String>,
#[serde(rename = "Email")]
pub email: Option<String>, pub email: Option<String>,
#[serde(rename = "Pohlaví")]
pub sex: Option<String>, pub sex: Option<String>,
#[serde(rename = "Rodné číslo")]
pub personal_identification_number: Option<String>, pub personal_identification_number: Option<String>,
#[serde(rename = "Název školy")]
pub school_name: Option<String>, pub school_name: Option<String>,
#[serde(rename = "Zdravotní pojištění")]
pub health_insurance: Option<String>, pub health_insurance: Option<String>,
#[serde(rename = "Vysvědčení 1/8")]
pub diploma_1_8: String, pub diploma_1_8: String,
#[serde(rename = "Vysvědčení 2/8")]
pub diploma_2_8: String, pub diploma_2_8: String,
#[serde(rename = "Vysvědčení 1/9")]
pub diploma_1_9: String, pub diploma_1_9: String,
#[serde(rename = "Vysvědčení 2/9")]
pub diploma_2_9: String,
#[serde(rename = "První škola - název")]
pub first_school_name: Option<String>, pub first_school_name: Option<String>,
#[serde(rename = "První škola - obor")]
pub first_school_field: Option<String>, pub first_school_field: Option<String>,
#[serde(rename = "Druhá škola - název")]
pub second_school_name: Option<String>, pub second_school_name: Option<String>,
#[serde(rename = "Druhá škola - obor")]
pub second_school_field: Option<String>, pub second_school_field: Option<String>,
#[serde(rename = "Jméno zákonného zástupce")]
pub parent_name: Option<String>, pub parent_name: Option<String>,
#[serde(rename = "Příjmení zákonného zástupce")]
pub parent_surname: Option<String>, pub parent_surname: Option<String>,
#[serde(rename = "Telefon zákonného zástupce")]
pub parent_telephone: Option<String>, pub parent_telephone: Option<String>,
#[serde(rename = "Email zákonného zástupce")]
pub parent_email: Option<String>, pub parent_email: Option<String>,
#[serde(rename = "Jméno druhého zákonného zástupce")]
pub second_parent_name: Option<String>, pub second_parent_name: Option<String>,
#[serde(rename = "Příjmení druhého zákonného zástupce")]
pub second_parent_surname: Option<String>, pub second_parent_surname: Option<String>,
#[serde(rename = "Telefon druhého zákonného zástupce")]
pub second_parent_telephone: Option<String>, pub second_parent_telephone: Option<String>,
#[serde(rename = "Email druhého zákonného zástupce")]
pub second_parent_email: Option<String>, pub second_parent_email: Option<String>,
} }

View file

@ -98,6 +98,7 @@ impl CandidateDetails {
pub fn validate_self(&self) -> Result<(), ServiceError> { pub fn validate_self(&self) -> Result<(), ServiceError> {
self.first_school.validate()?; self.first_school.validate()?;
self.second_school.validate()?; self.second_school.validate()?;
self.grades.validate_self()?;
self.validate() self.validate()
.map_err(ServiceError::ValidationError) .map_err(ServiceError::ValidationError)
} }

View file

@ -1,37 +1,70 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use validator::{Validate, ValidationError};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] use crate::error::ServiceError;
fn validate_semester(semester: &str) -> Result<(), ValidationError> {
match semester {
"1/8" | "2/8" | "1/9" | "2/9" => Ok(()),
_ => Err(ValidationError::new("Invalid semester"))
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate, PartialEq, Eq)]
pub struct Grade { pub struct Grade {
#[validate(length(min = 1, max = 255))]
subject: String, subject: String,
#[validate(length(min = 1, max = 255), custom = "validate_semester")]
semester: String, semester: String,
#[validate(range(min = 1, max = 5))]
value: i32, value: i32,
} }
impl Grade {
pub fn validate_self(&self) -> Result<(), ServiceError> {
self.validate()
.map_err(ServiceError::ValidationError)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct GradeList(Vec<Grade>); pub struct GradeList(Vec<Grade>);
impl GradeList { impl GradeList {
pub fn validate_self(&self) -> Result<(), ServiceError> {
self.0.iter()
.map(|grade| grade.validate_self())
.collect::<Result<Vec<_>, _>>()
.map(|_| ())
}
pub fn from_opt_str(grades: Option<String>) -> Option<Self> { pub fn from_opt_str(grades: Option<String>) -> Option<Self> {
grades.map( grades.map(
|grades| serde_json::from_str(&grades).unwrap() // TODO: handle error |grades| serde_json::from_str(&grades).unwrap() // TODO: handle error
) )
} }
pub fn group_by_semester(&self) -> (GradeList, GradeList, GradeList) { pub fn group_by_semester(&self) -> Result<(GradeList, GradeList, GradeList, GradeList), ServiceError> {
let mut first_semester = GradeList::default(); let mut first_semester = GradeList::default();
let mut second_semester = GradeList::default(); let mut second_semester = GradeList::default();
let mut third_semester = GradeList::default(); let mut third_semester = GradeList::default();
let mut fourth_semester = GradeList::default();
for grade in &self.0 { for grade in &self.0 {
match grade.semester.as_str() { match grade.semester.as_str() {
"1/8" => first_semester.0.push(grade.clone()), "1/8" => first_semester.0.push(grade.clone()),
"2/8" => second_semester.0.push(grade.clone()), "2/8" => second_semester.0.push(grade.clone()),
"1/9" => third_semester.0.push(grade.clone()), "1/9" => third_semester.0.push(grade.clone()),
_ => panic!("Invalid semester"), "2/9" => fourth_semester.0.push(grade.clone()),
_ => return Err(ServiceError::FormatError),
} }
} }
(first_semester, second_semester, third_semester) Ok(
(first_semester, second_semester, third_semester, fourth_semester)
)
} }
} }

View file

@ -6,11 +6,16 @@ use crate::{
}; };
use sea_orm::DbConn; use sea_orm::DbConn;
impl From<(i32, ApplicationDetails)> for ApplicationRow { impl TryFrom<(i32, ApplicationDetails)> for ApplicationRow {
fn from((application, d): (i32, ApplicationDetails)) -> Self { type Error = ServiceError;
fn try_from((application, d): (i32, ApplicationDetails)) -> Result<Self, ServiceError> {
let c = d.candidate; let c = d.candidate;
let (diploma_1_8, diploma_2_8, diploma_1_9) = c.grades.group_by_semester(); let (diploma_1_8,
Self { diploma_2_8,
diploma_1_9,
diploma_2_9
) = c.grades.group_by_semester()?;
Ok(Self {
application, application,
name: Some(c.name), name: Some(c.name),
surname: Some(c.surname), surname: Some(c.surname),
@ -30,6 +35,7 @@ impl From<(i32, ApplicationDetails)> for ApplicationRow {
diploma_1_8: diploma_1_8.to_string(), diploma_1_8: diploma_1_8.to_string(),
diploma_2_8: diploma_2_8.to_string(), diploma_2_8: diploma_2_8.to_string(),
diploma_1_9: diploma_1_9.to_string(), diploma_1_9: diploma_1_9.to_string(),
diploma_2_9: diploma_2_9.to_string(),
first_school_name: Some(c.first_school.name().to_owned()), first_school_name: Some(c.first_school.name().to_owned()),
first_school_field: Some(c.first_school.field().to_owned()), first_school_field: Some(c.first_school.field().to_owned()),
@ -45,7 +51,7 @@ impl From<(i32, ApplicationDetails)> for ApplicationRow {
second_parent_surname: d.parents.get(1).map(|p| p.surname.clone()), second_parent_surname: d.parents.get(1).map(|p| p.surname.clone()),
second_parent_telephone: d.parents.get(1).map(|p| p.telephone.clone()), second_parent_telephone: d.parents.get(1).map(|p| p.telephone.clone()),
second_parent_email: d.parents.get(1).map(|p| p.email.clone()), second_parent_email: d.parents.get(1).map(|p| p.email.clone()),
} })
} }
} }
@ -59,11 +65,15 @@ pub async fn export(db: &DbConn, private_key: String) -> Result<Vec<u8>, Service
let row: ApplicationRow = match EncryptedApplicationDetails::try_from((&candidate, &parents)) let row: ApplicationRow = match EncryptedApplicationDetails::try_from((&candidate, &parents))
{ {
Ok(d) => ApplicationRow::from( Ok(d) => ApplicationRow::try_from(
d.decrypt(private_key.to_string()) d.decrypt(private_key.to_string())
.await .await
.map(|d| (application.id, d))?, .map(|d| (application.id, d))?,
), )
.unwrap_or(ApplicationRow {
application: application.id,
..Default::default()
}),
Err(_) => ApplicationRow { Err(_) => ApplicationRow {
application: application.id, application: application.id,