module analytics.balances; import handy_http_primitives; import std.datetime; import std.stdio; import std.path; import std.file; import slf4d; import asdf; import profile.data; import profile.model; import account.data; import account.model; import account.service; import analytics.util; struct BalanceSnapshot { long balance; string timestamp; } struct AccountBalanceTimeSeries { ulong accountId; string currencyCode; BalanceSnapshot[] balanceTimeSeries; } struct TotalBalanceTimeSeries { string currencyCode; BalanceSnapshot[] balanceTimeSeries; } struct BalanceTimeSeriesAnalytics { AccountBalanceTimeSeries[] accounts; TotalBalanceTimeSeries[] totals; } void computeAccountBalanceTimeSeries(Profile profile, ProfileRepository profileRepo) { ProfileDataSource ds = profileRepo.getDataSource(profile); Account[] accounts = ds.getAccountRepository().findAll(); // Initialize the data structure that'll store the analytics info. BalanceTimeSeriesAnalytics data; foreach (account; accounts) { data.accounts ~= AccountBalanceTimeSeries( account.id, account.currency.code.idup, [] ); } foreach (timestamp; generateTimeSeriesTimestamps(days(1), 365)) { // Compute the balance of each account at this timestamp. foreach (idx, account; accounts) { auto balance = getBalance(ds, account.id, timestamp); if (!balance.isNull) { data.accounts[idx].balanceTimeSeries ~= BalanceSnapshot( balance.value, timestamp.toISOExtString() ); } } // Compute total balances for this timestamp. auto totalBalances = getTotalBalanceForAllAccounts(ds, timestamp); foreach (bal; totalBalances) { // Assign the balance to one of our running totals. bool currencyFound = false; foreach (ref currencyTotal; data.totals) { if (currencyTotal.currencyCode == bal.currency.code) { currencyTotal.balanceTimeSeries ~= BalanceSnapshot( bal.balance, timestamp.toISOExtString() ); currencyFound = true; break; } } if (!currencyFound) { data.totals ~= TotalBalanceTimeSeries( bal.currency.code.idup, [BalanceSnapshot(bal.balance, timestamp.toISOExtString())] ); } } } ds.doTransaction(() { ds.getPropertiesRepository().deleteAllByPrefix("analytics"); ds.getPropertiesRepository().setProperty( "analytics.balanceTimeSeries", serializeToJsonPretty(data) ); }); infoF!"Computed account balance analytics for user %s, profile %s."( profile.username, profile.name ); }