Merge pull request 'log-2022' (#64) from log-2022 into main
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m42s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Successful in 14m13s

Reviewed-on: #64
This commit is contained in:
philipp 2023-11-20 10:11:04 +01:00
commit a17c120ebe
10 changed files with 2701 additions and 123 deletions

136
.gitea/workflows/action.yml Normal file
View File

@ -0,0 +1,136 @@
name: CI/CD Pipeline
on: push
env:
CARGO_TARGET: x86_64-unknown-linux-musl
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
jobs:
test:
runs-on: ubuntu-latest
container: rust:latest
steps:
- name: Setup Environment
run: |
apt-get update -qq && apt-get install -y -qq sshpass musl musl-tools sqlite3 curl gnupg && mkdir -p /etc/apt/keyrings | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && apt-get update && apt-get install nodejs -y && apt-get install npm -y
- name: Checkout
uses: actions/checkout@v3
- name: Run Test DB Script
run: ./test_db.sh
- name: Build
run: |
cargo build
cd frontend && npm install && npm run build
- name: Run Tests
run: cargo test --verbose
deploy-staging:
runs-on: ubuntu-latest
container: rust:latest
needs: [test]
if: github.ref == 'refs/heads/staging'
steps:
- name: Setup Environment
run: |
rustup target add $CARGO_TARGET
apt-get update -qq && apt-get install -y -qq sshpass musl musl-tools sqlite3 curl gnupg
# Handling NodeSource GPG key
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o nodesource.gpg.key
if [ -f /etc/apt/keyrings/nodesource.gpg ]; then
rm /etc/apt/keyrings/nodesource.gpg
fi
gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg nodesource.gpg.key
# Adding NodeSource repository
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list
# Installing Node.js and npm
apt-get update
apt-get install nodejs -y
apt-get install npm -y
- name: Checkout
uses: actions/checkout@v3
- name: Run Test DB Script
run: ./test_db.sh
- name: Build
run: |
cargo build --release --target $CARGO_TARGET
strip target/$CARGO_TARGET/release/rot
cd frontend && npm install && npm run build
- name: Deploy to Staging
run: |
mkdir ~/.ssh
ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/rot-updating
scp staging-diff.sql $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rotstaging'
ssh $SSH_USER@$SSH_HOST 'rm /home/k004373/rowing-staging/db.sqlite && cp /home/k004373/rowing/db.sqlite /home/k004373/rowing-staging/db.sqlite && mkdir -p /home/k004373/rowing-staging/svelte/build && mkdir -p /home/k004373/rowing-staging/data-ergo/thirty && mkdir -p /home/k004373/rowing-staging/data-ergo/dozen && sqlite3 /home/k004373/rowing-staging/db.sqlite < /home/k004373/rowing-staging/staging-diff.sql'
ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing-staging/rot-updating /home/k004373/rowing-staging/rot'
ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}
deploy-main:
runs-on: ubuntu-latest
container: rust:latest
needs: [test]
if: github.ref == 'refs/heads/main'
steps:
- name: Setup Environment
run: |
rustup target add $CARGO_TARGET
apt-get update -qq && apt-get install -y -qq sshpass musl musl-tools sqlite3 curl gnupg && mkdir -p /etc/apt/keyrings | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && apt-get update && apt-get install nodejs -y && apt-get install npm -y
- name: Checkout
uses: actions/checkout@v3
- name: Run Test DB Script
run: ./test_db.sh
- name: Build
run: |
cargo build --release --target $CARGO_TARGET
strip target/$CARGO_TARGET/release/rot
cd frontend && npm install && npm run build
- name: Deploy to Main
run: |
mkdir ~/.ssh
ssh-keyscan -H $SSH_HOST >> ~/.ssh/known_hosts
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/k004373/rowing/rot-updating
scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing/
scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing/
scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing/
ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/k004373/rowing/svelte/build && mkdir -p /home/k004373/rowing/data-ergo/thirty && mkdir -p /home/k004373/rowing/data-ergo/dozen'
ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rot'
ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing/rot-updating /home/k004373/rowing/rot'
ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_USER: ${{ secrets.SSH_USER }}

View File

@ -1,70 +0,0 @@
image: rust:latest
variables:
CARGO_TARGET: x86_64-unknown-linux-musl
before_script:
- rustup target add $CARGO_TARGET
- apt-get update -qq && apt-get install -y -qq sshpass musl musl-tools sqlite3 curl gnupg && mkdir -p /etc/apt/keyrings | curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && apt-get update && apt-get install nodejs -y && apt-get install npm -y
- ./test_db.sh
build:
stage: build
script:
- cargo build --release --target $CARGO_TARGET
- strip target/$CARGO_TARGET/release/rot
- cd frontend && npm install && npm run build
artifacts:
paths:
- target/$CARGO_TARGET/release/rot
- static
expire_in: 3 hours
test:
stage: test
image: rust:latest
script:
- cargo test --verbose
deploy-staging:
stage: deploy
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan -H $SSH_HOST > ~/.ssh/known_hosts
script:
- scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/rot-updating
- scp staging-diff.sql $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
- scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
- scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
- scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing-staging/
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rotstaging'
- ssh $SSH_USER@$SSH_HOST 'rm /home/k004373/rowing-staging/db.sqlite && cp /home/k004373/rowing/db.sqlite /home/k004373/rowing-staging/db.sqlite && mkdir -p /home/k004373/rowing-staging/svelte/build && mkdir -p /home/k004373/rowing-staging/data-ergo/thirty && mkdir -p /home/k004373/rowing-staging/data-ergo/dozen && sqlite3 /home/k004373/rowing-staging/db.sqlite < /home/k004373/rowing-staging/staging-diff.sql'
- ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing-staging/rot-updating /home/k004373/rowing-staging/rot'
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rotstaging'
only:
- staging
deploy-main:
stage: deploy
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan -H $SSH_HOST > ~/.ssh/known_hosts
script:
- scp target/$CARGO_TARGET/release/rot $SSH_USER@$SSH_HOST:/home/k004373/rowing/rot-updating
- scp -r static $SSH_USER@$SSH_HOST:/home/k004373/rowing/
- scp -r templates $SSH_USER@$SSH_HOST:/home/k004373/rowing/
- scp -r svelte $SSH_USER@$SSH_HOST:/home/k004373/rowing/
- ssh $SSH_USER@$SSH_HOST 'mkdir -p /home/k004373/rowing/svelte/build && mkdir -p /home/k004373/rowing/data-ergo/thirty && mkdir -p /home/k004373/rowing/data-ergo/dozen'
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl stop rot'
- ssh $SSH_USER@$SSH_HOST 'mv /home/k004373/rowing/rot-updating /home/k004373/rowing/rot'
- ssh $SSH_USER@$SSH_HOST 'sudo systemctl start rot'
only:
- main

View File

@ -26,9 +26,9 @@ INSERT INTO "boat" (name, amount_seats, location_id) VALUES ('Ottensheim Boot',
INSERT INTO "boat" (name, amount_seats, location_id, owner) VALUES ('second_private_boat_from_rower', 1, 1, 2); INSERT INTO "boat" (name, amount_seats, location_id, owner) VALUES ('second_private_boat_from_rower', 1, 1, 2);
INSERT INTO "logbook_type" (name) VALUES ('Wanderfahrt'); INSERT INTO "logbook_type" (name) VALUES ('Wanderfahrt');
INSERT INTO "logbook_type" (name) VALUES ('Regatta'); INSERT INTO "logbook_type" (name) VALUES ('Regatta');
INSERT INTO "logbook" (boat_id, shipmaster,steering_person, shipmaster_only_steering, departure) VALUES (2, 2, 2, false, '1142-12-24 10:00'); INSERT INTO "logbook" (boat_id, shipmaster,steering_person, shipmaster_only_steering, departure) VALUES (2, 2, 2, false, strftime('%Y', 'now') || '-12-24 10:00');
INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km) VALUES (1, 4, 4, false, '1141-12-24 10:00', '2141-12-24 15:00', 'Ottensheim', 25); INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km) VALUES (1, 4, 4, false, strftime('%Y', 'now') || '-12-24 10:00', strftime('%Y', 'now') || '-12-24 15:00', 'Ottensheim', 25);
INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km) VALUES (3, 4, 4, false, '1142-12-24 10:00', '2142-12-24 11:30', 'Ottensheim + Regattastrecke', 29); INSERT INTO "logbook" (boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km) VALUES (3, 4, 4, false, strftime('%Y', 'now') || '-12-24 10:00', strftime('%Y', 'now') || '-12-24 11:30', 'Ottensheim + Regattastrecke', 29);
INSERT INTO "rower" (logbook_id, rower_id) VALUES(3,3); INSERT INTO "rower" (logbook_id, rower_id) VALUES(3,3);
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at) VALUES(4,'Dolle bei Position 2 fehlt', 5, '2142-12-24 15:02'); INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at) VALUES(4,'Dolle bei Position 2 fehlt', 5, '2142-12-24 15:02');
INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at, lock_boat) VALUES(5, 'TOHT', 5, '2142-12-24 15:02', 1); INSERT INTO "boat_damage" (boat_id, desc, user_id_created, created_at, lock_boat) VALUES(5, 'TOHT', 5, '2142-12-24 15:02', 1);

View File

@ -1,4 +1,4 @@
use chrono::NaiveDateTime; use chrono::{Datelike, NaiveDateTime, Utc};
use rocket::FromForm; use rocket::FromForm;
use serde::Serialize; use serde::Serialize;
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction}; use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
@ -99,6 +99,7 @@ pub enum LogbookUpdateError {
ShipmasterNotInRowers, ShipmasterNotInRowers,
SteeringPersonNotInRowers, SteeringPersonNotInRowers,
UserNotAllowedToUseBoat, UserNotAllowedToUseBoat,
OnlyAllowedToEndTripsEndingToday,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -120,6 +121,7 @@ pub enum LogbookCreateError {
ShipmasterNotInRowers, ShipmasterNotInRowers,
NotYourEntry, NotYourEntry,
ArrivalSetButNotRemainingTwo, ArrivalSetButNotRemainingTwo,
OnlyAllowedToEndTripsEndingToday,
} }
impl From<LogbookUpdateError> for LogbookCreateError { impl From<LogbookUpdateError> for LogbookCreateError {
@ -140,6 +142,9 @@ impl From<LogbookUpdateError> for LogbookCreateError {
LogbookUpdateError::UserNotAllowedToUseBoat => { LogbookUpdateError::UserNotAllowedToUseBoat => {
LogbookCreateError::UserNotAllowedToUseBoat LogbookCreateError::UserNotAllowedToUseBoat
} }
LogbookUpdateError::OnlyAllowedToEndTripsEndingToday => {
LogbookCreateError::OnlyAllowedToEndTripsEndingToday
}
} }
} }
} }
@ -220,14 +225,14 @@ ORDER BY departure DESC
} }
pub async fn completed(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> { pub async fn completed(db: &SqlitePool) -> Vec<LogbookWithBoatAndRowers> {
let logs = sqlx::query_as!( let year = chrono::Utc::now().year();
Logbook, let logs = sqlx::query_as(
" &format!("
SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype SELECT id, boat_id, shipmaster, steering_person, shipmaster_only_steering, departure, arrival, destination, distance_in_km, comments, logtype
FROM logbook FROM logbook
WHERE arrival is not null WHERE arrival is not null AND arrival LIKE '{}-%'
ORDER BY departure DESC ORDER BY departure DESC
" ", year)
) )
.fetch_all(db) .fetch_all(db)
.await .await
@ -446,13 +451,8 @@ ORDER BY departure DESC
} }
if !boat.shipmaster_allowed(user).await && self.shipmaster != user.id { if !boat.shipmaster_allowed(user).await && self.shipmaster != user.id {
//second part: //second part: shipmaster has entered a different user, then the user should be able to
//shipmaster has //`home` it
//entered a
//different user,
//then the user
//should be able
//to `home` it
return Err(LogbookUpdateError::UserNotAllowedToUseBoat); return Err(LogbookUpdateError::UserNotAllowedToUseBoat);
} }
@ -468,6 +468,10 @@ ORDER BY departure DESC
if arr.timestamp() <= dep.timestamp() { if arr.timestamp() <= dep.timestamp() {
return Err(LogbookUpdateError::ArrivalNotAfterDeparture); return Err(LogbookUpdateError::ArrivalNotAfterDeparture);
} }
let today = Utc::now().date_naive();
if arr.date() != today && !user.is_admin {
return Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday);
}
Log::create_with_tx(db, format!("New trip: {log:?}")).await; Log::create_with_tx(db, format!("New trip: {log:?}")).await;
@ -549,11 +553,11 @@ mod test {
assert_eq!( assert_eq!(
completed[0].logbook, completed[0].logbook,
Logbook::find_by_id(&pool, 3).await.unwrap() Logbook::find_by_id(&pool, 2).await.unwrap()
); );
assert_eq!( assert_eq!(
completed[1].logbook, completed[1].logbook,
Logbook::find_by_id(&pool, 2).await.unwrap() Logbook::find_by_id(&pool, 3).await.unwrap()
); );
} }
@ -793,6 +797,8 @@ mod test {
let logbook = Logbook::find_by_id(&pool, 1).await.unwrap(); let logbook = Logbook::find_by_id(&pool, 1).await.unwrap();
let user = User::find_by_id(&pool, 2).await.unwrap(); let user = User::find_by_id(&pool, 2).await.unwrap();
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
logbook logbook
.home( .home(
&pool, &pool,
@ -806,8 +812,8 @@ mod test {
shipmaster: Some(2), shipmaster: Some(2),
steering_person: Some(2), steering_person: Some(2),
shipmaster_only_steering: false, shipmaster_only_steering: false,
departure: "1990-01-01T10:00".into(), departure: format!("{}T10:00", current_date),
arrival: "1990-01-01T12:00".into(), arrival: format!("{}T12:00", current_date),
}, },
) )
.await .await

View File

@ -1,4 +1,5 @@
use crate::model::user::User; use crate::model::user::User;
use chrono::Datelike;
use serde::Serialize; use serde::Serialize;
use sqlx::{FromRow, Row, SqlitePool}; use sqlx::{FromRow, Row, SqlitePool};
@ -9,15 +10,20 @@ pub struct Stat {
} }
impl Stat { impl Stat {
pub async fn boats(db: &SqlitePool) -> Vec<Stat> { pub async fn boats(db: &SqlitePool, year: Option<i32>) -> Vec<Stat> {
let year = match year {
Some(year) => year,
None => chrono::Utc::now().year(),
};
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server) //TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
sqlx::query( sqlx::query(&format!(
" "
SELECT (SELECT name FROM boat WHERE id=logbook.boat_id) as name, CAST(SUM(distance_in_km) AS INTEGER) AS rowed_km SELECT (SELECT name FROM boat WHERE id=logbook.boat_id) as name, CAST(SUM(distance_in_km) AS INTEGER) AS rowed_km
FROM logbook FROM logbook
WHERE arrival LIKE '{}-%'
GROUP BY boat_id GROUP BY boat_id
ORDER BY rowed_km DESC; ORDER BY rowed_km DESC;
", ",year)
) )
.fetch_all(db) .fetch_all(db)
.await .await
@ -30,19 +36,24 @@ ORDER BY rowed_km DESC;
.collect() .collect()
} }
pub async fn people(db: &SqlitePool) -> Vec<Stat> { pub async fn people(db: &SqlitePool, year: Option<i32>) -> Vec<Stat> {
let year = match year {
Some(year) => year,
None => chrono::Utc::now().year(),
};
//TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server) //TODO: switch to query! macro again (once upgraded to sqlite 3.42 on server)
sqlx::query( sqlx::query(&format!(
" "
SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km SELECT u.name, CAST(SUM(l.distance_in_km) AS INTEGER) AS rowed_km
FROM user u FROM user u
INNER JOIN rower r ON u.id = r.rower_id INNER JOIN rower r ON u.id = r.rower_id
INNER JOIN logbook l ON r.logbook_id = l.id INNER JOIN logbook l ON r.logbook_id = l.id
WHERE u.is_guest = 0 AND l.distance_in_km IS NOT NULL WHERE u.is_guest = 0 AND l.distance_in_km IS NOT NULL AND l.arrival LIKE '{}-%'
GROUP BY u.name GROUP BY u.name
ORDER BY rowed_km DESC; ORDER BY rowed_km DESC;
", ",
) year
))
.fetch_all(db) .fetch_all(db)
.await .await
.unwrap() .unwrap()

View File

@ -182,6 +182,7 @@ async fn create_logbook(
Err(LogbookCreateError::ShipmasterNotInRowers) => Flash::error(Redirect::to("/log"), "Schiffsführer nicht in Liste der Ruderer!"), Err(LogbookCreateError::ShipmasterNotInRowers) => Flash::error(Redirect::to("/log"), "Schiffsführer nicht in Liste der Ruderer!"),
Err(LogbookCreateError::NotYourEntry) => Flash::error(Redirect::to("/log"), "Nicht deine Ausfahrt!"), Err(LogbookCreateError::NotYourEntry) => Flash::error(Redirect::to("/log"), "Nicht deine Ausfahrt!"),
Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error(Redirect::to("/log"), "Ankunftszeit gesetzt aber nicht Distanz + Strecke"), Err(LogbookCreateError::ArrivalSetButNotRemainingTwo) => Flash::error(Redirect::to("/log"), "Ankunftszeit gesetzt aber nicht Distanz + Strecke"),
Err(LogbookCreateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), format!("Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at).")),
} }
} }
@ -251,6 +252,7 @@ async fn home_logbook(
match logbook.home(db, &user.user, data.into_inner()).await { match logbook.home(db, &user.user, data.into_inner()).await {
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"), Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"),
Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")), Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")),
Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), format!("Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at).")),
Err(e) => Flash::error( Err(e) => Flash::error(
Redirect::to("/log"), Redirect::to("/log"),
format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"), format!("Eintrag {logbook_id} konnte nicht abgesendet werden (Fehler: {e:?})!"),
@ -522,11 +524,12 @@ mod test {
.header(ContentType::Form) // Set the content type to form .header(ContentType::Form) // Set the content type to form
.body("name=admin&password=admin"); // Add the form data to the request body; .body("name=admin&password=admin"); // Add the form data to the request body;
login.dispatch().await; login.dispatch().await;
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
let req = client let req = client.post("/log").header(ContentType::Form).body(format!(
.post("/log") "boat_id=1&shipmaster=4&departure={0}T10:00&steering_person=4&rowers[]=4",
.header(ContentType::Form) current_date
.body("boat_id=1&shipmaster=4&departure=2199-12-31T10:00&steering_person=4&rowers[]=4"); ));
let response = req.dispatch().await; let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
@ -554,10 +557,12 @@ mod test {
let req = client.get("/log/kiosk/ekrv2019/Linz"); let req = client.get("/log/kiosk/ekrv2019/Linz");
let _ = req.dispatch().await; let _ = req.dispatch().await;
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
let req = client let req = client
.post("/log/1") .post("/log/1")
.header(ContentType::Form) .header(ContentType::Form)
.body("destination=Ottensheim&distance_in_km=25&shipmaster=2&steering_person=2&departure=1990-01-01T10:00&arrival=1990-01-01T12:00&rowers[]=2"); .body(format!("destination=Ottensheim&distance_in_km=25&shipmaster=2&steering_person=2&departure={0}T10:00&arrival={0}T12:00&rowers[]=2", current_date));
let response = req.dispatch().await; let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
@ -664,9 +669,10 @@ mod test {
.unwrap() .unwrap()
.id; .id;
let shipmaster_id = User::find_by_name(&db, "rower2".into()).await.unwrap().id; let shipmaster_id = User::find_by_name(&db, "rower2".into()).await.unwrap().id;
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
let req = client.post("/log").header(ContentType::Form).body(format!( let req = client.post("/log").header(ContentType::Form).body(format!(
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure=1199-12-31T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}" "boat_id={boat_id}&shipmaster={shipmaster_id}&departure={0}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}", current_date
)); ));
let response = req.dispatch().await; let response = req.dispatch().await;
@ -695,7 +701,7 @@ mod test {
let req = client let req = client
.post(format!("/log/{log_id}")) .post(format!("/log/{log_id}"))
.header(ContentType::Form) .header(ContentType::Form)
.body(format!("destination=Ottensheim&distance_in_km=25&shipmaster={shipmaster_id}&steering_person={shipmaster_id}&departure=1990-01-01T10:00&arrival=1990-01-01T12:00&rowers[]={shipmaster_id}")); .body(format!("destination=Ottensheim&distance_in_km=25&shipmaster={shipmaster_id}&steering_person={shipmaster_id}&departure={0}T10:00&arrival={0}T12:00&rowers[]={shipmaster_id}", current_date));
let response = req.dispatch().await; let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
@ -882,11 +888,12 @@ mod test {
.header(ContentType::Form) // Set the content type to form .header(ContentType::Form) // Set the content type to form
.body("name=rower2&password=rower"); // Add the form data to the request body; .body("name=rower2&password=rower"); // Add the form data to the request body;
login.dispatch().await; login.dispatch().await;
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
let req = client let req = client
.post("/log/1") .post("/log/1")
.header(ContentType::Form) .header(ContentType::Form)
.body("destination=Ottensheim&distance_in_km=25&shipmaster=1&steering_person=1&departure=1199-12-12T10:00&arrival=1199-12-12T12:00&rowers[]=1"); .body(format!("destination=Ottensheim&distance_in_km=25&shipmaster=1&steering_person=1&departure={0}T10:00&arrival={0}T12:00&rowers[]=1", current_date));
let response = req.dispatch().await; let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
@ -912,8 +919,10 @@ mod test {
let boat_id = Boat::find_by_name(db, boat_name).await.unwrap().id; let boat_id = Boat::find_by_name(db, boat_name).await.unwrap().id;
let shipmaster_id = User::find_by_name(db, &shipmaster_name).await.unwrap().id; let shipmaster_id = User::find_by_name(db, &shipmaster_name).await.unwrap().id;
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
let req = client.post("/log").header(ContentType::Form).body(format!( let req = client.post("/log").header(ContentType::Form).body(format!(
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure=1199-12-31T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}" "boat_id={boat_id}&shipmaster={shipmaster_id}&departure={current_date}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}"
)); ));
let response = req.dispatch().await; let response = req.dispatch().await;
@ -935,7 +944,7 @@ mod test {
let req = client let req = client
.post(format!("/log/{log_id}")) .post(format!("/log/{log_id}"))
.header(ContentType::Form) .header(ContentType::Form)
.body(format!("destination=Ottensheim&distance_in_km=25&shipmaster={shipmaster_id}&steering_person={shipmaster_id}&departure=1199-12-31T10:00&arrival=1199-12-31T12:00&rowers[]={shipmaster_id}")); .body(format!("destination=Ottensheim&distance_in_km=25&shipmaster={shipmaster_id}&steering_person={shipmaster_id}&departure={current_date}T10:00&arrival={current_date}T12:00&rowers[]={shipmaster_id}"));
let response = req.dispatch().await; let response = req.dispatch().await;
assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.status(), Status::SeeOther);
@ -962,8 +971,10 @@ mod test {
let boat_id = Boat::find_by_name(db, boat_name).await.unwrap().id; let boat_id = Boat::find_by_name(db, boat_name).await.unwrap().id;
let shipmaster_id = User::find_by_name(db, &shipmaster_name).await.unwrap().id; let shipmaster_id = User::find_by_name(db, &shipmaster_name).await.unwrap().id;
let current_date = chrono::Local::now().format("%Y-%m-%d").to_string();
let req = client.post("/log").header(ContentType::Form).body(format!( let req = client.post("/log").header(ContentType::Form).body(format!(
"boat_id={boat_id}&shipmaster={shipmaster_id}&departure=2199-12-31T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}" "boat_id={boat_id}&shipmaster={shipmaster_id}&departure={current_date}T10:00&steering_person={shipmaster_id}&rowers[]={shipmaster_id}"
)); ));
let response = req.dispatch().await; let response = req.dispatch().await;

View File

@ -9,9 +9,9 @@ use crate::model::{
use super::log::KioskCookie; use super::log::KioskCookie;
#[get("/boats", rank = 2)] #[get("/boats?<year>", rank = 2)]
async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser) -> Template { async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
let stat = Stat::boats(db).await; let stat = Stat::boats(db, year).await;
let kiosk = false; let kiosk = false;
Template::render( Template::render(
@ -20,17 +20,21 @@ async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
) )
} }
#[get("/boats")] #[get("/boats?<year>")]
async fn index_boat_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie) -> Template { async fn index_boat_kiosk(
let stat = Stat::boats(db).await; db: &State<SqlitePool>,
_kiosk: KioskCookie,
year: Option<i32>,
) -> Template {
let stat = Stat::boats(db, year).await;
let kiosk = true; let kiosk = true;
Template::render("stat.boats", context!(stat, kiosk, show_kiosk_header: true)) Template::render("stat.boats", context!(stat, kiosk, show_kiosk_header: true))
} }
#[get("/", rank = 2)] #[get("/?<year>", rank = 2)]
async fn index(db: &State<SqlitePool>, user: NonGuestUser) -> Template { async fn index(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
let stat = Stat::people(db).await; let stat = Stat::people(db, year).await;
let personal = stat::get_personal(db, &user.user).await; let personal = stat::get_personal(db, &user.user).await;
let kiosk = false; let kiosk = false;
@ -40,9 +44,9 @@ async fn index(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
) )
} }
#[get("/")] #[get("/?<year>")]
async fn index_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie) -> Template { async fn index_kiosk(db: &State<SqlitePool>, _kiosk: KioskCookie, year: Option<i32>) -> Template {
let stat = Stat::people(db).await; let stat = Stat::people(db, year).await;
let kiosk = true; let kiosk = true;
Template::render( Template::render(

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
<div class="max-w-screen-xl w-full flex justify-between items-center"> <div class="max-w-screen-xl w-full flex justify-between items-center">
<div> <div>
<span class="text-[#ff0000]">&hearts;</span> <span class="text-[#ff0000]">&hearts;</span>
ASKÖ Ruderverein Donau Linz <small class="text-primary-100">&copy; {{ now() | date(format="%Y") }}</smalL> Erstellt vom ASKÖ Ruderverein Donau Linz <a onclick="alert('Wir suchen kreative und motivierte Köpfe, die diesen Ruderassistenten mitgestalten möchten. Das Backend ist in Rust (Rocket), das Frontend in TypeScript und Teraform, wobei wir mit dem Gedanken spielen, zu Svelte(Kit) zu wechseln.\n\nWenn du Lust hast, deine Skills in ein Projekt zu stecken, das Wellen schlagen wird, dann komm an Bord! Wir sind offen für frische Ideen, haben jedoch auch selber noch genügend; langweilig wird uns bestimmt nicht.\n\nWirf den Anker bei uns ausi und melde dich bei Marie oder Philipp oder it@rudernlinz.at für eine Zukunft ohne optische Kenterung in Form von hässlichen Alerts ;)');" style="text-decoration:underline">... und dir?</a>
</div> </div>
<div> <div>

View File

@ -8,7 +8,7 @@
{{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }} {{ macros::alert(message=flash.1, type=flash.0, class="sm:col-span-2 lg:col-span-3") }}
{% endif %} {% endif %}
<h1 class="h1 sm:col-span-2 lg:col-span-3">Ausfahrten</h1> <h1 class="h1 sm:col-span-2 lg:col-span-3">Ausfahrten!</h1>
{% include "includes/buttons" %} {% include "includes/buttons" %}