Added basic implementation of a total assets graph.
This commit is contained in:
parent
e08c528b71
commit
b6fef8d42f
|
@ -2,6 +2,7 @@ package com.andrewlalis.perfin.control;
|
||||||
|
|
||||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||||
import com.andrewlalis.perfin.model.Profile;
|
import com.andrewlalis.perfin.model.Profile;
|
||||||
|
import com.andrewlalis.perfin.view.component.module.TotalAssetsGraphModule;
|
||||||
import com.andrewlalis.perfin.view.component.module.*;
|
import com.andrewlalis.perfin.view.component.module.*;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.geometry.Bounds;
|
import javafx.geometry.Bounds;
|
||||||
|
@ -30,7 +31,10 @@ public class DashboardController implements RouteSelectionListener {
|
||||||
var m4 = new VendorSpendChartModule(modulesFlowPane);
|
var m4 = new VendorSpendChartModule(modulesFlowPane);
|
||||||
m4.columnsProperty.set(2);
|
m4.columnsProperty.set(2);
|
||||||
|
|
||||||
modulesFlowPane.getChildren().addAll(accountsModule, transactionsModule, m3, m4);
|
var m5 = new TotalAssetsGraphModule(modulesFlowPane);
|
||||||
|
m5.columnsProperty.set(1);
|
||||||
|
|
||||||
|
modulesFlowPane.getChildren().addAll(accountsModule, transactionsModule, m3, m4, m5);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,6 +9,7 @@ import javafx.application.Platform;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
@ -111,15 +112,16 @@ public interface DataSource {
|
||||||
/**
|
/**
|
||||||
* Gets a list of combined total assets for each currency that's tracked,
|
* Gets a list of combined total assets for each currency that's tracked,
|
||||||
* ordered with highest assets first.
|
* ordered with highest assets first.
|
||||||
|
* @param timestamp The timestamp at which to get the balance.
|
||||||
* @return A future that resolves to the list of amounts for each currency.
|
* @return A future that resolves to the list of amounts for each currency.
|
||||||
*/
|
*/
|
||||||
default CompletableFuture<List<MoneyValue>> getCombinedAccountBalances() {
|
default CompletableFuture<List<MoneyValue>> getCombinedAccountBalances(Instant timestamp) {
|
||||||
return mapRepoAsync(AccountRepository.class, repo -> {
|
return mapRepoAsync(AccountRepository.class, repo -> {
|
||||||
List<Account> accounts = repo.findAll(PageRequest.unpaged()).items();
|
List<Account> accounts = repo.findAll(PageRequest.unpaged()).items();
|
||||||
Map<Currency, BigDecimal> totals = new HashMap<>();
|
Map<Currency, BigDecimal> totals = new HashMap<>();
|
||||||
for (var account : accounts) {
|
for (var account : accounts) {
|
||||||
BigDecimal currencyTotal = totals.computeIfAbsent(account.getCurrency(), c -> BigDecimal.ZERO);
|
BigDecimal currencyTotal = totals.computeIfAbsent(account.getCurrency(), c -> BigDecimal.ZERO);
|
||||||
BigDecimal accountBalance = repo.deriveCurrentBalance(account.id);
|
BigDecimal accountBalance = repo.deriveBalance(account.id, timestamp);
|
||||||
if (account.getType() == AccountType.CREDIT_CARD) accountBalance = accountBalance.negate();
|
if (account.getType() == AccountType.CREDIT_CARD) accountBalance = accountBalance.negate();
|
||||||
totals.put(account.getCurrency(), currencyTotal.add(accountBalance));
|
totals.put(account.getCurrency(), currencyTotal.add(accountBalance));
|
||||||
}
|
}
|
||||||
|
@ -131,4 +133,8 @@ public interface DataSource {
|
||||||
return values;
|
return values;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default CompletableFuture<List<MoneyValue>> getCombinedAccountBalances() {
|
||||||
|
return getCombinedAccountBalances(Instant.now());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package com.andrewlalis.perfin.view.component.module;
|
||||||
|
|
||||||
|
import com.andrewlalis.perfin.model.Profile;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.chart.*;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A module for visualizing the total asset value in the user's profile over
|
||||||
|
* a configurable period of time.
|
||||||
|
*/
|
||||||
|
public class TotalAssetsGraphModule extends DashboardModule {
|
||||||
|
private final ObservableList<XYChart.Data<String, Number>> totalAssetDataPoints = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
public TotalAssetsGraphModule(Pane parent) {
|
||||||
|
super(parent);
|
||||||
|
Axis<String> xAxis = new CategoryAxis();
|
||||||
|
Axis<Number> yAxis = new NumberAxis();
|
||||||
|
|
||||||
|
LineChart<String, Number> chart = new LineChart<>(xAxis, yAxis, FXCollections.observableArrayList(
|
||||||
|
new XYChart.Series<>("Total Assets", totalAssetDataPoints)
|
||||||
|
));
|
||||||
|
chart.setLegendVisible(false);
|
||||||
|
this.getChildren().add(new ModuleHeader(
|
||||||
|
"Total Assets over Time"
|
||||||
|
));
|
||||||
|
this.getChildren().add(chart);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refreshContents() {
|
||||||
|
totalAssetDataPoints.clear();
|
||||||
|
String[] dateLabels = new String[12];
|
||||||
|
double[] values = new double[12];
|
||||||
|
CompletableFuture<?>[] futures = new CompletableFuture[12];
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
final int idx = i;
|
||||||
|
Instant timestamp = Instant.now().minus((12 - i - 1) * 30, ChronoUnit.DAYS);
|
||||||
|
dateLabels[i] = LocalDate.from(timestamp.atZone(ZoneId.systemDefault())).toString();
|
||||||
|
futures[i] = Profile.getCurrent().dataSource().getCombinedAccountBalances(timestamp)
|
||||||
|
.thenAccept(moneyValues -> {
|
||||||
|
values[idx] = moneyValues.getFirst().amount().doubleValue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
CompletableFuture.allOf(futures).thenRun(() -> {
|
||||||
|
List<XYChart.Data<String, Number>> dataPoints = new ArrayList<>(12);
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
dataPoints.add(new XYChart.Data<>(dateLabels[i], values[i]));
|
||||||
|
}
|
||||||
|
Platform.runLater(() -> totalAssetDataPoints.addAll(dataPoints));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,4 +20,5 @@ module com.andrewlalis.perfin {
|
||||||
opens com.andrewlalis.perfin.view.component to javafx.fxml;
|
opens com.andrewlalis.perfin.view.component to javafx.fxml;
|
||||||
opens com.andrewlalis.perfin.view.component.validation to javafx.fxml;
|
opens com.andrewlalis.perfin.view.component.validation to javafx.fxml;
|
||||||
exports com.andrewlalis.perfin.model.history to javafx.graphics;
|
exports com.andrewlalis.perfin.model.history to javafx.graphics;
|
||||||
|
opens com.andrewlalis.perfin.view.component.module to javafx.fxml;
|
||||||
}
|
}
|
Loading…
Reference in New Issue