add planned_trip functionality

This commit is contained in:
philipp 2023-04-04 12:19:56 +02:00
parent 3dfc64071c
commit 3d907487a1
12 changed files with 273 additions and 32 deletions

32
Cargo.lock generated
View File

@ -229,8 +229,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
dependencies = [ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys",
"num-integer", "num-integer",
"num-traits", "num-traits",
"serde",
"time 0.1.45",
"wasm-bindgen",
"winapi", "winapi",
] ]
@ -289,7 +293,7 @@ dependencies = [
"rand", "rand",
"sha2", "sha2",
"subtle", "subtle",
"time", "time 0.3.20",
"version_check", "version_check",
] ]
@ -716,7 +720,7 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
] ]
[[package]] [[package]]
@ -1203,7 +1207,7 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
dependencies = [ dependencies = [
"libc", "libc",
"log", "log",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.45.0", "windows-sys 0.45.0",
] ]
@ -1732,7 +1736,7 @@ dependencies = [
"serde", "serde",
"state", "state",
"tempfile", "tempfile",
"time", "time 0.3.20",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-util", "tokio-util",
@ -1792,7 +1796,7 @@ dependencies = [
"smallvec", "smallvec",
"stable-pattern", "stable-pattern",
"state", "state",
"time", "time 0.3.20",
"tokio", "tokio",
"uncased", "uncased",
] ]
@ -1802,6 +1806,7 @@ name = "rot"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"argon2", "argon2",
"chrono",
"env_logger", "env_logger",
"log", "log",
"rocket", "rocket",
@ -2237,6 +2242,17 @@ dependencies = [
"once_cell", "once_cell",
] ]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.20" version = "0.3.20"
@ -2618,6 +2634,12 @@ dependencies = [
"try-lock", "try-lock",
] ]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"

View File

@ -14,3 +14,5 @@ sqlx = { version = "0.6", features = ["sqlite", "runtime-tokio-rustls", "macros"
argon2 = "0.5" argon2 = "0.5"
serde = { version = "1.0", features = [ "derive" ]} serde = { version = "1.0", features = [ "derive" ]}
serde_json = "1.0" serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"]}

View File

@ -17,31 +17,12 @@
# DB # DB
- users
- id: i32
- name: String
- pw: String
- is_cox: bool (false)
- is_admin: bool (false)
- is_guest: bool (true)
- planned_event
- id: i32
- name: String (e.g. "USI")
- planned_amount_cox: i32
- trip_details_id: i32 (trip_details.id)
- allow_guests: bool (false)
- trip - trip
- id: i32 - id: i32
- cox_id: i32 (user.id) - cox_id: i32 (user.id)
- trip_details: Option<i32> (trip_details.id) - trip_details: Option<i32> (trip_details.id)
- planned_event_id: Option<i32> (planned_event.id) - planned_event_id: Option<i32> (planned_event.id)
- created: chrono::DateTime - created: chrono::DateTime
- trip_details
- id: i32
- planned_starting_time: String
- max_people: i32
- day: chrono::NaiveDate
- notes: String
- user_trip - user_trip
- trip_details_id: i32 (trip_details.id) - trip_details_id: i32 (trip_details.id)
- user_id: i32 (user.id) - user_id: i32 (user.id)
@ -54,7 +35,7 @@
- [x] User passwort zurücksetzen - [x] User passwort zurücksetzen
- [x] Cox + admin + guest setzen - [x] Cox + admin + guest setzen
- [ ] Ausfahrten - [ ] Ausfahrten
- [ ] CRUD planned_event - [x] CRUD planned_event
- [ ] CRUD trip_details - [x] CRUD trip_details
- [ ] CRUD trip - [ ] CRUD trip
- [ ] CRUD user_trip - [ ] CRUD user_trip

View File

@ -6,3 +6,21 @@ CREATE TABLE IF NOT EXISTS "user" (
"is_admin" boolean NOT NULL DEFAULT FALSE, "is_admin" boolean NOT NULL DEFAULT FALSE,
"is_guest" boolean NOT NULL DEFAULT TRUE "is_guest" boolean NOT NULL DEFAULT TRUE
); );
CREATE TABLE IF NOT EXISTS "trip_details" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"planned_starting_time" text NOT NULL,
"max_people" INTEGER NOT NULL,
"day" TEXT NOT NULL,
"notes" TEXT
);
CREATE TABLE IF NOT EXISTS "planned_event" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" text NOT NULL,
"planned_amount_cox" INTEGER unsigned NOT NULL,
"allow_guests" boolean NOT NULL default false,
"trip_details_id" INTEGER NOT NULL,
"created_at" text NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY(trip_details_id) REFERENCES trip_details(id) ON DELETE CASCADE
);

View File

@ -1,2 +1,25 @@
use chrono::NaiveDate;
use serde::Serialize;
use sqlx::SqlitePool;
use self::planned_event::PlannedEvent;
pub mod planned_event;
pub mod tripdetails;
pub mod user; pub mod user;
//pub mod users; //pub mod users;
#[derive(Serialize)]
pub struct Day {
day: NaiveDate,
planned_events: Vec<PlannedEvent>,
}
impl Day {
pub async fn new(db: &SqlitePool, day: NaiveDate) -> Self {
Self {
day,
planned_events: PlannedEvent::get_for_day(db, day).await,
}
}
}

View File

@ -0,0 +1,55 @@
use chrono::NaiveDate;
use serde::Serialize;
use sqlx::SqlitePool;
#[derive(Serialize)]
pub struct PlannedEvent {
id: i64,
name: String,
planned_amount_cox: i64,
allow_guests: bool,
trip_details_id: i64,
planned_starting_time: String,
max_people: i64,
day: String,
notes: Option<String>,
}
impl PlannedEvent {
pub async fn get_for_day(db: &SqlitePool, day: NaiveDate) -> Vec<Self> {
sqlx::query_as!(
PlannedEvent,
"
SELECT planned_event.id, name, planned_amount_cox, allow_guests, trip_details_id, planned_starting_time, max_people, day, notes
FROM planned_event
INNER JOIN trip_details ON planned_event.trip_details_id = trip_details.id
"
)
.fetch_all(db)
.await
.unwrap() //TODO: fixme
}
pub async fn new(
db: &SqlitePool,
name: String,
planned_amount_cox: i32,
allow_guests: bool,
trip_details_id: i64,
) {
sqlx::query!(
"INSERT INTO planned_event(name, planned_amount_cox, allow_guests, trip_details_id) VALUES(?, ?, ?, ?)",
name, planned_amount_cox, allow_guests, trip_details_id
)
.execute(db)
.await
.unwrap(); //TODO: fixme
}
pub async fn delete(db: &SqlitePool, id: i64) {
sqlx::query!("DELETE FROM planned_event WHERE id = ?", id)
.execute(db)
.await
.unwrap(); //TODO: fixme
}
}

33
src/model/tripdetails.rs Normal file
View File

@ -0,0 +1,33 @@
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub struct TripDetails {
pub id: i64,
planned_starting_time: String,
max_people: i32,
day: String,
notes: Option<String>,
}
impl TripDetails {
pub async fn new(
db: &SqlitePool,
planned_starting_time: String,
max_people: i32,
day: String,
notes: Option<String>,
) -> i64 {
let query = sqlx::query!(
"INSERT INTO trip_details(planned_starting_time, max_people, day, notes) VALUES(?, ?, ?, ?)" ,
planned_starting_time,
max_people,
day,
notes
)
.execute(db)
.await
.unwrap(); //TODO: fixme
query.last_insert_rowid()
}
}

View File

@ -1,9 +1,11 @@
use rocket::Route; use rocket::Route;
pub mod planned_event;
pub mod user; pub mod user;
pub fn routes() -> Vec<Route> { pub fn routes() -> Vec<Route> {
let mut ret = Vec::new(); let mut ret = Vec::new();
ret.append(&mut user::routes()); ret.append(&mut user::routes());
ret.append(&mut planned_event::routes());
ret ret
} }

View File

@ -0,0 +1,61 @@
use rocket::{
form::Form,
get, post,
response::{Flash, Redirect},
routes, FromForm, Route, State,
};
use sqlx::SqlitePool;
use crate::model::{planned_event::PlannedEvent, tripdetails::TripDetails, user::AdminUser};
//TODO: add constraints (e.g. planned_amount_cox > 0)
#[derive(FromForm)]
struct AddPlannedEventForm {
day: String,
name: String,
planned_amount_cox: i32,
allow_guests: bool,
planned_starting_time: String,
max_people: i32,
notes: Option<String>,
}
#[post("/planned-event", data = "<data>")]
async fn create(
db: &State<SqlitePool>,
data: Form<AddPlannedEventForm>,
_admin: AdminUser,
) -> Flash<Redirect> {
//TODO: fix clones()
let trip_details_id = TripDetails::new(
db,
data.planned_starting_time.clone(),
data.max_people,
data.day.clone(),
data.notes.clone(),
)
.await;
//TODO: fix clone()
PlannedEvent::new(
db,
data.name.clone(),
data.planned_amount_cox,
data.allow_guests,
trip_details_id,
)
.await;
Flash::success(Redirect::to("/"), "Successfully planned the event")
}
#[get("/planned-event/<id>/delete")]
async fn delete(db: &State<SqlitePool>, id: i64, _admin: AdminUser) -> Flash<Redirect> {
PlannedEvent::delete(db, id).await;
Flash::success(Redirect::to("/"), "Successfully deleted the event")
}
pub fn routes() -> Vec<Route> {
routes![create, delete]
}

View File

@ -38,7 +38,11 @@ struct UserEditForm {
} }
#[post("/user", data = "<data>")] #[post("/user", data = "<data>")]
async fn update(db: &State<SqlitePool>, data: Form<UserEditForm>) -> Flash<Redirect> { async fn update(
db: &State<SqlitePool>,
data: Form<UserEditForm>,
_admin: AdminUser,
) -> Flash<Redirect> {
let user = User::find_by_id(db, data.id).await; let user = User::find_by_id(db, data.id).await;
let user = match user { let user = match user {
Ok(user) => user, Ok(user) => user,

View File

@ -1,15 +1,21 @@
use rocket::{catch, catchers, get, response::Redirect, routes, Build, Rocket}; use chrono::{Duration, Local, NaiveDate};
use rocket::{catch, catchers, get, response::Redirect, routes, Build, Rocket, State};
use rocket_dyn_templates::{context, Template}; use rocket_dyn_templates::{context, Template};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use crate::model::user::User; use crate::model::{user::User, Day};
mod admin; mod admin;
mod auth; mod auth;
#[get("/")] #[get("/")]
fn index(user: User) -> Template { async fn index(db: &State<SqlitePool>, user: User) -> Template {
Template::render("index", context! {loggedin_user: user}) let mut days = Vec::new();
for i in 0..6 {
let date = (Local::now() + Duration::days(i)).date_naive();
days.push(Day::new(db, date).await);
}
Template::render("index", context! {loggedin_user: user, days})
} }
#[catch(401)] //unauthorized #[catch(401)] //unauthorized

View File

@ -27,6 +27,40 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<h1>Hi</h1> <h1>Ausfahrten</h1>
{% for day in days %}
<h2>{{ day.day }}</h2>
{% for planned_event in day.planned_events %}
<h3>Planned event '{{ planned_event.name }}'</h3>
Planned amount cox: {{ planned_event.planned_amount_cox }}<br />
Allow guests: {{ planned_event.allow_guests }}<br />
Planned starting time: {{ planned_event.planned_starting_time }}<br />
Max people: {{ planned_event.max_people }}<br />
Notes: {{ planned_event.notes }}<br />
{% if loggedin_user.is_admin %}
<a href="/admin/planned-event/{{ planned_event.id }}/delete">DELETE</a>
{% endif %}
{% endfor %}
{% if loggedin_user.is_admin %}
<h3>Add planned event</h3>
<form action="/admin/planned-event" method="post">
<input type="hidden" name="day" value="{{ day.day }}" />
<input type="text" name="name" placeholder="name (e.g. USI)" />
<input type="number" name="planned_amount_cox" placeholder="Anzahl Steuerleute" />
Gäste <input type="checkbox" name="allow_guests" />
<input type="text" name="planned_starting_time" placeholder="Startzeit" />
<input type="number" name="max_people" placeholder="Anzahl Ruderer" />
<input type="text" name="notes" placeholder="Anmerkungen" />
<input type="submit" />
</form>
{% endif %}
<hr/>
{% endfor %}
{% endblock content %} {% endblock content %}