Line data Source code
1 : // Copyright 2023 The terCAD team. All rights reserved.
2 : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
3 :
4 : import 'dart:collection';
5 :
6 : import 'package:app_finance/_classes/herald/app_start_of_month.dart';
7 : import 'package:app_finance/_classes/structure/bill_app_data.dart';
8 : import 'package:app_finance/_classes/structure/budget_app_data.dart';
9 : import 'package:app_finance/_classes/structure/currency/exchange.dart';
10 : import 'package:app_finance/_classes/structure/interface_app_data.dart';
11 : import 'package:app_finance/_classes/structure/transaction_log_data.dart';
12 : import 'package:app_finance/_ext/date_time_ext.dart';
13 : import 'package:app_finance/charts/interface/ohlc_data.dart';
14 : import 'package:material_ui/material_ui.dart';
15 :
16 : typedef DateCallback = double Function(InterfaceAppData item);
17 :
18 : class DataHandler {
19 0 : static double countBudgetTotal(List<InterfaceAppData> scope, {required Exchange exchange}) {
20 0 : final currency = exchange.getDefaultCurrency();
21 0 : return scope.fold(0.0, (v, e) => v + exchange.reform((e as BudgetAppData).amountLimit, e.currency, currency));
22 : }
23 :
24 0 : static List<Offset> getAmountGroupedByCategory(List<BillAppData> scope, List<BudgetAppData> budgets,
25 : {required Exchange exchange}) {
26 0 : final idx = budgets.map((e) => e.uuid).toList();
27 0 : fn(item) => idx.indexWhere((uuid) => uuid == item.category).toDouble();
28 0 : return _getGroupedAmount(scope, fn, exchange: exchange);
29 : }
30 :
31 0 : static List<Offset> getAmountGroupedByMonth(List<InterfaceAppData> scope, {required Exchange exchange}) {
32 0 : fn(item) => (item.createdAt as DateTime).getStartingDay(AppStartOfMonth.get()).millisecondsSinceEpoch.toDouble();
33 0 : return _getGroupedAmount(scope, fn, exchange: exchange);
34 : }
35 :
36 0 : static List<Offset> getAmountGroupedByDate(List<InterfaceAppData> scope, {required Exchange exchange}) {
37 0 : fn(item) =>
38 0 : DateTime(item.createdAt.year, item.createdAt.month, item.createdAt.day).millisecondsSinceEpoch.toDouble();
39 0 : return _getGroupedAmount(scope, fn, exchange: exchange);
40 : }
41 :
42 0 : static List<Offset> _getGroupedAmount(List<InterfaceAppData> scope, DateCallback fn, {required Exchange exchange}) {
43 0 : final data = SplayTreeMap<dynamic, List<double>>();
44 0 : final currency = exchange.getDefaultCurrency();
45 0 : for (final item in scope) {
46 0 : final actual = fn(item);
47 0 : if (actual == -1) {
48 : continue;
49 : }
50 0 : if (data[actual] == null) {
51 0 : data[actual] = [];
52 : }
53 0 : data[actual]!.add(exchange.reform(item.details, item.currency, currency));
54 : }
55 0 : final List<Offset> result = [];
56 0 : data.forEach((key, value) => result.add(Offset(key, value.fold(0.0, (v, e) => v + e))));
57 : return result;
58 : }
59 :
60 1 : static List<OhlcData> generateOhlcSummary(List<List<TransactionLogData>?> scope,
61 : {required Exchange exchange, DateTime? cut}) {
62 1 : final data = scope.firstOrNull;
63 3 : for (int i = 1; i < scope.length; i++) {
64 1 : if (scope[i] == null || data == null) {
65 : continue;
66 : }
67 4 : data.addAll(scope[i]!.where((e) => cut == null || e.timestamp.isAfter(cut)));
68 : }
69 1 : if (data != null && data.isNotEmpty) {
70 5 : data.sort((a, b) => a.timestamp.compareTo(b.timestamp));
71 1 : return _generateOhlc(data, exchange);
72 : }
73 1 : return [];
74 : }
75 :
76 1 : static List<OhlcData> _generateOhlc(List<TransactionLogData> scope, Exchange exchange) {
77 1 : final currency = exchange.getDefaultCurrency();
78 1 : final result = SplayTreeMap<DateTime, OhlcData>();
79 : double min = 0;
80 : double close = 0;
81 3 : for (int i = 0; i < scope.length; i++) {
82 13 : final key = DateTime(scope[i].timestamp.year, scope[i].timestamp.month, (scope[i].timestamp.day / 7).floor() * 6);
83 5 : final value = exchange.reform(scope[i].delta, scope[i].currency, currency);
84 1 : if (!result.containsKey(key)) {
85 5 : result[key] = OhlcData(date: key, open: close, close: value + close, high: value + close, low: value + close);
86 : } else {
87 3 : result[key]!.close += value;
88 : }
89 5 : if (result[key]!.close > result[key]!.high) {
90 4 : result[key]!.high = result[key]!.close;
91 : }
92 5 : if (result[key]!.close < result[key]!.low) {
93 4 : result[key]!.low = result[key]!.close;
94 : }
95 3 : if (min > result[key]!.close) {
96 2 : min = result[key]!.close;
97 : }
98 2 : close = result[key]!.close;
99 : }
100 2 : for (OhlcData data in result.values) {
101 2 : data.open -= min;
102 2 : data.close -= min;
103 2 : data.high -= min;
104 2 : data.low -= min;
105 : }
106 2 : return result.values.toList();
107 : }
108 : }
|