Compare commits

..

42 Commits

Author SHA1 Message Date
77a90a8086 Merge branch 'staging' into show-scheckbuch-info
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m18s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-16 21:31:12 +02:00
626be1c9fb Merge pull request 'no rain level -> 0; round values' (#497) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 10m0s
CI/CD Pipeline / deploy-staging (push) Successful in 5m58s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #497
2024-05-16 16:36:39 +02:00
7e2c185c03 no rain level -> 0; round values
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m17s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-16 16:35:49 +02:00
65068e44a5 Merge pull request 'show-waterlevel' (#496) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m44s
CI/CD Pipeline / deploy-staging (push) Successful in 5m59s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #496
2024-05-16 14:59:05 +02:00
133a517a2e Merge branch 'staging' into show-waterlevel
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m33s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-05-16 14:47:50 +02:00
3d45310c73 Merge branch 'main' into show-waterlevel
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
2024-05-16 14:45:31 +02:00
e4ef1f1584 add weather infos
Some checks failed
CI/CD Pipeline / test (push) Failing after 4m16s
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
2024-05-16 14:41:15 +02:00
2bf517ccd8 Merge pull request 'fix-guest-encoding' (#493) from fix-guest-encoding into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 23m39s
CI/CD Pipeline / deploy-staging (push) Successful in 7m37s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #493
2024-05-16 08:38:21 +02:00
18faf4a72d Merge pull request 'welcome-mail-scheckbuch' (#491) from welcome-mail-scheckbuch into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m41s
CI/CD Pipeline / deploy-staging (push) Successful in 5m37s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #491
2024-05-15 16:20:12 +02:00
97b0ce65f9 Merge pull request 'welcome-mail' (#489) from welcome-mail into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m13s
CI/CD Pipeline / deploy-staging (push) Successful in 6m3s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #489
2024-05-15 14:52:42 +02:00
fa0dc5b544 Merge pull request 'dont-show-guests-on-external-boats' (#486) from dont-show-guests-on-external-boats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m57s
CI/CD Pipeline / deploy-staging (push) Successful in 6m13s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #486
2024-05-12 23:08:41 +02:00
bd68bfc668 Merge pull request 'allow-membershippdf-upload' (#482) from allow-membershippdf-upload into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m21s
CI/CD Pipeline / deploy-staging (push) Successful in 4m47s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #482
2024-05-06 13:46:37 +02:00
6813d75db5 Merge pull request 'only have a single user with details struct' (#480) from simplify-user-structs into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #480
2024-05-06 13:30:33 +02:00
e728c4dbea Merge pull request 'Merge pull request 'allow scheckbuch people to be entered in logbook' (#477) from allow-scheckbuch-to-be-entered into main' (#478) from trim-new-user-names into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m16s
CI/CD Pipeline / deploy-staging (push) Successful in 6m10s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #478
2024-05-04 18:35:48 +02:00
96036b180b Merge pull request 'allow-scheckbuch-to-be-entered' (#476) from allow-scheckbuch-to-be-entered into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m21s
CI/CD Pipeline / deploy-staging (push) Successful in 5m57s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #476
2024-05-01 19:37:09 +02:00
c98f33e138 Merge pull request 'clippy :-)' (#474) from clippy into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m20s
CI/CD Pipeline / deploy-staging (push) Successful in 6m16s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #474
2024-04-30 22:26:04 +02:00
1503544a73 Merge pull request 'don't use default distance of 11; don't overwrite distance if already entered' (#472) from logbook-entry-improvement into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m12s
CI/CD Pipeline / deploy-staging (push) Successful in 6m17s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #472
2024-04-30 21:31:29 +02:00
67c8431157 Merge pull request 'better phrasing' (#471) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m43s
CI/CD Pipeline / deploy-staging (push) Successful in 6m23s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #471
2024-04-30 17:04:45 +02:00
d6ecd87593 better phrasing
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m47s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 17:02:22 +02:00
03073965a1 Merge pull request 'use recommended method of 'sleep'' (#470) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m52s
CI/CD Pipeline / deploy-staging (push) Successful in 6m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #470
2024-04-30 15:56:47 +02:00
2189b082c0 use recommended method of 'sleep'
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m34s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 15:56:21 +02:00
6d4bc81720 Merge pull request 'remove unnecessary async' (#469) from show-waterlevel into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #469
2024-04-30 15:48:45 +02:00
25fe4c23ef remove unnecessary async
Some checks failed
CI/CD Pipeline / deploy-staging (push) Has been cancelled
CI/CD Pipeline / deploy-main (push) Has been cancelled
CI/CD Pipeline / test (push) Has been cancelled
2024-04-30 15:47:40 +02:00
2fdfddbd2e Merge pull request 'add tooltip + link' (#468) from show-waterlevel into staging
Reviewed-on: #468
2024-04-30 15:14:26 +02:00
dea6520aa9 add tooltip + link
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m9s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 15:13:42 +02:00
f8e0cd2d5b Merge pull request 'deployed :-)' (#467) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m14s
CI/CD Pipeline / deploy-staging (push) Successful in 5m53s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #467
2024-04-30 14:35:45 +02:00
9fda9cbde2 deployed :-)
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m41s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 14:35:30 +02:00
74b24569dd Merge pull request 'show-waterlevel' (#465) from show-waterlevel into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m30s
CI/CD Pipeline / deploy-staging (push) Successful in 7m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #465
2024-04-30 13:06:08 +02:00
3a39315a01 show waterlevel for the next days
All checks were successful
CI/CD Pipeline / test (push) Successful in 9m49s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped
2024-04-30 11:59:33 +02:00
65d51c2cc2 Merge pull request 'no-boat-selected-by-default-in-logbook' (#458) from no-boat-selected-by-default-in-logbook into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m9s
CI/CD Pipeline / deploy-staging (push) Successful in 6m13s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #458
2024-04-29 22:30:24 +02:00
7dfdc55adb Merge pull request 'rephrase-scheckbuch-button' (#460) from rephrase-scheckbuch-button into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #460
2024-04-29 22:29:12 +02:00
79976b751f Merge pull request 'treat empty membership pdf as non-existing' (#456) from treat-empty-membershippdf-as-nonexisting into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m29s
CI/CD Pipeline / deploy-staging (push) Successful in 6m6s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #456
2024-04-29 21:33:44 +02:00
ebdfe37bec Merge pull request 'proper-time-in-notificatoins' (#452) from proper-time-in-notificatoins into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 7m55s
CI/CD Pipeline / deploy-staging (push) Successful in 5m22s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #452
2024-04-29 09:18:55 +02:00
f08d9728eb Merge pull request 'require-user-for-logentry' (#450) from require-user-for-logentry into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m57s
CI/CD Pipeline / deploy-staging (push) Successful in 6m0s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #450
2024-04-28 19:25:27 +02:00
1866034431 Merge pull request 'fix-spacing' (#446) from fix-spacing into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m15s
CI/CD Pipeline / deploy-staging (push) Successful in 5m28s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #446
2024-04-26 10:07:00 +02:00
688ce4c6fc Merge pull request 'prefill-own-user-default-log' (#444) from prefill-own-user-default-log into staging
Some checks are pending
CI/CD Pipeline / test (push) Waiting to run
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
Reviewed-on: #444
2024-04-26 09:59:02 +02:00
7a8b79ccef Merge pull request 'no-special-treatment-with-boatname' (#441) from no-special-treatment-with-boatname into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m14s
CI/CD Pipeline / deploy-staging (push) Successful in 5m56s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #441
2024-04-24 17:23:46 +02:00
5bb0cb4112 Merge pull request 'log-handle-external-boats' (#439) from log-handle-external-boats into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m17s
CI/CD Pipeline / deploy-staging (push) Successful in 5m24s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #439
2024-04-24 17:03:41 +02:00
6a18d7435a Merge pull request 'fix-ci-db' (#437) from fix-ci-db into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m15s
CI/CD Pipeline / deploy-staging (push) Successful in 5m20s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #437
2024-04-24 15:58:18 +02:00
14e7616b88 calc-general-boatcat (#435)
Some checks failed
CI/CD Pipeline / test (push) Successful in 7m59s
CI/CD Pipeline / deploy-staging (push) Failing after 5m39s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #435
2024-04-24 15:39:07 +02:00
1412852087 Merge pull request 'handoperatable-feature' (#431) from handoperatable-feature into staging
All checks were successful
CI/CD Pipeline / test (push) Successful in 8m28s
CI/CD Pipeline / deploy-staging (push) Successful in 17m27s
CI/CD Pipeline / deploy-main (push) Has been skipped
Reviewed-on: #431
2024-04-24 12:19:06 +02:00
7a59c67763 Merge pull request 'smarter-ci-cache' (#433) from smarter-ci-cache into staging
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled
Reviewed-on: #433
2024-04-24 12:04:22 +02:00
14 changed files with 567 additions and 5 deletions

117
Cargo.lock generated
View File

@ -485,6 +485,26 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "cron"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8c3e73077b4b4a6ab1ea5047c37c57aee77657bc8ecd6f29b0af082d0b0c07"
dependencies = [
"chrono",
"nom",
"once_cell",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.12"
@ -774,6 +794,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
[[package]]
name = "flate2"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "flume"
version = "0.11.0"
@ -1301,6 +1331,17 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "job_scheduler_ng"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87c252207f323e2996d087759ebdcff8f608cd3eaa9896909a0c2dd3050a3c6a"
dependencies = [
"chrono",
"cron",
"uuid",
]
[[package]]
name = "js-sys"
version = "0.3.69"
@ -2227,6 +2268,7 @@ dependencies = [
"futures",
"ics",
"itertools",
"job_scheduler_ng",
"lettre",
"log",
"openssl",
@ -2236,6 +2278,7 @@ dependencies = [
"serde_json",
"sqlx",
"tera",
"ureq",
]
[[package]]
@ -2284,10 +2327,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
dependencies = [
"ring",
"rustls-webpki",
"rustls-webpki 0.101.7",
"sct",
]
[[package]]
name = "rustls"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432"
dependencies = [
"log",
"ring",
"rustls-pki-types",
"rustls-webpki 0.102.3",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
@ -2297,6 +2354,12 @@ dependencies = [
"base64 0.21.7",
]
[[package]]
name = "rustls-pki-types"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
@ -2307,6 +2370,17 @@ dependencies = [
"untrusted",
]
[[package]]
name = "rustls-webpki"
version = "0.102.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.15"
@ -2590,7 +2664,7 @@ dependencies = [
"once_cell",
"paste",
"percent-encoding",
"rustls",
"rustls 0.21.10",
"rustls-pemfile",
"serde",
"serde_json",
@ -2603,7 +2677,7 @@ dependencies = [
"tokio-stream",
"tracing",
"url",
"webpki-roots",
"webpki-roots 0.25.4",
]
[[package]]
@ -3232,6 +3306,25 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd"
dependencies = [
"base64 0.22.0",
"flate2",
"log",
"once_cell",
"rustls 0.22.4",
"rustls-pki-types",
"rustls-webpki 0.102.3",
"serde",
"serde_json",
"url",
"webpki-roots 0.26.1",
]
[[package]]
name = "url"
version = "2.5.0"
@ -3255,6 +3348,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
dependencies = [
"getrandom",
]
[[package]]
name = "valuable"
version = "0.1.0"
@ -3364,6 +3466,15 @@ version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "whoami"
version = "1.5.1"

View File

@ -25,6 +25,8 @@ futures = "0.3"
lettre = "0.11"
csv = "1.3"
itertools = "0.12"
job_scheduler_ng = "2.0"
ureq = { version = "2.9", features = ["json"] }
[target.'cfg(not(windows))'.dependencies]
openssl = { version = "0.10", features = [ "vendored" ] }

View File

@ -4,3 +4,4 @@ rss_key = "rss-key-for-ci"
limits = { file = "10 MiB", data-form = "10 MiB"}
smtp_pw = "8kIjlLH79Ky6D3jQ"
usage_log_path = "./usage.txt"
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5"

View File

@ -175,3 +175,22 @@ CREATE TABLE IF NOT EXISTS "boat_reservation" (
"created_at" datetime not null default CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS "waterlevel" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"day" DATE NOT NULL,
"time" TEXT NOT NULL,
"max" INTEGER NOT NULL,
"min" INTEGER NOT NULL,
"mittel" INTEGER NOT NULL,
"tumax" INTEGER NOT NULL,
"tumin" INTEGER NOT NULL,
"tumittel" INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS "weather" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"day" DATE NOT NULL,
"max_temp" FLOAT NOT NULL,
"wind_gust" FLOAT NOT NULL,
"rain_mm" FLOAT NOT NULL
);

View File

@ -8,6 +8,8 @@ pub mod tera;
#[cfg(feature = "rest")]
pub mod rest;
pub mod scheduled;
#[cfg(test)]
#[macro_export]
macro_rules! testdb {

View File

@ -6,6 +6,7 @@ use std::str::FromStr;
use rot::rest;
#[cfg(feature = "rowing-tera")]
use rot::tera;
use rot::{scheduled, tera::Config};
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, ConnectOptions};
@ -26,7 +27,7 @@ async fn rocket() -> _ {
.await
.unwrap();
let rocket = rocket::build().manage(db);
let rocket = rocket::build().manage(db.clone());
#[cfg(feature = "rowing-tera")]
let rocket = tera::config(rocket);
@ -34,5 +35,11 @@ async fn rocket() -> _ {
#[cfg(feature = "rest")]
let rocket = rest::config(rocket);
let config: Config = rocket
.figment()
.extract()
.expect("Config extraction failed");
scheduled::schedule(&db, &config);
rocket
}

View File

@ -5,6 +5,8 @@ use sqlx::SqlitePool;
use self::{
planned_event::{PlannedEvent, PlannedEventWithUserAndTriptype},
trip::{Trip, TripWithUserAndType},
waterlevel::Waterlevel,
weather::Weather,
};
pub mod boat;
@ -27,6 +29,8 @@ pub mod tripdetails;
pub mod triptype;
pub mod user;
pub mod usertrip;
pub mod waterlevel;
pub mod weather;
#[derive(Serialize, Debug)]
pub struct Day {
@ -34,6 +38,8 @@ pub struct Day {
planned_events: Vec<PlannedEventWithUserAndTriptype>,
trips: Vec<TripWithUserAndType>,
is_pinned: bool,
max_waterlevel: Option<i64>,
weather: Option<Weather>,
}
impl Day {
@ -44,6 +50,8 @@ impl Day {
planned_events: PlannedEvent::get_pinned_for_day(db, day).await,
trips: Trip::get_pinned_for_day(db, day).await,
is_pinned,
max_waterlevel: Waterlevel::max_waterlevel_for_day(db, day).await,
weather: Weather::find_by_day(db, day).await,
}
} else {
Self {
@ -51,6 +59,8 @@ impl Day {
planned_events: PlannedEvent::get_for_day(db, day).await,
trips: Trip::get_for_day(db, day).await,
is_pinned,
max_waterlevel: Waterlevel::max_waterlevel_for_day(db, day).await,
weather: Weather::find_by_day(db, day).await,
}
}
}

72
src/model/waterlevel.rs Normal file
View File

@ -0,0 +1,72 @@
use std::ops::DerefMut;
use chrono::NaiveDate;
use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
#[derive(FromRow, Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Clone)]
pub struct Waterlevel {
pub id: i64,
pub day: NaiveDate,
pub time: String,
pub max: i64,
pub min: i64,
pub mittel: i64,
pub tumax: i64,
pub tumin: i64,
pub tumittel: i64,
}
impl Waterlevel {
pub async fn find_by_id(db: &SqlitePool, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM waterlevel WHERE id like ?", id)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, id: i32) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM waterlevel WHERE id like ?", id)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn create(
db: &mut Transaction<'_, Sqlite>,
day: NaiveDate,
time: String,
max: i64,
min: i64,
mittel: i64,
tumax: i64,
tumin: i64,
tumittel: i64,
) -> Result<(), String> {
sqlx::query!(
"INSERT INTO waterlevel(day, time, max, min, mittel, tumax, tumin, tumittel) VALUES (?,?,?,?,?,?,?,?)",
day, time, max, min, mittel, tumax, tumin, tumittel
)
.execute(db.deref_mut())
.await
.map_err(|e| e.to_string())?;
Ok(())
}
pub async fn max_waterlevel_for_day(db: &SqlitePool, day: NaiveDate) -> Option<i64> {
sqlx::query!(
"SELECT MAX(mittel) as max FROM waterlevel WHERE day = ?",
day
)
.fetch_one(db)
.await
.unwrap()
.max
}
pub async fn delete_all(db: &mut Transaction<'_, Sqlite>) {
sqlx::query!("DELETE FROM waterlevel;")
.execute(db.deref_mut())
.await
.unwrap();
}
}

56
src/model/weather.rs Normal file
View File

@ -0,0 +1,56 @@
use std::ops::DerefMut;
use chrono::NaiveDate;
use rocket::serde::{Deserialize, Serialize};
use sqlx::{FromRow, Sqlite, SqlitePool, Transaction};
#[derive(FromRow, Debug, Serialize, Deserialize, PartialEq, Clone)]
pub struct Weather {
pub id: i64,
pub day: NaiveDate,
pub max_temp: f64,
pub wind_gust: f64,
pub rain_mm: f64,
}
impl Weather {
pub async fn find_by_day(db: &SqlitePool, day: NaiveDate) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM weather WHERE day = ?", day)
.fetch_one(db)
.await
.ok()
}
pub async fn find_by_id_tx(db: &mut Transaction<'_, Sqlite>, day: NaiveDate) -> Option<Self> {
sqlx::query_as!(Self, "SELECT * FROM weather WHERE day = ?", day)
.fetch_one(db.deref_mut())
.await
.ok()
}
pub async fn create(
db: &mut Transaction<'_, Sqlite>,
day: NaiveDate,
max_temp: f64,
wind_gust: f64,
rain_mm: f64,
) -> Result<(), String> {
sqlx::query!(
"INSERT INTO weather(day, max_temp, wind_gust, rain_mm) VALUES (?,?,?,?)",
day,
max_temp,
wind_gust,
rain_mm
)
.execute(db.deref_mut())
.await
.map_err(|e| e.to_string())?;
Ok(())
}
pub async fn delete_all(db: &mut Transaction<'_, Sqlite>) {
sqlx::query!("DELETE FROM weather;")
.execute(db.deref_mut())
.await
.unwrap();
}
}

43
src/scheduled/mod.rs Normal file
View File

@ -0,0 +1,43 @@
mod waterlevel;
mod weather;
use std::time::Duration;
use job_scheduler_ng::{Job, JobScheduler};
use rocket::tokio::{self, task, time};
use sqlx::SqlitePool;
use crate::tera::Config;
pub fn schedule(db: &SqlitePool, config: &Config) {
let db = db.clone();
let openweathermap_key = config.openweathermap_key.clone();
tokio::task::spawn(async {
waterlevel::update(&db).await.unwrap();
weather::update(&db, &openweathermap_key).await.unwrap();
let mut sched = JobScheduler::new();
// Every hour
sched.add(Job::new("0 0 * * * * *".parse().unwrap(), move || {
let db_clone = db.clone();
// Use block_in_place to run async code in the synchronous function; TODO: Make it
// nicer one's rust (stable) support async closures
task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(async {
waterlevel::update(&db_clone).await.unwrap();
weather::update(&db_clone, &openweathermap_key)
.await
.unwrap();
});
});
}));
let mut interval = time::interval(Duration::from_secs(60));
loop {
sched.tick();
interval.tick().await;
}
});
}

112
src/scheduled/waterlevel.rs Normal file
View File

@ -0,0 +1,112 @@
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveTime};
use serde::{Deserialize, Serialize};
use sqlx::SqlitePool;
use crate::model::waterlevel::Waterlevel;
pub async fn update(db: &SqlitePool) -> Result<(), String> {
let mut tx = db.begin().await.unwrap();
// 1. Delete water levels starting from yesterday
Waterlevel::delete_all(&mut tx).await;
// 2. Fetch
let station = fetch()?;
for d in station.data {
let (Some(max), Some(min), Some(mittel), Some(tumax), Some(tumin), Some(tumittel)) =
(d.max, d.min, d.mittel, d.tumax, d.tumin, d.tumittel)
else {
println!("Ignored invalid values: {d:?}");
continue;
};
let Ok(datetime): Result<DateTime<FixedOffset>, _> = d.timestamp.parse() else {
return Err("Failed to parse datetime from hydro json".into());
};
let date: NaiveDate = datetime.naive_utc().date();
// Extract time component and format as string
let time: NaiveTime = datetime.naive_utc().time();
let time_str = time.format("%H:%M").to_string();
Waterlevel::create(
&mut tx, date, time_str, max, min, mittel, tumax, tumin, tumittel,
)
.await?
}
// 3. Save in DB
tx.commit().await.unwrap();
Ok(())
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Station {
station_no: String,
station_latitude: String,
station_longitude: String,
parametertype_name: String,
ts_shortname: String,
ts_name: String,
ts_unitname: String,
ts_unitsymbol: String,
ts_precision: String,
rows: String,
columns: String,
data: Vec<Data>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Data {
timestamp: String,
max: Option<i64>,
min: Option<i64>,
mittel: Option<i64>,
tumax: Option<i64>,
tumin: Option<i64>,
tumittel: Option<i64>,
}
fn fetch() -> Result<Station, String> {
let url = "https://hydro.ooe.gv.at/daten/internet/stations/OG/207068/S/forecast.json";
match ureq::get(url).call() {
Ok(response) => {
let forecast: Result<Vec<Station>, _> = response.into_json();
if let Ok(data) = forecast {
if data.len() == 1 {
return Ok(data[0].clone());
} else {
return Err(format!(
"Expected 1 station (Linz); got {} while fetching from {url}. Maybe the hydro data format changed?",
data.len()
));
}
} else {
return Err(format!(
"Failed to parse the json received by {url}: {}",
forecast.err().unwrap()
));
}
}
Err(_) => {
return Err(format!(
"Could not fetch {url}, do you have internet? Maybe their server is down?"
));
}
}
}
//#[cfg(test)]
//mod test {
// use crate::testdb;
//
// use super::*;
// #[sqlx::test]
// fn test_fetch_succ() {
// let pool = testdb!();
// fetch();
// }
//}

120
src/scheduled/weather.rs Normal file
View File

@ -0,0 +1,120 @@
use chrono::DateTime;
use serde::{Deserialize, Serialize};
use sqlx::SqlitePool;
use crate::model::weather::Weather;
pub async fn update(db: &SqlitePool, api_key: &str) -> Result<(), String> {
let mut tx = db.begin().await.unwrap();
// 1. Delete weather data
Weather::delete_all(&mut tx).await;
// 2. Fetch
let data = fetch(api_key)?;
for d in data.daily {
let Some(date) = DateTime::from_timestamp(d.dt, 0) else {
println!("Skipping {} because convertion to datetime failed", d.dt);
continue;
};
let max_temp = d.temp.max;
let wind_gust = d.wind_gust;
let rain_mm = d.rain.unwrap_or(0.);
Weather::create(
&mut tx,
date.naive_utc().into(),
max_temp,
wind_gust,
rain_mm,
)
.await?
}
// 3. Save in DB
tx.commit().await.unwrap();
Ok(())
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Data {
lat: f64,
lon: f64,
timezone: String,
timezone_offset: i64,
daily: Vec<Daily>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Daily {
dt: i64,
sunrise: i64,
sunset: i64,
moonrise: i64,
moonset: i64,
moon_phase: f64,
summary: String,
temp: Temp,
feels_like: FeelsLike,
pressure: i64,
humidity: i64,
dew_point: f64,
wind_speed: f64,
wind_deg: i64,
wind_gust: f64,
weather: Vec<DailyWeather>,
clouds: i64,
pop: f64,
rain: Option<f64>,
uvi: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct Temp {
day: f64,
min: f64,
max: f64,
night: f64,
eve: f64,
morn: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct FeelsLike {
day: f64,
night: f64,
eve: f64,
morn: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct DailyWeather {
id: i64,
main: String,
description: String,
icon: String,
}
fn fetch(api_key: &str) -> Result<Data, String> {
let url = format!("https://api.openweathermap.org/data/3.0/onecall?lat=48.31970&lon=14.29451&units=metric&exclude=current,minutely,hourly,alert&appid={api_key}");
match ureq::get(&url).call() {
Ok(response) => {
let data: Result<Data, _> = response.into_json();
if let Ok(data) = data {
return Ok(data);
} else {
return Err(format!(
"Failed to parse the json received by {url}: {}",
data.err().unwrap()
));
}
}
Err(_) => {
return Err(format!(
"Could not fetch {url}, do you have internet? Maybe their server is down?"
));
}
}
}

View File

@ -166,6 +166,7 @@ pub struct Config {
rss_key: String,
smtp_pw: String,
usage_log_path: String,
pub openweathermap_key: String,
}
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {

View File

@ -96,7 +96,13 @@
<div>
<h2 class="font-bold uppercase tracking-wide text-center rounded-t-md {% if day.is_pinned %} text-white bg-primary-950 {% else %} text-primary-950 dark:text-white bg-gray-200 dark:bg-primary-950 bg-opacity-80 {% endif %} text-lg px-3 py-3 ">
{{ day.day| date(format="%d.%m.%Y") }}
<small class="inline-block ml-1 text-xs {% if day.is_pinned %} text-gray-200 {% else %} text-gray-500 dark:text-gray-100 {% endif %}">{{ day.day | date(format="%A", locale="de_AT") }}</small>
<small class="inline-block ml-1 text-xs {% if day.is_pinned %} text-gray-200 {% else %} text-gray-500 dark:text-gray-100 {% endif %}">{{ day.day | date(format="%A", locale="de_AT") }}
{% if day.max_waterlevel %}
&bullet; <a href="https://hydro.ooe.gv.at/#/overview/Wasserstand/station/16668/Linz/Wasserstand"
target="_blank"
title="Prognostizierter maximaler Wasserstand am {{ day.day | date(format="%A", locale="de_AT") }}: {{ day.max_waterlevel }} cm">🌊{{ day.max_waterlevel }} cm</a>
{% endif %}
</small>
</h2>
{% if day.weather %}
<div class="bg-gray-300 text-center">