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