From b18c08bbbe4ce37c13a1d4641bce7f42b5cfcee1 Mon Sep 17 00:00:00 2001 From: Robert Goldmann <deadlocker@gmx.de> Date: Sat, 30 Sep 2017 12:37:59 +0200 Subject: [PATCH] Fixed #186 - Add possibility to search by amount/amount range --- .../logic/search/SearchPreferences.java | 50 +++++++++++- .../serverconnection/ServerConnection.java | 26 ++++++- .../ui/controller/SearchController.java | 60 +++++++++++---- .../logic/database/DatabaseHandler.java | 24 ++++++ .../server/SparkServer.java | 2 + .../payment/search/PaymentMaxAmount.java | 39 ++++++++++ .../server/payment/search/PaymentSearch.java | 77 +++++++++++++++++-- 7 files changed, 249 insertions(+), 29 deletions(-) create mode 100644 src/de/deadlocker8/budgetmasterserver/server/payment/search/PaymentMaxAmount.java diff --git a/src/de/deadlocker8/budgetmaster/logic/search/SearchPreferences.java b/src/de/deadlocker8/budgetmaster/logic/search/SearchPreferences.java index 42a892728..3c327559e 100644 --- a/src/de/deadlocker8/budgetmaster/logic/search/SearchPreferences.java +++ b/src/de/deadlocker8/budgetmaster/logic/search/SearchPreferences.java @@ -7,21 +7,27 @@ public class SearchPreferences private boolean searchDescription; private boolean searchCategorNames; private boolean searchTags; + private boolean searchAmount; + private int minAmount; + private int maxAmount; public SearchPreferences() { } - - public SearchPreferences(String lastQuery, boolean searchName, boolean searchDescription, boolean searchCategorNames, boolean searchTags) + + public SearchPreferences(String lastQuery, boolean searchName, boolean searchDescription, boolean searchCategorNames, boolean searchTags, boolean searchAmount, int minAmount, int maxAmount) { this.lastQuery = lastQuery; this.searchName = searchName; this.searchDescription = searchDescription; this.searchCategorNames = searchCategorNames; this.searchTags = searchTags; + this.searchAmount = searchAmount; + this.minAmount = minAmount; + this.maxAmount = maxAmount; } - + public String getLastQuery() { return lastQuery; @@ -72,9 +78,45 @@ public class SearchPreferences this.searchTags = searchTags; } + public boolean isSearchAmount() + { + return searchAmount; + } + + public void setSearchAmount(boolean searchAmount) + { + this.searchAmount = searchAmount; + } + + public int getMinAmount() + { + return minAmount; + } + + public void setMinAmount(int minAmount) + { + this.minAmount = minAmount; + } + + public int getMaxAmount() + { + return maxAmount; + } + + public void setMaxAmount(int maxAmount) + { + this.maxAmount = maxAmount; + } + + public void searchPreferences(int maxAmount) + { + this.maxAmount = maxAmount; + } + @Override public String toString() { - return "SearchPreferences [lastQuery=" + lastQuery + ", searchName=" + searchName + ", searchDescription=" + searchDescription + ", searchCategorNames=" + searchCategorNames + ", searchTags=" + searchTags + "]"; + return "SearchPreferences [lastQuery=" + lastQuery + ", searchName=" + searchName + ", searchDescription=" + searchDescription + ", searchCategorNames=" + searchCategorNames + ", searchTags=" + searchTags + ", searchAmount=" + searchAmount + ", minAmount=" + minAmount + ", maxAmount=" + + maxAmount + "]"; } } \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmaster/logic/serverconnection/ServerConnection.java b/src/de/deadlocker8/budgetmaster/logic/serverconnection/ServerConnection.java index eea404083..b1c424d06 100644 --- a/src/de/deadlocker8/budgetmaster/logic/serverconnection/ServerConnection.java +++ b/src/de/deadlocker8/budgetmaster/logic/serverconnection/ServerConnection.java @@ -331,7 +331,7 @@ public class ServerConnection } } - public ArrayList<Payment> getPaymentsForSearch(String query, boolean searchName, boolean searchDescription, boolean searchCategoryName, boolean searchTags) throws Exception + public ArrayList<Payment> getPaymentsForSearch(String query, boolean searchName, boolean searchDescription, boolean searchCategoryName, boolean searchTags, boolean searchAmount, int minAmount, int maxAmount) throws Exception { String urlString = settings.getUrl() + "/payment/search?secret=" + Helpers.getURLEncodedString(settings.getSecret()) + "&query=" + query; if(searchName) @@ -354,6 +354,12 @@ public class ServerConnection urlString += "&tags=" + 1; } + if(searchAmount) + { + urlString += "&minAmount=" + minAmount; + urlString += "&maxAmount=" + maxAmount; + } + URL url = new URL(urlString); HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); httpsCon.setDoOutput(true); @@ -371,6 +377,24 @@ public class ServerConnection throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); } } + + public int getMaxAmount() throws Exception + { + URL url = new URL(settings.getUrl() + "/payment/search/maxAmount?secret=" + Helpers.getURLEncodedString(settings.getSecret())); + HttpsURLConnection httpsCon = (HttpsURLConnection)url.openConnection(); + httpsCon.setDoOutput(true); + httpsCon.setRequestMethod("GET"); + + if(httpsCon.getResponseCode() == HttpsURLConnection.HTTP_OK) + { + String result = Read.getStringFromInputStream(httpsCon.getInputStream()); + return gson.fromJson(result, Integer.class); + } + else + { + throw new ServerConnectionException(String.valueOf(httpsCon.getResponseCode())); + } + } /* * CATEGORYBUDGET diff --git a/src/de/deadlocker8/budgetmaster/ui/controller/SearchController.java b/src/de/deadlocker8/budgetmaster/ui/controller/SearchController.java index 34558b992..7e2aa27a0 100644 --- a/src/de/deadlocker8/budgetmaster/ui/controller/SearchController.java +++ b/src/de/deadlocker8/budgetmaster/ui/controller/SearchController.java @@ -57,6 +57,7 @@ public class SearchController extends BaseController implements Styleable private Stage parentStage; private Controller controller; + private RangeSlider rangeSlider; public SearchController(Stage parentStage, Controller controller) { @@ -125,10 +126,20 @@ public class SearchController extends BaseController implements Styleable hboxSearchByAmount.setDisable(true); - //TODO get max from server - int maximum = 3500; + + int maximum; + try + { + maximum = getMaxAmountFromServer(); + } + catch(Exception e) + { + Logger.error(e); + controller.showConnectionErrorAlert(ExceptionHandler.getMessageForException(e)); + return; + } - RangeSlider rangeSlider = new RangeSlider(); + rangeSlider = new RangeSlider(); rangeSlider.setMin(0); rangeSlider.setMax(maximum); rangeSlider.setLowValue(rangeSlider.getMin()); @@ -202,23 +213,30 @@ public class SearchController extends BaseController implements Styleable }); textFieldAmountMin.setText("0"); - textFieldAmountMax.setText(String.valueOf(maximum)); - - //TODO save search by amount to SearchPreferences + textFieldAmountMax.setText(String.valueOf(maximum)); //prefill + SearchPreferences searchPreferences = controller.getSearchPreferences(); if(controller.getSearchPreferences() != null) { - textFieldSearch.setText(controller.getSearchPreferences().getLastQuery()); - checkBoxName.setSelected(controller.getSearchPreferences().isSearchName()); - checkBoxDescription.setSelected(controller.getSearchPreferences().isSearchDescription()); - checkBoxCategoryName.setSelected(controller.getSearchPreferences().isSearchCategorNames()); - checkBoxTags.setSelected(controller.getSearchPreferences().isSearchTags()); + textFieldSearch.setText(searchPreferences.getLastQuery()); + checkBoxName.setSelected(searchPreferences.isSearchName()); + checkBoxDescription.setSelected(searchPreferences.isSearchDescription()); + checkBoxCategoryName.setSelected(searchPreferences.isSearchCategorNames()); + checkBoxTags.setSelected(searchPreferences.isSearchTags()); + checkBoxSearchByAmount.setSelected(searchPreferences.isSearchAmount()); + rangeSlider.setLowValue(searchPreferences.getMinAmount()); + rangeSlider.setHighValue(searchPreferences.getMaxAmount()); } applyStyle(); } + private int getMaxAmountFromServer() throws Exception + { + ServerConnection connection = new ServerConnection(controller.getSettings()); + return connection.getMaxAmount(); + } private int getMayorTickUnit(int maximum) { @@ -239,11 +257,15 @@ public class SearchController extends BaseController implements Styleable { controller.setSearchPreferences(new SearchPreferences()); } - controller.getSearchPreferences().setLastQuery(query); - controller.getSearchPreferences().setSearchName(checkBoxName.isSelected()); - controller.getSearchPreferences().setSearchDescription(checkBoxDescription.isSelected()); - controller.getSearchPreferences().setSearchCategorNames(checkBoxCategoryName.isSelected()); - controller.getSearchPreferences().setSearchTags(checkBoxTags.isSelected()); + SearchPreferences searchPreferences = controller.getSearchPreferences(); + searchPreferences.setLastQuery(query); + searchPreferences.setSearchName(checkBoxName.isSelected()); + searchPreferences.setSearchDescription(checkBoxDescription.isSelected()); + searchPreferences.setSearchCategorNames(checkBoxCategoryName.isSelected()); + searchPreferences.setSearchTags(checkBoxTags.isSelected()); + searchPreferences.setSearchAmount(checkBoxSearchByAmount.isSelected()); + searchPreferences.setMinAmount((int)rangeSlider.getLowValue()); + searchPreferences.setMaxAmount((int)rangeSlider.getHighValue()); Stage modalStage = Helpers.showModal(Localization.getString(Strings.TITLE_MODAL), Localization.getString(Strings.LOAD_SEARCH), getStage(), controller.getIcon()); @@ -255,7 +277,11 @@ public class SearchController extends BaseController implements Styleable checkBoxName.isSelected(), checkBoxDescription.isSelected(), checkBoxCategoryName.isSelected(), - checkBoxTags.isSelected()); + checkBoxTags.isSelected(), + checkBoxSearchByAmount.isSelected(), + (int)rangeSlider.getLowValue()*100, + (int)rangeSlider.getHighValue()*100); + Platform.runLater(() -> { listView.getItems().clear(); if(payments != null) diff --git a/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseHandler.java b/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseHandler.java index 05d82c97b..42854c52b 100644 --- a/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseHandler.java +++ b/src/de/deadlocker8/budgetmasterserver/logic/database/DatabaseHandler.java @@ -656,6 +656,30 @@ public class DatabaseHandler return result; } + + public int getNormalPaymentMaxAmount() + { + PreparedStatement stmt = null; + try + { + stmt = connection.prepareStatement("SELECT MAX(ABS(Amount)) as'max' FROM payment;"); ; + ResultSet rs = stmt.executeQuery(); + while(rs.next()) + { + return rs.getInt("max"); + } + } + catch(SQLException e) + { + Logger.error(e); + } + finally + { + closeConnection(stmt); + } + + return -1; + } /* * DELETE diff --git a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java index 00a21bba4..72558d5da 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java +++ b/src/de/deadlocker8/budgetmasterserver/server/SparkServer.java @@ -41,6 +41,7 @@ import de.deadlocker8.budgetmasterserver.server.payment.repeating.RepeatingPayme import de.deadlocker8.budgetmasterserver.server.payment.repeating.RepeatingPaymentDelete; import de.deadlocker8.budgetmasterserver.server.payment.repeating.RepeatingPaymentGet; import de.deadlocker8.budgetmasterserver.server.payment.repeating.RepeatingPaymentGetAll; +import de.deadlocker8.budgetmasterserver.server.payment.search.PaymentMaxAmount; import de.deadlocker8.budgetmasterserver.server.payment.search.PaymentSearch; import de.deadlocker8.budgetmasterserver.server.rest.RestGet; import de.deadlocker8.budgetmasterserver.server.tag.match.TagMatchAddForPayment; @@ -120,6 +121,7 @@ public class SparkServer // Payment get("/payment/search", new PaymentSearch(handler, tagHandler)); + get("/payment/search/maxAmount", new PaymentMaxAmount(handler, gson)); // Normal get("/payment", new PaymentGet(handler, gson)); post("/payment", new PaymentAdd(handler, gson)); diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/search/PaymentMaxAmount.java b/src/de/deadlocker8/budgetmasterserver/server/payment/search/PaymentMaxAmount.java new file mode 100644 index 000000000..967671a4d --- /dev/null +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/search/PaymentMaxAmount.java @@ -0,0 +1,39 @@ +package de.deadlocker8.budgetmasterserver.server.payment.search; + +import static spark.Spark.halt; + +import com.google.gson.Gson; + +import de.deadlocker8.budgetmasterserver.logic.database.DatabaseHandler; +import spark.Request; +import spark.Response; +import spark.Route; + +public class PaymentMaxAmount implements Route +{ + private DatabaseHandler handler; + private Gson gson; + + public PaymentMaxAmount(DatabaseHandler handler, Gson gson) + { + this.handler = handler; + this.gson = gson; + } + + @Override + public Object handle(Request req, Response res) throws Exception + { + try + { + int maxNormal = handler.getNormalPaymentMaxAmount(); + //plus 1 to allow all amounts up to maxNormal.99 € + return gson.toJson((maxNormal+1)/100); + } + catch(IllegalStateException ex) + { + halt(500, "Internal Server Error"); + } + + return null; + } +} \ No newline at end of file diff --git a/src/de/deadlocker8/budgetmasterserver/server/payment/search/PaymentSearch.java b/src/de/deadlocker8/budgetmasterserver/server/payment/search/PaymentSearch.java index 7976f0086..f48eeeb12 100644 --- a/src/de/deadlocker8/budgetmasterserver/server/payment/search/PaymentSearch.java +++ b/src/de/deadlocker8/budgetmasterserver/server/payment/search/PaymentSearch.java @@ -75,16 +75,25 @@ public class PaymentSearch implements Route private boolean meetsCriteria(Request req, Payment payment) { if(req.queryMap("query").value().toLowerCase().equals("")) - return true; + return checkAmount(req, payment); - if(!req.queryParams().contains("name") && !req.queryParams().contains("description") && !req.queryParams().contains("categoryName") && !req.queryParams().contains("tags")) + if(!req.queryParams().contains("name") + && !req.queryParams().contains("description") + && !req.queryParams().contains("categoryName") + && !req.queryParams().contains("tags") + && !req.queryParams().contains("minAmount") + && !req.queryParams().contains("maxAmount")) return false; if(req.queryParams().contains("name")) { if(payment.getName().toLowerCase().contains(req.queryMap("query").value().toLowerCase())) { - return true; + return checkAmount(req, payment); + } + else + { + return false; } } @@ -92,7 +101,11 @@ public class PaymentSearch implements Route { if(payment.getDescription().toLowerCase().contains(req.queryMap("query").value().toLowerCase())) { - return true; + return checkAmount(req, payment); + } + else + { + return false; } } @@ -105,7 +118,11 @@ public class PaymentSearch implements Route Category category = handler.getCategory(payment.getCategoryID()); if(category.getName().toLowerCase().contains(req.queryMap("query").value().toLowerCase())) { - return true; + return checkAmount(req, payment); + } + else + { + return false; } } @@ -130,13 +147,59 @@ public class PaymentSearch implements Route { if(currentTag.getName().toLowerCase().contains(req.queryMap("query").value().toLowerCase())) { - return true; + return checkAmount(req, payment); + } + else + { + return false; } } } } } - return false; + /* + * check here again if amount should be considered + * otherwise the following situation is evaluated incorrectly: + * + * -at least one of the previous criteria is enabled (e.g. search by name) + * -payment doesn't match the criteria + * --> checkAmount() is being processed + * --> if amount is not in request (search shouldn't search by amount) the checkAmount()-Method returns true + * --> payment that doesn't match the criteria AND shouldn't be searched by amount should be evaluated as FALSE instead of TRUE + */ + if(req.queryParams().contains("minAmount") && req.queryParams().contains("maxAmount")) + { + return checkAmount(req, payment); + } + return false; + } + + private boolean checkAmount(Request req, Payment payment) + { + if(req.queryParams().contains("minAmount") && req.queryParams().contains("maxAmount")) + { + try + { + int minAmount = Integer.parseInt(req.queryMap("minAmount").value()); + int maxAmount = Integer.parseInt(req.queryMap("maxAmount").value()); + int amount = Math.abs(payment.getAmount()); + + if(amount >= minAmount && amount <= maxAmount) + { + return true; + } + else + { + return false; + } + } + catch(NumberFormatException e) + { + halt(400, "Bad Request"); + } + } + + return true; } } \ No newline at end of file -- GitLab