backend/model/entity/
timeline.rs1use chrono::NaiveDate;
2use diesel::{
3 sql_query,
4 sql_types::{Date, Integer},
5 QueryResult, QueryableByName,
6};
7use diesel_async::{AsyncPgConnection, RunQueryDsl};
8use std::collections::HashMap;
9
10use crate::model::dto::timeline::{TimelineDto, TimelineEntryDto, TimelineParameters};
11
12const CALCULATE_TIMELINE_QUERY: &str = include_str!("../sql/calculate_timeline.sql");
14
15#[derive(QueryableByName, Debug)]
17struct TimelineQeueryResult {
18 #[diesel(sql_type = Date)]
20 date: NaiveDate,
21 #[diesel(sql_type = Integer)]
23 additions: i32,
24 #[diesel(sql_type = Integer)]
26 removals: i32,
27}
28
29pub async fn calculate(
34 map_id: i32,
35 params: TimelineParameters,
36 conn: &mut AsyncPgConnection,
37) -> QueryResult<TimelineDto> {
38 let query = sql_query(CALCULATE_TIMELINE_QUERY)
39 .bind::<diesel::sql_types::Integer, _>(map_id)
40 .bind::<diesel::sql_types::Date, _>(params.start)
41 .bind::<diesel::sql_types::Date, _>(params.end);
42
43 let results = query.load::<TimelineQeueryResult>(conn).await?;
44
45 let mut years: HashMap<String, TimelineEntryDto> = HashMap::new();
46 let mut months: HashMap<String, TimelineEntryDto> = HashMap::new();
47 let mut dates: HashMap<String, TimelineEntryDto> = HashMap::new();
48
49 for result in results {
50 let date = result.date;
51 let date_string = date.format("%Y-%m-%d").to_string();
52 let month_string = date.format("%Y-%m").to_string();
53 let year_string = date.format("%Y").to_string();
54
55 dates.insert(
56 date_string,
57 TimelineEntryDto {
58 additions: result.additions,
59 removals: result.removals,
60 },
61 );
62
63 let (month_additions, month_removals) =
64 months
65 .get(&month_string)
66 .map_or((result.additions, result.removals), |entry| {
67 (
68 entry.additions + result.additions,
69 entry.removals + result.removals,
70 )
71 });
72 months.insert(
73 month_string,
74 TimelineEntryDto {
75 additions: month_additions,
76 removals: month_removals,
77 },
78 );
79
80 let (year_additions, year_removals) =
81 years
82 .get(&year_string)
83 .map_or((result.additions, result.removals), |entry| {
84 (
85 entry.additions + result.additions,
86 entry.removals + result.removals,
87 )
88 });
89 years.insert(
90 year_string,
91 TimelineEntryDto {
92 additions: year_additions,
93 removals: year_removals,
94 },
95 );
96 }
97
98 Ok(TimelineDto {
99 years,
100 months,
101 dates,
102 })
103}