diff --git a/README.md b/README.md index e04419d..8a2315c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Next - User edit view (for admins) +- If is\_cox -> show 100(?) instead of 7 days # UI - Next 7 days as columns diff --git a/db.sqlite b/db.sqlite index 7e4d0e9..8289edb 100644 Binary files a/db.sqlite and b/db.sqlite differ diff --git a/src/main.rs b/src/main.rs index 818f2c9..801d8ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,236 +2,9 @@ extern crate rocket; mod models; - -use std::collections::HashMap; -use std::ops::Deref; - -use chrono::Duration; -use chrono::Local; -use chrono::NaiveDate; -use rocket::fairing::AdHoc; -use rocket::form; -use rocket::form::validate::range; -use rocket::form::ValueField; -use rocket::http::Cookie; -use rocket::http::CookieJar; -use rocket::request; -use rocket::request::FromRequest; -use rocket::request::Outcome; -use rocket::response::Redirect; -use rocket::Request; -use rocket::{form::Form, fs::FileServer, State}; -use rocket_dyn_templates::context; -use rocket_dyn_templates::Template; -use sea_orm::ColumnTrait; -use sea_orm::ModelTrait; -use sea_orm::QueryFilter; -use sea_orm::{ - ActiveModelTrait, Database, DatabaseConnection, EntityTrait, Order, QueryOrder, Set, -}; -use serde::Serialize; - -use crate::models::{day, trip, user}; - -#[derive(Serialize, Debug)] -struct TripWithUser { - trip: trip::Model, - user: user::Model, -} - -impl TripWithUser { - async fn new(trip: trip::Model, db: &DatabaseConnection) -> Self { - Self { - trip: trip.clone(), - user: trip - .find_related(user::Entity) - .one(db) - .await - .unwrap() - .unwrap(), - } - } -} - -#[derive(Serialize, Debug)] -struct DayWithTrips { - day: day::Model, - trips: Vec, -} - -impl DayWithTrips { - async fn new(day: day::Model, db: &DatabaseConnection) -> Self { - let mut trips = Vec::new(); - for trip in day.find_related(trip::Entity).all(db).await.unwrap() { - trips.push(TripWithUser::new(trip, db).await); - } - - Self { day, trips } - } -} - -#[derive(Serialize)] -struct Name(user::Model); -#[rocket::async_trait] -impl<'r> FromRequest<'r> for Name { - type Error = rocket::Error; - - async fn from_request(req: &'r Request<'_>) -> request::Outcome { - match req.cookies().get("name") { - Some(name) => { - let db = req.guard::<&'r State>(); - let name = name.value(); - let user = find_or_create_user(name, db.await.unwrap().inner()).await; - Outcome::Success(Name(user)) - } - None => Outcome::Forward(()), //No cookie set - } - } -} - -#[get("/")] -async fn index(db: &State, name: Name) -> Template { - let days: Vec = day::Entity::find() - .filter(day::Column::Day.gte(format!("{}", Local::now().format("%Y-%m-%d")))) // don't show stuff from the past - .order_by(day::Column::Day, Order::Asc) - .all(db.inner()) - .await - .unwrap(); - - let mut dwu = HashMap::new(); - for day in days { - dwu.insert( - format!("{}", day.day.format("%Y-%m-%d")), - DayWithTrips::new(day, db.inner()).await, - ); - } - - let mut next_days = Vec::new(); - for i in 0..6 { - next_days.push(Local::now() + Duration::days(i)); - } - - Template::render("index", context! { dwu, next_days, name }) -} - -#[get("/", rank = 2)] -fn name() -> Template { - Template::render("name", context! {}) -} - -#[derive(FromForm)] -struct NameForm(String); - -#[post("/setname", data = "")] -fn setname(cookies: &CookieJar<'_>, name: Form) -> Redirect { - cookies.add(Cookie::new("name", name.0.clone())); - Redirect::to("/") -} - -#[derive(Debug)] -struct NaiveDateForm(NaiveDate); - -impl<'v> rocket::form::FromFormField<'v> for NaiveDateForm { - fn from_value(field: ValueField<'v>) -> form::Result<'v, NaiveDateForm> { - let naivedate = chrono::NaiveDate::parse_from_str(&field.value, "%Y-%m-%d").unwrap(); //TODO: - //fixme - Ok(NaiveDateForm(naivedate)) - } -} - -impl Deref for NaiveDateForm { - type Target = NaiveDate; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[derive(FromForm, Debug)] -struct DayForm { - day: NaiveDateForm, - #[field(validate = range(0..20))] - planned_amount_cox: i32, - planned_starting_time: Option, - open_registration: bool, -} - -#[put("/day", data = "")] -async fn create(db: &State, day: Form) -> Redirect { - let new_day = day::ActiveModel { - day: Set(*day.day), - planned_amount_cox: Set(day.planned_amount_cox), - planned_starting_time: Set(day.planned_starting_time.clone()), - open_registration: Set(day.open_registration), - }; - - let day: Option = day::Entity::find_by_id(*day.day) - .one(db.inner()) - .await - .unwrap(); - match day { - Some(_) => { - new_day.update(db.inner()).await.unwrap(); //TODO: fixme - } - None => { - new_day.insert(db.inner()).await.unwrap(); //TODO: fixme - } - } - - Redirect::to("/") -} - -#[derive(FromForm)] -struct RegisterForm { - day: NaiveDateForm, - #[field(validate = len(3..))] - name: String, -} - -async fn find_or_create_user(name: &str, db: &DatabaseConnection) -> user::Model { - let user = user::Entity::find() - .filter(user::Column::Name.eq(name)) - .one(db) - .await - .unwrap(); - - match user { - Some(user) => user, - None => { - let user = user::ActiveModel { - name: Set(name.clone().into()), - ..Default::default() - }; - user.insert(db).await.unwrap() - } - } -} - -#[put("/register", data = "")] -async fn register(db: &State, register: Form) -> Redirect { - let day = day::Entity::find_by_id(*register.day) - .one(db.inner()) - .await - .unwrap() - .expect("There's no trip on this date (yet)"); - - let user = find_or_create_user(®ister.name, db.inner()).await; - - let day = format!("{}", day.day.format("%Y-%m-%d")); - let trip = trip::ActiveModel { - day: Set(day), - user_id: Set(user.id), - ..Default::default() - }; - trip.insert(db.inner()).await.unwrap(); - - Redirect::to("/") -} +mod rest; #[launch] async fn rocket() -> _ { - rocket::build() - .attach(Template::fairing()) - .manage(Database::connect("sqlite://db.sqlite").await.unwrap()) - .mount("/public", FileServer::from("static/")) - .mount("/", routes![index, create, register, name, setname]) + rest::start().await } diff --git a/src/models/all.rs b/src/models/all.rs new file mode 100644 index 0000000..89500f1 --- /dev/null +++ b/src/models/all.rs @@ -0,0 +1,41 @@ +use sea_orm::{DatabaseConnection, ModelTrait}; +use serde::Serialize; + +use super::{day, trip, user}; + +#[derive(Serialize, Debug)] +pub struct TripWithUser { + pub trip: trip::Model, + pub user: user::Model, +} + +impl TripWithUser { + async fn new(trip: trip::Model, db: &DatabaseConnection) -> Self { + Self { + trip: trip.clone(), + user: trip + .find_related(user::Entity) + .one(db) + .await + .unwrap() + .unwrap(), + } + } +} + +#[derive(Serialize, Debug)] +pub struct DayWithTrips { + pub day: day::Model, + pub trips: Vec, +} + +impl DayWithTrips { + pub async fn new(day: day::Model, db: &DatabaseConnection) -> Self { + let mut trips = Vec::new(); + for trip in day.find_related(trip::Entity).all(db).await.unwrap() { + trips.push(TripWithUser::new(trip, db).await); + } + + Self { day, trips } + } +} diff --git a/src/models/day.rs b/src/models/day.rs index b48aa40..e1eb06c 100644 --- a/src/models/day.rs +++ b/src/models/day.rs @@ -1,6 +1,6 @@ //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 -use sea_orm::entity::prelude::*; +use sea_orm::{entity::prelude::*, Set}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] @@ -13,6 +13,22 @@ pub struct Model { pub open_registration: bool, } +impl Model { + pub async fn find_or_create_day(date: chrono::NaiveDate, db: &DatabaseConnection) -> Model { + let day = Entity::find_by_id(date).one(db).await.unwrap(); + match day { + Some(day) => day, + None => { + let new_day = ActiveModel { + day: Set(date), + ..Default::default() + }; + new_day.insert(db).await.unwrap() + } + } + } +} + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation { #[sea_orm(has_many = "super::trip::Entity")] diff --git a/src/models/mod.rs b/src/models/mod.rs index 35c7ee8..878c36e 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -2,6 +2,8 @@ pub mod prelude; +pub mod all; + pub mod day; pub mod trip; pub mod user; diff --git a/src/models/user.rs b/src/models/user.rs index 4a44287..a5e4b8e 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,6 +1,11 @@ //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.0 -use sea_orm::entity::prelude::*; +use rocket::{ + http::Status, + request::{self, FromRequest, Outcome}, + Request, State, +}; +use sea_orm::{entity::prelude::*, Set}; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] @@ -13,6 +18,49 @@ pub struct Model { pub is_admin: bool, } +impl Model { + pub async fn find_or_create_user(name: &str, db: &DatabaseConnection) -> Model { + let user = Entity::find() + .filter(Column::Name.eq(name)) + .one(db) + .await + .unwrap(); + + match user { + Some(user) => user, + None => { + let user = ActiveModel { + name: Set(name.clone().into()), + ..Default::default() + }; + user.insert(db).await.unwrap() + } + } + } +} + +#[derive(Debug)] +pub enum UserError { + NoCookieSet, +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for Model { + type Error = UserError; + + async fn from_request(req: &'r Request<'_>) -> request::Outcome { + match req.cookies().get("name") { + Some(name) => { + let db = req.guard::<&'r State>(); + let name = name.value(); + let user = Model::find_or_create_user(name, db.await.unwrap().inner()).await; + Outcome::Success(user) + } + None => Outcome::Failure((Status::Unauthorized, UserError::NoCookieSet)), + } + } +} + #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} diff --git a/src/rest/mod.rs b/src/rest/mod.rs new file mode 100644 index 0000000..012c369 --- /dev/null +++ b/src/rest/mod.rs @@ -0,0 +1,79 @@ +mod restday; +mod restreg; + +use std::ops::Deref; + +use chrono::{Duration, Local, NaiveDate}; +use rocket::{ + form::{self, Form, ValueField}, + fs::FileServer, + http::{Cookie, CookieJar}, + response::Redirect, + Build, Rocket, State, +}; +use rocket_dyn_templates::{context, Template}; +use sea_orm::{Database, DatabaseConnection}; + +use super::models::{all::DayWithTrips, day, user}; + +#[derive(Debug)] +struct NaiveDateForm(NaiveDate); + +impl<'v> rocket::form::FromFormField<'v> for NaiveDateForm { + fn from_value(field: ValueField<'v>) -> form::Result<'v, NaiveDateForm> { + let naivedate = chrono::NaiveDate::parse_from_str(&field.value, "%Y-%m-%d").unwrap(); //TODO: + //fixme + Ok(NaiveDateForm(naivedate)) + } +} + +impl Deref for NaiveDateForm { + type Target = NaiveDate; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +#[get("/")] +async fn index(db: &State, user: user::Model) -> Template { + let mut data = Vec::new(); + for i in 0..6 { + let date = (Local::now() + Duration::days(i)).date_naive(); + let day = day::Model::find_or_create_day(date, db.inner()).await; + data.push(DayWithTrips::new(day, db.inner()).await); + } + + Template::render("index", context! { data, user }) +} + +#[get("/name")] +fn name() -> Template { + Template::render("name", context! {}) +} + +#[derive(FromForm)] +struct NameForm { + name: String, +} + +#[put("/name", data = "")] +fn savename(name: Form, cookies: &CookieJar) -> Redirect { + cookies.add(Cookie::new("name", name.name.clone())); + Redirect::to("/") +} + +#[catch(401)] //unauthorized +fn unauthorized_error() -> Redirect { + Redirect::to("/name") +} + +pub async fn start() -> Rocket { + rocket::build() + .attach(Template::fairing()) + .manage(Database::connect("sqlite://db.sqlite").await.unwrap()) + .mount("/public", FileServer::from("static/")) + .mount("/", routes![index, name, savename]) + .mount("/day", restday::routes()) + .mount("/register", restreg::routes()) + .register("/", catchers![unauthorized_error]) +} diff --git a/src/rest/restday.rs b/src/rest/restday.rs new file mode 100644 index 0000000..130fe47 --- /dev/null +++ b/src/rest/restday.rs @@ -0,0 +1,44 @@ +use rocket::{form::Form, response::Redirect, Route, State}; +use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set}; + +use crate::models::day; + +use super::NaiveDateForm; + +#[derive(FromForm, Debug)] +struct DayForm { + day: NaiveDateForm, + #[field(validate = range(0..20))] + planned_amount_cox: i32, + planned_starting_time: Option, + open_registration: bool, +} + +#[put("/", data = "")] +async fn create(db: &State, day: Form) -> Redirect { + let new_day = day::ActiveModel { + day: Set(*day.day), + planned_amount_cox: Set(day.planned_amount_cox), + planned_starting_time: Set(day.planned_starting_time.clone()), + open_registration: Set(day.open_registration), + }; + + let day: Option = day::Entity::find_by_id(*day.day) + .one(db.inner()) + .await + .unwrap(); + match day { + Some(_) => { + new_day.update(db.inner()).await.unwrap(); //TODO: fixme + } + None => { + new_day.insert(db.inner()).await.unwrap(); //TODO: fixme + } + } + + Redirect::to("/") +} + +pub fn routes() -> Vec { + routes![create] +} diff --git a/src/rest/restreg.rs b/src/rest/restreg.rs new file mode 100644 index 0000000..7a332d4 --- /dev/null +++ b/src/rest/restreg.rs @@ -0,0 +1,39 @@ +use rocket::{form::Form, response::Redirect, Route, State}; +use sea_orm::{ActiveModelTrait, DatabaseConnection, EntityTrait, Set}; + +use crate::models::{day, trip, user}; + +use super::NaiveDateForm; + +#[derive(FromForm)] +struct RegisterForm { + day: NaiveDateForm, + #[field(validate = len(3..))] + name: String, +} + +#[put("/", data = "")] +async fn register(db: &State, register: Form) -> Redirect { + let day = day::Entity::find_by_id(*register.day) + .one(db.inner()) + .await + .unwrap() + .expect("There's no trip on this date (yet)"); + + let user = user::Model::find_or_create_user(®ister.name, db.inner()).await; + + let day = format!("{}", day.day.format("%Y-%m-%d")); + let trip = trip::ActiveModel { + day: Set(day), + user_id: Set(user.id), + ..Default::default() + }; + + trip.insert(db.inner()).await.unwrap(); + + Redirect::to("/") +} + +pub fn routes() -> Vec { + routes![register] +} diff --git a/templates/index.html.tera b/templates/index.html.tera index 17782b0..86024b9 100644 --- a/templates/index.html.tera +++ b/templates/index.html.tera @@ -1,31 +1,26 @@ {% extends "base" %} {% block content %} -{% for day in next_days %} - {% set day_string = day | date(format="%Y-%m-%d") %} - {{ day | date(format="%d.%m.%Y")}} +{% for day_with_trip in data %} + {% set day = day_with_trip.day %} + {% set day_string = day.day | date(format="%Y-%m-%d") %} + {% set trips = day_with_trip.trips %} + {{ day.day | date(format="%d.%m.%Y")}}
- - {% if dwu[day_string] and dwu[day_string].day.planned_amount_cox > 0%} - {% set cur_day = dwu[day_string].day %} - {% set_global already_registered = false %} - Geplante Steuerpersonen: {{ cur_day.planned_amount_cox}}
- Geplante Abfahrtszeit: {{ cur_day.planned_starting_time }}
+ + + {% if day.planned_amount_cox > 0%} + Geplante Steuerpersonen: {{ day.planned_amount_cox}}
+ Geplante Abfahrtszeit: {{ day.planned_starting_time }}
Angemeldete Personen:
    - {% for trip in dwu[day_string].trips %} -
  1. - {% if trip.user.name == name.name %} - {% set_global already_registered = true %} - DU - {% else %} - {{ trip.user.name }} - {% endif %} + {% for trip in trips %} +
  2. {{ trip.user.name }}
  3. {% endfor %}
- {% if cur_day.open_registration %} + {% if day.open_registration %}
+
@@ -34,7 +29,7 @@
- +
@@ -57,15 +52,15 @@
- +
- +
- +
diff --git a/templates/name.html.tera b/templates/name.html.tera index 52ae584..3502458 100644 --- a/templates/name.html.tera +++ b/templates/name.html.tera @@ -2,7 +2,8 @@ {% block content %} What's your name? - + +