From e0087ccbb63ec6aa6d7b245a398defd3b5689d9c Mon Sep 17 00:00:00 2001
From: Robert Goldmann <deadlocker@gmx.de>
Date: Sun, 23 Apr 2017 15:20:06 +0200
Subject: [PATCH] added BarChart --> series 1 = income per month, series 2 =
 payments per month

start- and enddate comboboxes are only dummies
---
 .../budgetmaster/logic/MonthInOutSum.java     | 60 +++++++++++++
 .../budgetmaster/logic/ServerConnection.java  | 24 +++++
 .../chartGenerators/BarChartGenerator.java    | 88 +++++++++++++++++++
 .../budgetmaster/ui/ChartController.java      | 37 +++++++-
 .../deadlocker8/budgetmaster/ui/ChartTab.fxml | 51 ++++++++++-
 .../server/SparkServer.java                   |  2 +
 .../server/charts/MonthInOutSum.java          | 78 ++++++++++++++++
 7 files changed, 336 insertions(+), 4 deletions(-)
 create mode 100644 src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java
 create mode 100644 src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java
 create mode 100644 src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java

diff --git a/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java b/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java
new file mode 100644
index 000000000..df77968c8
--- /dev/null
+++ b/src/de/deadlocker8/budgetmaster/logic/MonthInOutSum.java
@@ -0,0 +1,60 @@
+package de.deadlocker8.budgetmaster.logic;
+
+import org.joda.time.DateTime;
+
+public class MonthInOutSum
+{
+	private int month;
+	private int year;
+	private int budgetIN;
+	private int budgetOUT;
+	
+	public MonthInOutSum(int month, int year, int budgetIN, int budgetOUT)
+	{
+		this.month = month;
+		this.year = year;
+		this.budgetIN = budgetIN;
+		this.budgetOUT = budgetOUT;
+	}
+	
+	public DateTime getDate()
+	{
+		return DateTime.now().withYear(year).withMonthOfYear(month).withDayOfMonth(1);
+	}
+	
+	public int getMonth()
+	{
+		return month;
+	}
+
+	public int getYear()
+	{
+		return year;
+	}
+
+	public int getBudgetIN()
+	{
+		return budgetIN;
+	}
+
+	public void setBudgetIN(int budgetIN)
+	{
+		this.budgetIN = budgetIN;
+	}
+
+	public int getBudgetOUT()
+	{
+		return budgetOUT;
+	}
+
+	public void setBudgetOUT(int budgetOUT)
+	{
+		this.budgetOUT = budgetOUT;
+	}
+
+	@Override
+	public String toString()
+	{
+		return "MonthInOutSum [month=" + month + ", year=" + year + ", budgetIN=" + budgetIN + ", budgetOUT=" + budgetOUT + "]";
+	}
+}
\ No newline at end of file
diff --git a/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java b/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java
index 5b807553a..64e7cb420 100644
--- a/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java
+++ b/src/de/deadlocker8/budgetmaster/logic/ServerConnection.java
@@ -348,4 +348,28 @@ public class ServerConnection
 			return null;
 		}
 	}
+	
+	public ArrayList<MonthInOutSum> getMonthInOutSum(DateTime startDate, DateTime endDate) throws Exception
+	{
+		URL url = new URL(settings.getUrl() + "/charts/monthInOutSum?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + 
+				"&startDate=" + startDate.toString("yyyy-MM-dd") + 
+				"&endDate=" + endDate.toString("yyyy-MM-dd"));	
+		HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection();
+		httpsCon.setDoOutput(true);
+		httpsCon.setRequestMethod("GET");
+
+		if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK)
+		{
+			String result = Read.getStringFromInputStream(httpsCon.getInputStream());
+			// required by GSON
+			Type listType = new TypeToken<ArrayList<MonthInOutSum>>()
+			{
+			}.getType();
+			return gson.fromJson(result, listType);
+		}
+		else
+		{
+			return null;
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java
new file mode 100644
index 000000000..c8918cc82
--- /dev/null
+++ b/src/de/deadlocker8/budgetmaster/logic/chartGenerators/BarChartGenerator.java
@@ -0,0 +1,88 @@
+package de.deadlocker8.budgetmaster.logic.chartGenerators;
+
+import java.util.ArrayList;
+
+import de.deadlocker8.budgetmaster.logic.Helpers;
+import de.deadlocker8.budgetmaster.logic.MonthInOutSum;
+import javafx.event.EventHandler;
+import javafx.geometry.Point2D;
+import javafx.scene.Node;
+import javafx.scene.chart.BarChart;
+import javafx.scene.chart.CategoryAxis;
+import javafx.scene.chart.NumberAxis;
+import javafx.scene.chart.XYChart;
+import javafx.scene.control.Tooltip;
+import javafx.scene.input.MouseEvent;
+
+public class BarChartGenerator
+{
+	private ArrayList<MonthInOutSum> monthInOutSums;
+	private String currency;
+
+	public BarChartGenerator(ArrayList<MonthInOutSum> monthInOutSums, String currency)
+	{
+		this.monthInOutSums = monthInOutSums;
+		this.currency = currency;
+	}
+
+	public BarChart<String, Number> generate()
+	{
+		final CategoryAxis xAxis = new CategoryAxis();
+		final NumberAxis yAxis = new NumberAxis();
+		final BarChart<String, Number> generatedChart = new BarChart<>(xAxis, yAxis);
+		generatedChart.setTitle(null);
+
+		xAxis.setLabel("");
+		yAxis.setLabel("Summe in " + currency);
+
+		XYChart.Series<String, Number> seriesIN = new XYChart.Series<String, Number>();
+		seriesIN.setName("Einnahmen");
+		XYChart.Series<String, Number> seriesOUT = new XYChart.Series<String, Number>();
+		seriesOUT.setName("Ausgaben");
+
+		for(MonthInOutSum currentItem : monthInOutSums)
+		{
+			String label = currentItem.getDate().toString("MMMM");
+
+			seriesIN.getData().add(new XYChart.Data<String, Number>(label, currentItem.getBudgetIN()/100.0));
+			seriesOUT.getData().add(new XYChart.Data<String, Number>(label, currentItem.getBudgetOUT()/100.0));
+		}
+
+		generatedChart.getData().add(seriesIN);
+		generatedChart.getData().add(seriesOUT);
+
+		generatedChart.setLegendVisible(true);
+		
+		// add tooltip to every segment
+		generatedChart.getData().stream().forEach(tool -> {
+			for(XYChart.Data<String, Number> data : tool.getData())
+			{
+				Tooltip tooltip = new Tooltip();
+
+				tooltip.setText(Helpers.NUMBER_FORMAT.format(data.getYValue()).replace(".", ",") + currency);
+				Tooltip.install(tool.getNode(), tooltip);
+				Node node = data.getNode();
+				node.setOnMouseEntered(new EventHandler<MouseEvent>()
+				{
+					@Override
+					public void handle(MouseEvent event)
+					{
+						Point2D p = node.localToScreen(event.getX() + 5, event.getY() + 7);
+						tooltip.show(node, p.getX(), p.getY());
+					}
+				});
+				node.setOnMouseExited(new EventHandler<MouseEvent>()
+				{
+
+					@Override
+					public void handle(MouseEvent event)
+					{
+						tooltip.hide();
+					}
+				});
+			}
+		});
+
+		return generatedChart;
+	}
+}
\ No newline at end of file
diff --git a/src/de/deadlocker8/budgetmaster/ui/ChartController.java b/src/de/deadlocker8/budgetmaster/ui/ChartController.java
index 7947993a7..6a4113d9a 100644
--- a/src/de/deadlocker8/budgetmaster/ui/ChartController.java
+++ b/src/de/deadlocker8/budgetmaster/ui/ChartController.java
@@ -6,11 +6,14 @@ import java.util.ArrayList;
 import org.joda.time.DateTime;
 
 import de.deadlocker8.budgetmaster.logic.CategoryInOutSum;
+import de.deadlocker8.budgetmaster.logic.MonthInOutSum;
 import de.deadlocker8.budgetmaster.logic.ServerConnection;
+import de.deadlocker8.budgetmaster.logic.chartGenerators.BarChartGenerator;
 import de.deadlocker8.budgetmaster.logic.chartGenerators.PieChartGenerator;
 import fontAwesome.FontIcon;
 import fontAwesome.FontIconType;
 import javafx.fxml.FXML;
+import javafx.scene.chart.BarChart;
 import javafx.scene.control.Accordion;
 import javafx.scene.control.Button;
 import javafx.scene.control.DateCell;
@@ -84,8 +87,35 @@ public class ChartController implements Refreshable
 			hboxChartCategories.getChildren().add(generator.generate());
 			generator = new PieChartGenerator("Ausgaben nach Kategorien", sums, false, controller.getSettings().getCurrency());
 			hboxChartCategories.getChildren().add(generator.generate());
+		}
+		catch(Exception e)
+		{
+			Logger.error(e);
+			//TODO
+			//controller.showConnectionErrorAlert(e.getMessage());
+		}
+	}
+	
+	public void chartMonthShow()
+	{
+		//DEBUG get date from comboboxes
+		DateTime startDate = controller.getCurrentDate().withMonthOfYear(1);
+		DateTime endDate =  controller.getCurrentDate().withMonthOfYear(12);	
+		
+		try
+		{
+			ServerConnection connection = new ServerConnection(controller.getSettings());
+			//DEBUG
+			ArrayList<MonthInOutSum> sums = connection.getMonthInOutSum(startDate, endDate);
 
-			accordion.setExpandedPane(accordion.getPanes().get(0));
+			anchorPaneChartMonth.getChildren().clear();
+			BarChartGenerator generator = new BarChartGenerator(sums, controller.getSettings().getCurrency());
+			BarChart<String, Number> chartMonth = generator.generate();
+			anchorPaneChartMonth.getChildren().add(chartMonth);
+			AnchorPane.setTopAnchor(chartMonth, 0.0);
+			AnchorPane.setRightAnchor(chartMonth, 0.0);
+			AnchorPane.setBottomAnchor(chartMonth, 0.0);
+			AnchorPane.setLeftAnchor(chartMonth, 0.0);
 		}
 		catch(Exception e)
 		{
@@ -106,5 +136,10 @@ public class ChartController implements Refreshable
 		datePickerEnd.setValue(endDate);
 		
 		chartCategoriesShow();
+		
+		//chart month
+		chartMonthShow();
+		
+		accordion.setExpandedPane(accordion.getPanes().get(0));
 	}
 }
\ No newline at end of file
diff --git a/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml b/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml
index b92658ac8..41854a762 100644
--- a/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml
+++ b/src/de/deadlocker8/budgetmaster/ui/ChartTab.fxml
@@ -3,6 +3,7 @@
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Accordion?>
 <?import javafx.scene.control.Button?>
+<?import javafx.scene.control.ComboBox?>
 <?import javafx.scene.control.DatePicker?>
 <?import javafx.scene.control.Label?>
 <?import javafx.scene.control.TitledPane?>
@@ -66,12 +67,56 @@
                </font>
           </TitledPane>
           <TitledPane animated="false" text="Einnahmen/Ausgaben pro Monat">
-            <content>
-              <AnchorPane fx:id="anchorPaneChartMonth" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" />
-            </content>
                <font>
                   <Font name="System Bold" size="12.0" />
                </font>
+               <content>
+                  <VBox spacing="20.0">
+                     <children>
+                        <HBox alignment="CENTER" prefHeight="8.0" prefWidth="750.0">
+                           <children>
+                              <HBox alignment="CENTER_RIGHT" spacing="10.0" HBox.hgrow="ALWAYS">
+                                 <children>
+                                    <Label text="Von:">
+                                       <font>
+                                          <Font name="System Bold" size="16.0" />
+                                       </font>
+                                    </Label>
+                                    <ComboBox prefHeight="25.0" prefWidth="100.0" />
+                                    <ComboBox prefWidth="70.0" />
+                                 </children>
+                                 <HBox.margin>
+                                    <Insets right="15.0" />
+                                 </HBox.margin>
+                              </HBox>
+                              <HBox alignment="CENTER_LEFT" spacing="10.0" HBox.hgrow="ALWAYS">
+                                 <children>
+                                    <Label text="Bis:">
+                                       <font>
+                                          <Font name="System Bold" size="16.0" />
+                                       </font>
+                                    </Label>
+                                    <ComboBox prefWidth="100.0" />
+                                    <ComboBox prefWidth="70.0" />
+                                    <Button fx:id="buttonChartCategoriesShow1" mnemonicParsing="false" onAction="#chartCategoriesShow">
+                                       <font>
+                                          <Font name="System Bold" size="12.0" />
+                                       </font>
+                                       <HBox.margin>
+                                          <Insets left="15.0" />
+                                       </HBox.margin>
+                                    </Button>
+                                 </children>
+                                 <HBox.margin>
+                                    <Insets left="15.0" />
+                                 </HBox.margin>
+                              </HBox>
+                           </children>
+                        </HBox>
+                        <AnchorPane fx:id="anchorPaneChartMonth" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS" />
+                     </children>
+                  </VBox>
+               </content>
           </TitledPane>
         </panes>
       </Accordion>
diff --git a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java
index 67e52c46a..b5b450a50 100644
--- a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java
+++ b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java
@@ -26,6 +26,7 @@ import de.deadlocker8.budgetmasterserver.server.category.CategoryGetAll;
 import de.deadlocker8.budgetmasterserver.server.category.CategoryUpdate;
 import de.deadlocker8.budgetmasterserver.server.categorybudget.CategoryBudgetGet;
 import de.deadlocker8.budgetmasterserver.server.charts.CategoryInOutSumForMonth;
+import de.deadlocker8.budgetmasterserver.server.charts.MonthInOutSum;
 import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentAdd;
 import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentDelete;
 import de.deadlocker8.budgetmasterserver.server.payment.normal.PaymentGet;
@@ -114,6 +115,7 @@ public class SparkServer
 		
 		//charts
 		get("/charts/categoryInOutSum", new CategoryInOutSumForMonth(handler, gson));
+		get("/charts/monthInOutSum", new MonthInOutSum(handler, gson));
 				
 
 		after((request, response) -> {
diff --git a/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java b/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java
new file mode 100644
index 000000000..1fe7eaa26
--- /dev/null
+++ b/src/de/deadlocker8/budgetmasterserver/server/charts/MonthInOutSum.java
@@ -0,0 +1,78 @@
+package de.deadlocker8.budgetmasterserver.server.charts;
+
+import static spark.Spark.halt;
+
+import java.util.ArrayList;
+
+import org.joda.time.DateTime;
+
+import com.google.gson.Gson;
+
+import de.deadlocker8.budgetmaster.logic.Payment;
+import de.deadlocker8.budgetmasterserver.main.DatabaseHandler;
+import spark.Request;
+import spark.Response;
+import spark.Route;
+
+public class MonthInOutSum implements Route
+{
+	private DatabaseHandler handler;
+	private Gson gson;
+
+	public MonthInOutSum(DatabaseHandler handler, Gson gson)
+	{
+		this.handler = handler;
+		this.gson = gson;
+	}
+
+	@Override
+	public Object handle(Request req, Response res) throws Exception
+	{
+		if(!req.queryParams().contains("startDate") || !req.queryParams().contains("endDate"))
+		{
+			halt(400, "Bad Request");
+		}		
+	
+		try
+		{				
+			DateTime startDate = DateTime.parse(req.queryMap("startDate").value()).withDayOfMonth(1);
+			DateTime endDate = DateTime.parse(req.queryMap("endDate").value()).withDayOfMonth(1);		
+			
+			ArrayList<de.deadlocker8.budgetmaster.logic.MonthInOutSum> monthInOutSums = new ArrayList<>();
+			
+			while(startDate.isBefore(endDate) || startDate.isEqual(endDate))
+			{
+				ArrayList<Payment> currentMonthPayments = new ArrayList<>();
+				currentMonthPayments.addAll(handler.getPayments(startDate.getYear(), startDate.getMonthOfYear()));
+				currentMonthPayments.addAll(handler.getRepeatingPayments(startDate.getYear(), startDate.getMonthOfYear()));				
+				
+				int sumIN = 0;
+				int sumOUT = 0;						
+						
+				for(Payment currentPayment : currentMonthPayments)
+				{
+					if(currentPayment.getAmount() > 0)
+					{
+						sumIN += currentPayment.getAmount();
+					}
+					else
+					{
+						sumOUT += -currentPayment.getAmount();
+					}
+				}
+				
+				monthInOutSums.add(new de.deadlocker8.budgetmaster.logic.MonthInOutSum(startDate.getMonthOfYear(), startDate.getYear(), sumIN, sumOUT));				
+				
+				startDate = startDate.plusMonths(1);
+			}	
+		
+			return gson.toJson(monthInOutSums);
+		}
+		catch(IllegalStateException ex)
+		{
+			halt(500, "Internal Server Error");
+		}		
+		
+		return null;
+	}
+}
\ No newline at end of file
-- 
GitLab