2024-05-16 14:41:15 +02:00
|
|
|
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;
|
2024-05-16 16:35:49 +02:00
|
|
|
let rain_mm = d.rain.unwrap_or(0.);
|
2024-05-16 14:41:15 +02:00
|
|
|
|
|
|
|
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?"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|