Display balance evolution chart
This commit is contained in:
@ -1,7 +1,10 @@
|
||||
use crate::controllers::HttpResult;
|
||||
use crate::extractors::auth_extractor::AuthExtractor;
|
||||
use crate::services::{files_service, movements_service};
|
||||
use actix_web::HttpResponse;
|
||||
use crate::models::accounts::Account;
|
||||
use crate::services::{accounts_service, files_service, movements_service};
|
||||
use crate::utils::time_utils::time;
|
||||
use actix_web::{HttpResponse, web};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct GlobalStats {
|
||||
@ -23,3 +26,75 @@ pub async fn global(auth: AuthExtractor) -> HttpResult {
|
||||
total_files_size: files.iter().fold(0, |sum, m| sum + m.file_size as u64),
|
||||
}))
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
pub struct BalanceVariationQuery {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
interval: Option<u64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
start: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
end: Option<i64>,
|
||||
}
|
||||
|
||||
/// Statistic dataset entry
|
||||
#[derive(serde::Serialize, Debug, Clone)]
|
||||
struct StatEntry {
|
||||
time: i64,
|
||||
/// Account balances. Note: due to JSON limitation, we had to turn account id into strings
|
||||
#[serde(flatten)]
|
||||
balances: HashMap<String, f32>,
|
||||
}
|
||||
|
||||
impl StatEntry {
|
||||
fn init_first(time: i64, accounts: &[Account]) -> Self {
|
||||
Self {
|
||||
time,
|
||||
balances: accounts
|
||||
.iter()
|
||||
.map(|a| (a.id().0.to_string(), 0.0))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Accounts balance variation
|
||||
pub async fn balance_variation(
|
||||
auth: AuthExtractor,
|
||||
query: web::Query<BalanceVariationQuery>,
|
||||
) -> HttpResult {
|
||||
let start = query.start.unwrap_or((time() - 3600 * 24 * 30) as i64);
|
||||
let end = query.end.unwrap_or(time() as i64);
|
||||
let interval = query.interval.unwrap_or(3600 * 24);
|
||||
|
||||
let accounts = accounts_service::get_list_user(auth.user.id()).await?;
|
||||
let mut movements = movements_service::get_all_movements_user(auth.user.id()).await?;
|
||||
movements.sort_by(|a, b| a.time.cmp(&b.time));
|
||||
|
||||
let mut dataset = vec![];
|
||||
let mut stat_entry = StatEntry::init_first(start, &accounts);
|
||||
|
||||
// Iter movements
|
||||
for m in movements {
|
||||
if m.time > end {
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if it is time to go the the next entry
|
||||
while m.time > stat_entry.time {
|
||||
dataset.push(stat_entry.clone());
|
||||
stat_entry.time += interval as i64;
|
||||
}
|
||||
|
||||
let target_account_id = m.account_id().0.to_string();
|
||||
let old_amount = *stat_entry.balances.get(&target_account_id).unwrap_or(&0.0);
|
||||
stat_entry
|
||||
.balances
|
||||
.insert(target_account_id, old_amount + m.amount);
|
||||
}
|
||||
|
||||
// Final push
|
||||
dataset.push(stat_entry);
|
||||
|
||||
Ok(HttpResponse::Ok().json(dataset))
|
||||
}
|
||||
|
Reference in New Issue
Block a user