show waterlevel for the next days
This commit is contained in:
33
src/scheduled/mod.rs
Normal file
33
src/scheduled/mod.rs
Normal file
@ -0,0 +1,33 @@
|
||||
mod waterlevel;
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use job_scheduler_ng::{Job, JobScheduler};
|
||||
use rocket::tokio::{self, task};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
pub async fn schedule(db: &SqlitePool) {
|
||||
let db = db.clone();
|
||||
|
||||
waterlevel::update(&db).await.unwrap();
|
||||
|
||||
tokio::task::spawn(async {
|
||||
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
|
||||
task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current().block_on(async {
|
||||
waterlevel::update(&db_clone).await.unwrap();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
loop {
|
||||
sched.tick();
|
||||
std::thread::sleep(Duration::from_secs(60));
|
||||
}
|
||||
});
|
||||
}
|
112
src/scheduled/waterlevel.rs
Normal file
112
src/scheduled/waterlevel.rs
Normal 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();
|
||||
// }
|
||||
//}
|
Reference in New Issue
Block a user