use crate::{Error, Gateway, Link, Parameter, Period, BASE_URL};
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
use serde::Deserialize;
impl Gateway {
pub async fn get_observations<'a>(
&self,
station_id: u32,
parameter: &'a Parameter,
) -> Result<Vec<(NaiveDateTime, f64)>, Error> {
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct PeriodResponse {
period: Vec<Period>,
}
let parameter_id = serde_json::to_string(¶meter).unwrap();
let url = format!(
"{}/parameter/{}/station/{}/period.json",
BASE_URL, parameter_id, station_id
);
let res: PeriodResponse = self.get(&url).await?;
let mut corrected_archive_url = None;
let mut latest_months_url = None;
for period in res.period {
if period.key == "corrected-archive" {
for link in period.link {
if link.r#type == "application/json" {
corrected_archive_url = Some(link.href);
break;
}
}
} else if period.key == "latest-months" {
for link in period.link {
if link.r#type == "application/json" {
latest_months_url = Some(link.href);
break;
}
}
}
if let (Some(_), Some(_)) = (&corrected_archive_url, &latest_months_url) {
break;
}
}
let corrected_archive_url = match corrected_archive_url {
Some(corrected_archive_url) => corrected_archive_url,
None => {
return Err(Error::ParseError(format!(
"Could not find corrected-archive in period."
)));
}
};
let latest_months_url = match latest_months_url {
Some(latest_months_url) => latest_months_url,
None => {
return Err(Error::ParseError(format!(
"Could not find latest-months in period."
)));
}
};
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct CorrectedArchiveData {
link: Vec<Link>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct CorrectedArchiveResponse {
data: Vec<CorrectedArchiveData>,
}
let res: CorrectedArchiveResponse = self.get(&corrected_archive_url).await?;
let mut corrected_archive_data_url = None;
'off: for data in res.data {
for link in data.link {
if link.rel == "data" {
corrected_archive_data_url = Some(link.href);
break 'off;
}
}
}
let corrected_archive_data_url = match corrected_archive_data_url {
Some(corrected_archive_data_url) => corrected_archive_data_url,
None => {
return Err(Error::ParseError(format!(
"Could not find data in corrected-archive."
)));
}
};
let corrected_archive_data = self.get_string(&corrected_archive_data_url).await?;
let mut observations = vec![];
let rows = corrected_archive_data.split("\n").collect::<Vec<&str>>();
for row in rows {
if row == "" {
continue;
}
let columns = row.split(";").collect::<Vec<&str>>();
if columns.len() < 4 {
continue;
}
let date = match NaiveDate::parse_from_str(columns[0], "%Y-%m-%d") {
Ok(date) => date,
Err(_) => {
println!("Error parsing {} as date.", columns[0]);
continue;
}
};
let time = match NaiveTime::parse_from_str(columns[1], "%H:%M:%S") {
Ok(time) => time,
Err(_) => {
println!("Error parsing {} as time.", columns[1]);
continue;
}
};
let value = match columns[2].parse::<f64>() {
Ok(value) => value,
Err(_) => {
println!("Error parsing {} as a number.", columns[2]);
continue;
}
};
if columns[3] != "G" && columns[3] != "Y" {
continue;
}
observations.push((date.and_time(time), value));
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct LatestMonthsData {
link: Vec<Link>,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct LatestMonthsResponse {
data: Vec<LatestMonthsData>,
}
let res: LatestMonthsResponse = self.get(&latest_months_url).await?;
let mut latest_months_data_url = None;
'out: for data in res.data {
for link in data.link {
if link.r#type == "application/json" {
latest_months_data_url = Some(link.href);
break 'out;
}
}
}
let latest_months_data_url = match latest_months_data_url {
Some(latest_months_data_url) => latest_months_data_url,
None => {
return Err(Error::ParseError(format!(
"Could not find json data in latest-months."
)));
}
};
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct Value {
date: u64,
value: String,
quality: String,
}
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct LatestMonthsDataResponse {
value: Vec<Value>,
}
let res: LatestMonthsDataResponse = self.get(&latest_months_data_url).await?;
for data in res.value {
let timestamp = (data.date as f64 / 1000.0) as i64;
let date = match NaiveDateTime::from_timestamp_opt(timestamp, 0) {
Some(date) => date,
None => {
println!("Error parsing {} as date.", timestamp);
continue;
}
};
println!("XXX {} {}", data.date, date);
let value = match data.value.parse::<f64>() {
Ok(value) => value,
Err(_) => {
println!("Error parsing {} as a number.", data.value);
continue;
}
};
if data.quality != "G" && data.quality != "Y" {
continue;
}
observations.push((date, value));
}
Ok(observations)
}
}