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> {
|
pub fn get_day_power_entities(date: &chrono::NaiveDate, database_path: &str) -> Vec<LogEntity> {
|
||||||
let mut items = Vec::new();
|
let mut items = Vec::new();
|
||||||
|
|
||||||
@@ -195,3 +197,135 @@ pub struct LogDateSummary {
|
|||||||
pub total_power_return_night: f64,
|
pub total_power_return_night: f64,
|
||||||
pub gas_consumption_in_cubic_meters: 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))
|
.run(([127, 0, 0, 1], configuration.listening_port))
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@@ -301,3 +355,54 @@ struct DaysResponseItem {
|
|||||||
total_power_return: f64,
|
total_power_return: f64,
|
||||||
total_gas_use: 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