use chrono::NaiveDate;
use diesel::{
sql_query,
sql_types::{Date, Integer},
QueryResult, QueryableByName,
};
use diesel_async::{AsyncPgConnection, RunQueryDsl};
use std::collections::HashMap;
use crate::model::dto::timeline::{TimelineDto, TimelineEntryDto, TimelineParameters};
const CALCULATE_TIMELINE_QUERY: &str = include_str!("../sql/calculate_timeline.sql");
#[derive(QueryableByName, Debug)]
struct TimelineQeueryResult {
#[diesel(sql_type = Date)]
date: NaiveDate,
#[diesel(sql_type = Integer)]
additions: i32,
#[diesel(sql_type = Integer)]
removals: i32,
}
pub async fn calculate(
map_id: i32,
params: TimelineParameters,
conn: &mut AsyncPgConnection,
) -> QueryResult<TimelineDto> {
let query = sql_query(CALCULATE_TIMELINE_QUERY)
.bind::<diesel::sql_types::Integer, _>(map_id)
.bind::<diesel::sql_types::Date, _>(params.start)
.bind::<diesel::sql_types::Date, _>(params.end);
let results = query.load::<TimelineQeueryResult>(conn).await?;
let mut years: HashMap<String, TimelineEntryDto> = HashMap::new();
let mut months: HashMap<String, TimelineEntryDto> = HashMap::new();
let mut dates: HashMap<String, TimelineEntryDto> = HashMap::new();
for result in results {
let date = result.date;
let date_string = date.format("%Y-%m-%d").to_string();
let month_string = date.format("%Y-%m").to_string();
let year_string = date.format("%Y").to_string();
dates.insert(
date_string,
TimelineEntryDto {
additions: result.additions,
removals: result.removals,
},
);
let (month_additions, month_removals) =
months
.get(&month_string)
.map_or((result.additions, result.removals), |entry| {
(
entry.additions + result.additions,
entry.removals + result.removals,
)
});
months.insert(
month_string,
TimelineEntryDto {
additions: month_additions,
removals: month_removals,
},
);
let (year_additions, year_removals) =
years
.get(&year_string)
.map_or((result.additions, result.removals), |entry| {
(
entry.additions + result.additions,
entry.removals + result.removals,
)
});
years.insert(
year_string,
TimelineEntryDto {
additions: year_additions,
removals: year_removals,
},
);
}
Ok(TimelineDto {
years,
months,
dates,
})
}