LCOV - code coverage report
Current view: top level - lib/charts/data - data_handler.dart Coverage Total Hit
Test: lcov.info Lines: 55.4 % 56 31
Test Date: 2026-11-14 15:04:37 Functions: - 0 0

            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              : }
        

Generated by: LCOV version 2.0-1