From d5194e82f83297f28d2bfe7d14fae002aa6c0763 Mon Sep 17 00:00:00 2001 From: Franz Dietrich Date: Sat, 11 Feb 2023 09:31:16 +0100 Subject: [PATCH] Add printable export --- .env | 2 +- terminwahl_back/src/api/write.rs | 8 +- terminwahl_back/src/db/read.rs | 75 +++++++++++++++++- terminwahl_back/src/db/write.rs | 10 +-- terminwahl_back/src/main.rs | 4 + terminwahl_back/src/views.rs | 28 +++++++ .../templates/lehrer_einzeln.html.hbs | 76 +++++++++++++++++++ terminwahl_typen/src/lib.rs | 12 +-- 8 files changed, 198 insertions(+), 17 deletions(-) create mode 100644 terminwahl_back/templates/lehrer_einzeln.html.hbs diff --git a/.env b/.env index f2757b4..a82d9af 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ DATABASE_URL="sqlite://terminwahl_back/db.sqlite" -RUST_LOG="debug" +RUST_LOG="debug,sqlx=error" SMTP_USER="SMTP_USERNAME" SMTP_PASSWORD="SMTP_PASSWORD" PORT="8087" diff --git a/terminwahl_back/src/api/write.rs b/terminwahl_back/src/api/write.rs index 939b2ca..95fc840 100644 --- a/terminwahl_back/src/api/write.rs +++ b/terminwahl_back/src/api/write.rs @@ -19,12 +19,12 @@ use crate::db::{ }; #[derive(Serialize)] -pub struct FullAppointment { +pub struct AppointmentTeacherSlot { pub teacher: Teacher, pub slot: AppointmentSlot, } -impl FullAppointment { +impl AppointmentTeacherSlot { pub async fn get(pool: &Pool, teacher_id: i64, slot_id: i64) -> Result> { Ok(Self { teacher: get_teacher_by_id(pool, teacher_id) @@ -62,7 +62,7 @@ pub async fn save_appointments_json( |PlannedAppointment { teacher_id, slot_id, - }| { FullAppointment::get(&pool, teacher_id, slot_id) }, + }| { AppointmentTeacherSlot::get(&pool, teacher_id, slot_id) }, )) .await .expect("Failed to get full list of appointments"); @@ -80,7 +80,7 @@ pub async fn save_appointments_json( } pub async fn send_confirmation_request<'a>( - appointments: &Vec, + appointments: &Vec, nutzer: &Nutzer, validation_key: &str, handlebars: &Handlebars<'a>, diff --git a/terminwahl_back/src/db/read.rs b/terminwahl_back/src/db/read.rs index db5c724..70319c7 100644 --- a/terminwahl_back/src/db/read.rs +++ b/terminwahl_back/src/db/read.rs @@ -1,9 +1,11 @@ use std::collections::HashSet; +use chrono::NaiveDateTime; +use serde::{Deserialize, Serialize}; use sqlx::query_as; use terminwahl_typen::{ - AppointmentSlot, AppointmentSlots, IdType, SlotId, Subject, Subjects, Teacher, Teachers, + AppointmentSlot, AppointmentSlots, IdType, Nutzer, SlotId, Subject, Subjects, Teacher, Teachers, }; use super::Pool; @@ -85,3 +87,74 @@ pub async fn get_unavailable(db: &Pool) -> Result, sqlx::Error> Err(e) => Err(e), } } + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct TeacherWithAppointments { + teacher: Teacher, + appointments: Vec, +} + +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct FullAppointment { + id: IdType, + teacher_id: IdType, + slot_id: IdType, + nutzer_id: IdType, + expires: Option, +} +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct AssignedAppointment { + appointment: FullAppointment, + slot: AppointmentSlot, + nutzer: Nutzer, +} + +pub async fn get_all_teachers(db: &Pool) -> Result, sqlx::Error> { + let teachers = get_teachers(db).await?; + let mut response = Vec::new(); + + for teacher in teachers.into_iter() { + let teacher_id: i64 = teacher.id; + let mut appointments: Vec = query_as!( + FullAppointment, + r#" + SELECT id,teacher_id,slot_id,nutzer_id,expires + FROM `appointments` WHERE teacher_id = ?"#, + teacher_id + ) + .fetch_all(db) + .await?; + appointments.sort_by(|a, b| a.slot_id.cmp(&b.slot_id)); + let mut assigned_appointments: Vec = Vec::new(); + for appointment in appointments.into_iter() { + let slot = query_as!( + AppointmentSlot, + r#" + SELECT * + FROM `appointment_slots` WHERE id = ? "#, + appointment.slot_id + ) + .fetch_one(db) + .await?; + let nutzer = query_as!( + Nutzer, + r#" + SELECT name, schueler as schüler,email + FROM `nutzer` WHERE id = ? "#, + appointment.nutzer_id + ) + .fetch_one(db) + .await?; + assigned_appointments.push(AssignedAppointment { + appointment, + slot, + nutzer, + }) + } + response.push(TeacherWithAppointments { + teacher, + appointments: assigned_appointments, + }) + } + Ok(response) +} diff --git a/terminwahl_back/src/db/write.rs b/terminwahl_back/src/db/write.rs index 4de9c5e..89c7235 100644 --- a/terminwahl_back/src/db/write.rs +++ b/terminwahl_back/src/db/write.rs @@ -3,7 +3,7 @@ use futures::future; use sqlx::{query, query_as}; use terminwahl_typen::{IdType, Nutzer, PlannedAppointment}; -use crate::api::write::FullAppointment; +use crate::api::write::AppointmentTeacherSlot; use super::Pool; @@ -57,7 +57,7 @@ pub async fn save_nutzer(pool: &Pool, nutzer: &Nutzer) -> Result Result, sqlx::Error> { +) -> Result, sqlx::Error> { let _ = query!( "UPDATE appointments SET expires = NULL WHERE validation_key = ?", validation_key @@ -76,7 +76,7 @@ pub async fn confirm_appointments( |PlannedAppointment { teacher_id, slot_id, - }| { FullAppointment::get(pool, teacher_id, slot_id) }, + }| { AppointmentTeacherSlot::get(pool, teacher_id, slot_id) }, )) .await .expect("Failed to get full list of appointments"); @@ -89,7 +89,7 @@ pub async fn cancel_appointment( teacher_id: IdType, slot_id: IdType, validation_key: &str, -) -> Result, sqlx::Error> { +) -> Result, sqlx::Error> { let _ = query!( "DELETE FROM appointments WHERE teacher_id = ? AND slot_id = ? AND validation_key = ?", teacher_id, @@ -113,7 +113,7 @@ pub async fn cancel_appointment( |PlannedAppointment { teacher_id, slot_id, - }| { FullAppointment::get(pool, teacher_id, slot_id) }, + }| { AppointmentTeacherSlot::get(pool, teacher_id, slot_id) }, )) .await .expect("Failed to get full list of appointments"); diff --git a/terminwahl_back/src/main.rs b/terminwahl_back/src/main.rs index 8d77abc..d3915a9 100644 --- a/terminwahl_back/src/main.rs +++ b/terminwahl_back/src/main.rs @@ -92,6 +92,10 @@ async fn main() -> std::io::Result<()> { web::resource("/cancel/{teacher_id}/{slot_id}/{validation_key}") .route(web::get().to(views::delete_appointment)), ) + .service( + web::resource("/export/all/{password}") + .route(web::get().to(views::export_appointments)), + ) .service(Files::new("/", wasm_statics.clone()).index_file("index.html")) }) .bind(("127.0.0.1", port))? diff --git a/terminwahl_back/src/views.rs b/terminwahl_back/src/views.rs index f387a17..945467e 100644 --- a/terminwahl_back/src/views.rs +++ b/terminwahl_back/src/views.rs @@ -6,6 +6,7 @@ use terminwahl_typen::IdType; use crate::{ db::{ + read::get_all_teachers, write::{cancel_appointment, confirm_appointments}, Pool, }, @@ -52,3 +53,30 @@ pub async fn delete_appointment( Err(e) => Err(error::ErrorBadRequest(e)), } } + +pub async fn export_appointments( + pool: web::Data, + _mailer: web::Data>, + handlebars: web::Data>, + css: web::Data, + path: web::Path, +) -> Result { + let password = path.into_inner(); + dbg!(&password); + if password == "AllExport1517" { + match get_all_teachers(&pool).await { + Ok(teachers) => { + dbg!(&teachers); + let data = json!({ + "css_file" : css.path, + "teachers": teachers, + }); + Ok(HttpResponse::Ok() + .body(handlebars.render("lehrer_einzeln.html", &data).unwrap())) + } + Err(e) => Err(error::ErrorBadRequest(e)), + } + } else { + Err(error::ErrorBadRequest("Wrong Password")) + } +} diff --git a/terminwahl_back/templates/lehrer_einzeln.html.hbs b/terminwahl_back/templates/lehrer_einzeln.html.hbs new file mode 100644 index 0000000..15bd004 --- /dev/null +++ b/terminwahl_back/templates/lehrer_einzeln.html.hbs @@ -0,0 +1,76 @@ + + + + + Elternsprechtagtermine + + + + + + + +
+
+
+ + {{#each teachers as |lehrer|}} +
+
+
+

+ {{lehrer.teacher.ansprache}} + {{lehrer.teacher.last_name}} +

+
+
+
+ + + + + + + + + {{#each lehrer.appointments as |appointment|}} + + + + + {{/each}} + +
ZeitName
+ {{time_of appointment.slot.start_time}} + – + {{time_of appointment.slot.end_time}} + + {{appointment.nutzer.name}} +
+
+
+
+
+ {{/each}} +
+
+
+ + \ No newline at end of file diff --git a/terminwahl_typen/src/lib.rs b/terminwahl_typen/src/lib.rs index 5263d0b..06c5e43 100644 --- a/terminwahl_typen/src/lib.rs +++ b/terminwahl_typen/src/lib.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; pub type IdType = i64; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct Teacher { pub id: IdType, pub ansprache: String, @@ -15,14 +15,14 @@ pub struct Teacher { pub type Teachers = Vec; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct Subject { pub id: IdType, pub name: String, } pub type Subjects = Vec; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct Appointment { pub id: IdType, pub teacher_id: IdType, @@ -36,14 +36,14 @@ pub struct PlannedAppointment { pub type AppointmentSlots = HashMap; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct AppointmentSlot { pub id: IdType, pub start_time: NaiveDateTime, pub end_time: NaiveDateTime, } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub struct OccupiedSlot { pub id: IdType, pub teacher_id: IdType, @@ -65,7 +65,7 @@ impl SlotId { } } -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Deserialize, Serialize, Clone)] pub enum RequestState { Success, Message(String),