Compare commits
No commits in common. "862ec5624a25a0dd5809a54051a74ffc82a19b1b" and "ebb4fe84bba701e563278fdc4be2a3da14b3e8d3" have entirely different histories.
862ec5624a
...
ebb4fe84bb
117
Cargo.lock
generated
117
Cargo.lock
generated
@ -485,26 +485,6 @@ version = "2.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
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]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.12"
|
version = "0.5.12"
|
||||||
@ -794,16 +774,6 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6"
|
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]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@ -1331,17 +1301,6 @@ version = "1.0.11"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
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]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
@ -2268,7 +2227,6 @@ dependencies = [
|
|||||||
"futures",
|
"futures",
|
||||||
"ics",
|
"ics",
|
||||||
"itertools",
|
"itertools",
|
||||||
"job_scheduler_ng",
|
|
||||||
"lettre",
|
"lettre",
|
||||||
"log",
|
"log",
|
||||||
"openssl",
|
"openssl",
|
||||||
@ -2278,7 +2236,6 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tera",
|
"tera",
|
||||||
"ureq",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2327,24 +2284,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-webpki 0.101.7",
|
"rustls-webpki",
|
||||||
"sct",
|
"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]]
|
[[package]]
|
||||||
name = "rustls-pemfile"
|
name = "rustls-pemfile"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@ -2354,12 +2297,6 @@ dependencies = [
|
|||||||
"base64 0.21.7",
|
"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]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.101.7"
|
version = "0.101.7"
|
||||||
@ -2370,17 +2307,6 @@ dependencies = [
|
|||||||
"untrusted",
|
"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]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.15"
|
version = "1.0.15"
|
||||||
@ -2664,7 +2590,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"paste",
|
"paste",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rustls 0.21.10",
|
"rustls",
|
||||||
"rustls-pemfile",
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -2677,7 +2603,7 @@ dependencies = [
|
|||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"webpki-roots 0.25.4",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3306,25 +3232,6 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
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]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@ -3348,15 +3255,6 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "uuid"
|
|
||||||
version = "1.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "valuable"
|
name = "valuable"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -3466,15 +3364,6 @@ version = "0.25.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
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]]
|
[[package]]
|
||||||
name = "whoami"
|
name = "whoami"
|
||||||
version = "1.5.1"
|
version = "1.5.1"
|
||||||
|
@ -25,8 +25,6 @@ futures = "0.3"
|
|||||||
lettre = "0.11"
|
lettre = "0.11"
|
||||||
csv = "1.3"
|
csv = "1.3"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
job_scheduler_ng = "2.0"
|
|
||||||
ureq = { version = "2.9", features = ["json"] }
|
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
openssl = { version = "0.10", features = [ "vendored" ] }
|
openssl = { version = "0.10", features = [ "vendored" ] }
|
||||||
|
@ -4,4 +4,3 @@ rss_key = "rss-key-for-ci"
|
|||||||
limits = { file = "10 MiB", data-form = "10 MiB"}
|
limits = { file = "10 MiB", data-form = "10 MiB"}
|
||||||
smtp_pw = "8kIjlLH79Ky6D3jQ"
|
smtp_pw = "8kIjlLH79Ky6D3jQ"
|
||||||
usage_log_path = "./usage.txt"
|
usage_log_path = "./usage.txt"
|
||||||
openweathermap_key = "c8dab8f91b5b815d76e9879cbaecd8d5"
|
|
||||||
|
@ -175,22 +175,3 @@ CREATE TABLE IF NOT EXISTS "boat_reservation" (
|
|||||||
"created_at" datetime not null default CURRENT_TIMESTAMP
|
"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
|
|
||||||
);
|
|
||||||
|
@ -8,8 +8,6 @@ pub mod tera;
|
|||||||
#[cfg(feature = "rest")]
|
#[cfg(feature = "rest")]
|
||||||
pub mod rest;
|
pub mod rest;
|
||||||
|
|
||||||
pub mod scheduled;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! testdb {
|
macro_rules! testdb {
|
||||||
|
@ -6,7 +6,6 @@ use std::str::FromStr;
|
|||||||
use rot::rest;
|
use rot::rest;
|
||||||
#[cfg(feature = "rowing-tera")]
|
#[cfg(feature = "rowing-tera")]
|
||||||
use rot::tera;
|
use rot::tera;
|
||||||
use rot::{scheduled, tera::Config};
|
|
||||||
|
|
||||||
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, ConnectOptions};
|
use sqlx::{pool::PoolOptions, sqlite::SqliteConnectOptions, ConnectOptions};
|
||||||
|
|
||||||
@ -27,7 +26,7 @@ async fn rocket() -> _ {
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let rocket = rocket::build().manage(db.clone());
|
let rocket = rocket::build().manage(db);
|
||||||
|
|
||||||
#[cfg(feature = "rowing-tera")]
|
#[cfg(feature = "rowing-tera")]
|
||||||
let rocket = tera::config(rocket);
|
let rocket = tera::config(rocket);
|
||||||
@ -35,11 +34,5 @@ async fn rocket() -> _ {
|
|||||||
#[cfg(feature = "rest")]
|
#[cfg(feature = "rest")]
|
||||||
let rocket = rest::config(rocket);
|
let rocket = rest::config(rocket);
|
||||||
|
|
||||||
let config: Config = rocket
|
|
||||||
.figment()
|
|
||||||
.extract()
|
|
||||||
.expect("Config extraction failed");
|
|
||||||
scheduled::schedule(&db, &config);
|
|
||||||
|
|
||||||
rocket
|
rocket
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,6 @@ use sqlx::SqlitePool;
|
|||||||
use self::{
|
use self::{
|
||||||
planned_event::{PlannedEvent, PlannedEventWithUserAndTriptype},
|
planned_event::{PlannedEvent, PlannedEventWithUserAndTriptype},
|
||||||
trip::{Trip, TripWithUserAndType},
|
trip::{Trip, TripWithUserAndType},
|
||||||
waterlevel::Waterlevel,
|
|
||||||
weather::Weather,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod boat;
|
pub mod boat;
|
||||||
@ -29,8 +27,6 @@ pub mod tripdetails;
|
|||||||
pub mod triptype;
|
pub mod triptype;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
pub mod usertrip;
|
pub mod usertrip;
|
||||||
pub mod waterlevel;
|
|
||||||
pub mod weather;
|
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct Day {
|
pub struct Day {
|
||||||
@ -38,8 +34,6 @@ pub struct Day {
|
|||||||
planned_events: Vec<PlannedEventWithUserAndTriptype>,
|
planned_events: Vec<PlannedEventWithUserAndTriptype>,
|
||||||
trips: Vec<TripWithUserAndType>,
|
trips: Vec<TripWithUserAndType>,
|
||||||
is_pinned: bool,
|
is_pinned: bool,
|
||||||
max_waterlevel: Option<i64>,
|
|
||||||
weather: Option<Weather>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Day {
|
impl Day {
|
||||||
@ -50,8 +44,6 @@ impl Day {
|
|||||||
planned_events: PlannedEvent::get_pinned_for_day(db, day).await,
|
planned_events: PlannedEvent::get_pinned_for_day(db, day).await,
|
||||||
trips: Trip::get_pinned_for_day(db, day).await,
|
trips: Trip::get_pinned_for_day(db, day).await,
|
||||||
is_pinned,
|
is_pinned,
|
||||||
max_waterlevel: Waterlevel::max_waterlevel_for_day(db, day).await,
|
|
||||||
weather: Weather::find_by_day(db, day).await,
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Self {
|
Self {
|
||||||
@ -59,8 +51,6 @@ impl Day {
|
|||||||
planned_events: PlannedEvent::get_for_day(db, day).await,
|
planned_events: PlannedEvent::get_for_day(db, day).await,
|
||||||
trips: Trip::get_for_day(db, day).await,
|
trips: Trip::get_for_day(db, day).await,
|
||||||
is_pinned,
|
is_pinned,
|
||||||
max_waterlevel: Waterlevel::max_waterlevel_for_day(db, day).await,
|
|
||||||
weather: Weather::find_by_day(db, day).await,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
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();
|
|
||||||
// }
|
|
||||||
//}
|
|
@ -1,120 +0,0 @@
|
|||||||
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?"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -166,7 +166,6 @@ pub struct Config {
|
|||||||
rss_key: String,
|
rss_key: String,
|
||||||
smtp_pw: String,
|
smtp_pw: String,
|
||||||
usage_log_path: String,
|
usage_log_path: String,
|
||||||
pub openweathermap_key: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
|
@ -91,19 +91,8 @@
|
|||||||
<div>
|
<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 ">
|
<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") }}
|
{{ 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 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>
|
||||||
{% if day.max_waterlevel %}
|
|
||||||
• <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>
|
</h2>
|
||||||
{% if day.weather %}
|
|
||||||
<div class="bg-gray-300 rounded text-center">
|
|
||||||
Max temp: {{ day.weather.max_temp | round }}° • Windböe: {{ day.weather.wind_gust | round }} km/h • Regen: {{ day.weather.rain_mm | round }} mm
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% if day.planned_events | length > 0 or day.trips | length > 0 %}
|
{% if day.planned_events | length > 0 or day.trips | length > 0 %}
|
||||||
<div class="grid grid-cols-1 gap-3 mb-3">
|
<div class="grid grid-cols-1 gap-3 mb-3">
|
||||||
{# --- START Events --- #}
|
{# --- START Events --- #}
|
||||||
|
Loading…
Reference in New Issue
Block a user