Compare commits
3 Commits
a3558b33e6
...
b6fef8d42f
Author | SHA1 | Date |
---|---|---|
Andrew Lalis | b6fef8d42f | |
Andrew Lalis | e08c528b71 | |
Andrew Lalis | 28002fd32d |
|
@ -2,6 +2,7 @@ package com.andrewlalis.perfin.control;
|
|||
|
||||
import com.andrewlalis.javafx_scene_router.RouteSelectionListener;
|
||||
import com.andrewlalis.perfin.model.Profile;
|
||||
import com.andrewlalis.perfin.view.component.module.TotalAssetsGraphModule;
|
||||
import com.andrewlalis.perfin.view.component.module.*;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Bounds;
|
||||
|
@ -30,7 +31,10 @@ public class DashboardController implements RouteSelectionListener {
|
|||
var m4 = new VendorSpendChartModule(modulesFlowPane);
|
||||
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
|
||||
|
|
|
@ -9,6 +9,7 @@ import javafx.application.Platform;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
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,
|
||||
* 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.
|
||||
*/
|
||||
default CompletableFuture<List<MoneyValue>> getCombinedAccountBalances() {
|
||||
default CompletableFuture<List<MoneyValue>> getCombinedAccountBalances(Instant timestamp) {
|
||||
return mapRepoAsync(AccountRepository.class, repo -> {
|
||||
List<Account> accounts = repo.findAll(PageRequest.unpaged()).items();
|
||||
Map<Currency, BigDecimal> totals = new HashMap<>();
|
||||
for (var account : accounts) {
|
||||
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();
|
||||
totals.put(account.getCurrency(), currencyTotal.add(accountBalance));
|
||||
}
|
||||
|
@ -131,4 +133,8 @@ public interface DataSource {
|
|||
return values;
|
||||
});
|
||||
}
|
||||
|
||||
default CompletableFuture<List<MoneyValue>> getCombinedAccountBalances() {
|
||||
return getCombinedAccountBalances(Instant.now());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ public abstract class PieChartModule extends DashboardModule {
|
|||
|
||||
this.timeRangeChoiceBox.getItems().addAll(RANGE_CHOICES);
|
||||
this.timeRangeChoiceBox.getSelectionModel().select("All Time");
|
||||
this.currencyChoiceBox.managedProperty().bind(this.currencyChoiceBox.visibleProperty());
|
||||
|
||||
PieChart chart = new PieChart(chartData);
|
||||
chart.setLegendVisible(false);
|
||||
|
@ -68,9 +69,7 @@ public abstract class PieChartModule extends DashboardModule {
|
|||
chartData.clear();
|
||||
}
|
||||
});
|
||||
timeRangeChoiceBox.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
renderChart();
|
||||
});
|
||||
timeRangeChoiceBox.valueProperty().addListener((observable, oldValue, newValue) -> renderChart());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -136,6 +135,7 @@ public abstract class PieChartModule extends DashboardModule {
|
|||
} else {
|
||||
currencyChoiceBox.getSelectionModel().selectFirst();
|
||||
}
|
||||
currencyChoiceBox.setVisible(orderedCurrencies.size() > 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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.validation to javafx.fxml;
|
||||
exports com.andrewlalis.perfin.model.history to javafx.graphics;
|
||||
opens com.andrewlalis.perfin.view.component.module to javafx.fxml;
|
||||
}
|
Loading…
Reference in New Issue