forked from Ruderverein-Donau-Linz/rowt
		
	Merge pull request 'remove users from trip after they have read the notificatoin' (#518) from remove-users-from-canceled-trips-after-reading-notification into main
Reviewed-on: Ruderverein-Donau-Linz/rowt#518
This commit is contained in:
		
							
								
								
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							@@ -2272,6 +2272,7 @@ dependencies = [
 | 
			
		||||
 "lettre",
 | 
			
		||||
 "log",
 | 
			
		||||
 "openssl",
 | 
			
		||||
 "regex",
 | 
			
		||||
 "rocket",
 | 
			
		||||
 "rocket_dyn_templates",
 | 
			
		||||
 "serde",
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ csv = "1.3"
 | 
			
		||||
itertools = "0.12"
 | 
			
		||||
job_scheduler_ng = "2.0"
 | 
			
		||||
ureq = { version = "2.9", features = ["json"] }
 | 
			
		||||
regex = "1.10"
 | 
			
		||||
 | 
			
		||||
[target.'cfg(not(windows))'.dependencies]
 | 
			
		||||
openssl = { version = "0.10", features = [ "vendored" ] }
 | 
			
		||||
 
 | 
			
		||||
@@ -160,6 +160,7 @@ CREATE TABLE IF NOT EXISTS "notification" (
 | 
			
		||||
	"read_at" DATETIME,
 | 
			
		||||
	"created_at" DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
 | 
			
		||||
	"category" TEXT NOT NULL,
 | 
			
		||||
	"action_after_reading" TEXT,
 | 
			
		||||
	"link" TEXT
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -137,7 +137,7 @@ ORDER BY created_at DESC
 | 
			
		||||
 | 
			
		||||
        if !was_unusable_before && boat.is_locked(db).await {
 | 
			
		||||
            let cox = Role::find_by_name(db, "cox").await.unwrap();
 | 
			
		||||
            Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, bitte beachten, dass {} bis auf weiteres aufgrund von Reparaturarbeiten gesperrt ist.", boat.name), "Boot gesperrt", None).await;
 | 
			
		||||
            Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, bitte beachten, dass {} bis auf weiteres aufgrund von Reparaturarbeiten gesperrt ist.", boat.name), "Boot gesperrt", None, None).await;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let technicals =
 | 
			
		||||
@@ -158,6 +158,7 @@ ORDER BY created_at DESC
 | 
			
		||||
                    ),
 | 
			
		||||
                    "Neuer Bootsschaden angelegt",
 | 
			
		||||
                    None,
 | 
			
		||||
                    None,
 | 
			
		||||
                )
 | 
			
		||||
                .await;
 | 
			
		||||
            }
 | 
			
		||||
@@ -178,6 +179,7 @@ ORDER BY created_at DESC
 | 
			
		||||
            ),
 | 
			
		||||
            "Neuer Bootsschaden angelegt",
 | 
			
		||||
            None,
 | 
			
		||||
            None,
 | 
			
		||||
        )
 | 
			
		||||
        .await;
 | 
			
		||||
 | 
			
		||||
@@ -235,7 +237,7 @@ ORDER BY created_at DESC
 | 
			
		||||
                       boat.name,
 | 
			
		||||
                    ),
 | 
			
		||||
                    "Bootsschaden repariert",
 | 
			
		||||
                    None,
 | 
			
		||||
                    None,None
 | 
			
		||||
                )
 | 
			
		||||
                .await;
 | 
			
		||||
            }
 | 
			
		||||
@@ -261,7 +263,7 @@ ORDER BY created_at DESC
 | 
			
		||||
                    boat_damage.desc, boat.name,
 | 
			
		||||
                ),
 | 
			
		||||
                "Bootsschaden repariert",
 | 
			
		||||
                None,
 | 
			
		||||
                None,None
 | 
			
		||||
            )
 | 
			
		||||
            .await;
 | 
			
		||||
            }
 | 
			
		||||
@@ -320,6 +322,7 @@ ORDER BY created_at DESC
 | 
			
		||||
                 ),
 | 
			
		||||
                 "Bootsschaden repariert & verifiziert",
 | 
			
		||||
                 None,
 | 
			
		||||
                 None
 | 
			
		||||
             )
 | 
			
		||||
             .await;
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -333,13 +336,14 @@ ORDER BY created_at DESC
 | 
			
		||||
                ),
 | 
			
		||||
                "Bootsschaden verifiziert",
 | 
			
		||||
                None,
 | 
			
		||||
                None
 | 
			
		||||
            ).await;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if was_unusable_before && !boat.is_locked(db).await {
 | 
			
		||||
            let cox = Role::find_by_name(db, "cox").await.unwrap();
 | 
			
		||||
            Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, {} wurde repariert und freut sich ab sofort wieder gerudert zu werden :-)", boat.name), "Boot repariert", None).await;
 | 
			
		||||
            Notification::create_for_role(db, &cox, &format!("Liebe Steuerberechtigte, {} wurde repariert und freut sich ab sofort wieder gerudert zu werden :-)", boat.name), "Boot repariert", None, None).await;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
 
 | 
			
		||||
@@ -175,7 +175,7 @@ WHERE end_date >= CURRENT_DATE ORDER BY end_date
 | 
			
		||||
                        boatreservation.usage
 | 
			
		||||
                    ),
 | 
			
		||||
                    "Neue Bootsreservierung",
 | 
			
		||||
                    None,
 | 
			
		||||
                    None,None
 | 
			
		||||
                )
 | 
			
		||||
                .await;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -549,6 +549,7 @@ ORDER BY departure DESC
 | 
			
		||||
                ),
 | 
			
		||||
                "Neuer Logbucheintrag",
 | 
			
		||||
                None,
 | 
			
		||||
                None,
 | 
			
		||||
            )
 | 
			
		||||
            .await;
 | 
			
		||||
        }
 | 
			
		||||
@@ -578,7 +579,7 @@ ORDER BY departure DESC
 | 
			
		||||
                &vorstand,
 | 
			
		||||
                &format!("'{}' hat eine mehrtägige Ausfahrt vom {} bis {} eingetragen ({} km; Ziel: {}; Anmerkungen: {}). Falls das nicht stimmen sollte, bitte nachhaken.",user.name,log.departure, log.arrival, log.distance_in_km, log.destination, log.comments.clone().unwrap_or("".into())),
 | 
			
		||||
                "Mehrtägige Ausfahrt eingetragen",
 | 
			
		||||
                None,
 | 
			
		||||
                None,None
 | 
			
		||||
            ).await;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -590,7 +591,7 @@ ORDER BY departure DESC
 | 
			
		||||
                &vorstand,
 | 
			
		||||
                &format!("'{}' hat eine Ausfahrt mit externem Boot '{}' am {} eingetragen ({} km; Ziel: {}; Anmerkungen: {}). Falls das nicht stimmen sollte, bitte nachhaken.",user.name,boat.name,log.departure,log.distance_in_km, log.destination, log.comments.unwrap_or("".into())),
 | 
			
		||||
                "Ausfahrt mit externem Boot eingetragen",
 | 
			
		||||
                None,
 | 
			
		||||
                None,None,
 | 
			
		||||
            ).await;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
use std::ops::DerefMut;
 | 
			
		||||
 | 
			
		||||
use chrono::NaiveDateTime;
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
 | 
			
		||||
 | 
			
		||||
@@ -15,6 +16,7 @@ pub struct Notification {
 | 
			
		||||
    pub created_at: NaiveDateTime,
 | 
			
		||||
    pub category: String,
 | 
			
		||||
    pub link: Option<String>,
 | 
			
		||||
    pub action_after_reading: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Notification {
 | 
			
		||||
@@ -30,13 +32,15 @@ impl Notification {
 | 
			
		||||
        message: &str,
 | 
			
		||||
        category: &str,
 | 
			
		||||
        link: Option<&str>,
 | 
			
		||||
        action_after_reading: Option<&str>,
 | 
			
		||||
    ) {
 | 
			
		||||
        sqlx::query!(
 | 
			
		||||
            "INSERT INTO notification(user_id, message, category, link) VALUES (?, ?, ?, ?)",
 | 
			
		||||
            "INSERT INTO notification(user_id, message, category, link, action_after_reading) VALUES (?, ?, ?, ?, ?)",
 | 
			
		||||
            user.id,
 | 
			
		||||
            message,
 | 
			
		||||
            category,
 | 
			
		||||
            link
 | 
			
		||||
            link,
 | 
			
		||||
            action_after_reading
 | 
			
		||||
        )
 | 
			
		||||
        .execute(db.deref_mut())
 | 
			
		||||
        .await
 | 
			
		||||
@@ -49,9 +53,10 @@ impl Notification {
 | 
			
		||||
        message: &str,
 | 
			
		||||
        category: &str,
 | 
			
		||||
        link: Option<&str>,
 | 
			
		||||
        action_after_reading: Option<&str>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let mut tx = db.begin().await.unwrap();
 | 
			
		||||
        Self::create_with_tx(&mut tx, user, message, category, link).await;
 | 
			
		||||
        Self::create_with_tx(&mut tx, user, message, category, link, action_after_reading).await;
 | 
			
		||||
        tx.commit().await.unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -61,11 +66,12 @@ impl Notification {
 | 
			
		||||
        message: &str,
 | 
			
		||||
        category: &str,
 | 
			
		||||
        link: Option<&str>,
 | 
			
		||||
        action_after_reading: Option<&str>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let users = User::all_with_role_tx(db, role).await;
 | 
			
		||||
 | 
			
		||||
        for user in users {
 | 
			
		||||
            Self::create_with_tx(db, &user, message, category, link).await;
 | 
			
		||||
            Self::create_with_tx(db, &user, message, category, link, action_after_reading).await;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -75,16 +81,18 @@ impl Notification {
 | 
			
		||||
        message: &str,
 | 
			
		||||
        category: &str,
 | 
			
		||||
        link: Option<&str>,
 | 
			
		||||
        action_after_reading: Option<&str>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let mut tx = db.begin().await.unwrap();
 | 
			
		||||
        Self::create_for_role_tx(&mut tx, role, message, category, link).await;
 | 
			
		||||
        Self::create_for_role_tx(&mut tx, role, message, category, link, action_after_reading)
 | 
			
		||||
            .await;
 | 
			
		||||
        tx.commit().await.unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub async fn for_user(db: &SqlitePool, user: &User) -> Vec<Self> {
 | 
			
		||||
        let rows = sqlx::query!(
 | 
			
		||||
            "
 | 
			
		||||
SELECT id, user_id, message, read_at, datetime(created_at, 'localtime') as created_at, category, link FROM notification 
 | 
			
		||||
SELECT id, user_id, message, read_at, datetime(created_at, 'localtime') as created_at, category, link, action_after_reading FROM notification 
 | 
			
		||||
WHERE 
 | 
			
		||||
  user_id = ? 
 | 
			
		||||
  AND (
 | 
			
		||||
@@ -113,6 +121,7 @@ ORDER BY read_at DESC, created_at DESC;
 | 
			
		||||
                .unwrap(),
 | 
			
		||||
                category: rec.category,
 | 
			
		||||
                link: rec.link,
 | 
			
		||||
                action_after_reading: rec.action_after_reading,
 | 
			
		||||
            })
 | 
			
		||||
            .collect()
 | 
			
		||||
    }
 | 
			
		||||
@@ -125,5 +134,24 @@ ORDER BY read_at DESC, created_at DESC;
 | 
			
		||||
        .execute(db)
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
        if let Some(action) = self.action_after_reading.as_ref() {
 | 
			
		||||
            // User read notification about cancelled trip/event
 | 
			
		||||
            let re = Regex::new(r"^remove_user_trip_with_trip_details_id:(\d+)$").unwrap();
 | 
			
		||||
            if let Some(caps) = re.captures(action) {
 | 
			
		||||
                if let Some(matched) = caps.get(1) {
 | 
			
		||||
                    if let Ok(number) = matched.as_str().parse::<i32>() {
 | 
			
		||||
                        let _ = sqlx::query!(
 | 
			
		||||
                            "DELETE FROM user_trip WHERE user_id = ? AND trip_details_id = ?",
 | 
			
		||||
                            self.user_id,
 | 
			
		||||
                            number
 | 
			
		||||
                        )
 | 
			
		||||
                        .execute(db)
 | 
			
		||||
                        .await
 | 
			
		||||
                        .unwrap();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,7 @@ impl Trip {
 | 
			
		||||
                            ),
 | 
			
		||||
                            "Neue Ausfahrt zur selben Zeit",
 | 
			
		||||
                            None,
 | 
			
		||||
                            None,
 | 
			
		||||
                        )
 | 
			
		||||
                        .await;
 | 
			
		||||
                    }
 | 
			
		||||
@@ -209,10 +210,9 @@ WHERE day=?
 | 
			
		||||
            let rowers = TripWithUserAndType::from(db, trip.clone()).await.rower;
 | 
			
		||||
            for user in rowers {
 | 
			
		||||
                if let Some(user) = User::find_by_name(db, &user.name).await {
 | 
			
		||||
                    let notes = if let Some(notes) = notes {
 | 
			
		||||
                        format!(": {notes}")
 | 
			
		||||
                    } else {
 | 
			
		||||
                        String::from(".")
 | 
			
		||||
                    let notes = match notes {
 | 
			
		||||
                        Some(n) if !n.is_empty() => n,
 | 
			
		||||
                        _ => ".",
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    Notification::create(
 | 
			
		||||
@@ -224,6 +224,10 @@ WHERE day=?
 | 
			
		||||
                        ),
 | 
			
		||||
                        "Absage Ausfahrt",
 | 
			
		||||
                        None,
 | 
			
		||||
                        Some(&format!(
 | 
			
		||||
                            "remove_user_trip_with_trip_details_id:{}",
 | 
			
		||||
                            trip_details_id
 | 
			
		||||
                        )),
 | 
			
		||||
                    )
 | 
			
		||||
                    .await;
 | 
			
		||||
                }
 | 
			
		||||
 
 | 
			
		||||
@@ -120,7 +120,7 @@ WHERE day = ? AND planned_starting_time = ?
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Notification::create(db, &user, &format!("Du hast dich als Ruderer bei der Ausfahrt von {} am {} um {} angemeldet. Bei allen Ausfahrten zu dieser Zeit sind nun alle Plätze ausgebucht. Damit noch mehr (Nicht-Steuerleute) mitfahren können, wäre es super, wenn du eine eigene Ausfahrt zur selben Zeit ausschreiben könntest.", cox.name, self.day, self.planned_starting_time), "Volle Ausfahrt", None).await;
 | 
			
		||||
                Notification::create(db, &user, &format!("Du hast dich als Ruderer bei der Ausfahrt von {} am {} um {} angemeldet. Bei allen Ausfahrten zu dieser Zeit sind nun alle Plätze ausgebucht. Damit noch mehr (Nicht-Steuerleute) mitfahren können, wäre es super, wenn du eine eigene Ausfahrt zur selben Zeit ausschreiben könntest.", cox.name, self.day, self.planned_starting_time), "Volle Ausfahrt", None, None).await;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -202,7 +202,7 @@ ASKÖ Ruderverein Donau Linz", self.name, SCHECKBUCH/100),
 | 
			
		||||
                self.name
 | 
			
		||||
            ),
 | 
			
		||||
            "Neues Scheckbuch",
 | 
			
		||||
            None,
 | 
			
		||||
            None,None
 | 
			
		||||
        )
 | 
			
		||||
        .await;
 | 
			
		||||
    }
 | 
			
		||||
@@ -246,6 +246,7 @@ ASKÖ Ruderverein Donau Linz", self.name),
 | 
			
		||||
            ),
 | 
			
		||||
            "Neues Vereinsmitglied",
 | 
			
		||||
            None,
 | 
			
		||||
            None,
 | 
			
		||||
        )
 | 
			
		||||
        .await;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -76,6 +76,7 @@ impl UserTrip {
 | 
			
		||||
                ),
 | 
			
		||||
                "Registrierung bei deiner Ausfahrt",
 | 
			
		||||
                None,
 | 
			
		||||
                None,
 | 
			
		||||
            )
 | 
			
		||||
            .await;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ async fn send_group(
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    for user in User::all_with_role(db, &role).await {
 | 
			
		||||
        Notification::create(db, &user, &d.message, &d.category, None).await;
 | 
			
		||||
        Notification::create(db, &user, &d.message, &d.category, None, None).await;
 | 
			
		||||
    }
 | 
			
		||||
    Log::create(db, "Notification successfully sent".into()).await;
 | 
			
		||||
    Flash::success(
 | 
			
		||||
@@ -102,7 +102,7 @@ async fn send_user(
 | 
			
		||||
        return Flash::error(Redirect::to("/admin/notification"), "User gibt's ned");
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Notification::create(db, &user, &d.message, &d.category, None).await;
 | 
			
		||||
    Notification::create(db, &user, &d.message, &d.category, None, None).await;
 | 
			
		||||
 | 
			
		||||
    Log::create(db, "Notification successfully sent".into()).await;
 | 
			
		||||
    Flash::success(
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user