Skip to content

Commit

Permalink
Merge pull request #174 from lyskouski/NF-148
Browse files Browse the repository at this point in the history
[#146] [RF] Improve Forecast Chart. Apply critical fixes
  • Loading branch information
lyskouski authored Aug 21, 2023
2 parents 2181d86 + 7ac0a44 commit a241265
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 35 deletions.
20 changes: 13 additions & 7 deletions lib/_classes/math/bill_recalculation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import 'package:app_finance/_classes/structure/account_app_data.dart';
import 'package:app_finance/_classes/structure/bill_app_data.dart';
import 'package:app_finance/_classes/structure/budget_app_data.dart';

typedef LogFunction = void Function(String uuid, dynamic initial, double initialValue, double value, [String? ref]);

class BillRecalculation extends AbstractRecalculation {
BillAppData change;
BillAppData? initial;
Expand Down Expand Up @@ -33,15 +35,19 @@ class BillRecalculation extends AbstractRecalculation {
return initial!.hidden ? 0.0 : initial?.details;
}

BillRecalculation updateAccount(AccountAppData accountChange, AccountAppData? accountInitial) {
if (accountInitial != null &&
accountChange.uuid != accountInitial.uuid &&
accountInitial.createdAt.isBefore(initial!.createdAt)) {
accountInitial.details += super.exchange.reform(getPrevDelta(), initial?.currency, accountInitial.currency);
BillRecalculation updateAccount(AccountAppData accountChange, AccountAppData? accountInitial, LogFunction callback) {
double? diffDelta;
if (accountInitial != null && initial != null && accountChange.uuid != accountInitial.uuid) {
diffDelta = getPrevDelta();
callback(accountInitial.uuid!, initial, 0.0, diffDelta, initial!.uuid);
}
double delta = getStateDelta(accountInitial, accountChange);
callback(accountChange.uuid!, change, 0.0, -delta, change.uuid);
if (diffDelta != null && accountInitial!.createdAt.isBefore(initial!.createdAt)) {
accountInitial.details += super.exchange.reform(diffDelta, initial?.currency, accountInitial.currency);
}
if (accountChange.createdAt.isBefore(change.createdAt)) {
accountChange.details -=
super.exchange.reform(getStateDelta(accountInitial, accountChange), change.currency, accountChange.currency);
accountChange.details -= super.exchange.reform(delta, change.currency, accountChange.currency);
}
return this;
}
Expand Down
3 changes: 1 addition & 2 deletions lib/_classes/storage/app_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,11 @@ class AppData extends ChangeNotifier {
}
final rec = BillRecalculation(change: change, initial: initial)..exchange = Exchange(store: this);
if (currAccount != null) {
rec.updateAccount(currAccount, prevAccount);
rec.updateAccount(currAccount, prevAccount, addLog);
_data[AppDataType.accounts]?.add(change.account);
}
if (currBudget != null) {
rec.updateBudget(currBudget, prevBudget);
// addLog(currBudget.uuid, change, 0.0, change.details, change.uuid);
_data[AppDataType.budgets]?.add(change.category);
}
_set(AppDataType.bills, change);
Expand Down
44 changes: 32 additions & 12 deletions lib/_classes/storage/transaction_log.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ class TransactionLog with SharedPreferencesMixin {

static IV get code => IV.fromLength(8);

static bool _isLocked = false;

static File? _logFile;

static Future<File> _get(Directory path) async {
File file = File('${path.absolute.path}/terCAD/app-finance.log');
if (!file.existsSync()) {
Expand All @@ -35,14 +39,18 @@ class TransactionLog with SharedPreferencesMixin {
return file;
}

static Future<File> get _logFle async {
static Future<File> get logFle async {
if (_logFile != null) {
return Future.value(_logFile);
}
File? file;
try {
file = await _get(await getApplicationDocumentsDirectory());
final dir = await getApplicationDocumentsDirectory();
file = await _get(dir);
} catch (e) {
file = await _get(Directory.systemTemp);
}
return file;
return _logFile = file;
}

static String _formatBytes(int bytes) {
Expand All @@ -57,7 +65,7 @@ class TransactionLog with SharedPreferencesMixin {
if (kIsWeb) {
size = increment * 256;
} else {
size = (await _logFle).lengthSync();
size = (await logFle).lengthSync();
}
return _formatBytes(size);
}
Expand All @@ -71,20 +79,32 @@ class TransactionLog with SharedPreferencesMixin {
return self.getPreference(self.prefDoEncrypt) == prefIsEncrypted;
}

static Future<void> saveRaw(String line) async {
if (kIsWeb) {
TransactionLog().setPreference('log$increment', line);
} else {
(await _logFle).writeAsString("$line\n", mode: FileMode.append);
static void saveRaw(String line) {
int retrial = 1;
while (_isLocked && retrial < 1000) {
sleep(Duration(microseconds: retrial * 10));
retrial++;
}
_isLocked = true;
try {
if (kIsWeb) {
TransactionLog().setPreference('log$increment', line);
} else {
_logFile!.writeAsStringSync("$line\n", mode: FileMode.append);
}
_isLocked = false;
} catch (e) {
_isLocked = false;
rethrow;
}
}

static Future<void> save(dynamic content, [bool isDirect = false]) async {
static void save(dynamic content, [bool isDirect = false]) async {
String line = content.toString();
if (!isDirect && doEncrypt()) {
line = salt.encrypt(line, iv: code).base64;
}
await saveRaw(line);
saveRaw(line);
increment++;
}

Expand Down Expand Up @@ -129,7 +149,7 @@ class TransactionLog with SharedPreferencesMixin {
if (kIsWeb) {
lines = _loadWeb();
} else {
lines = (await _logFle).openRead().transform(utf8.decoder).transform(const LineSplitter());
lines = (await logFle).openRead().transform(utf8.decoder).transform(const LineSplitter());
}
await for (var line in lines) {
if (!kIsWeb) {
Expand Down
19 changes: 12 additions & 7 deletions lib/charts/painter/forecast_chart_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,19 +72,24 @@ class ForecastChartPainter extends CustomPainter {
}

double _paint(Canvas canvas, List<Offset> scope, Size size, Color color, [double total = 0.0]) {
if (scope.isEmpty) {
return 0.0;
}
Offset startPoint;
[_, startPoint] = _bind(scope.first, size, total);
int i = 0;
Offset point;
[i, point] = _paintDots(canvas, scope, size, color, total);
int third = i ~/ 3;
Offset startBezier = _getValue(_getMedian(scope.sublist(0, third)), size, total);
total += _sumY(scope.sublist(0, third));
Offset middleBezier = _getValue(_getMedian(scope.sublist(third, 2 * third)), size, total);
total += _sumY(scope.sublist(third, 2 * third));
Offset endBezier = _getValue(_getMedian(scope.sublist(2 * third, i)), size, total);
_paintCurve(canvas, startPoint, startBezier, middleBezier, color);
_paintCurve(canvas, middleBezier, endBezier, point, color);
if (third > 0) {
Offset startBezier = _getValue(_getMedian(scope.sublist(0, third)), size, total);
total += _sumY(scope.sublist(0, third));
Offset middleBezier = _getValue(_getMedian(scope.sublist(third, 2 * third)), size, total);
total += _sumY(scope.sublist(third, 2 * third));
Offset endBezier = _getValue(_getMedian(scope.sublist(2 * third, i)), size, total);
_paintCurve(canvas, startPoint, startBezier, middleBezier, color);
_paintCurve(canvas, middleBezier, endBezier, point, color);
}
return total + point.dy;
}

Expand Down
5 changes: 3 additions & 2 deletions lib/widgets/settings/import_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,13 @@ class ImportTabState extends State<ImportTab> with SharedPreferencesMixin {
currency: _getCurrency(line),
hidden: false,
));
await TransactionLog.save(newItem);
TransactionLog.save(newItem);
} catch (e) {
setState(() => errorMessage.writeln('[$i / ${fileContent!.length}] ${e.toString()}'));
}
}
await state.restate();
setState(() => fileContent = null);
}

Future<void> wrapCall(Function callback) async {
Expand Down Expand Up @@ -188,7 +189,7 @@ class ImportTabState extends State<ImportTab> with SharedPreferencesMixin {
));
}
_cache[type]![line[index]] = newItem.uuid;
await TransactionLog.save(newItem);
TransactionLog.save(newItem);
return newItem.uuid;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/settings/recover_tab.dart
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class SyncTabState extends State<RecoverTab> {
List<int> uint8list = await client.read(path.text);
List<String> lines = String.fromCharCodes(uint8list).split('\n');
for (String line in lines) {
await TransactionLog.save(line, true);
TransactionLog.save(line, true);
}
setState(() {
inProgress = false;
Expand Down
9 changes: 6 additions & 3 deletions test/e2e/e2e_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,17 @@ import '_steps/file_runner.dart';
import '_steps/screen_capture.dart';

void main() {
TestWidgetsFlutterBinding.ensureInitialized();
ScreenCapture.enableScreenCapture();

Iterable<File> features = Directory('./test/e2e')
.listSync(recursive: true)
.where((entity) => entity is File && entity.path.endsWith('.feature'))
.cast<File>();

setUpAll(() {
TestWidgetsFlutterBinding.ensureInitialized();
ScreenCapture.enableScreenCapture();
PumpMain.cleanUpData();
});

group('Behavioral Tests', () {
for (var file in features) {
testWidgets(file.path, (WidgetTester tester) async {
Expand Down
20 changes: 20 additions & 0 deletions test/pump_main.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
// Copyright 2023 The terCAD team. All rights reserved.
// Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.

import 'dart:io';

import 'package:app_finance/_classes/storage/app_data.dart';
import 'package:app_finance/_classes/herald/app_locale.dart';
import 'package:app_finance/_classes/herald/app_theme.dart';
import 'package:app_finance/_classes/gen/generate_with_method_setters.dart';
import 'package:app_finance/_mixins/shared_preferences_mixin.dart';
import 'package:app_finance/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:provider/provider.dart';
Expand All @@ -21,8 +24,11 @@ import 'pump_main.mocks.dart';
import 'pump_main.wrapper.dart';

class PumpMain {
static const path = './coverage/data';

static init(WidgetTester tester, [bool isIntegration = false]) async {
final pumpMain = PumpMain();
wrapProvider(tester, 'plugins.flutter.io/path_provider', '$path/${UniqueKey()}');
await pumpMain.initPref(isIntegration);
await pumpMain.initMain(tester, isIntegration);
}
Expand All @@ -35,6 +41,20 @@ class PumpMain {
return appData;
}

static void cleanUpData() {
final dir = Directory(path);
if (dir.existsSync()) {
dir.deleteSync(recursive: true);
}
}

static void wrapProvider(WidgetTester tester, String channel, String output) {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
MethodChannel(channel),
(MethodCall methodCall) => Future.value(output),
);
}

Future<void> initPref(bool isIntegration) async {
if (isIntegration) {
SharedPreferencesMixin.pref = await SharedPreferences.getInstance();
Expand Down
3 changes: 2 additions & 1 deletion test/unit/_classes/data/bill_recalculation_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ void main() {
final change = AccountAppData(title: '', type: '')
..uuid = v.changeAccount.uuid
..createdAtFormatted = v.changeAccount.createdAtFormatted;
mock.updateAccount(change, initial);
fn(String a, dynamic b, double c, double d, [String? e]) => null;
mock.updateAccount(change, initial, fn);
expect(initial.details, v.result.initialAccountDetails);
expect(change.details, v.result.changeAccountDetails);
});
Expand Down

0 comments on commit a241265

Please sign in to comment.