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),
#[error("Csv into inner error")]
CsvIntoInnerError,
#[error("Format error")]
FormatError,
}
impl ServiceError {
@ -122,6 +124,7 @@ impl ServiceError {
ServiceError::ZipError(_) => 500,
ServiceError::CsvError(_) => 500,
ServiceError::CsvIntoInnerError => 500,
ServiceError::FormatError => 500,
}
}

View file

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

View file

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

View file

@ -1,37 +1,70 @@
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 {
#[validate(length(min = 1, max = 255))]
subject: String,
#[validate(length(min = 1, max = 255), custom = "validate_semester")]
semester: String,
#[validate(range(min = 1, max = 5))]
value: i32,
}
impl Grade {
pub fn validate_self(&self) -> Result<(), ServiceError> {
self.validate()
.map_err(ServiceError::ValidationError)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct GradeList(Vec<Grade>);
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> {
grades.map(
|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 second_semester = GradeList::default();
let mut third_semester = GradeList::default();
let mut fourth_semester = GradeList::default();
for grade in &self.0 {
match grade.semester.as_str() {
"1/8" => first_semester.0.push(grade.clone()),
"2/8" => second_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;
impl From<(i32, ApplicationDetails)> for ApplicationRow {
fn from((application, d): (i32, ApplicationDetails)) -> Self {
impl TryFrom<(i32, ApplicationDetails)> for ApplicationRow {
type Error = ServiceError;
fn try_from((application, d): (i32, ApplicationDetails)) -> Result<Self, ServiceError> {
let c = d.candidate;
let (diploma_1_8, diploma_2_8, diploma_1_9) = c.grades.group_by_semester();
Self {
let (diploma_1_8,
diploma_2_8,
diploma_1_9,
diploma_2_9
) = c.grades.group_by_semester()?;
Ok(Self {
application,
name: Some(c.name),
surname: Some(c.surname),
@ -30,6 +35,7 @@ impl From<(i32, ApplicationDetails)> for ApplicationRow {
diploma_1_8: diploma_1_8.to_string(),
diploma_2_8: diploma_2_8.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_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_telephone: d.parents.get(1).map(|p| p.telephone.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))
{
Ok(d) => ApplicationRow::from(
Ok(d) => ApplicationRow::try_from(
d.decrypt(private_key.to_string())
.await
.map(|d| (application.id, d))?,
),
)
.unwrap_or(ApplicationRow {
application: application.id,
..Default::default()
}),
Err(_) => ApplicationRow {
application: application.id,