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