Tweaking find all metars

This commit is contained in:
2025-04-20 14:40:47 -04:00
parent d714287fd9
commit 19ed8ef2ca
5 changed files with 52 additions and 36 deletions

2
.env
View File

@@ -28,7 +28,7 @@ MINIO_BROWSER_REDIRECT_URL=${NGINX_PROTOCOL}://${NGINX_HOST}:${NGINX_HTTP_PORT}/
UI_PORT=3000 UI_PORT=3000
API_PORT=5000 API_PORT=5000
API_METAR_TIME_OFFSET=3000 API_METAR_TIME_OFFSET=1800
SSL_CA_NAME=ca SSL_CA_NAME=ca
SSL_CA_PATH=../ssl/${SSL_CA_NAME}.pem SSL_CA_PATH=../ssl/${SSL_CA_NAME}.pem

View File

@@ -214,7 +214,7 @@ impl Airport {
let metar_fut = async { let metar_fut = async {
if metar { if metar {
match Metar::find_all(client, &vec![icao.to_string()], &false).await { match Metar::find_all_distinct(client, &vec![icao.to_string()]).await {
Ok(m) => Some(m.into_iter().nth(0)), Ok(m) => Some(m.into_iter().nth(0)),
Err(err) => { Err(err) => {
log::error!("{}", err); log::error!("{}", err);
@@ -345,7 +345,7 @@ impl Airport {
let runway_future = Runway::select_all_map(icaos.clone()); let runway_future = Runway::select_all_map(icaos.clone());
let frequency_future = Frequency::select_all_map(icaos.clone()); let frequency_future = Frequency::select_all_map(icaos.clone());
let metar_future = if query.metars.unwrap_or(false) { let metar_future = if query.metars.unwrap_or(false) {
Some(Metar::find_all(client, &icaos, &false)) Some(Metar::find_all_distinct(client, &icaos))
} else { } else {
None None
}; };

View File

@@ -3,20 +3,31 @@ use redis::{AsyncCommands, RedisResult};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::db::redis_async_connection; use crate::db::redis_async_connection;
use crate::error::ApiResult; use crate::error::ApiResult;
use crate::metars::Metar;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct MetarCheck { pub struct MetarCheck {
pub icao: String, pub icao: String,
pub status: bool, pub status: bool,
pub updated_at: DateTime<Utc>, pub updated_at: DateTime<Utc>,
pub last_metar: Option<Metar>,
} }
impl MetarCheck { impl MetarCheck {
pub fn new(icao: String, status: bool) -> Self { pub async fn new(icao: String, status: bool) -> Self {
Self { match Self::get(&icao).await {
Some(c) => Self {
icao, icao,
status, status,
updated_at: Utc::now(), updated_at: Utc::now(),
last_metar: c.last_metar,
},
None => Self {
icao,
status,
updated_at: Utc::now(),
last_metar: None,
}
} }
} }

View File

@@ -14,7 +14,7 @@ use crate::metars::MetarCheck;
const TABLE_NAME: &str = "metars"; const TABLE_NAME: &str = "metars";
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Metar { pub struct Metar {
pub icao: String, pub icao: String,
pub raw_text: String, pub raw_text: String,
@@ -60,7 +60,7 @@ pub struct Metar {
pub density_altitude: Option<f64>, pub density_altitude: Option<f64>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ReportModifier { pub enum ReportModifier {
#[serde(rename = "AUTO")] #[serde(rename = "AUTO")]
Auto, Auto,
@@ -88,7 +88,7 @@ impl Display for ReportModifier {
} }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RunwayVisualRange { pub struct RunwayVisualRange {
pub runway: String, pub runway: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@@ -110,7 +110,7 @@ impl Default for RunwayVisualRange {
} }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum AutomatedStationType { pub enum AutomatedStationType {
#[serde(rename = "AO1")] #[serde(rename = "AO1")]
WithoutPrecipitationDiscriminator, WithoutPrecipitationDiscriminator,
@@ -141,7 +141,7 @@ impl Display for AutomatedStationType {
} }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Remarks { pub struct Remarks {
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub peak_wind: Option<PeakWind>, pub peak_wind: Option<PeakWind>,
@@ -165,7 +165,7 @@ pub struct Remarks {
pub sky_condition_at_secondary_location_not_available: Option<String>, pub sky_condition_at_secondary_location_not_available: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeakWind { pub struct PeakWind {
pub degrees: i32, pub degrees: i32,
pub speed: i32, pub speed: i32,
@@ -190,7 +190,7 @@ impl Default for Remarks {
} }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SkyCondition { pub struct SkyCondition {
pub sky_cover: String, pub sky_cover: String,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
@@ -209,7 +209,7 @@ impl Default for SkyCondition {
} }
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FlightCategory { pub enum FlightCategory {
VFR, VFR,
MVFR, MVFR,
@@ -993,10 +993,9 @@ impl Metar {
}) })
} }
pub async fn find_all( pub async fn find_all_distinct(
client: &Client, client: &Client,
icao_list: &Vec<String>, icao_list: &Vec<String>
_force: &bool,
) -> ApiResult<Vec<Self>> { ) -> ApiResult<Vec<Self>> {
if icao_list.is_empty() { if icao_list.is_empty() {
return Ok(Vec::new()); return Ok(Vec::new());
@@ -1017,9 +1016,9 @@ impl Metar {
let current_time = Utc::now().timestamp(); let current_time = Utc::now().timestamp();
let time_offset = env::var("API_METAR_TIME_OFFSET") let time_offset = env::var("API_METAR_TIME_OFFSET")
.unwrap_or("3000".to_string()) .unwrap_or("1800".to_string())
.parse::<i64>() .parse::<i64>()
.unwrap_or(3000); .unwrap_or(1800);
let short_time_offset: i64 = 300; let short_time_offset: i64 = 300;
// Setup metars and missing metar structures // Setup metars and missing metar structures
@@ -1042,8 +1041,8 @@ impl Metar {
}; };
// If the metar was cached more than short_time_offset minutes ago, refresh it // If the metar was cached more than short_time_offset minutes ago, refresh it
if refresh_seconds >= short_time_offset { if refresh_seconds >= short_time_offset {
log::trace!("{} METAR data is outdated, refreshing now", &icao); log::trace!("{} METAR data is outdated, marked for refresh", &icao);
missing_metar_icaos.push(icao); missing_metar_icaos.push(icao.clone());
} }
// Otherwise return outdated data and wait // Otherwise return outdated data and wait
else { else {
@@ -1058,7 +1057,7 @@ impl Metar {
// Otherwise add the metar to the vector // Otherwise add the metar to the vector
else { else {
found_metar_icaos.insert(icao.clone()); found_metar_icaos.insert(icao.clone());
let metar_check = MetarCheck::new(icao, true); let metar_check = MetarCheck::new(icao, true).await;
metar_check.insert(time_offset as u64).await?; metar_check.insert(time_offset as u64).await?;
metars.push(Metar::from_db(metar_row)?); metars.push(Metar::from_db(metar_row)?);
} }
@@ -1092,21 +1091,29 @@ impl Metar {
if remote_metars.len() > 0 { if remote_metars.len() > 0 {
// Insert missing METARs // Insert missing METARs
for remote_metar in &remote_metars { for remote_metar in remote_metars.clone() {
found_metar_icaos.insert(remote_metar.icao.to_string());
let metar_check = MetarCheck::new(remote_metar.icao.clone(), true);
metar_check.insert(time_offset as u64).await?;
remote_metar.insert().await?; remote_metar.insert().await?;
found_metar_icaos.insert(remote_metar.icao.to_string());
let mut metar_check = MetarCheck::new(remote_metar.icao.clone(), true).await;
metar_check.last_metar = Some(remote_metar);
metar_check.insert(time_offset as u64).await?;
} }
metars.append(&mut remote_metars) metars.append(&mut remote_metars);
} }
// Update still missing metars // Update still missing metars
let mut still_missing_metar_icaos: Vec<String> = vec![]; // let mut still_missing_metar_icaos: Vec<String> = vec![];
for difference in found_metar_icaos.symmetric_difference(&requested_icaos) { for difference in found_metar_icaos.symmetric_difference(&requested_icaos) {
still_missing_metar_icaos.push(difference.to_string()); // still_missing_metar_icaos.push(difference.to_string());
let metar_check = MetarCheck::new(difference.to_string(), false); let metar_check = MetarCheck::new(difference.to_string(), false).await;
metar_check.insert(short_time_offset as u64).await? metar_check.insert(short_time_offset as u64).await?;
// Only add cached metar data if it's less than 4 hours old
if let Some(last_metar) = metar_check.last_metar {
let four_hours_ago = Utc::now() - chrono::Duration::hours(4);
if last_metar.observation_time < four_hours_ago {
metars.push(last_metar);
}
}
} }
// if !still_missing_metar_icaos.is_empty() { // if !still_missing_metar_icaos.is_empty() {
// log::trace!("Still missing METAR data from {:?}", still_missing_metar_icaos); // log::trace!("Still missing METAR data from {:?}", still_missing_metar_icaos);

View File

@@ -7,7 +7,6 @@ use crate::AppState;
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
struct FindAllParameters { struct FindAllParameters {
icaos: Option<String>, icaos: Option<String>,
force: Option<bool>,
} }
#[get("metars")] #[get("metars")]
@@ -19,10 +18,9 @@ async fn find_all(data: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
None => return HttpResponse::UnprocessableEntity().body("Missing icaos parameter"), None => return HttpResponse::UnprocessableEntity().body("Missing icaos parameter"),
}; };
let icaos: Vec<String> = icao_string.split(',').map(|s| s.to_string()).collect(); let icaos: Vec<String> = icao_string.split(',').map(|s| s.to_string()).collect();
let force = &parameters.force.unwrap_or(false);
let client = &data.client; let client = &data.client;
let metars = match Metar::find_all(client, &icaos, force).await { let metars = match Metar::find_all_distinct(client, &icaos).await {
Ok(a) => a, Ok(a) => a,
Err(err) => { Err(err) => {
error!("{}", err); error!("{}", err);