2024-04-30 11:59:33 +02:00
|
|
|
use chrono::{DateTime, FixedOffset, NaiveDate, NaiveTime};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use sqlx::SqlitePool;
|
|
|
|
|
2024-05-22 00:13:23 +02:00
|
|
|
use crate::model::waterlevel::{self, Waterlevel};
|
2024-04-30 11:59:33 +02:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
2024-05-22 00:13:23 +02:00
|
|
|
let create = waterlevel::Create {
|
|
|
|
day: date,
|
|
|
|
time: time_str,
|
|
|
|
max,
|
|
|
|
min,
|
|
|
|
mittel,
|
|
|
|
tumax,
|
|
|
|
tumin,
|
|
|
|
tumittel,
|
|
|
|
};
|
|
|
|
|
|
|
|
Waterlevel::create(&mut tx, &create).await?
|
2024-04-30 11:59:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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 {
|
2024-05-22 00:13:23 +02:00
|
|
|
Ok(data[0].clone())
|
2024-04-30 11:59:33 +02:00
|
|
|
} else {
|
2024-05-22 00:13:23 +02:00
|
|
|
Err(format!(
|
2024-04-30 11:59:33 +02:00
|
|
|
"Expected 1 station (Linz); got {} while fetching from {url}. Maybe the hydro data format changed?",
|
|
|
|
data.len()
|
2024-05-22 00:13:23 +02:00
|
|
|
))
|
2024-04-30 11:59:33 +02:00
|
|
|
}
|
|
|
|
} else {
|
2024-05-22 00:13:23 +02:00
|
|
|
Err(format!(
|
2024-04-30 11:59:33 +02:00
|
|
|
"Failed to parse the json received by {url}: {}",
|
|
|
|
forecast.err().unwrap()
|
2024-05-22 00:13:23 +02:00
|
|
|
))
|
2024-04-30 11:59:33 +02:00
|
|
|
}
|
|
|
|
}
|
2024-05-22 00:13:23 +02:00
|
|
|
Err(_) => Err(format!(
|
|
|
|
"Could not fetch {url}, do you have internet? Maybe their server is down?"
|
|
|
|
)),
|
2024-04-30 11:59:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//#[cfg(test)]
|
|
|
|
//mod test {
|
|
|
|
// use crate::testdb;
|
|
|
|
//
|
|
|
|
// use super::*;
|
|
|
|
// #[sqlx::test]
|
|
|
|
// fn test_fetch_succ() {
|
|
|
|
// let pool = testdb!();
|
|
|
|
// fetch();
|
|
|
|
// }
|
|
|
|
//}
|