Add months endpoint to electricity API
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use shared_api_lib::year_month::YearMonth;
|
||||
|
||||
pub fn get_day_power_entities(date: &chrono::NaiveDate, database_path: &str) -> Vec<LogEntity> {
|
||||
let mut items = Vec::new();
|
||||
|
||||
@@ -195,3 +197,135 @@ pub struct LogDateSummary {
|
||||
pub total_power_return_night: f64,
|
||||
pub gas_consumption_in_cubic_meters: f64,
|
||||
}
|
||||
|
||||
pub fn get_month_power_summaries(
|
||||
start: &YearMonth,
|
||||
stop: &YearMonth,
|
||||
database_path: &str,
|
||||
) -> Vec<LogMonthSummary> {
|
||||
let mut items = Vec::new();
|
||||
|
||||
let maybe_connection = rusqlite::Connection::open(database_path);
|
||||
if maybe_connection.is_err() {
|
||||
log::error!(
|
||||
"Failed to open database connection to {} with error {}",
|
||||
database_path,
|
||||
maybe_connection.unwrap_err()
|
||||
);
|
||||
return items;
|
||||
}
|
||||
|
||||
let connection = maybe_connection.unwrap();
|
||||
let maybe_statement = connection.prepare(
|
||||
"SELECT
|
||||
STRFTIME('%Y-%m', \"Date\") AS YearMonth,
|
||||
MAX(TotalPowerConsumptionDay),
|
||||
MIN(TotalPowerConsumptionDay),
|
||||
MAX(TotalPowerConsumptionNight),
|
||||
MIN(TotalPowerConsumptionNight),
|
||||
MAX(TotalPowerReturnDay),
|
||||
MIN(TotalPowerReturnDay),
|
||||
MAX(TotalPowerReturnNight),
|
||||
MIN(TotalPowerReturnNight),
|
||||
MAX(GasConsumptionInCubicMeters),
|
||||
MIN(GasConsumptionInCubicMeters)
|
||||
FROM \"ElectricityLog\"
|
||||
WHERE \"Date\" >= :start_date AND \"Date\" <= :stop_date
|
||||
GROUP BY YearMonth
|
||||
ORDER BY YearMonth;",
|
||||
);
|
||||
if maybe_statement.is_err() {
|
||||
log::error!(
|
||||
"Failed to prepate database statement with error {}",
|
||||
maybe_statement.unwrap_err()
|
||||
);
|
||||
return items;
|
||||
}
|
||||
|
||||
let formatted_start = format!("{}-01", start);
|
||||
let formatted_stop = format!("{}-31", stop);
|
||||
log::info!(
|
||||
"Fetching month power data for date range {} to {}",
|
||||
formatted_start,
|
||||
formatted_stop
|
||||
);
|
||||
let mut statement = maybe_statement.unwrap();
|
||||
let maybe_row_iterator = statement.query_map(
|
||||
&[
|
||||
(":start_date", &formatted_start),
|
||||
(":stop_date", &formatted_stop),
|
||||
],
|
||||
|row| {
|
||||
Ok(LogMonthSummaryEntity {
|
||||
year_month: row.get(0)?,
|
||||
total_power_consumption_day: row.get::<usize, f64>(1)?
|
||||
- row.get::<usize, f64>(2)?,
|
||||
total_power_consumption_night: row.get::<usize, f64>(3)?
|
||||
- row.get::<usize, f64>(4)?,
|
||||
total_power_return_day: row.get::<usize, f64>(5)? - row.get::<usize, f64>(6)?,
|
||||
total_power_return_night: row.get::<usize, f64>(7)? - row.get::<usize, f64>(8)?,
|
||||
gas_consumption_in_cubic_meters: row.get::<usize, f64>(9)?
|
||||
- row.get::<usize, f64>(10)?,
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
match maybe_row_iterator {
|
||||
Ok(iterator) => {
|
||||
for row in iterator {
|
||||
match row {
|
||||
Ok(entity) => {
|
||||
if let Some(year_month) = YearMonth::try_parse(&entity.year_month) {
|
||||
items.push(LogMonthSummary {
|
||||
year: year_month.year,
|
||||
month: year_month.month,
|
||||
total_power_consumption_day: entity.total_power_consumption_day,
|
||||
total_power_consumption_night: entity.total_power_consumption_night,
|
||||
total_power_return_day: entity.total_power_return_day,
|
||||
total_power_return_night: entity.total_power_return_night,
|
||||
gas_consumption_in_cubic_meters: entity
|
||||
.gas_consumption_in_cubic_meters,
|
||||
});
|
||||
} else {
|
||||
log::error!(
|
||||
"Failed to parse year month {} from SQL row",
|
||||
entity.year_month
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(error) => log::error!(
|
||||
"Failed to interpret row from SQL query with error {}",
|
||||
error
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"Failed to execute month power data SQL query with error {}",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
|
||||
struct LogMonthSummaryEntity {
|
||||
pub year_month: String,
|
||||
pub total_power_consumption_day: f64,
|
||||
pub total_power_consumption_night: f64,
|
||||
pub total_power_return_day: f64,
|
||||
pub total_power_return_night: f64,
|
||||
pub gas_consumption_in_cubic_meters: f64,
|
||||
}
|
||||
|
||||
pub struct LogMonthSummary {
|
||||
pub year: i32,
|
||||
pub month: u8,
|
||||
pub total_power_consumption_day: f64,
|
||||
pub total_power_consumption_night: f64,
|
||||
pub total_power_return_day: f64,
|
||||
pub total_power_return_night: f64,
|
||||
pub gas_consumption_in_cubic_meters: f64,
|
||||
}
|
||||
|
||||
@@ -131,7 +131,61 @@ async fn serve(configuration: &std::sync::Arc<CommandLineArgs>) {
|
||||
},
|
||||
);
|
||||
|
||||
warp::serve(day.or(days))
|
||||
let months = warp::get()
|
||||
.and(warp::path("months"))
|
||||
.and(warp::query::<HashMap<String, String>>())
|
||||
.and(warp::header::headers_cloned())
|
||||
.and(warp::any().map({
|
||||
let configuration = configuration.clone();
|
||||
move || configuration.clone()
|
||||
}))
|
||||
.map(
|
||||
|query: HashMap<String, String>,
|
||||
headers,
|
||||
configuration: std::sync::Arc<CommandLineArgs>| {
|
||||
if !has_required_header(
|
||||
&headers,
|
||||
&configuration.http_header_name_to_validate,
|
||||
&configuration.http_header_value_to_validate,
|
||||
) {
|
||||
log::info!("Access requested to /months with invalid header value");
|
||||
return Response::builder()
|
||||
.status(403)
|
||||
.body(String::from("Forbidden"));
|
||||
}
|
||||
|
||||
let maybe_start =
|
||||
shared_api_lib::query_helpers::try_parse_query_month_year(query.get("start"));
|
||||
if maybe_start.is_none() {
|
||||
return Response::builder()
|
||||
.status(400)
|
||||
.body(String::from("Unsupported \"start\" param in query."));
|
||||
}
|
||||
|
||||
let maybe_stop =
|
||||
shared_api_lib::query_helpers::try_parse_query_month_year(query.get("stop"));
|
||||
if maybe_stop.is_none() {
|
||||
return Response::builder()
|
||||
.status(400)
|
||||
.body(String::from("Unsupported \"stop\" param in query."));
|
||||
}
|
||||
|
||||
let start = maybe_start.unwrap();
|
||||
let stop = maybe_stop.unwrap();
|
||||
if start > stop {
|
||||
return Response::builder().status(400).body(String::from(
|
||||
"Param \"start\" must be smaller than or equal to param \"stop\" in query.",
|
||||
));
|
||||
}
|
||||
|
||||
let json = get_months_power_json(&start, &stop, &configuration.database_path);
|
||||
return Response::builder()
|
||||
.header("Content-Type", "application/json")
|
||||
.body(json);
|
||||
},
|
||||
);
|
||||
|
||||
warp::serve(day.or(days).or(months))
|
||||
.run(([127, 0, 0, 1], configuration.listening_port))
|
||||
.await;
|
||||
}
|
||||
@@ -301,3 +355,54 @@ struct DaysResponseItem {
|
||||
total_power_return: f64,
|
||||
total_gas_use: f64,
|
||||
}
|
||||
|
||||
fn get_months_power_json(
|
||||
start: &shared_api_lib::year_month::YearMonth,
|
||||
stop: &shared_api_lib::year_month::YearMonth,
|
||||
database_path: &str,
|
||||
) -> String {
|
||||
let summaries = database::get_month_power_summaries(&start, &stop, &database_path);
|
||||
let mut response_model = MonthsResponse {
|
||||
month_logs: Vec::new(),
|
||||
};
|
||||
|
||||
for summary in summaries {
|
||||
response_model.month_logs.push(MonthsResponseItem {
|
||||
year: summary.year,
|
||||
month: summary.month,
|
||||
total_power_use: summary.total_power_consumption_day
|
||||
+ summary.total_power_consumption_night,
|
||||
total_power_return: summary.total_power_return_day + summary.total_power_return_night,
|
||||
total_gas_use: summary.gas_consumption_in_cubic_meters,
|
||||
});
|
||||
}
|
||||
|
||||
match serde_json::to_string(&response_model) {
|
||||
Ok(json) => json,
|
||||
Err(error) => {
|
||||
log::error!(
|
||||
"Failed to format JSON data for date range {} to {} with error {}",
|
||||
start,
|
||||
stop,
|
||||
error
|
||||
);
|
||||
return String::from("[]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct MonthsResponse {
|
||||
month_logs: Vec<MonthsResponseItem>,
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct MonthsResponseItem {
|
||||
year: i32,
|
||||
month: u8,
|
||||
total_power_use: f64,
|
||||
total_power_return: f64,
|
||||
total_gas_use: f64,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user