From 7f13a07f819c21ffaa5467ef5fb88d40f0b667c4 Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 28 Sep 2022 12:51:08 +0800 Subject: [PATCH 001/416] Create my personal portfolio page --- docs/team/{johndoe.md => chuahanyongdarren.md} | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) rename docs/team/{johndoe.md => chuahanyongdarren.md} (93%) diff --git a/docs/team/johndoe.md b/docs/team/chuahanyongdarren.md similarity index 93% rename from docs/team/johndoe.md rename to docs/team/chuahanyongdarren.md index ab75b391b..8022ee564 100644 --- a/docs/team/johndoe.md +++ b/docs/team/chuahanyongdarren.md @@ -1,6 +1,6 @@ -# John Doe - Project Portfolio Page - -## Overview - - -### Summary of Contributions +# John Doe - Project Portfolio Page + +## Overview + + +### Summary of Contributions From 6585729906774a156b937a3f0bb66e4c64f75ac4 Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 28 Sep 2022 12:52:18 +0800 Subject: [PATCH 002/416] Update about us for Thin Hong and add chiathinhong.md --- docs/AboutUs.md | 10 +++------- docs/team/chiathinhong.md | 1 + 2 files changed, 4 insertions(+), 7 deletions(-) create mode 100644 docs/team/chiathinhong.md diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953e..d04b4524a 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,5 @@ # About us -Display | Name | Github Profile | Portfolio ---------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +Display | Name | Github Profile | Portfolio +--------|:--------------:|:--------------:|:---------: +![](https://via.placeholder.com/100.png?text=Photo) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](docs/team/chiathinhong.md) diff --git a/docs/team/chiathinhong.md b/docs/team/chiathinhong.md new file mode 100644 index 000000000..53578d56c --- /dev/null +++ b/docs/team/chiathinhong.md @@ -0,0 +1 @@ +Name: Chia Thin Hong \ No newline at end of file From aa49ffa58b74b11d0872c9946266aacd1e0cd523 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 28 Sep 2022 12:52:35 +0800 Subject: [PATCH 003/416] update about us for Paul --- docs/AboutUs.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953e..999a84cf7 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,8 +2,4 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](docs/team/paullow.md) From ff678b9ba7f0c91c8c1ace1f92baa0c2ba2bab99 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Wed, 28 Sep 2022 12:54:53 +0800 Subject: [PATCH 004/416] initial commit --- docs/AboutUs.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953e..8e207c74a 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,7 @@ # About us -Display | Name | Github Profile | Portfolio ---------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +Display | Name | Github Profile | Portfolio +--------|:--------:|:--------------:|:---------: + +![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](docs/team/chinhan99.md) + From 9ce1ffbf180138597627f42d7da0e425f5cdf3d8 Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 28 Sep 2022 12:54:54 +0800 Subject: [PATCH 005/416] Create my personal portfolio page --- docs/AboutUs.md | 15 ++++++++------- docs/team/chuahanyongdarren.md | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 0f072953e..e293b5879 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -1,9 +1,10 @@ # About us -Display | Name | Github Profile | Portfolio ---------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | John Doe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) -![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +Display | Name | Github Profile | Portfolio +--------|:--------------------:|:--------------------------------------:|:---------: +![](https://via.placeholder.com/100.png?text=Photo) | Chua Han Yong Darren | [Github](https://github.com/chydarren) | [Portfolio](docs/team/chuahanyongdarren.md) +![](https://via.placeholder.com/100.png?text=Photo) | Don Joe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Ron John | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | John Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) +![](https://via.placeholder.com/100.png?text=Photo) | Don Roe | [Github](https://github.com/) | [Portfolio](docs/team/johndoe.md) + diff --git a/docs/team/chuahanyongdarren.md b/docs/team/chuahanyongdarren.md index 8022ee564..527062389 100644 --- a/docs/team/chuahanyongdarren.md +++ b/docs/team/chuahanyongdarren.md @@ -1,4 +1,4 @@ -# John Doe - Project Portfolio Page +# Chua Han Yong Darren - Project Portfolio Page ## Overview From b69fc177d1b94ec9208bccce2ca6174a3b3bb988 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 28 Sep 2022 13:00:16 +0800 Subject: [PATCH 006/416] commit about us change paul --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 999a84cf7..6b851e9c5 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,4 +2,4 @@ Display | Name | Github Profile | Portfolio --------|:----:|:--------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](docs/team/paullow.md) +![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](docs/team/paullow.md) From 439d94fc05f2bf2105971879737fb9b19409d5ca Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 2 Oct 2022 15:27:51 +0800 Subject: [PATCH 007/416] added base classes for the project --- docs/AboutUs.md | 2 +- docs/team/yongchinhan.md | 6 ++ src/main/java/seedu/duke/Duke.java | 21 ------- src/main/java/seedu/duke/Main.java | 27 +++++++++ src/main/java/seedu/duke/Parser.java | 57 +++++++++++++++++++ src/main/java/seedu/duke/Tasklist.java | 6 ++ src/main/java/seedu/duke/Transaction.java | 67 +++++++++++++++++++++++ src/main/java/seedu/duke/Ui.java | 38 +++++++++++++ 8 files changed, 202 insertions(+), 22 deletions(-) create mode 100644 docs/team/yongchinhan.md delete mode 100644 src/main/java/seedu/duke/Duke.java create mode 100644 src/main/java/seedu/duke/Main.java create mode 100644 src/main/java/seedu/duke/Parser.java create mode 100644 src/main/java/seedu/duke/Tasklist.java create mode 100644 src/main/java/seedu/duke/Transaction.java create mode 100644 src/main/java/seedu/duke/Ui.java diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 7b166b707..03dd09f4c 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,6 +4,6 @@ Display | Name | Github Profile | Portfo --------|:--------------------:|:--------------------------------------:|:---------: ![](https://via.placeholder.com/100.png?text=Photo) | Chua Han Yong Darren | [Github](https://github.com/chydarren) | [Portfolio](docs/team/chuahanyongdarren.md) ![](https://via.placeholder.com/100.png?text=Photo) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](docs/team/chiathinhong.md) -![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](docs/team/chinhan99.md) +![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](docs/team/yongchinhan.md) ![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](docs/team/paullow.md) diff --git a/docs/team/yongchinhan.md b/docs/team/yongchinhan.md new file mode 100644 index 000000000..ed476156a --- /dev/null +++ b/docs/team/yongchinhan.md @@ -0,0 +1,6 @@ +# Yong Chin Han - Project Portfolio Page + +## Overview +Computer Engineering major + +### Summary of Contributions diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java deleted file mode 100644 index 5c74e68d5..000000000 --- a/src/main/java/seedu/duke/Duke.java +++ /dev/null @@ -1,21 +0,0 @@ -package seedu.duke; - -import java.util.Scanner; - -public class Duke { - /** - * Main entry-point for the java.duke.Duke application. - */ - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - System.out.println("What is your name?"); - - Scanner in = new Scanner(System.in); - System.out.println("Hello " + in.nextLine()); - } -} diff --git a/src/main/java/seedu/duke/Main.java b/src/main/java/seedu/duke/Main.java new file mode 100644 index 000000000..57cd6c438 --- /dev/null +++ b/src/main/java/seedu/duke/Main.java @@ -0,0 +1,27 @@ +package seedu.duke; + +import java.util.ArrayList; +import java.util.Scanner; + + +public class Main { + static final boolean EXIT = false; + + + public static void main(String[] args) { + ArrayList transactions = new ArrayList(); + String inData; + Scanner scan = new Scanner(System.in); + Ui.showGreeting(); + while (true) { + // continuously receive user input + inData = scan.nextLine(); + inData = inData.trim(); + + if (Parser.processInput(inData, transactions) == EXIT) { + break; + } + } + + } +} diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java new file mode 100644 index 000000000..cca40f5b5 --- /dev/null +++ b/src/main/java/seedu/duke/Parser.java @@ -0,0 +1,57 @@ +package seedu.duke; + +import java.util.ArrayList; + +public class Parser { + static final boolean EXIT = false; + static final boolean CONTINUE = true; + + + /** + * parses the user input and processes it with the transactions arraylist. + * + * @param inData the user input . + * @param transactions the array which would be operated on. + * @return EXIT if input equals "bye", else return CONTINUE. + */ + + public static boolean processInput(String inData, ArrayList transactions) { + + if (inData.equals("help")) { + Ui.showHelpList(); + } else if (inData.equals("list")) { + //prints all transactions if input is equal to "list" + + } else if (inData.equals("purge")) { + // show confirmation prompt before deleting all transactions + } else if (inData.equals(("bye"))) { + Ui.showExitMessage(); + return EXIT; //exit loop + } else if (inData.isBlank() || inData.isEmpty()) { + Ui.showWrongCommand(); + } else if (inData.contains(" ")) { + //further parse the user input for user transaction commands + String[] userInput = inData.split(" "); + String command = userInput[0]; + switch (command) { + + case "delete": + // Checks if userInput is in the correct format by further parsing(e.g. such as correct entry numbers), before deleting the entry + break; + case "add": + // Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist + break; + case "edit": + // Checks if userInput is in the correct input format by further parsing, before the entry in the arraylist + break; + default: + // for invalid commands + Ui.showWrongCommand(); + } + } else { + //for any single-word inData , which are not valid commands. + Ui.showWrongCommand(); + } + return CONTINUE; + } +} diff --git a/src/main/java/seedu/duke/Tasklist.java b/src/main/java/seedu/duke/Tasklist.java new file mode 100644 index 000000000..68624cfa5 --- /dev/null +++ b/src/main/java/seedu/duke/Tasklist.java @@ -0,0 +1,6 @@ +package seedu.duke; + +public class Tasklist { + + +} diff --git a/src/main/java/seedu/duke/Transaction.java b/src/main/java/seedu/duke/Transaction.java new file mode 100644 index 000000000..718127702 --- /dev/null +++ b/src/main/java/seedu/duke/Transaction.java @@ -0,0 +1,67 @@ +package seedu.duke; + +public class Transaction { + private String description; + private int amount; + private String type; // income or expense + private String category; //category of income or expense + private int day; + private int month; + private int year; + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public int getDay() { + return day; + } + + public void setDay(int day) { + this.day = day; + } + + public int getMonth() { + return month; + } + + public void setMonth(int month) { + this.month = month; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java new file mode 100644 index 000000000..cc790c776 --- /dev/null +++ b/src/main/java/seedu/duke/Ui.java @@ -0,0 +1,38 @@ +package seedu.duke; + +public class Ui { + + public static void showGreeting() { + System.out.println("____________________________________________________________"); + System.out.println("Hello! I'm your Moolah Manager!"); + System.out.println("Enter if you need the list of commands"); + System.out.println("____________________________________________________________"); + } + + public static void showHelpList() { // To be updated. + System.out.println("____________________________________________________________"); + System.out.println("Hello! I'm here to help"); + System.out.println("Here are the possible commands! "); + System.out.println(""); + System.out.println(""); + System.out.println(""); + System.out.println(""); + System.out.println(""); + System.out.println("____________________________________________________________"); + } + + public static void showExitMessage() { + System.out.println("____________________________________________________________"); + System.out.println("Good bye! See you soon!!"); + System.out.println("____________________________________________________________"); + } + + public static void showWrongCommand() { + System.out.println("____________________________________________________________"); + System.out.println("You have entered the wrong command. "); + System.out.println("Please enter for the list of available commands"); + System.out.println("____________________________________________________________"); + } + + +} \ No newline at end of file From 177d8580a9e7156aad0aa5fa879d5f86ed476aaa Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 2 Oct 2022 15:40:49 +0800 Subject: [PATCH 008/416] reformatted comment lines longer than 120 characters --- src/main/java/seedu/duke/Parser.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index cca40f5b5..4e072b62d 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -36,13 +36,24 @@ public static boolean processInput(String inData, ArrayList transac switch (command) { case "delete": - // Checks if userInput is in the correct format by further parsing(e.g. such as correct entry numbers), before deleting the entry + /* + Checks if userInput is in the correct format by further parsing(e.g. such as correct entry numbers) + before deleting the entry + */ break; + case "add": - // Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist + /* + Checks if userInput is in the correct input format by further parsing, + before adding entry to arraylist + */ + break; case "edit": - // Checks if userInput is in the correct input format by further parsing, before the entry in the arraylist + /* + Checks if userInput is in the correct input format by further parsing, + before the entry in the arraylist + */ break; default: // for invalid commands From 25513d8d8be142ef6043c6d7d568bbfd0b31d8de Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 2 Oct 2022 15:44:07 +0800 Subject: [PATCH 009/416] renamed Main class to Duke class --- src/main/java/seedu/duke/{Main.java => Duke.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/main/java/seedu/duke/{Main.java => Duke.java} (96%) diff --git a/src/main/java/seedu/duke/Main.java b/src/main/java/seedu/duke/Duke.java similarity index 96% rename from src/main/java/seedu/duke/Main.java rename to src/main/java/seedu/duke/Duke.java index 57cd6c438..333951609 100644 --- a/src/main/java/seedu/duke/Main.java +++ b/src/main/java/seedu/duke/Duke.java @@ -4,7 +4,7 @@ import java.util.Scanner; -public class Main { +public class Duke { static final boolean EXIT = false; From 240fb28e14f7b62cc53717f6b054a1c29c440c4f Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 2 Oct 2022 15:52:14 +0800 Subject: [PATCH 010/416] Update input.TXT and EXPECTED.TXT files --- text-ui-test/EXPECTED.TXT | 16 +++++++--------- text-ui-test/input.txt | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 892cb6cae..0bf93f6cf 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,9 +1,7 @@ -Hello from - ____ _ -| _ \ _ _| | _____ -| | | | | | | |/ / _ \ -| |_| | |_| | < __/ -|____/ \__,_|_|\_\___| - -What is your name? -Hello James Gosling +____________________________________________________________ +Hello! I'm your Moolah Manager! +Enter if you need the list of commands +____________________________________________________________ +____________________________________________________________ +Good bye! See you soon!! +____________________________________________________________ \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f6ec2e9f9..0abaeaa99 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +1 @@ -James Gosling \ No newline at end of file +bye \ No newline at end of file From ff3cbee45e3508ba1b38840ed150d50b278b4b0e Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 2 Oct 2022 16:26:07 +0800 Subject: [PATCH 011/416] Update input.txt and EXPECTED.txt for a commandless input --- text-ui-test/EXPECTED.TXT | 3 --- text-ui-test/input.txt | 1 - 2 files changed, 4 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 0bf93f6cf..238d99f12 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -2,6 +2,3 @@ ____________________________________________________________ Hello! I'm your Moolah Manager! Enter if you need the list of commands ____________________________________________________________ -____________________________________________________________ -Good bye! See you soon!! -____________________________________________________________ \ No newline at end of file diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 0abaeaa99..e69de29bb 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1 +0,0 @@ -bye \ No newline at end of file From 40aac95e2159195211c8bbf4760f2f72cfd9668e Mon Sep 17 00:00:00 2001 From: brian-vb Date: Sun, 2 Oct 2022 17:00:47 +0800 Subject: [PATCH 012/416] Add more details into AboutUs.md and brianwongyunlong.md --- docs/AboutUs.md | 8 ++++---- docs/team/brianwongyunlong.md | 6 ++++++ 2 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 docs/team/brianwongyunlong.md diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 7b166b707..dccd2c33a 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -3,7 +3,7 @@ Display | Name | Github Profile | Portfolio --------|:--------------------:|:--------------------------------------:|:---------: ![](https://via.placeholder.com/100.png?text=Photo) | Chua Han Yong Darren | [Github](https://github.com/chydarren) | [Portfolio](docs/team/chuahanyongdarren.md) -![](https://via.placeholder.com/100.png?text=Photo) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](docs/team/chiathinhong.md) -![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](docs/team/chinhan99.md) -![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](docs/team/paullow.md) - +![](https://via.placeholder.com/100.png?text=Photo) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](docs/team/chiathinhong.md) +![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](docs/team/chinhan99.md) +![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](docs/team/paullow.md) +![](https://via.placeholder.com/100.png?text=Photo) | Brian Wong Yun Long | [Github](https://github.com/brian-vb) | [Portfolio](docs/team/brianwongyunlong.md) diff --git a/docs/team/brianwongyunlong.md b/docs/team/brianwongyunlong.md new file mode 100644 index 000000000..dec66c3e2 --- /dev/null +++ b/docs/team/brianwongyunlong.md @@ -0,0 +1,6 @@ +# Brian Wong Yun Long - Project Portfolio Page + +## Overview + + +### Summary of Contributions From c9f26cbca14bd575edbd84a2e9330402b1504959 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 2 Oct 2022 17:16:36 +0800 Subject: [PATCH 013/416] Change markdown file naming in AboutUs.md --- docs/AboutUs.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AboutUs.md b/docs/AboutUs.md index db63baadb..50758f489 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,7 +4,7 @@ Display | Name | Github Profile | Portfo --------|:--------------------:|:--------------------------------------:|:---------: ![](https://via.placeholder.com/100.png?text=Photo) | Chua Han Yong Darren | [Github](https://github.com/chydarren) | [Portfolio](docs/team/chuahanyongdarren.md) ![](https://via.placeholder.com/100.png?text=Photo) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](docs/team/chiathinhong.md) -![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](docs/team/chinhan99.md) +![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](docs/team/yongchinhan.md) ![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](docs/team/paullow.md) ![](https://via.placeholder.com/100.png?text=Photo) | Brian Wong Yun Long | [Github](https://github.com/brian-vb) | [Portfolio](docs/team/brianwongyunlong.md) From 75108b5b9cb84f7151fe3f096694634c71babecb Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 3 Oct 2022 10:31:47 +0800 Subject: [PATCH 014/416] Create paullow.md --- docs/team/paullow.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/team/paullow.md diff --git a/docs/team/paullow.md b/docs/team/paullow.md new file mode 100644 index 000000000..aed682261 --- /dev/null +++ b/docs/team/paullow.md @@ -0,0 +1,6 @@ +# Paul Solomon Low Si En - Project Portfolio Page + +## Overview + + +### Summary of Contributions From 2b6ea9440f680f7ef7983fe44764e583cf743383 Mon Sep 17 00:00:00 2001 From: Chua Han Yong Darren Date: Mon, 3 Oct 2022 11:01:48 +0800 Subject: [PATCH 015/416] Revert "Create paullow.md" --- docs/team/paullow.md | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 docs/team/paullow.md diff --git a/docs/team/paullow.md b/docs/team/paullow.md deleted file mode 100644 index aed682261..000000000 --- a/docs/team/paullow.md +++ /dev/null @@ -1,6 +0,0 @@ -# Paul Solomon Low Si En - Project Portfolio Page - -## Overview - - -### Summary of Contributions From cca70ebb973e66759e289539c396b6bf8304ea55 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 3 Oct 2022 11:10:51 +0800 Subject: [PATCH 016/416] Recreate paullow.md --- docs/team/paullow.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 docs/team/paullow.md diff --git a/docs/team/paullow.md b/docs/team/paullow.md new file mode 100644 index 000000000..aed682261 --- /dev/null +++ b/docs/team/paullow.md @@ -0,0 +1,6 @@ +# Paul Solomon Low Si En - Project Portfolio Page + +## Overview + + +### Summary of Contributions From bea2bca469b9f6b4d75cfd3f430351e3cadf7cb1 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 4 Oct 2022 15:11:33 +0800 Subject: [PATCH 017/416] Add enum variables for Info and Error messages while refractoring UI output class --- src/main/java/seedu/duke/Duke.java | 7 +-- src/main/java/seedu/duke/Parser.java | 38 ++++++------ src/main/java/seedu/duke/Ui.java | 60 +++++++++++-------- .../java/seedu/duke/common/ErrorMessages.java | 28 +++++++++ .../java/seedu/duke/common/InfoMessages.java | 31 ++++++++++ 5 files changed, 113 insertions(+), 51 deletions(-) create mode 100644 src/main/java/seedu/duke/common/ErrorMessages.java create mode 100644 src/main/java/seedu/duke/common/InfoMessages.java diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 333951609..a754a64ae 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -3,11 +3,7 @@ import java.util.ArrayList; import java.util.Scanner; - public class Duke { - static final boolean EXIT = false; - - public static void main(String[] args) { ArrayList transactions = new ArrayList(); String inData; @@ -18,10 +14,9 @@ public static void main(String[] args) { inData = scan.nextLine(); inData = inData.trim(); - if (Parser.processInput(inData, transactions) == EXIT) { + if (!Parser.processInput(inData, transactions)) { break; } } - } } diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 4e072b62d..7816b0f8e 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -3,45 +3,41 @@ import java.util.ArrayList; public class Parser { - static final boolean EXIT = false; - static final boolean CONTINUE = true; - + static final boolean IS_EXIT = false; + static final boolean IS_CONTINUE = true; /** - * parses the user input and processes it with the transactions arraylist. + * Parses the user input and processes it with the transactions arraylist. * - * @param inData the user input . - * @param transactions the array which would be operated on. - * @return EXIT if input equals "bye", else return CONTINUE. + * @param inData The user input. + * @param transactions The array which would be operated on. + * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. */ - public static boolean processInput(String inData, ArrayList transactions) { - if (inData.equals("help")) { Ui.showHelpList(); } else if (inData.equals("list")) { - //prints all transactions if input is equal to "list" + // Prints all transactions if input is equal to "list" } else if (inData.equals("purge")) { - // show confirmation prompt before deleting all transactions + // Shows confirmation prompt before deleting all transactions } else if (inData.equals(("bye"))) { Ui.showExitMessage(); - return EXIT; //exit loop + // Exits loop + return IS_EXIT; } else if (inData.isBlank() || inData.isEmpty()) { - Ui.showWrongCommand(); + Ui.showInvalidCommand(); } else if (inData.contains(" ")) { - //further parse the user input for user transaction commands + // Further parses the user input for user transaction commands String[] userInput = inData.split(" "); String command = userInput[0]; switch (command) { - case "delete": /* Checks if userInput is in the correct format by further parsing(e.g. such as correct entry numbers) before deleting the entry */ break; - case "add": /* Checks if userInput is in the correct input format by further parsing, @@ -56,13 +52,13 @@ Checks if userInput is in the correct format by further parsing(e.g. such as cor */ break; default: - // for invalid commands - Ui.showWrongCommand(); + // For invalid commands + Ui.showInvalidCommand(); } } else { - //for any single-word inData , which are not valid commands. - Ui.showWrongCommand(); + // For any single-word inData , which are not valid commands + Ui.showInvalidCommand(); } - return CONTINUE; + return IS_CONTINUE; } } diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index cc790c776..94577e33c 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,38 +1,50 @@ package seedu.duke; +import seedu.duke.common.ErrorMessages; +import seedu.duke.common.InfoMessages; + public class Ui { + /** + * Prints each message from a variable messages string line by line into the output stream. + * + * @param messages A string of variable arguments. + */ + public static void printMessages(String... messages) { + //@@author chydarren-reused + // Reused from https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/ui/TextUi.java + // with minor modifications + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER.toString()); + // Prints the string of arguments line by line in a loop + for (String message : messages) { + System.out.println(message); + } + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER.toString()); + } public static void showGreeting() { - System.out.println("____________________________________________________________"); - System.out.println("Hello! I'm your Moolah Manager!"); - System.out.println("Enter if you need the list of commands"); - System.out.println("____________________________________________________________"); + printMessages( + InfoMessages.MESSAGE_INFO_GREET.toString(), + InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString() + ); } - public static void showHelpList() { // To be updated. - System.out.println("____________________________________________________________"); - System.out.println("Hello! I'm here to help"); - System.out.println("Here are the possible commands! "); - System.out.println(""); - System.out.println(""); - System.out.println(""); - System.out.println(""); - System.out.println(""); - System.out.println("____________________________________________________________"); + public static void showHelpList() { + printMessages( + InfoMessages.MESSAGE_INFO_HELP_GREET.toString() + // To include the other messages for commands + ); } public static void showExitMessage() { - System.out.println("____________________________________________________________"); - System.out.println("Good bye! See you soon!!"); - System.out.println("____________________________________________________________"); + printMessages( + InfoMessages.MESSAGE_INFO_EXIT.toString() + ); } - public static void showWrongCommand() { - System.out.println("____________________________________________________________"); - System.out.println("You have entered the wrong command. "); - System.out.println("Please enter for the list of available commands"); - System.out.println("____________________________________________________________"); + public static void showInvalidCommand() { + printMessages( + ErrorMessages.MESSAGE_ERROR_INVALID_COMMAND.toString(), + InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString() + ); } - - } \ No newline at end of file diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java new file mode 100644 index 000000000..f8b0bef3b --- /dev/null +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -0,0 +1,28 @@ +package seedu.duke.common; + +/** + * Provides enum variables for storing custom program error messages. + */ +public enum ErrorMessages { + MESSAGE_ERROR_INVALID_COMMAND("Oops, you have entered an invalid command."); + + public final String message; + + /** + * Instantiates a new error message when user initialises a new instance of this enum. + * + * @param message A string containing the message. + */ + ErrorMessages(String message) { + this.message = message; + } + + /** + * Gets the error message as a string. + * + * @return A string containing the message. + */ + public String toString() { + return message; + } +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java new file mode 100644 index 000000000..a9127a525 --- /dev/null +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -0,0 +1,31 @@ +package seedu.duke.common; + +/** + * Provides enum variables for storing custom program information messages. + */ +public enum InfoMessages { + MESSAGE_INFO_DIVIDER("____________________________________________________________"), + MESSAGE_INFO_GREET("Hello! I'm your Moolah Manager!"), + MESSAGE_INFO_HELP_PROMPT("Enter if you need the list of commands."), + MESSAGE_INFO_HELP_GREET("I'm here to help. Here are the possible commands!"), + MESSAGE_INFO_EXIT("Goodbye and see you soon."); + public final String message; + + /** + * Instantiates a new information message when user initialises a new instance of this enum. + * + * @param message A string containing the message. + */ + InfoMessages(String message) { + this.message = message; + } + + /** + * Gets the information message as a string. + * + * @return A string containing the message. + */ + public String toString() { + return message; + } +} \ No newline at end of file From fc26e8ac7b5770c942931bd4971179aef82f5b40 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 4 Oct 2022 15:20:21 +0800 Subject: [PATCH 018/416] Fix indentation error with a comment in UI class --- src/main/java/seedu/duke/Ui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 94577e33c..2e15f30bd 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -29,9 +29,9 @@ public static void showGreeting() { } public static void showHelpList() { + // To include the other messages for commands printMessages( InfoMessages.MESSAGE_INFO_HELP_GREET.toString() - // To include the other messages for commands ); } From 177715e8b65c22b8c274e8d1d4a8b947b545c142 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 4 Oct 2022 15:27:49 +0800 Subject: [PATCH 019/416] Add a hasNextLine function to check if scanner has input before entering loop function --- src/main/java/seedu/duke/Duke.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index a754a64ae..962e2617b 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -9,7 +9,8 @@ public static void main(String[] args) { String inData; Scanner scan = new Scanner(System.in); Ui.showGreeting(); - while (true) { + + while (scan.hasNextLine() && true) { // continuously receive user input inData = scan.nextLine(); inData = inData.trim(); From ff77887d4f73ce50562f94153bb3b03dee80335d Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 4 Oct 2022 15:38:59 +0800 Subject: [PATCH 020/416] Add a full stop to one of the lines in EXPECTED.TXT --- text-ui-test/EXPECTED.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 238d99f12..192007e60 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,4 +1,4 @@ ____________________________________________________________ Hello! I'm your Moolah Manager! -Enter if you need the list of commands +Enter if you need the list of commands. ____________________________________________________________ From a234d4e057236a8e2b5c0a6ac045143d069d744b Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 4 Oct 2022 23:52:46 +0800 Subject: [PATCH 021/416] Refractor Transaction.java into a package Edit Duke.java and Parser.java to follow the refractored path. --- src/main/java/seedu/duke/Duke.java | 2 ++ src/main/java/seedu/duke/Parser.java | 2 ++ .../duke/{ => transaction}/Transaction.java | 20 +++++++++---------- 3 files changed, 14 insertions(+), 10 deletions(-) rename src/main/java/seedu/duke/{ => transaction}/Transaction.java (76%) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 962e2617b..7a1a0fa15 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -1,5 +1,7 @@ package seedu.duke; +import seedu.duke.transaction.Transaction; + import java.util.ArrayList; import java.util.Scanner; diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 7816b0f8e..1d24aeaea 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,5 +1,7 @@ package seedu.duke; +import seedu.duke.transaction.Transaction; + import java.util.ArrayList; public class Parser { diff --git a/src/main/java/seedu/duke/Transaction.java b/src/main/java/seedu/duke/transaction/Transaction.java similarity index 76% rename from src/main/java/seedu/duke/Transaction.java rename to src/main/java/seedu/duke/transaction/Transaction.java index 718127702..aeadc9890 100644 --- a/src/main/java/seedu/duke/Transaction.java +++ b/src/main/java/seedu/duke/transaction/Transaction.java @@ -1,14 +1,22 @@ -package seedu.duke; +package seedu.duke.transaction; public class Transaction { private String description; private int amount; - private String type; // income or expense private String category; //category of income or expense private int day; private int month; private int year; + public Transaction(String description, int amount, String category, int day, int month, int year) { + this.description = description; + this.amount = amount; + this.category = category; + this.day = day; + this.month = month; + this.year = year; + } + public String getDescription() { return description; } @@ -25,14 +33,6 @@ public void setAmount(int amount) { this.amount = amount; } - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - public String getCategory() { return category; } From 37ddb01488dc646a61f2720856c8b03a1fc5bd36 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 4 Oct 2022 23:53:34 +0800 Subject: [PATCH 022/416] Create Expense.java and Income.java as the subclasses of transactions --- src/main/java/seedu/duke/transaction/Expense.java | 11 +++++++++++ src/main/java/seedu/duke/transaction/Income.java | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 src/main/java/seedu/duke/transaction/Expense.java create mode 100644 src/main/java/seedu/duke/transaction/Income.java diff --git a/src/main/java/seedu/duke/transaction/Expense.java b/src/main/java/seedu/duke/transaction/Expense.java new file mode 100644 index 000000000..8829ef093 --- /dev/null +++ b/src/main/java/seedu/duke/transaction/Expense.java @@ -0,0 +1,11 @@ +package seedu.duke.transaction; + +/** + * Represents the expense of the user added to the application. + * Records the amount, category and the date of spending. + */ +public class Expense extends Transaction { + public Expense(String description, int amount, String category, int day, int month, int year) { + super(description, amount, category, day, month, year); + } +} diff --git a/src/main/java/seedu/duke/transaction/Income.java b/src/main/java/seedu/duke/transaction/Income.java new file mode 100644 index 000000000..c2db09b2e --- /dev/null +++ b/src/main/java/seedu/duke/transaction/Income.java @@ -0,0 +1,11 @@ +package seedu.duke.transaction; + +/** + * Represents the income of the user added to the application. + * Records the amount, category and date of the income. + */ +public class Income extends Transaction { + public Income(String description, int amount, String category, int day, int month, int year) { + super(description, amount, category, day, month, year); + } +} From 01bcd1e61c7ad72fa770f69ad7b044359cc41bab Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 5 Oct 2022 00:01:50 +0800 Subject: [PATCH 023/416] Refractor Transaction package into Data package Data package is created to contain all the classes that represent any type of data inserted by the user and to be stored inside the application. --- src/main/java/seedu/duke/Duke.java | 2 +- src/main/java/seedu/duke/Parser.java | 2 +- src/main/java/seedu/duke/{ => data}/transaction/Expense.java | 2 +- src/main/java/seedu/duke/{ => data}/transaction/Income.java | 2 +- .../java/seedu/duke/{ => data}/transaction/Transaction.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename src/main/java/seedu/duke/{ => data}/transaction/Expense.java (90%) rename src/main/java/seedu/duke/{ => data}/transaction/Income.java (90%) rename src/main/java/seedu/duke/{ => data}/transaction/Transaction.java (97%) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 7a1a0fa15..d32957f88 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -1,6 +1,6 @@ package seedu.duke; -import seedu.duke.transaction.Transaction; +import seedu.duke.data.transaction.Transaction; import java.util.ArrayList; import java.util.Scanner; diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 1d24aeaea..4743c9be2 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,6 +1,6 @@ package seedu.duke; -import seedu.duke.transaction.Transaction; +import seedu.duke.data.transaction.Transaction; import java.util.ArrayList; diff --git a/src/main/java/seedu/duke/transaction/Expense.java b/src/main/java/seedu/duke/data/transaction/Expense.java similarity index 90% rename from src/main/java/seedu/duke/transaction/Expense.java rename to src/main/java/seedu/duke/data/transaction/Expense.java index 8829ef093..1e8fac771 100644 --- a/src/main/java/seedu/duke/transaction/Expense.java +++ b/src/main/java/seedu/duke/data/transaction/Expense.java @@ -1,4 +1,4 @@ -package seedu.duke.transaction; +package seedu.duke.data.transaction; /** * Represents the expense of the user added to the application. diff --git a/src/main/java/seedu/duke/transaction/Income.java b/src/main/java/seedu/duke/data/transaction/Income.java similarity index 90% rename from src/main/java/seedu/duke/transaction/Income.java rename to src/main/java/seedu/duke/data/transaction/Income.java index c2db09b2e..e47f7fc26 100644 --- a/src/main/java/seedu/duke/transaction/Income.java +++ b/src/main/java/seedu/duke/data/transaction/Income.java @@ -1,4 +1,4 @@ -package seedu.duke.transaction; +package seedu.duke.data.transaction; /** * Represents the income of the user added to the application. diff --git a/src/main/java/seedu/duke/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java similarity index 97% rename from src/main/java/seedu/duke/transaction/Transaction.java rename to src/main/java/seedu/duke/data/transaction/Transaction.java index aeadc9890..7d283dbe8 100644 --- a/src/main/java/seedu/duke/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -1,4 +1,4 @@ -package seedu.duke.transaction; +package seedu.duke.data.transaction; public class Transaction { private String description; From 3be67fad7d45a6e7eddf8db16c1b8501e0245274 Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 5 Oct 2022 00:14:38 +0800 Subject: [PATCH 024/416] Replace Tasklist.java with TransactionList.java and refractor it Store the transaction list into data as it contains all the transactions added from the user input and stored in the applications. --- src/main/java/seedu/duke/Duke.java | 5 ++--- src/main/java/seedu/duke/Parser.java | 5 +++-- src/main/java/seedu/duke/Tasklist.java | 6 ------ .../java/seedu/duke/data/TransactionList.java | 18 ++++++++++++++++++ 4 files changed, 23 insertions(+), 11 deletions(-) delete mode 100644 src/main/java/seedu/duke/Tasklist.java create mode 100644 src/main/java/seedu/duke/data/TransactionList.java diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index d32957f88..e8bbb7da6 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -1,13 +1,12 @@ package seedu.duke; -import seedu.duke.data.transaction.Transaction; +import seedu.duke.data.TransactionList; -import java.util.ArrayList; import java.util.Scanner; public class Duke { public static void main(String[] args) { - ArrayList transactions = new ArrayList(); + TransactionList transactions = new TransactionList(); String inData; Scanner scan = new Scanner(System.in); Ui.showGreeting(); diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 4743c9be2..ab270f30f 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,5 +1,6 @@ package seedu.duke; +import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; import java.util.ArrayList; @@ -11,11 +12,11 @@ public class Parser { /** * Parses the user input and processes it with the transactions arraylist. * - * @param inData The user input. + * @param inData The user input. * @param transactions The array which would be operated on. * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. */ - public static boolean processInput(String inData, ArrayList transactions) { + public static boolean processInput(String inData, TransactionList transactions) { if (inData.equals("help")) { Ui.showHelpList(); } else if (inData.equals("list")) { diff --git a/src/main/java/seedu/duke/Tasklist.java b/src/main/java/seedu/duke/Tasklist.java deleted file mode 100644 index 68624cfa5..000000000 --- a/src/main/java/seedu/duke/Tasklist.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.duke; - -public class Tasklist { - - -} diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java new file mode 100644 index 000000000..b796cd1b3 --- /dev/null +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -0,0 +1,18 @@ +package seedu.duke.data; + +import seedu.duke.data.transaction.Transaction; + +import java.util.ArrayList; + +/** + * Represents a list of transactions added by the user into the application. + * Operations related to modifying the list of transactions are defined under this class. + * These operations include adding, listing, modifying, deleting and purging. + */ +public class TransactionList { + private ArrayList transactions; + + public TransactionList() { + this.transactions = new ArrayList<>(); + } +} From 90601bb11ca58f6d1c4ba0ca3b99c2792cdf89a1 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Wed, 5 Oct 2022 02:07:46 +0800 Subject: [PATCH 025/416] Add Delete Function (Current Progress) --- src/main/java/seedu/duke/Parser.java | 3 ++- src/main/java/seedu/duke/data/TransactionList.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index ab270f30f..5e14d939f 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -40,13 +40,14 @@ public static boolean processInput(String inData, TransactionList transactions) Checks if userInput is in the correct format by further parsing(e.g. such as correct entry numbers) before deleting the entry */ + int index = Integer.parseInt(userInput[1]); + TransactionList.deleteEntry(transactions, index); break; case "add": /* Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist */ - break; case "edit": /* diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b796cd1b3..181c131e6 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -1,5 +1,6 @@ package seedu.duke.data; +import seedu.duke.common.InfoMessages; import seedu.duke.data.transaction.Transaction; import java.util.ArrayList; @@ -15,4 +16,17 @@ public class TransactionList { public TransactionList() { this.transactions = new ArrayList<>(); } + + public Transaction getEntry(int index) { return transactions.get(index - 1);} + + public void removeEntry(int index) {transactions.remove(index - 1);} + + public static void deleteEntry(TransactionList input, int index) { + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + Transaction deleted = input.getEntry(index); + String information = deleted.getDescription(); + input.removeEntry(index); + System.out.println("MOOOOOO.... I HAVE DELETED THE FOLLOWING TRANSACTION:" + information); + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + } } From 9c84718f5756b2d6133fdf021e0b6b245a7121c5 Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 5 Oct 2022 02:31:20 +0800 Subject: [PATCH 026/416] Change the date format for transactions Temporary change to read the date as a string. To be updated into LocalDate type in future. --- .../seedu/duke/data/transaction/Expense.java | 4 +-- .../seedu/duke/data/transaction/Income.java | 4 +-- .../duke/data/transaction/Transaction.java | 35 ++++--------------- 3 files changed, 10 insertions(+), 33 deletions(-) diff --git a/src/main/java/seedu/duke/data/transaction/Expense.java b/src/main/java/seedu/duke/data/transaction/Expense.java index 1e8fac771..939a35497 100644 --- a/src/main/java/seedu/duke/data/transaction/Expense.java +++ b/src/main/java/seedu/duke/data/transaction/Expense.java @@ -5,7 +5,7 @@ * Records the amount, category and the date of spending. */ public class Expense extends Transaction { - public Expense(String description, int amount, String category, int day, int month, int year) { - super(description, amount, category, day, month, year); + public Expense(String description, int amount, String category, String date) { + super(description, amount, category, date); } } diff --git a/src/main/java/seedu/duke/data/transaction/Income.java b/src/main/java/seedu/duke/data/transaction/Income.java index e47f7fc26..057ccb5b3 100644 --- a/src/main/java/seedu/duke/data/transaction/Income.java +++ b/src/main/java/seedu/duke/data/transaction/Income.java @@ -5,7 +5,7 @@ * Records the amount, category and date of the income. */ public class Income extends Transaction { - public Income(String description, int amount, String category, int day, int month, int year) { - super(description, amount, category, day, month, year); + public Income(String description, int amount, String category, String date) { + super(description, amount, category, date); } } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 7d283dbe8..8557e7db1 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -4,17 +4,13 @@ public class Transaction { private String description; private int amount; private String category; //category of income or expense - private int day; - private int month; - private int year; + private String date; - public Transaction(String description, int amount, String category, int day, int month, int year) { + public Transaction(String description, int amount, String category, String date) { this.description = description; this.amount = amount; this.category = category; - this.day = day; - this.month = month; - this.year = year; + this.date = date; } public String getDescription() { @@ -41,27 +37,8 @@ public void setCategory(String category) { this.category = category; } - public int getDay() { - return day; - } - - public void setDay(int day) { - this.day = day; - } - - public int getMonth() { - return month; - } - - public void setMonth(int month) { - this.month = month; - } - - public int getYear() { - return year; - } - - public void setYear(int year) { - this.year = year; + @Override + public String toString() { + return "Description: " + description + " Amount: " + amount + " Category: " + category + " Date: " + date; } } \ No newline at end of file From 55ba7986955d53304b1d891dc78422deed351bf6 Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 5 Oct 2022 02:35:49 +0800 Subject: [PATCH 027/416] Create an exception class and define two type of exceptions MoolahException.java is the abstract class of all the exceptions defined in the application. Two new exceptions are defined. AddTransactionMissingTagException.java: For missing tags in add command AddTransactionUnknownTypeException.java: For unknown entered type of transaction given Add two error messages in ErrorMessages.java for the two new exceptions. --- .../java/seedu/duke/common/ErrorMessages.java | 4 +++- .../AddTransactionMissingTagException.java | 16 ++++++++++++++++ .../AddTransactionUnknownTypeException.java | 16 ++++++++++++++++ .../seedu/duke/exception/MoolahException.java | 13 +++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java create mode 100644 src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java create mode 100644 src/main/java/seedu/duke/exception/MoolahException.java diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index f8b0bef3b..5c5b1f2cf 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -4,7 +4,9 @@ * Provides enum variables for storing custom program error messages. */ public enum ErrorMessages { - MESSAGE_ERROR_INVALID_COMMAND("Oops, you have entered an invalid command."); + MESSAGE_ERROR_INVALID_COMMAND("Oops, you have entered an invalid command."), + MESSAGE_ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), + MESSAGE_ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"); public final String message; diff --git a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java b/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java new file mode 100644 index 000000000..03a58e0fe --- /dev/null +++ b/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class AddTransactionMissingTagException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_MISSING_TAG.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java b/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java new file mode 100644 index 000000000..afcbf1735 --- /dev/null +++ b/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class AddTransactionUnknownTypeException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_TYPE.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/MoolahException.java b/src/main/java/seedu/duke/exception/MoolahException.java new file mode 100644 index 000000000..d3ba3d9a7 --- /dev/null +++ b/src/main/java/seedu/duke/exception/MoolahException.java @@ -0,0 +1,13 @@ +package seedu.duke.exception; + +/** + * Represents the base class of the exceptions defined for Moolah Manager. + */ +public abstract class MoolahException extends Exception { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + public abstract String getMessage(); +} From a3d65a993dffd0b170bc0e3ce7d87536425488f5 Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 5 Oct 2022 02:41:18 +0800 Subject: [PATCH 028/416] Implement a basic add transaction feature Parser.java: Add the logic for checking for missing tags and incorrect transaction type, and the logic for calling the transaction list to add the new transaction. TransactionList.java: Add two methods for adding transaction of corresponding type. Add a skeleton code for listing transaction for testing purpose. Duke.java: To call Parser.parse() which can capture Moolah manager exception now. Ui.java: Add a method to print error message. --- src/main/java/seedu/duke/Duke.java | 2 +- src/main/java/seedu/duke/Parser.java | 119 +++++++++++++++++- src/main/java/seedu/duke/Ui.java | 6 + .../java/seedu/duke/data/TransactionList.java | 18 +++ 4 files changed, 142 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index e8bbb7da6..3482bf36a 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -16,7 +16,7 @@ public static void main(String[] args) { inData = scan.nextLine(); inData = inData.trim(); - if (!Parser.processInput(inData, transactions)) { + if (!Parser.parse(inData, transactions)) { break; } } diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index ab270f30f..4ef76c16f 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -2,6 +2,9 @@ import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.AddTransactionMissingTagException; +import seedu.duke.exception.AddTransactionUnknownTypeException; +import seedu.duke.exception.MoolahException; import java.util.ArrayList; @@ -9,18 +12,37 @@ public class Parser { static final boolean IS_EXIT = false; static final boolean IS_CONTINUE = true; + /** + * Parses the user input and deal with any input error returned + * + * @param inData The user input. + * @param transactions The array which would be operated on. + * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. + */ + public static boolean parse(String inData, TransactionList transactions) { + boolean isContinue = IS_CONTINUE; + try { + isContinue = processInput(inData, transactions); + } catch (MoolahException exception) { + Ui.showErrorMessage(exception.getMessage()); + } + return isContinue; + } + /** * Parses the user input and processes it with the transactions arraylist. * * @param inData The user input. * @param transactions The array which would be operated on. * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. + * @throws MoolahException Any command input exceptions captured by Moolah Manager. */ - public static boolean processInput(String inData, TransactionList transactions) { + private static boolean processInput(String inData, TransactionList transactions) throws MoolahException { if (inData.equals("help")) { Ui.showHelpList(); } else if (inData.equals("list")) { // Prints all transactions if input is equal to "list" + transactions.list(); } else if (inData.equals("purge")) { // Shows confirmation prompt before deleting all transactions @@ -32,7 +54,7 @@ public static boolean processInput(String inData, TransactionList transactions) Ui.showInvalidCommand(); } else if (inData.contains(" ")) { // Further parses the user input for user transaction commands - String[] userInput = inData.split(" "); + String[] userInput = inData.split(" ", 2); String command = userInput[0]; switch (command) { case "delete": @@ -46,6 +68,7 @@ Checks if userInput is in the correct format by further parsing(e.g. such as cor Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist */ + parseAddCommand(userInput[1], transactions); break; case "edit": @@ -64,4 +87,96 @@ Checks if userInput is in the correct format by further parsing(e.g. such as cor } return IS_CONTINUE; } + + /** + * Parses the add transaction command by checking if the compulsory tags exist followed by adding the transaction. + * Then executes the command to add the transaction into the list. + * + * @param userInput The user input after the "add" command word. + * @throws AddTransactionMissingTagException Exceptions related to "add" command. + */ + private static void parseAddCommand(String userInput, TransactionList transactions) throws MoolahException { + String[] splits = userInput.split(" "); + checkTagsExist(splits); + // TODO: To check that each parameter is in correct format, e.g. amount should be valid integer/double + // TODO: To move the add transaction logic below to Command class in Command.execute() + String description = ""; + int amount = 0; + String category = ""; + String date = ""; + String type = ""; + + for (String split : splits) { + String tag = split.substring(0, 2); + String parameter = split.substring(2); + switch (tag) { + case "t/": + type = parameter; + break; + case "c/": + category = parameter; + break; + case "a/": + amount = Integer.parseInt(parameter); + break; + case "d/": + date = parameter; + break; + case "i/": + description = parameter; + break; + default: + break; + } + } + + if (type.equals("expense")) { + transactions.addExpense(description, amount, category, date); + } else if (type.equals("income")) { + transactions.addIncome(description, amount, category, date); + } else { + throw new AddTransactionUnknownTypeException(); + } + } + + /** + * Check if the targeted tags exists in the split user inputs. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws AddTransactionMissingTagException Missing tag exception + */ + private static void checkTagsExist(String[] splits) throws AddTransactionMissingTagException { + // TODO: To add the tags into Command class instead + String[] tags = new String[]{ + "t/", + "c/", + "a/", + "d/", + "i/" + }; + for (String tag : tags) { + boolean found = findMatchingTagFromInputs(tag, splits); + if (!found) { + throw new AddTransactionMissingTagException(); + } + } + } + + /** + * Returns a boolean value on whether a tag can be found among the split user inputs. + * + * @param tag A specific tag used to locate the command parameter. + * @param splits The user input after the command word, split into a list for every space found. + * @return Whether the tag is found within the split inputs. + */ + private static boolean findMatchingTagFromInputs(String tag, String[] splits) { + boolean found = false; + for (String split : splits) { + if (split.startsWith(tag)) { + found = true; + break; + } + } + return found; + } } diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 2e15f30bd..64497018e 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -41,6 +41,12 @@ public static void showExitMessage() { ); } + public static void showErrorMessage(String errorMessage) { + printMessages( + errorMessage + ); + } + public static void showInvalidCommand() { printMessages( ErrorMessages.MESSAGE_ERROR_INVALID_COMMAND.toString(), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b796cd1b3..51bf1284d 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -1,5 +1,7 @@ package seedu.duke.data; +import seedu.duke.data.transaction.Expense; +import seedu.duke.data.transaction.Income; import seedu.duke.data.transaction.Transaction; import java.util.ArrayList; @@ -15,4 +17,20 @@ public class TransactionList { public TransactionList() { this.transactions = new ArrayList<>(); } + + public void addExpense(String description, int amount, String category, String date) { + transactions.add(new Expense(description, amount, category, date)); + } + + public void addIncome(String description, int amount, String category, String date) { + transactions.add(new Income(description, amount, category, date)); + } + + public void list () { + for (Transaction t : transactions) { + System.out.println(t.toString()); + } + } + + } From 07c78b4efd162b9b6a85953d4ac778d626f4c58c Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 5 Oct 2022 02:43:42 +0800 Subject: [PATCH 029/416] Add test cases for adding transaction feature --- text-ui-test/EXPECTED.TXT | 16 ++++++++++++++++ text-ui-test/input.txt | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 192007e60..cbde0834e 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -2,3 +2,19 @@ ____________________________________________________________ Hello! I'm your Moolah Manager! Enter if you need the list of commands. ____________________________________________________________ +Description: NIL Amount: 20 Category: food Date: 13092022 +Description: jan_salary Amount: 2000 Category: salary Date: jan +Description: bus_fare Amount: 1 Category: transport Date: monday +____________________________________________________________ +Type of transaction given is invalid, please check your input! +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! +____________________________________________________________ +Description: NIL Amount: 20 Category: food Date: 13092022 +Description: jan_salary Amount: 2000 Category: salary Date: jan +Description: bus_fare Amount: 1 Category: transport Date: monday +Description: thank_you_boss Amount: 1000000 Category: bonus Date: everyday +____________________________________________________________ +Goodbye and see you soon. +____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index e69de29bb..7261d85af 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -0,0 +1,9 @@ +add t/expense c/food a/20 d/13092022 i/NIL +add t/income c/salary a/2000 d/jan i/jan_salary +add t/expense c/transport a/1 d/monday i/bus_fare +list +add t/income c/bonus a/1000000 d/everyday i/thank_you_boss +add t/heheheh c/bonus a/1000000 d/everyday i/thank_you_boss +add t/income c/bonus a/1000000 d/everyday +list +bye From 2fdfd063a74410dfe34cb31ef9fe3189baa9ae7a Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 5 Oct 2022 02:55:21 +0800 Subject: [PATCH 030/416] Format files to align with Java coding standard --- src/main/java/seedu/duke/Parser.java | 17 ++++++++--------- .../java/seedu/duke/data/TransactionList.java | 2 +- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 4ef76c16f..bfcae5a5e 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -13,7 +13,7 @@ public class Parser { static final boolean IS_CONTINUE = true; /** - * Parses the user input and deal with any input error returned + * Parses the user input and deal with any input error returned. * * @param inData The user input. * @param transactions The array which would be operated on. @@ -107,8 +107,8 @@ private static void parseAddCommand(String userInput, TransactionList transactio String type = ""; for (String split : splits) { - String tag = split.substring(0, 2); - String parameter = split.substring(2); + String tag = split.substring(0, 2); + String parameter = split.substring(2); switch (tag) { case "t/": type = parameter; @@ -147,12 +147,11 @@ private static void parseAddCommand(String userInput, TransactionList transactio */ private static void checkTagsExist(String[] splits) throws AddTransactionMissingTagException { // TODO: To add the tags into Command class instead - String[] tags = new String[]{ - "t/", - "c/", - "a/", - "d/", - "i/" + String[] tags = new String[]{"t/", + "c/", + "a/", + "d/", + "i/" }; for (String tag : tags) { boolean found = findMatchingTagFromInputs(tag, splits); diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 51bf1284d..7362904db 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -26,7 +26,7 @@ public void addIncome(String description, int amount, String category, String da transactions.add(new Income(description, amount, category, date)); } - public void list () { + public void list() { for (Transaction t : transactions) { System.out.println(t.toString()); } From 645e75c83a23f1da087b93635ffc8d31e3dde6cb Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 5 Oct 2022 08:34:49 +0800 Subject: [PATCH 031/416] Modify some comments to comply with Java Coding Standard --- src/main/java/seedu/duke/Duke.java | 2 +- src/main/java/seedu/duke/Ui.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 3482bf36a..a3a1bec43 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -12,7 +12,7 @@ public static void main(String[] args) { Ui.showGreeting(); while (scan.hasNextLine() && true) { - // continuously receive user input + // Receives user input continuously inData = scan.nextLine(); inData = inData.trim(); diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 64497018e..3293ee3b9 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -9,10 +9,11 @@ public class Ui { * * @param messages A string of variable arguments. */ + + //@@author chydarren-reused + // Reused from https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/ui/TextUi.java + // with minor modifications public static void printMessages(String... messages) { - //@@author chydarren-reused - // Reused from https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/ui/TextUi.java - // with minor modifications System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER.toString()); // Prints the string of arguments line by line in a loop for (String message : messages) { @@ -20,6 +21,7 @@ public static void printMessages(String... messages) { } System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER.toString()); } + //@@author public static void showGreeting() { printMessages( From a7629107995ba034d1cef89b71da362cc2b3caf2 Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 5 Oct 2022 09:17:11 +0800 Subject: [PATCH 032/416] Add ability to list all transactions --- src/main/java/seedu/duke/Parser.java | 9 +++++++-- src/main/java/seedu/duke/Ui.java | 13 +++++++++++++ .../java/seedu/duke/common/InfoMessages.java | 3 +++ .../java/seedu/duke/data/TransactionList.java | 18 +++++++++++++++--- .../duke/data/transaction/Transaction.java | 5 +++-- 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index bfcae5a5e..cd078278a 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,5 +1,6 @@ package seedu.duke; +import seedu.duke.common.InfoMessages; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.AddTransactionMissingTagException; @@ -42,8 +43,12 @@ private static boolean processInput(String inData, TransactionList transactions) Ui.showHelpList(); } else if (inData.equals("list")) { // Prints all transactions if input is equal to "list" - transactions.list(); - + String transactionsList = transactions.listTransactions(); + if (transactionsList.isEmpty()) { + Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_LIST_EMPTY.toString()); + return IS_CONTINUE; + } + Ui.showTransactionsList(transactionsList, InfoMessages.MESSAGE_INFO_LIST.toString()); } else if (inData.equals("purge")) { // Shows confirmation prompt before deleting all transactions } else if (inData.equals(("bye"))) { diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 3293ee3b9..521725c4e 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -37,6 +37,13 @@ public static void showHelpList() { ); } + public static void showTransactionsList(String transactionsList, String message) { + printMessages( + message, + transactionsList + ); + } + public static void showExitMessage() { printMessages( InfoMessages.MESSAGE_INFO_EXIT.toString() @@ -49,6 +56,12 @@ public static void showErrorMessage(String errorMessage) { ); } + public static void showInfoMessage(String infoMessage) { + printMessages( + infoMessage + ); + } + public static void showInvalidCommand() { printMessages( ErrorMessages.MESSAGE_ERROR_INVALID_COMMAND.toString(), diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index a9127a525..56ed3489b 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -8,6 +8,9 @@ public enum InfoMessages { MESSAGE_INFO_GREET("Hello! I'm your Moolah Manager!"), MESSAGE_INFO_HELP_PROMPT("Enter if you need the list of commands."), MESSAGE_INFO_HELP_GREET("I'm here to help. Here are the possible commands!"), + MESSAGE_INFO_LIST_EMPTY("There are no records of your transactions found."), + MESSAGE_INFO_LIST("Below are the records of your transactions:"), + MESSAGE_INFO_TRANSACTION_COUNT("You have %d transactions in your transaction history."), MESSAGE_INFO_EXIT("Goodbye and see you soon."); public final String message; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 7362904db..81229eed4 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -1,5 +1,6 @@ package seedu.duke.data; +import seedu.duke.common.InfoMessages; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; import seedu.duke.data.transaction.Transaction; @@ -12,6 +13,9 @@ * These operations include adding, listing, modifying, deleting and purging. */ public class TransactionList { + private static final String EMPTY_STRING = ""; + private static final String LINE_SEPARATOR = System.lineSeparator(); + private ArrayList transactions; public TransactionList() { @@ -26,10 +30,18 @@ public void addIncome(String description, int amount, String category, String da transactions.add(new Income(description, amount, category, date)); } - public void list() { - for (Transaction t : transactions) { - System.out.println(t.toString()); + public String listTransactions() { + String transactionsList = EMPTY_STRING; + // Loops each task from the transactions list + for (Transaction transaction : transactions) { + transactionsList += transaction.toString() + LINE_SEPARATOR; + } + if (!transactionsList.equals(EMPTY_STRING)) { + // Includes the count of the transactions with the transaction list + transactionsList += String.format(InfoMessages.MESSAGE_INFO_TRANSACTION_COUNT.toString(), transactions.size()); } + + return transactionsList; } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 8557e7db1..8ef11d7d4 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -3,7 +3,7 @@ public class Transaction { private String description; private int amount; - private String category; //category of income or expense + private String category; // category of income or expense private String date; public Transaction(String description, int amount, String category, String date) { @@ -39,6 +39,7 @@ public void setCategory(String category) { @Override public String toString() { - return "Description: " + description + " Amount: " + amount + " Category: " + category + " Date: " + date; + return "Date: " + date + " Category: " + category + " Description: " + description + + " Amount: " + amount; } } \ No newline at end of file From 0acc1ec08b4e1b0c12102aa77e04fff990d4d4e5 Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 5 Oct 2022 09:24:37 +0800 Subject: [PATCH 033/416] Shorten the length of characters in line 41 of TransactionList class --- src/main/java/seedu/duke/data/TransactionList.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 81229eed4..4568b4b06 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -38,7 +38,8 @@ public String listTransactions() { } if (!transactionsList.equals(EMPTY_STRING)) { // Includes the count of the transactions with the transaction list - transactionsList += String.format(InfoMessages.MESSAGE_INFO_TRANSACTION_COUNT.toString(), transactions.size()); + transactionsList += String.format(InfoMessages.MESSAGE_INFO_TRANSACTION_COUNT.toString(), + transactions.size()); } return transactionsList; From ec659cb249eda08bd9d98d3ec20413446a96dc41 Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 5 Oct 2022 09:30:51 +0800 Subject: [PATCH 034/416] Update input.txt and EXPECTED.TXT to include testing of new feature --- text-ui-test/EXPECTED.TXT | 25 ++++++++++++++++++------- text-ui-test/input.txt | 1 + 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index cbde0834e..117ddf750 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -2,19 +2,30 @@ ____________________________________________________________ Hello! I'm your Moolah Manager! Enter if you need the list of commands. ____________________________________________________________ -Description: NIL Amount: 20 Category: food Date: 13092022 -Description: jan_salary Amount: 2000 Category: salary Date: jan -Description: bus_fare Amount: 1 Category: transport Date: monday +____________________________________________________________ +There are no records of your transactions found. +____________________________________________________________ +____________________________________________________________ +Below are the records of your transactions: +Date: 13092022 Category: food Description: NIL Amount: 20 +Date: jan Category: salary Description: jan_salary Amount: 2000 +Date: monday Category: transport Description: bus_fare Amount: 1 +You have 3 transactions in your transaction history. +____________________________________________________________ ____________________________________________________________ Type of transaction given is invalid, please check your input! ____________________________________________________________ ____________________________________________________________ Mandatory tag(s) missing, please check your input! ____________________________________________________________ -Description: NIL Amount: 20 Category: food Date: 13092022 -Description: jan_salary Amount: 2000 Category: salary Date: jan -Description: bus_fare Amount: 1 Category: transport Date: monday -Description: thank_you_boss Amount: 1000000 Category: bonus Date: everyday +____________________________________________________________ +Below are the records of your transactions: +Date: 13092022 Category: food Description: NIL Amount: 20 +Date: jan Category: salary Description: jan_salary Amount: 2000 +Date: monday Category: transport Description: bus_fare Amount: 1 +Date: everyday Category: bonus Description: thank_you_boss Amount: 1000000 +You have 4 transactions in your transaction history. +____________________________________________________________ ____________________________________________________________ Goodbye and see you soon. ____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 7261d85af..c99999244 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,3 +1,4 @@ +list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/jan i/jan_salary add t/expense c/transport a/1 d/monday i/bus_fare From fb5f16fcb58539a995a1e30069ee800b55c30fe0 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Wed, 5 Oct 2022 14:41:11 +0800 Subject: [PATCH 035/416] Resolve conflicts --- src/main/java/seedu/duke/Parser.java | 4 ---- src/main/java/seedu/duke/data/TransactionList.java | 7 +------ src/main/java/seedu/duke/data/transaction/Transaction.java | 2 +- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 6cd76ab11..72888141d 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -70,11 +70,7 @@ Checks if userInput is in the correct format by further parsing(e.g. such as cor Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist */ -<<<<<<< HEAD -======= parseAddCommand(userInput[1], transactions); - ->>>>>>> dcc2af74f889027e26bf612505d944dff4c144e3 break; case "edit": /* diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index a30d2cb16..5d7b20554 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -1,11 +1,8 @@ package seedu.duke.data; -<<<<<<< HEAD import seedu.duke.common.InfoMessages; -======= import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; ->>>>>>> dcc2af74f889027e26bf612505d944dff4c144e3 import seedu.duke.data.transaction.Transaction; import java.util.ArrayList; @@ -22,7 +19,6 @@ public TransactionList() { this.transactions = new ArrayList<>(); } -<<<<<<< HEAD public Transaction getEntry(int index) { return transactions.get(index - 1);} public void removeEntry(int index) {transactions.remove(index - 1);} @@ -35,7 +31,7 @@ public static void deleteEntry(TransactionList input, int index) { System.out.println("MOOOOOO.... I HAVE DELETED THE FOLLOWING TRANSACTION:" + information); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } -======= + public void addExpense(String description, int amount, String category, String date) { transactions.add(new Expense(description, amount, category, date)); } @@ -51,5 +47,4 @@ public void list() { } ->>>>>>> dcc2af74f889027e26bf612505d944dff4c144e3 } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 8557e7db1..e2a997d7f 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -4,7 +4,7 @@ public class Transaction { private String description; private int amount; private String category; //category of income or expense - private String date; + private final String date; public Transaction(String description, int amount, String category, String date) { this.description = description; From e0576ed91a1de69644e7b09d2f3f9a012c3bf27c Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Wed, 5 Oct 2022 18:15:41 +0800 Subject: [PATCH 036/416] Add exceptions for Category and Amount tags --- src/main/java/seedu/duke/Parser.java | 74 +++++++++++++++---- src/main/java/seedu/duke/Ui.java | 6 ++ .../java/seedu/duke/common/ErrorMessages.java | 3 +- ...ddTransactionInvalidCategoryException.java | 16 ++++ 4 files changed, 83 insertions(+), 16 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index cd078278a..2ebee7f3e 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,8 +1,10 @@ package seedu.duke; +import seedu.duke.common.ErrorMessages; import seedu.duke.common.InfoMessages; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.AddTransactionInvalidCategoryException; import seedu.duke.exception.AddTransactionMissingTagException; import seedu.duke.exception.AddTransactionUnknownTypeException; import seedu.duke.exception.MoolahException; @@ -110,6 +112,7 @@ private static void parseAddCommand(String userInput, TransactionList transactio String category = ""; String date = ""; String type = ""; + boolean inputIsValid = true; for (String split : splits) { String tag = split.substring(0, 2); @@ -119,10 +122,22 @@ private static void parseAddCommand(String userInput, TransactionList transactio type = parameter; break; case "c/": - category = parameter; + try { + parseCategoryTag(parameter); + category = parameter; + } catch (AddTransactionInvalidCategoryException e) { + Ui.printMessages(String.valueOf(ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY)); + inputIsValid = false; + } + break; case "a/": - amount = Integer.parseInt(parameter); + try { + amount = Integer.parseInt(parameter); + } catch (NumberFormatException e) { + Ui.showNonNumericError(); + inputIsValid = false; + } break; case "d/": date = parameter; @@ -134,30 +149,59 @@ private static void parseAddCommand(String userInput, TransactionList transactio break; } } + if (inputIsValid) { + switch (type) { + case "expense": + transactions.addExpense(description, amount, category, date); + break; + case "income": + transactions.addIncome(description, amount, category, date); + break; + default: + throw new AddTransactionUnknownTypeException(); + } + } + } - if (type.equals("expense")) { - transactions.addExpense(description, amount, category, date); - } else if (type.equals("income")) { - transactions.addIncome(description, amount, category, date); - } else { - throw new AddTransactionUnknownTypeException(); + /** + * Processes the parameter. If it is an invalid parameter an exception error would be thrown. + * + * @param parameter The user input after the user tag. + * @throws AddTransactionInvalidCategoryException Invalid category parameter exception. + */ + private static void parseCategoryTag(String parameter) throws AddTransactionInvalidCategoryException { + if (isNumeric(parameter)) { + throw new AddTransactionInvalidCategoryException(); + } + + } + + /** + * Check if the parameter contains numeric characters. + * + * @param parameter The user input after the user tag. + * @return true if there are numeric characters within the parameter. + */ + public static boolean isNumeric(String parameter) { + char[] characters = parameter.toCharArray(); + for (char character : characters) { + if (Character.isDigit(character)) { + return true; + } } + return false; } + /** * Check if the targeted tags exists in the split user inputs. * * @param splits The user input after the command word, split into a list for every space found. - * @throws AddTransactionMissingTagException Missing tag exception + * @throws AddTransactionMissingTagException Missing tag exception. */ private static void checkTagsExist(String[] splits) throws AddTransactionMissingTagException { // TODO: To add the tags into Command class instead - String[] tags = new String[]{"t/", - "c/", - "a/", - "d/", - "i/" - }; + String[] tags = new String[]{"t/", "c/", "a/", "d/", "i/"}; for (String tag : tags) { boolean found = findMatchingTagFromInputs(tag, splits); if (!found) { diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 521725c4e..261484fe4 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -56,6 +56,12 @@ public static void showErrorMessage(String errorMessage) { ); } + + public static void showNonNumericError() { + printMessages( + "Non-Numeric input detected! Please enter a numerical amount!" + ); + } public static void showInfoMessage(String infoMessage) { printMessages( infoMessage diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 5c5b1f2cf..01f8586c0 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -6,7 +6,8 @@ public enum ErrorMessages { MESSAGE_ERROR_INVALID_COMMAND("Oops, you have entered an invalid command."), MESSAGE_ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), - MESSAGE_ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"); + MESSAGE_ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), + MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"); public final String message; diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java b/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java new file mode 100644 index 000000000..5721f2b3e --- /dev/null +++ b/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class AddTransactionInvalidCategoryException extends MoolahException{ + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY.toString(); + } + +} From 405ee4afa35269974d60ec6164393af4a0506284 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Wed, 5 Oct 2022 18:22:52 +0800 Subject: [PATCH 037/416] Reformat spacing in classes --- src/main/java/seedu/duke/Ui.java | 36 +++++-------------- .../AddTransactionUnknownTypeException.java | 2 +- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 261484fe4..46085469c 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -24,54 +24,36 @@ public static void printMessages(String... messages) { //@@author public static void showGreeting() { - printMessages( - InfoMessages.MESSAGE_INFO_GREET.toString(), - InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString() - ); + printMessages(InfoMessages.MESSAGE_INFO_GREET.toString(), InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString()); } public static void showHelpList() { // To include the other messages for commands - printMessages( - InfoMessages.MESSAGE_INFO_HELP_GREET.toString() - ); + printMessages(InfoMessages.MESSAGE_INFO_HELP_GREET.toString()); } public static void showTransactionsList(String transactionsList, String message) { - printMessages( - message, - transactionsList - ); + printMessages(message, transactionsList); } public static void showExitMessage() { - printMessages( - InfoMessages.MESSAGE_INFO_EXIT.toString() - ); + printMessages(InfoMessages.MESSAGE_INFO_EXIT.toString()); } public static void showErrorMessage(String errorMessage) { - printMessages( - errorMessage - ); + printMessages(errorMessage); } public static void showNonNumericError() { - printMessages( - "Non-Numeric input detected! Please enter a numerical amount!" - ); + printMessages("Non-Numeric input detected! Please enter a numerical amount!"); } + public static void showInfoMessage(String infoMessage) { - printMessages( - infoMessage - ); + printMessages(infoMessage); } public static void showInvalidCommand() { - printMessages( - ErrorMessages.MESSAGE_ERROR_INVALID_COMMAND.toString(), - InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString() - ); + printMessages(ErrorMessages.MESSAGE_ERROR_INVALID_COMMAND.toString(), InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString()); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java b/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java index afcbf1735..f03987ddb 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java @@ -13,4 +13,4 @@ public class AddTransactionUnknownTypeException extends MoolahException { public String getMessage() { return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_TYPE.toString(); } -} +} \ No newline at end of file From 4c82f58c333dc50946ace1501b63bf412c4823a7 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Wed, 5 Oct 2022 18:27:06 +0800 Subject: [PATCH 038/416] Reformat spacing and indented long code --- src/main/java/seedu/duke/Ui.java | 3 ++- .../duke/exception/AddTransactionInvalidCategoryException.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 46085469c..83b4e31c8 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -54,6 +54,7 @@ public static void showInfoMessage(String infoMessage) { } public static void showInvalidCommand() { - printMessages(ErrorMessages.MESSAGE_ERROR_INVALID_COMMAND.toString(), InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString()); + printMessages(ErrorMessages.MESSAGE_ERROR_INVALID_COMMAND.toString(), + InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString()); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java b/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java index 5721f2b3e..5fba79d07 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddTransactionInvalidCategoryException extends MoolahException{ +public class AddTransactionInvalidCategoryException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * From 4d89b5aba8e116c795832b58772be0f0b3f4ec81 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Wed, 5 Oct 2022 18:33:26 +0800 Subject: [PATCH 039/416] Add Purge Command and Delete Command --- src/main/java/seedu/duke/Parser.java | 9 +++++++++ .../java/seedu/duke/common/InfoMessages.java | 4 +++- .../java/seedu/duke/data/TransactionList.java | 17 ++++++++++++++--- .../duke/data/transaction/Transaction.java | 5 ----- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 8593aaabd..5784845e7 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -8,6 +8,7 @@ import seedu.duke.exception.MoolahException; import java.util.ArrayList; +import java.util.Scanner; public class Parser { static final boolean IS_EXIT = false; @@ -51,6 +52,14 @@ private static boolean processInput(String inData, TransactionList transactions) Ui.showTransactionsList(transactionsList, InfoMessages.MESSAGE_INFO_LIST.toString()); } else if (inData.equals("purge")) { // Shows confirmation prompt before deleting all transactions + Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_WARNING.toString()); + Scanner confirmation = new Scanner(System.in); + String input = confirmation.nextLine(); + if (input.equals("Yes")) { + TransactionList.purgeEntries(transactions); + } else { + System.out.println("MOOOOOO.... Aborting Command, returning to Home."); + } } else if (inData.equals(("bye"))) { Ui.showExitMessage(); // Exits loop diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 56ed3489b..5c9180ecf 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -11,7 +11,9 @@ public enum InfoMessages { MESSAGE_INFO_LIST_EMPTY("There are no records of your transactions found."), MESSAGE_INFO_LIST("Below are the records of your transactions:"), MESSAGE_INFO_TRANSACTION_COUNT("You have %d transactions in your transaction history."), - MESSAGE_INFO_EXIT("Goodbye and see you soon."); + MESSAGE_INFO_EXIT("Goodbye and see you soon."), + + MESSAGE_INFO_WARNING("MOOOOOO.... Are you sure you want to proceed with this command? Please enter Yes to confirm."); public final String message; /** diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 53ffb7be3..b5b8af63b 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -22,16 +22,27 @@ public TransactionList() { this.transactions = new ArrayList<>(); } - public Transaction getEntry(int index) { return transactions.get(index - 1);} + public static void purgeEntries(TransactionList input) { + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + input.empty(); + System.out.println("MOOOOOO.... All of your transactions have been purged."); + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + } + + private void empty() { + transactions.clear(); + } + + private Transaction getEntry(int index) { return transactions.get(index - 1);} - public void removeEntry(int index) {transactions.remove(index - 1);} + private void removeEntry(int index) {transactions.remove(index - 1);} public static void deleteEntry(TransactionList input, int index) { System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); Transaction deleted = input.getEntry(index); String information = deleted.getDescription(); input.removeEntry(index); - System.out.println("MOOOOOO.... I HAVE DELETED THE FOLLOWING TRANSACTION:" + information); + System.out.println("MOOOOOO.... I HAVE DELETED THE FOLLOWING TRANSACTION: " + information); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 261ee44d1..8ef11d7d4 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -3,13 +3,8 @@ public class Transaction { private String description; private int amount; -<<<<<<< HEAD - private String category; //category of income or expense - private final String date; -======= private String category; // category of income or expense private String date; ->>>>>>> 5e7989d03357ce54e45d43032825b2dfac52f35f public Transaction(String description, int amount, String category, String date) { this.description = description; From 50c69078a02c2f452a422b337c516f4052b64e69 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Wed, 5 Oct 2022 18:42:57 +0800 Subject: [PATCH 040/416] Edited some changes to certain functions to provide output. --- src/main/java/seedu/duke/Parser.java | 2 ++ src/main/java/seedu/duke/data/TransactionList.java | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 927d39a65..0038f527e 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -60,7 +60,9 @@ private static boolean processInput(String inData, TransactionList transactions) if (input.equals("Yes")) { TransactionList.purgeEntries(transactions); } else { + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); System.out.println("MOOOOOO.... Aborting Command, returning to Home."); + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } } else if (inData.equals(("bye"))) { Ui.showExitMessage(); diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b5b8af63b..23682cf01 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -48,10 +48,16 @@ public static void deleteEntry(TransactionList input, int index) { public void addExpense(String description, int amount, String category, String date) { transactions.add(new Expense(description, amount, category, date)); + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println("MOOOOOO... I have added an Expense Transaction."); + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } public void addIncome(String description, int amount, String category, String date) { transactions.add(new Income(description, amount, category, date)); + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println("MOOOOOO... I have added an Income Transaction."); + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } public String listTransactions() { From 45d13674899dd423fde782baf5803599c07df963 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Wed, 5 Oct 2022 22:49:41 +0800 Subject: [PATCH 041/416] Edited to follow Coding Standards --- src/main/java/seedu/duke/Parser.java | 2 +- src/main/java/seedu/duke/common/InfoMessages.java | 2 +- src/main/java/seedu/duke/data/TransactionList.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 0038f527e..37858892e 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -57,7 +57,7 @@ private static boolean processInput(String inData, TransactionList transactions) Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_WARNING.toString()); Scanner confirmation = new Scanner(System.in); String input = confirmation.nextLine(); - if (input.equals("Yes")) { + if (input.equals("Y")) { TransactionList.purgeEntries(transactions); } else { System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 5c9180ecf..6159aea1f 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -13,7 +13,7 @@ public enum InfoMessages { MESSAGE_INFO_TRANSACTION_COUNT("You have %d transactions in your transaction history."), MESSAGE_INFO_EXIT("Goodbye and see you soon."), - MESSAGE_INFO_WARNING("MOOOOOO.... Are you sure you want to proceed with this command? Please enter Yes to confirm."); + MESSAGE_INFO_WARNING("MOOOOOO.... Are you sure you want to proceed with this command? Please enter Y to confirm."); public final String message; /** diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 23682cf01..3cbe058ac 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -33,9 +33,9 @@ private void empty() { transactions.clear(); } - private Transaction getEntry(int index) { return transactions.get(index - 1);} + private Transaction getEntry(int index) { return transactions.get(index - 1); } - private void removeEntry(int index) {transactions.remove(index - 1);} + private void removeEntry(int index) { transactions.remove(index - 1); } public static void deleteEntry(TransactionList input, int index) { System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); From 7a0060af6ef677435fc15cdcb0b7479c85b677fa Mon Sep 17 00:00:00 2001 From: brian-vb Date: Wed, 5 Oct 2022 23:06:45 +0800 Subject: [PATCH 042/416] Edit new errors that emerged after Gradle Test --- src/main/java/seedu/duke/data/TransactionList.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 3cbe058ac..5d459deb2 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -33,9 +33,13 @@ private void empty() { transactions.clear(); } - private Transaction getEntry(int index) { return transactions.get(index - 1); } + private Transaction getEntry(int index) { + return transactions.get(index - 1); + } - private void removeEntry(int index) { transactions.remove(index - 1); } + private void removeEntry(int index) { + transactions.remove(index - 1); + } public static void deleteEntry(TransactionList input, int index) { System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); From 042d4485cc987ebca6922a8189d4affca03b959b Mon Sep 17 00:00:00 2001 From: brian-vb Date: Thu, 6 Oct 2022 00:05:10 +0800 Subject: [PATCH 043/416] Modified the TextUITest File. --- text-ui-test/EXPECTED.TXT | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 117ddf750..0c2eeb1e4 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -6,6 +6,15 @@ ____________________________________________________________ There are no records of your transactions found. ____________________________________________________________ ____________________________________________________________ +MOOOOOO... I have added an Expense Transaction. +____________________________________________________________ +____________________________________________________________ +MOOOOOO... I have added an Income Transaction. +____________________________________________________________ +____________________________________________________________ +MOOOOOO... I have added an Expense Transaction. +____________________________________________________________ +____________________________________________________________ Below are the records of your transactions: Date: 13092022 Category: food Description: NIL Amount: 20 Date: jan Category: salary Description: jan_salary Amount: 2000 @@ -13,6 +22,9 @@ Date: monday Category: transport Description: bus_fare Amount: 1 You have 3 transactions in your transaction history. ____________________________________________________________ ____________________________________________________________ +MOOOOOO... I have added an Income Transaction. +____________________________________________________________ +____________________________________________________________ Type of transaction given is invalid, please check your input! ____________________________________________________________ ____________________________________________________________ From fe6b0cfe82450f2adf475f7917eafe5128887c52 Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 6 Oct 2022 01:58:09 +0800 Subject: [PATCH 044/416] Use LocalDate class to represent dates in transactions instead of String --- src/main/java/seedu/duke/Parser.java | 7 +++++-- src/main/java/seedu/duke/data/TransactionList.java | 6 +++--- src/main/java/seedu/duke/data/transaction/Expense.java | 4 +++- src/main/java/seedu/duke/data/transaction/Income.java | 4 +++- src/main/java/seedu/duke/data/transaction/Transaction.java | 6 ++++-- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 37858892e..44b74c39f 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -9,6 +9,8 @@ import seedu.duke.exception.AddTransactionUnknownTypeException; import seedu.duke.exception.MoolahException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Scanner; @@ -122,7 +124,7 @@ private static void parseAddCommand(String userInput, TransactionList transactio String description = ""; int amount = 0; String category = ""; - String date = ""; + LocalDate date = null; String type = ""; boolean inputIsValid = true; @@ -152,7 +154,8 @@ private static void parseAddCommand(String userInput, TransactionList transactio } break; case "d/": - date = parameter; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + date = LocalDate.parse(userInput, formatter); break; case "i/": description = parameter; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 5d459deb2..959a1c7c1 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -5,6 +5,7 @@ import seedu.duke.data.transaction.Income; import seedu.duke.data.transaction.Transaction; +import java.time.LocalDate; import java.util.ArrayList; /** @@ -50,14 +51,14 @@ public static void deleteEntry(TransactionList input, int index) { System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } - public void addExpense(String description, int amount, String category, String date) { + public void addExpense(String description, int amount, String category, LocalDate date) { transactions.add(new Expense(description, amount, category, date)); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); System.out.println("MOOOOOO... I have added an Expense Transaction."); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } - public void addIncome(String description, int amount, String category, String date) { + public void addIncome(String description, int amount, String category, LocalDate date) { transactions.add(new Income(description, amount, category, date)); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); System.out.println("MOOOOOO... I have added an Income Transaction."); @@ -79,5 +80,4 @@ public String listTransactions() { return transactionsList; } - } diff --git a/src/main/java/seedu/duke/data/transaction/Expense.java b/src/main/java/seedu/duke/data/transaction/Expense.java index 939a35497..9c3b59bf0 100644 --- a/src/main/java/seedu/duke/data/transaction/Expense.java +++ b/src/main/java/seedu/duke/data/transaction/Expense.java @@ -1,11 +1,13 @@ package seedu.duke.data.transaction; +import java.time.LocalDate; + /** * Represents the expense of the user added to the application. * Records the amount, category and the date of spending. */ public class Expense extends Transaction { - public Expense(String description, int amount, String category, String date) { + public Expense(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } } diff --git a/src/main/java/seedu/duke/data/transaction/Income.java b/src/main/java/seedu/duke/data/transaction/Income.java index 057ccb5b3..826826b07 100644 --- a/src/main/java/seedu/duke/data/transaction/Income.java +++ b/src/main/java/seedu/duke/data/transaction/Income.java @@ -1,11 +1,13 @@ package seedu.duke.data.transaction; +import java.time.LocalDate; + /** * Represents the income of the user added to the application. * Records the amount, category and date of the income. */ public class Income extends Transaction { - public Income(String description, int amount, String category, String date) { + public Income(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 8ef11d7d4..fa4043e21 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -1,12 +1,14 @@ package seedu.duke.data.transaction; +import java.time.LocalDate; + public class Transaction { private String description; private int amount; private String category; // category of income or expense - private String date; + private LocalDate date; - public Transaction(String description, int amount, String category, String date) { + public Transaction(String description, int amount, String category, LocalDate date) { this.description = description; this.amount = amount; this.category = category; From 2ce96d1740aabe98a03704c64b482cca764badbf Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 6 Oct 2022 02:12:50 +0800 Subject: [PATCH 045/416] Add new exception class and error message for invalid date during "add" --- .../java/seedu/duke/common/ErrorMessages.java | 3 ++- .../AddTransactionInvalidDateException.java | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 01f8586c0..72ea7ef3d 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -7,7 +7,8 @@ public enum ErrorMessages { MESSAGE_ERROR_INVALID_COMMAND("Oops, you have entered an invalid command."), MESSAGE_ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), MESSAGE_ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), - MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"); + MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), + MESSAGE_ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"); public final String message; diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java b/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java new file mode 100644 index 000000000..e89285369 --- /dev/null +++ b/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class AddTransactionInvalidDateException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_DATE.toString(); + } +} From 441c7c34541c051822177384a509bd5e9901c266 Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 6 Oct 2022 02:13:36 +0800 Subject: [PATCH 046/416] Update Parser.java to parse the user input for date to LocalDate object --- src/main/java/seedu/duke/Parser.java | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 44b74c39f..355a5ba07 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -3,15 +3,15 @@ import seedu.duke.common.ErrorMessages; import seedu.duke.common.InfoMessages; import seedu.duke.data.TransactionList; -import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.AddTransactionInvalidCategoryException; import seedu.duke.exception.AddTransactionMissingTagException; import seedu.duke.exception.AddTransactionUnknownTypeException; +import seedu.duke.exception.AddTransactionInvalidDateException; import seedu.duke.exception.MoolahException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; +import java.time.format.DateTimeParseException; import java.util.Scanner; public class Parser { @@ -154,8 +154,7 @@ private static void parseAddCommand(String userInput, TransactionList transactio } break; case "d/": - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - date = LocalDate.parse(userInput, formatter); + date = parseDateTag(userInput); break; case "i/": description = parameter; @@ -192,7 +191,7 @@ private static void parseCategoryTag(String parameter) throws AddTransactionInva } /** - * Check if the parameter contains numeric characters. + * Checks if the parameter contains numeric characters. * * @param parameter The user input after the user tag. * @return true if there are numeric characters within the parameter. @@ -209,7 +208,23 @@ public static boolean isNumeric(String parameter) { /** - * Check if the targeted tags exists in the split user inputs. + * Parse the user parameter input for date into a LocalDate object and returns it. + * @param parameter The user input after the user tag. + * @return The LocalDate object parsed from user input given. + * @throws AddTransactionInvalidDateException Invalid date format exception. + */ + private static LocalDate parseDateTag(String parameter) throws AddTransactionInvalidDateException { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ddMMyyyy"); + LocalDate date = LocalDate.parse(parameter, formatter); + return date; + } catch (DateTimeParseException exception) { + throw new AddTransactionInvalidDateException(); + } + } + + /** + * Checks if the targeted tags exists in the split user inputs. * * @param splits The user input after the command word, split into a list for every space found. * @throws AddTransactionMissingTagException Missing tag exception. From f9eb9b0534d107a83ec35673716870e050266b00 Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 6 Oct 2022 02:54:17 +0800 Subject: [PATCH 047/416] Modify output of transaction with formatted date DateFormats.java: New enum file to store the supported patterns for the date's input and output formats. Transaction.java: Update toString() method with new printing format. Parser.java: Update the file to use the enum created for date pattern. --- src/main/java/seedu/duke/Parser.java | 7 +++-- .../java/seedu/duke/common/DateFormats.java | 29 +++++++++++++++++++ .../duke/data/transaction/Transaction.java | 16 ++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/main/java/seedu/duke/common/DateFormats.java diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 355a5ba07..3b16b4c2a 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -14,6 +14,8 @@ import java.time.format.DateTimeParseException; import java.util.Scanner; +import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; + public class Parser { static final boolean IS_EXIT = false; static final boolean IS_CONTINUE = true; @@ -154,7 +156,7 @@ private static void parseAddCommand(String userInput, TransactionList transactio } break; case "d/": - date = parseDateTag(userInput); + date = parseDateTag(parameter); break; case "i/": description = parameter; @@ -209,13 +211,14 @@ public static boolean isNumeric(String parameter) { /** * Parse the user parameter input for date into a LocalDate object and returns it. + * * @param parameter The user input after the user tag. * @return The LocalDate object parsed from user input given. * @throws AddTransactionInvalidDateException Invalid date format exception. */ private static LocalDate parseDateTag(String parameter) throws AddTransactionInvalidDateException { try { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ddMMyyyy"); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_INPUT_PATTERN.toString()); LocalDate date = LocalDate.parse(parameter, formatter); return date; } catch (DateTimeParseException exception) { diff --git a/src/main/java/seedu/duke/common/DateFormats.java b/src/main/java/seedu/duke/common/DateFormats.java new file mode 100644 index 000000000..73ad808c8 --- /dev/null +++ b/src/main/java/seedu/duke/common/DateFormats.java @@ -0,0 +1,29 @@ +package seedu.duke.common; + +/** + * Provides enum variables for the approved date formats for input and output. + */ +public enum DateFormats { + DATE_INPUT_PATTERN("ddMMyyyy"), + DATE_OUTPUT_PATTERN("MMM dd yyyy"); + + public final String message; + + /** + * Instantiates a new date format when application initialises a new instance of this enum. + * + * @param message A string containing the date format. + */ + DateFormats(String message) { + this.message = message; + } + + /** + * Gets the date format as a string. + * + * @return A string containing the date format. + */ + public String toString() { + return message; + } +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index fa4043e21..b6b8e5416 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -1,6 +1,9 @@ package seedu.duke.data.transaction; import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; public class Transaction { private String description; @@ -39,9 +42,18 @@ public void setCategory(String category) { this.category = category; } + public String printFormattedDate() { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_OUTPUT_PATTERN.toString()); + return date.format(formatter); + } + + public String printFormattedCategory() { + return "[" + category + "]"; + } + @Override public String toString() { - return "Date: " + date + " Category: " + category + " Description: " + description - + " Amount: " + amount; + return printFormattedCategory() + " $" + amount + " at " + printFormattedDate() + + " | Description: " + description; } } \ No newline at end of file From 005370d42dc93fce695983ad81eb3fc5913ea8ce Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 6 Oct 2022 02:55:49 +0800 Subject: [PATCH 048/416] Update transactions output format for clearer and more concise display --- src/main/java/seedu/duke/data/TransactionList.java | 12 ++++++++---- .../java/seedu/duke/data/transaction/Expense.java | 11 +++++++++++ .../java/seedu/duke/data/transaction/Income.java | 11 +++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 959a1c7c1..699cd7312 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -52,16 +52,20 @@ public static void deleteEntry(TransactionList input, int index) { } public void addExpense(String description, int amount, String category, LocalDate date) { - transactions.add(new Expense(description, amount, category, date)); + Expense expense = new Expense(description, amount, category, date); + transactions.add(expense); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); - System.out.println("MOOOOOO... I have added an Expense Transaction."); + System.out.println("MOOOOOO... I have added the following Expense Transaction: "); + System.out.println(expense); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } public void addIncome(String description, int amount, String category, LocalDate date) { - transactions.add(new Income(description, amount, category, date)); + Income income = new Income(description, amount, category, date); + transactions.add(income); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); - System.out.println("MOOOOOO... I have added an Income Transaction."); + System.out.println("MOOOOOO... I have added the following Income Transaction: "); + System.out.println(income); System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); } diff --git a/src/main/java/seedu/duke/data/transaction/Expense.java b/src/main/java/seedu/duke/data/transaction/Expense.java index 9c3b59bf0..4ed21156c 100644 --- a/src/main/java/seedu/duke/data/transaction/Expense.java +++ b/src/main/java/seedu/duke/data/transaction/Expense.java @@ -7,7 +7,18 @@ * Records the amount, category and the date of spending. */ public class Expense extends Transaction { + private static String ICON = "[-]"; + public Expense(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } + + public String getIcon() { + return ICON; + } + + @Override + public String toString() { + return getIcon() + super.toString(); + } } diff --git a/src/main/java/seedu/duke/data/transaction/Income.java b/src/main/java/seedu/duke/data/transaction/Income.java index 826826b07..cad8499d6 100644 --- a/src/main/java/seedu/duke/data/transaction/Income.java +++ b/src/main/java/seedu/duke/data/transaction/Income.java @@ -7,7 +7,18 @@ * Records the amount, category and date of the income. */ public class Income extends Transaction { + private static String ICON = "[+]"; + public Income(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } + + public String getIcon() { + return ICON; + } + + @Override + public String toString() { + return getIcon() + super.toString(); + } } From d9e97f594285098f3d1ff27893640bb9e448b17e Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 6 Oct 2022 03:04:50 +0800 Subject: [PATCH 049/416] Update test cases on the new date feature for regression testing --- text-ui-test/EXPECTED.TXT | 47 ++++++++++++++++++++++++++++----------- text-ui-test/input.txt | 15 ++++++++----- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 0c2eeb1e4..f8d62a5f0 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -6,37 +6,58 @@ ____________________________________________________________ There are no records of your transactions found. ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added an Expense Transaction. +MOOOOOO... I have added the following Expense Transaction: +[-][food] $20 at Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added an Income Transaction. +MOOOOOO... I have added the following Income Transaction: +[+][salary] $2000 at Sep 30 2022 | Description: jan_salary ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added an Expense Transaction. +MOOOOOO... I have added the following Expense Transaction: +[-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ Below are the records of your transactions: -Date: 13092022 Category: food Description: NIL Amount: 20 -Date: jan Category: salary Description: jan_salary Amount: 2000 -Date: monday Category: transport Description: bus_fare Amount: 1 +[-][food] $20 at Sep 13 2022 | Description: NIL +[+][salary] $2000 at Sep 30 2022 | Description: jan_salary +[-][transport] $1 at Oct 02 2022 | Description: bus_fare You have 3 transactions in your transaction history. ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added an Income Transaction. +MOOOOOO... I have added the following Income Transaction: +[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ Type of transaction given is invalid, please check your input! ____________________________________________________________ ____________________________________________________________ -Mandatory tag(s) missing, please check your input! +Invalid category, please ensure your category is correct! +____________________________________________________________ +____________________________________________________________ +Non-Numeric input detected! Please enter a numerical amount! +____________________________________________________________ +____________________________________________________________ +MOOOOOO... I have added the following Income Transaction: +[+][bonus] $-1 at Oct 03 2022 | Description: thank_you_boss +____________________________________________________________ +____________________________________________________________ +Invalid date, please ensure your date format is correct! +____________________________________________________________ +____________________________________________________________ +Invalid date, please ensure your date format is correct! +____________________________________________________________ +____________________________________________________________ +Invalid date, please ensure your date format is correct! ____________________________________________________________ ____________________________________________________________ Below are the records of your transactions: -Date: 13092022 Category: food Description: NIL Amount: 20 -Date: jan Category: salary Description: jan_salary Amount: 2000 -Date: monday Category: transport Description: bus_fare Amount: 1 -Date: everyday Category: bonus Description: thank_you_boss Amount: 1000000 -You have 4 transactions in your transaction history. +[-][food] $20 at Sep 13 2022 | Description: NIL +[+][salary] $2000 at Sep 30 2022 | Description: jan_salary +[-][transport] $1 at Oct 02 2022 | Description: bus_fare +[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +[+][bonus] $-1 at Oct 03 2022 | Description: thank_you_boss +You have 5 transactions in your transaction history. ____________________________________________________________ ____________________________________________________________ Goodbye and see you soon. diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index c99999244..d0b3ef190 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,10 +1,15 @@ list add t/expense c/food a/20 d/13092022 i/NIL -add t/income c/salary a/2000 d/jan i/jan_salary -add t/expense c/transport a/1 d/monday i/bus_fare +add t/income c/salary a/2000 d/30092022 i/jan_salary +add t/expense c/transport a/1 d/02102022 i/bus_fare list -add t/income c/bonus a/1000000 d/everyday i/thank_you_boss -add t/heheheh c/bonus a/1000000 d/everyday i/thank_you_boss -add t/income c/bonus a/1000000 d/everyday +add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss +add t/heheheh c/bonus a/1000000 d/03102022 i/thank_you_boss +add t/income c/123 a/10000000 d/03102022 i/thank_you_boss +add t/income c/bonus a/abc d/03102022 i/thank_you_boss +add t/income c/bonus a/-1 d/03102022 i/thank_you_boss +add t/income c/bonus a/10000000 d/20220101 i/thank_you_boss +add t/income c/bonus a/10000000 d/obviouslyFalseDate i/thank_you_boss +add t/income c/bonus a/10000000 d/ i/thank_you_boss list bye From 41e2db19e9afe35ae6f57167fe0fd834fb7c457e Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 6 Oct 2022 03:05:14 +0800 Subject: [PATCH 050/416] Reformat transaction.java to comply with Java coding standards --- src/main/java/seedu/duke/data/transaction/Transaction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index b6b8e5416..8bf7e8f52 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -53,7 +53,7 @@ public String printFormattedCategory() { @Override public String toString() { - return printFormattedCategory() + " $" + amount + " at " + printFormattedDate() + - " | Description: " + description; + return printFormattedCategory() + " $" + amount + " at " + printFormattedDate() + + " | Description: " + description; } } \ No newline at end of file From 224a27d1679112b6897e88d3b52d4afd64461e82 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Thu, 6 Oct 2022 13:52:46 +0800 Subject: [PATCH 051/416] Added JUI Test --- src/test/java/seedu/duke/DukeTest.java | 12 --------- src/test/java/seedu/duke/ParserTest.java | 31 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 12 deletions(-) delete mode 100644 src/test/java/seedu/duke/DukeTest.java create mode 100644 src/test/java/seedu/duke/ParserTest.java diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java deleted file mode 100644 index 2dda5fd65..000000000 --- a/src/test/java/seedu/duke/DukeTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.duke; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class DukeTest { - @Test - public void sampleTest() { - assertTrue(true); - } -} diff --git a/src/test/java/seedu/duke/ParserTest.java b/src/test/java/seedu/duke/ParserTest.java new file mode 100644 index 000000000..397b69319 --- /dev/null +++ b/src/test/java/seedu/duke/ParserTest.java @@ -0,0 +1,31 @@ +package seedu.duke; + +import org.junit.jupiter.api.Test; + +import org.junit.jupiter.api.BeforeEach; +import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Transaction; + +import java.util.ArrayList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ParserTest { + + private Parser parser; + private ArrayList transactions; + + @BeforeEach + public void setUp() { + TransactionList inputArray = new TransactionList(); + } + + @Test + public void parse_helpCommand_parsedCorrectly() { + TransactionList inputArray = new TransactionList(); + final String input = "help"; + boolean output = Parser.parse(input, inputArray); + assertEquals(output, "True"); + } +} From f37afcb7a305cbbe3d5897f63eb66032bbe1f44e Mon Sep 17 00:00:00 2001 From: brian-vb Date: Thu, 6 Oct 2022 15:28:52 +0800 Subject: [PATCH 052/416] Add Exception Handling for Delete Function --- src/main/java/seedu/duke/Parser.java | 31 ++++++++++++++++--- .../java/seedu/duke/common/ErrorMessages.java | 2 ++ .../java/seedu/duke/data/TransactionList.java | 3 ++ .../AddDeleteInvalidIndexException.java | 16 ++++++++++ text-ui-test/EXPECTED.TXT | 5 ++- text-ui-test/input.txt | 4 +++ 6 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 3b16b4c2a..ef9d8137d 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -3,9 +3,10 @@ import seedu.duke.common.ErrorMessages; import seedu.duke.common.InfoMessages; import seedu.duke.data.TransactionList; -import seedu.duke.exception.AddTransactionInvalidCategoryException; import seedu.duke.exception.AddTransactionMissingTagException; +import seedu.duke.exception.AddDeleteInvalidIndexException; import seedu.duke.exception.AddTransactionUnknownTypeException; +import seedu.duke.exception.AddTransactionInvalidCategoryException; import seedu.duke.exception.AddTransactionInvalidDateException; import seedu.duke.exception.MoolahException; @@ -81,11 +82,10 @@ private static boolean processInput(String inData, TransactionList transactions) switch (command) { case "delete": /* - Checks if userInput is in the correct format by further parsing(e.g. such as correct entry numbers) - before deleting the entry + Checks if userInput is in the correct input format by further parsing, + before adding entry to arraylist */ - int index = Integer.parseInt(userInput[1]); - TransactionList.deleteEntry(transactions, index); + parseDeleteIndex(userInput[1], transactions); break; case "add": /* @@ -260,4 +260,25 @@ private static boolean findMatchingTagFromInputs(String tag, String[] splits) { } return found; } + + private static void parseDeleteIndex(String parameter, TransactionList transactions) throws MoolahException { + boolean isInputValid = true; + int index; + int numberOfTransactions; + numberOfTransactions = transactions.size(); + try { + index = Integer.parseInt(parameter); + } catch (NumberFormatException e) { + Ui.showNonNumericError(); + return; + } + if ((index > numberOfTransactions) || (index <= 0)) { + isInputValid = false; + } + if (isInputValid) { + TransactionList.deleteEntry(transactions, index); + } else { + throw new AddDeleteInvalidIndexException(); + } + } } diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 72ea7ef3d..a44f1f09b 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -8,6 +8,8 @@ public enum ErrorMessages { MESSAGE_ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), MESSAGE_ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), + + MESSAGE_ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), MESSAGE_ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"); public final String message; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 699cd7312..14b0797a0 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -84,4 +84,7 @@ public String listTransactions() { return transactionsList; } + public int size() { + return transactions.size(); + } } diff --git a/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java b/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java new file mode 100644 index 000000000..cd875fe4b --- /dev/null +++ b/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class AddDeleteInvalidIndexException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + + @Override + public String getMessage() { + return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_INDEX.toString(); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index f8d62a5f0..15b938785 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -60,5 +60,8 @@ Below are the records of your transactions: You have 5 transactions in your transaction history. ____________________________________________________________ ____________________________________________________________ -Goodbye and see you soon. +MOOOOOO.... I HAVE DELETED THE FOLLOWING TRANSACTION: NIL +____________________________________________________________ +____________________________________________________________ +MOOOOOO.... Are you sure you want to proceed with this command? Please enter Y to confirm. ____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index d0b3ef190..2a933747c 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -12,4 +12,8 @@ add t/income c/bonus a/10000000 d/20220101 i/thank_you_boss add t/income c/bonus a/10000000 d/obviouslyFalseDate i/thank_you_boss add t/income c/bonus a/10000000 d/ i/thank_you_boss list +delete 1 +purge +Y +list bye From 58fcbd77a8ef61e75e8d1c3a3f3e1fe757fce33f Mon Sep 17 00:00:00 2001 From: brian-vb <69341707+brian-vb@users.noreply.github.com> Date: Thu, 6 Oct 2022 15:36:50 +0800 Subject: [PATCH 053/416] Update and rename ParserTest.java to DukeTest.java Revert back to original JUI Test --- src/test/java/seedu/duke/DukeTest.java | 12 +++++++++ src/test/java/seedu/duke/ParserTest.java | 31 ------------------------ 2 files changed, 12 insertions(+), 31 deletions(-) create mode 100644 src/test/java/seedu/duke/DukeTest.java delete mode 100644 src/test/java/seedu/duke/ParserTest.java diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java new file mode 100644 index 000000000..2dda5fd65 --- /dev/null +++ b/src/test/java/seedu/duke/DukeTest.java @@ -0,0 +1,12 @@ +package seedu.duke; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class DukeTest { + @Test + public void sampleTest() { + assertTrue(true); + } +} diff --git a/src/test/java/seedu/duke/ParserTest.java b/src/test/java/seedu/duke/ParserTest.java deleted file mode 100644 index 397b69319..000000000 --- a/src/test/java/seedu/duke/ParserTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package seedu.duke; - -import org.junit.jupiter.api.Test; - -import org.junit.jupiter.api.BeforeEach; -import seedu.duke.data.TransactionList; -import seedu.duke.data.transaction.Transaction; - -import java.util.ArrayList; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class ParserTest { - - private Parser parser; - private ArrayList transactions; - - @BeforeEach - public void setUp() { - TransactionList inputArray = new TransactionList(); - } - - @Test - public void parse_helpCommand_parsedCorrectly() { - TransactionList inputArray = new TransactionList(); - final String input = "help"; - boolean output = Parser.parse(input, inputArray); - assertEquals(output, "True"); - } -} From bb2328eebfa9291eff0231b1810c7766203e23d6 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Oct 2022 22:12:34 +0800 Subject: [PATCH 054/416] Add Command Refactoring --- src/main/java/seedu/duke/Duke.java | 64 +++- src/main/java/seedu/duke/Parser.java | 284 ++---------------- src/main/java/seedu/duke/Storage.java | 13 + .../java/seedu/duke/command/AddCommand.java | 187 ++++++++++++ .../java/seedu/duke/command/ByeCommand.java | 18 ++ src/main/java/seedu/duke/command/Command.java | 13 + .../seedu/duke/command/DeleteCommand.java | 47 +++ .../java/seedu/duke/command/EditCommand.java | 27 ++ .../java/seedu/duke/command/HelpCommand.java | 17 ++ .../java/seedu/duke/command/ListCommand.java | 23 ++ .../java/seedu/duke/command/PurgeCommand.java | 30 ++ .../duke/exception/UnknownInputException.java | 15 + 12 files changed, 466 insertions(+), 272 deletions(-) create mode 100644 src/main/java/seedu/duke/Storage.java create mode 100644 src/main/java/seedu/duke/command/AddCommand.java create mode 100644 src/main/java/seedu/duke/command/ByeCommand.java create mode 100644 src/main/java/seedu/duke/command/Command.java create mode 100644 src/main/java/seedu/duke/command/DeleteCommand.java create mode 100644 src/main/java/seedu/duke/command/EditCommand.java create mode 100644 src/main/java/seedu/duke/command/HelpCommand.java create mode 100644 src/main/java/seedu/duke/command/ListCommand.java create mode 100644 src/main/java/seedu/duke/command/PurgeCommand.java create mode 100644 src/main/java/seedu/duke/exception/UnknownInputException.java diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index a3a1bec43..6ab64f9e0 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -1,24 +1,62 @@ package seedu.duke; +import seedu.duke.command.Command; import seedu.duke.data.TransactionList; +import seedu.duke.exception.MoolahException; import java.util.Scanner; public class Duke { - public static void main(String[] args) { - TransactionList transactions = new TransactionList(); - String inData; - Scanner scan = new Scanner(System.in); - Ui.showGreeting(); - - while (scan.hasNextLine() && true) { - // Receives user input continuously - inData = scan.nextLine(); - inData = inData.trim(); - - if (!Parser.parse(inData, transactions)) { - break; + private Storage storage; + private TransactionList transactions; + private Ui ui; + + public Duke() { // NEED TO ADD FILE PATH + ui = new Ui(); + transactions = new TransactionList(); + + // ideal code after u add all the storage stuff + /**storage = new Storage(filePath); + try { + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + ui.showLoadingError(); + tasks = new TaskList(); + }**/ + } + + public void run() { + ui.showGreeting(); + boolean isExit = false; + String inData; // temp + while (!isExit) { + try { + Scanner scan = new Scanner(System.in); + inData = scan.nextLine(); + inData = inData.trim(); + + Command c = Parser.parse(inData); + c.execute(transactions, ui, storage); + isExit = c.isExit(); + } catch (MoolahException e) { + Ui.showErrorMessage(e.getMessage()); } + //ideal code + /***try { + String fullCommand = ui.readCommand(); + ui.showLine(); // divider line + Command c = Parser.parse(fullCommand); + c.execute(tasks, ui, storage); + isExit = c.isExit(); + } catch (DukeException e) { + ui.showError(e.getErrorMessage()); + } finally { + ui.showLine(); + }**/ } } + + public static void main(String[] args) { + new Duke().run(); // NEED TO ADD FILE PATH + } } diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index ef9d8137d..2ca3b851d 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,284 +1,50 @@ package seedu.duke; -import seedu.duke.common.ErrorMessages; -import seedu.duke.common.InfoMessages; -import seedu.duke.data.TransactionList; -import seedu.duke.exception.AddTransactionMissingTagException; -import seedu.duke.exception.AddDeleteInvalidIndexException; -import seedu.duke.exception.AddTransactionUnknownTypeException; -import seedu.duke.exception.AddTransactionInvalidCategoryException; -import seedu.duke.exception.AddTransactionInvalidDateException; +import seedu.duke.command.*; import seedu.duke.exception.MoolahException; - -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; -import java.util.Scanner; - -import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; +import seedu.duke.exception.UnknownInputException; public class Parser { - static final boolean IS_EXIT = false; - static final boolean IS_CONTINUE = true; /** * Parses the user input and deal with any input error returned. * * @param inData The user input. - * @param transactions The array which would be operated on. - * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. - */ - public static boolean parse(String inData, TransactionList transactions) { - boolean isContinue = IS_CONTINUE; - try { - isContinue = processInput(inData, transactions); - } catch (MoolahException exception) { - Ui.showErrorMessage(exception.getMessage()); - } - return isContinue; - } - - /** - * Parses the user input and processes it with the transactions arraylist. - * - * @param inData The user input. - * @param transactions The array which would be operated on. * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. * @throws MoolahException Any command input exceptions captured by Moolah Manager. */ - private static boolean processInput(String inData, TransactionList transactions) throws MoolahException { - if (inData.equals("help")) { - Ui.showHelpList(); - } else if (inData.equals("list")) { - // Prints all transactions if input is equal to "list" - String transactionsList = transactions.listTransactions(); - if (transactionsList.isEmpty()) { - Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_LIST_EMPTY.toString()); - return IS_CONTINUE; - } - Ui.showTransactionsList(transactionsList, InfoMessages.MESSAGE_INFO_LIST.toString()); - } else if (inData.equals("purge")) { - // Shows confirmation prompt before deleting all transactions - Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_WARNING.toString()); - Scanner confirmation = new Scanner(System.in); - String input = confirmation.nextLine(); - if (input.equals("Y")) { - TransactionList.purgeEntries(transactions); - } else { - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); - System.out.println("MOOOOOO.... Aborting Command, returning to Home."); - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); - } - } else if (inData.equals(("bye"))) { - Ui.showExitMessage(); - // Exits loop - return IS_EXIT; - } else if (inData.isBlank() || inData.isEmpty()) { - Ui.showInvalidCommand(); - } else if (inData.contains(" ")) { - // Further parses the user input for user transaction commands - String[] userInput = inData.split(" ", 2); - String command = userInput[0]; - switch (command) { - case "delete": - /* - Checks if userInput is in the correct input format by further parsing, - before adding entry to arraylist - */ - parseDeleteIndex(userInput[1], transactions); - break; - case "add": - /* - Checks if userInput is in the correct input format by further parsing, - before adding entry to arraylist - */ - parseAddCommand(userInput[1], transactions); - break; - case "edit": - /* - Checks if userInput is in the correct input format by further parsing, - before the entry in the arraylist - */ - break; - default: - // For invalid commands - Ui.showInvalidCommand(); - } - } else { - // For any single-word inData , which are not valid commands - Ui.showInvalidCommand(); - } - return IS_CONTINUE; - } - - /** - * Parses the add transaction command by checking if the compulsory tags exist followed by adding the transaction. - * Then executes the command to add the transaction into the list. - * - * @param userInput The user input after the "add" command word. - * @throws AddTransactionMissingTagException Exceptions related to "add" command. - */ - private static void parseAddCommand(String userInput, TransactionList transactions) throws MoolahException { - String[] splits = userInput.split(" "); - checkTagsExist(splits); - // TODO: To check that each parameter is in correct format, e.g. amount should be valid integer/double - // TODO: To move the add transaction logic below to Command class in Command.execute() - String description = ""; - int amount = 0; - String category = ""; - LocalDate date = null; - String type = ""; - boolean inputIsValid = true; - - for (String split : splits) { - String tag = split.substring(0, 2); - String parameter = split.substring(2); - switch (tag) { - case "t/": - type = parameter; - break; - case "c/": - try { - parseCategoryTag(parameter); - category = parameter; - } catch (AddTransactionInvalidCategoryException e) { - Ui.printMessages(String.valueOf(ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY)); - inputIsValid = false; - } + public static Command parse(String inData) throws MoolahException{ + Command command = null; + String[] splitInput = inData.split(" ", 2);; + // list commands duke to list all the tasks stored and their completion status + // try at the start cos of the errors possibly + switch (splitInput[0]) { + case "help": + command = new HelpCommand(); break; - case "a/": - try { - amount = Integer.parseInt(parameter); - } catch (NumberFormatException e) { - Ui.showNonNumericError(); - inputIsValid = false; - } + case "list": + command = new ListCommand(); break; - case "d/": - date = parseDateTag(parameter); + case "purge": + command = new PurgeCommand(); break; - case "i/": - description = parameter; + case "delete": + command = new DeleteCommand(splitInput[1]); break; - default: + case "add": + command = new AddCommand(splitInput[1]); break; - } - } - if (inputIsValid) { - switch (type) { - case "expense": - transactions.addExpense(description, amount, category, date); + case "edit": + command = new EditCommand(splitInput[1]); break; - case "income": - transactions.addIncome(description, amount, category, date); + case "bye": + command = new ByeCommand(); break; default: - throw new AddTransactionUnknownTypeException(); - } - } - } - - /** - * Processes the parameter. If it is an invalid parameter an exception error would be thrown. - * - * @param parameter The user input after the user tag. - * @throws AddTransactionInvalidCategoryException Invalid category parameter exception. - */ - private static void parseCategoryTag(String parameter) throws AddTransactionInvalidCategoryException { - if (isNumeric(parameter)) { - throw new AddTransactionInvalidCategoryException(); - } - - } - - /** - * Checks if the parameter contains numeric characters. - * - * @param parameter The user input after the user tag. - * @return true if there are numeric characters within the parameter. - */ - public static boolean isNumeric(String parameter) { - char[] characters = parameter.toCharArray(); - for (char character : characters) { - if (Character.isDigit(character)) { - return true; - } - } - return false; - } - - - /** - * Parse the user parameter input for date into a LocalDate object and returns it. - * - * @param parameter The user input after the user tag. - * @return The LocalDate object parsed from user input given. - * @throws AddTransactionInvalidDateException Invalid date format exception. - */ - private static LocalDate parseDateTag(String parameter) throws AddTransactionInvalidDateException { - try { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_INPUT_PATTERN.toString()); - LocalDate date = LocalDate.parse(parameter, formatter); - return date; - } catch (DateTimeParseException exception) { - throw new AddTransactionInvalidDateException(); - } - } - - /** - * Checks if the targeted tags exists in the split user inputs. - * - * @param splits The user input after the command word, split into a list for every space found. - * @throws AddTransactionMissingTagException Missing tag exception. - */ - private static void checkTagsExist(String[] splits) throws AddTransactionMissingTagException { - // TODO: To add the tags into Command class instead - String[] tags = new String[]{"t/", "c/", "a/", "d/", "i/"}; - for (String tag : tags) { - boolean found = findMatchingTagFromInputs(tag, splits); - if (!found) { - throw new AddTransactionMissingTagException(); - } - } - } - - /** - * Returns a boolean value on whether a tag can be found among the split user inputs. - * - * @param tag A specific tag used to locate the command parameter. - * @param splits The user input after the command word, split into a list for every space found. - * @return Whether the tag is found within the split inputs. - */ - private static boolean findMatchingTagFromInputs(String tag, String[] splits) { - boolean found = false; - for (String split : splits) { - if (split.startsWith(tag)) { - found = true; - break; - } - } - return found; - } - - private static void parseDeleteIndex(String parameter, TransactionList transactions) throws MoolahException { - boolean isInputValid = true; - int index; - int numberOfTransactions; - numberOfTransactions = transactions.size(); - try { - index = Integer.parseInt(parameter); - } catch (NumberFormatException e) { - Ui.showNonNumericError(); - return; - } - if ((index > numberOfTransactions) || (index <= 0)) { - isInputValid = false; - } - if (isInputValid) { - TransactionList.deleteEntry(transactions, index); - } else { - throw new AddDeleteInvalidIndexException(); + Ui.showInvalidCommand(); // if u still want this + throw new UnknownInputException(); } + return command; } } diff --git a/src/main/java/seedu/duke/Storage.java b/src/main/java/seedu/duke/Storage.java new file mode 100644 index 000000000..854361793 --- /dev/null +++ b/src/main/java/seedu/duke/Storage.java @@ -0,0 +1,13 @@ +package seedu.duke; + +import seedu.duke.data.TransactionList; + +public class Storage { + + public Storage() { + + } + + public void writeToFile(TransactionList transactions) { + } +} diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java new file mode 100644 index 000000000..15ae2fccf --- /dev/null +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -0,0 +1,187 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.common.ErrorMessages; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.*; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; + +public class AddCommand extends Command { + + private String input; + + public AddCommand(String input) { + this.input = input; + } + + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + /* + Checks if userInput is in the correct input format by further parsing, + before adding entry to arraylist + */ + /** + * Parses the add transaction command by checking if the compulsory tags exist followed by adding the transaction. + * Then executes the command to add the transaction into the list. + * + * @param userInput The user input after the "add" command word. + * @throws AddTransactionMissingTagException Exceptions related to "add" command. + */ + String[] splits = input.split(" "); + checkTagsExist(splits); + // TODO: To check that each parameter is in correct format, e.g. amount should be valid integer/double + // TODO: To move the add transaction logic below to Command class in Command.execute() + String description = ""; + int amount = 0; + String category = ""; + LocalDate date = null; + String type = ""; + boolean inputIsValid = true; + + for (String split : splits) { + String tag = split.substring(0, 2); + String parameter = split.substring(2); + switch (tag) { + case "t/": + type = parameter; + break; + case "c/": + try { + parseCategoryTag(parameter); + category = parameter; + } catch (AddTransactionInvalidCategoryException e) { + Ui.printMessages(String.valueOf(ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY)); + inputIsValid = false; + } + + break; + case "a/": + try { + amount = Integer.parseInt(parameter); + } catch (NumberFormatException e) { + Ui.showNonNumericError(); + inputIsValid = false; + } + break; + case "d/": + date = parseDateTag(parameter); + break; + case "i/": + description = parameter; + break; + default: + break; + } + } + if (inputIsValid) { + switch (type) { + case "expense": + transactions.addExpense(description, amount, category, date); + break; + case "income": + transactions.addIncome(description, amount, category, date); + break; + default: + throw new AddTransactionUnknownTypeException(); + } + } + } + + + @Override + public boolean isExit() { + return false; + } + + + // the other add functions + + /** + * Processes the parameter. If it is an invalid parameter an exception error would be thrown. + * + * @param parameter The user input after the user tag. + * @throws AddTransactionInvalidCategoryException Invalid category parameter exception. + */ + private static void parseCategoryTag(String parameter) throws AddTransactionInvalidCategoryException { + if (isNumeric(parameter)) { + throw new AddTransactionInvalidCategoryException(); + } + + } + + /** + * Checks if the parameter contains numeric characters. + * + * @param parameter The user input after the user tag. + * @return true if there are numeric characters within the parameter. + */ + public static boolean isNumeric(String parameter) { + char[] characters = parameter.toCharArray(); + for (char character : characters) { + if (Character.isDigit(character)) { + return true; + } + } + return false; + } + + + /** + * Parse the user parameter input for date into a LocalDate object and returns it. + * + * @param parameter The user input after the user tag. + * @return The LocalDate object parsed from user input given. + * @throws AddTransactionInvalidDateException Invalid date format exception. + */ + private static LocalDate parseDateTag(String parameter) throws AddTransactionInvalidDateException { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_INPUT_PATTERN.toString()); + LocalDate date = LocalDate.parse(parameter, formatter); + return date; + } catch (DateTimeParseException exception) { + throw new AddTransactionInvalidDateException(); + } + } + + /** + * Checks if the targeted tags exists in the split user inputs. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws AddTransactionMissingTagException Missing tag exception. + */ + private static void checkTagsExist(String[] splits) throws AddTransactionMissingTagException { + // TODO: To add the tags into Command class instead + String[] tags = new String[]{"t/", "c/", "a/", "d/", "i/"}; + for (String tag : tags) { + boolean found = findMatchingTagFromInputs(tag, splits); + if (!found) { + throw new AddTransactionMissingTagException(); + } + } + } + + /** + * Returns a boolean value on whether a tag can be found among the split user inputs. + * + * @param tag A specific tag used to locate the command parameter. + * @param splits The user input after the command word, split into a list for every space found. + * @return Whether the tag is found within the split inputs. + */ + private static boolean findMatchingTagFromInputs(String tag, String[] splits) { + boolean found = false; + for (String split : splits) { + if (split.startsWith(tag)) { + found = true; + break; + } + } + return found; + } + +} diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java new file mode 100644 index 000000000..3aa62f26f --- /dev/null +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -0,0 +1,18 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; + +public class ByeCommand extends Command { + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) { + Ui.showExitMessage(); + //storage.writeToFile(transactions); + } + + @Override + public boolean isExit() { + return true; + } +} diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java new file mode 100644 index 000000000..5c4552c56 --- /dev/null +++ b/src/main/java/seedu/duke/command/Command.java @@ -0,0 +1,13 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.MoolahException; + +public abstract class Command { + + public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; + + public abstract boolean isExit(); +} diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java new file mode 100644 index 000000000..ac3de3f0c --- /dev/null +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -0,0 +1,47 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.AddDeleteInvalidIndexException; +import seedu.duke.exception.MoolahException; + +public class DeleteCommand extends Command { + + private String input; + + public DeleteCommand(String input) { + this.input = input; + } + + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + /* + Checks if userInput is in the correct input format by further parsing, + before adding entry to arraylist + */ + boolean isInputValid = true; + int index; + int numberOfTransactions; + numberOfTransactions = transactions.size(); + try { + index = Integer.parseInt(input); + } catch (NumberFormatException e) { + Ui.showNonNumericError(); + return; + } + if ((index > numberOfTransactions) || (index <= 0)) { + isInputValid = false; + } + if (isInputValid) { + TransactionList.deleteEntry(transactions, index); + } else { + throw new AddDeleteInvalidIndexException(); + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java new file mode 100644 index 000000000..0a0398a51 --- /dev/null +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -0,0 +1,27 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; + +public class EditCommand extends Command { + + private String input; + + public EditCommand(String input) { + this.input = input; + } + + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) { + /* + Checks if userInput is in the correct input format by further parsing, + before the entry in the arraylist + */ + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java new file mode 100644 index 000000000..ed2e1513f --- /dev/null +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -0,0 +1,17 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; + +public class HelpCommand extends Command { + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) { + Ui.showHelpList(); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java new file mode 100644 index 000000000..a466900c8 --- /dev/null +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -0,0 +1,23 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.common.InfoMessages; +import seedu.duke.data.TransactionList; + +public class ListCommand extends Command { + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) { + // Prints all transactions if input is equal to "list" + String transactionsList = transactions.listTransactions(); + if (transactionsList.isEmpty()) { + Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_LIST_EMPTY.toString()); + } + Ui.showTransactionsList(transactionsList, InfoMessages.MESSAGE_INFO_LIST.toString()); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java new file mode 100644 index 000000000..a0ae77902 --- /dev/null +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -0,0 +1,30 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.common.InfoMessages; +import seedu.duke.data.TransactionList; + +import java.util.Scanner; + +public class PurgeCommand extends Command { + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) { + // Shows confirmation prompt before deleting all transactions + Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_WARNING.toString()); + Scanner confirmation = new Scanner(System.in); + String input = confirmation.nextLine(); + if (input.equals("Y")) { + TransactionList.purgeEntries(transactions); + } else { + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println("MOOOOOO.... Aborting Command, returning to Home."); + System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/exception/UnknownInputException.java b/src/main/java/seedu/duke/exception/UnknownInputException.java new file mode 100644 index 000000000..5982bb3cc --- /dev/null +++ b/src/main/java/seedu/duke/exception/UnknownInputException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.Ui; + +public class UnknownInputException extends MoolahException { + + // HELP ME CHANGE PLS + + @Override + public String getMessage() { + Ui.showInvalidCommand(); + return "OOPS!!! I'm sorry, but I don't know what that means :-( "; + } + +} From 00d0e6970072aaa122ffe01d6c29a7eaba2c74d5 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Oct 2022 22:21:08 +0800 Subject: [PATCH 055/416] Cleanup Code from tests --- src/main/java/seedu/duke/Parser.java | 62 ++++++++------ .../java/seedu/duke/command/AddCommand.java | 83 ++++++++++--------- 2 files changed, 79 insertions(+), 66 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 2ca3b851d..e6a2acc6e 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,6 +1,13 @@ package seedu.duke; -import seedu.duke.command.*; +import seedu.duke.command.AddCommand; +import seedu.duke.command.ByeCommand; +import seedu.duke.command.Command; +import seedu.duke.command.DeleteCommand; +import seedu.duke.command.EditCommand; +import seedu.duke.command.HelpCommand; +import seedu.duke.command.ListCommand; +import seedu.duke.command.PurgeCommand; import seedu.duke.exception.MoolahException; import seedu.duke.exception.UnknownInputException; @@ -13,37 +20,38 @@ public class Parser { * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. * @throws MoolahException Any command input exceptions captured by Moolah Manager. */ - public static Command parse(String inData) throws MoolahException{ - Command command = null; + public static Command parse(String inData) throws MoolahException { + Command command; + command = null; String[] splitInput = inData.split(" ", 2);; // list commands duke to list all the tasks stored and their completion status // try at the start cos of the errors possibly switch (splitInput[0]) { - case "help": - command = new HelpCommand(); - break; - case "list": - command = new ListCommand(); - break; - case "purge": - command = new PurgeCommand(); - break; - case "delete": - command = new DeleteCommand(splitInput[1]); - break; - case "add": - command = new AddCommand(splitInput[1]); - break; - case "edit": - command = new EditCommand(splitInput[1]); - break; - case "bye": - command = new ByeCommand(); - break; - default: - Ui.showInvalidCommand(); // if u still want this - throw new UnknownInputException(); + case "help": + command = new HelpCommand(); + break; + case "list": + command = new ListCommand(); + break; + case "purge": + command = new PurgeCommand(); + break; + case "delete": + command = new DeleteCommand(splitInput[1]); + break; + case "add": + command = new AddCommand(splitInput[1]); + break; + case "edit": + command = new EditCommand(splitInput[1]); + break; + case "bye": + command = new ByeCommand(); + break; + default: + Ui.showInvalidCommand(); // if u still want this + throw new UnknownInputException(); } return command; } diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 15ae2fccf..8ee5adffe 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -4,7 +4,11 @@ import seedu.duke.Ui; import seedu.duke.common.ErrorMessages; import seedu.duke.data.TransactionList; -import seedu.duke.exception.*; +import seedu.duke.exception.AddTransactionInvalidCategoryException; +import seedu.duke.exception.AddTransactionInvalidDateException; +import seedu.duke.exception.AddTransactionMissingTagException; +import seedu.duke.exception.AddTransactionUnknownTypeException; +import seedu.duke.exception.MoolahException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -27,7 +31,8 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws before adding entry to arraylist */ /** - * Parses the add transaction command by checking if the compulsory tags exist followed by adding the transaction. + * Parses the add transaction command by checking if the compulsory tags exist followed by + * adding the transaction. * Then executes the command to add the transaction into the list. * * @param userInput The user input after the "add" command word. @@ -48,47 +53,47 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws String tag = split.substring(0, 2); String parameter = split.substring(2); switch (tag) { - case "t/": - type = parameter; - break; - case "c/": - try { - parseCategoryTag(parameter); - category = parameter; - } catch (AddTransactionInvalidCategoryException e) { - Ui.printMessages(String.valueOf(ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY)); - inputIsValid = false; - } - - break; - case "a/": - try { - amount = Integer.parseInt(parameter); - } catch (NumberFormatException e) { - Ui.showNonNumericError(); - inputIsValid = false; - } - break; - case "d/": - date = parseDateTag(parameter); - break; - case "i/": - description = parameter; - break; - default: - break; + case "t/": + type = parameter; + break; + case "c/": + try { + parseCategoryTag(parameter); + category = parameter; + } catch (AddTransactionInvalidCategoryException e) { + Ui.printMessages(String.valueOf(ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY)); + inputIsValid = false; + } + + break; + case "a/": + try { + amount = Integer.parseInt(parameter); + } catch (NumberFormatException e) { + Ui.showNonNumericError(); + inputIsValid = false; + } + break; + case "d/": + date = parseDateTag(parameter); + break; + case "i/": + description = parameter; + break; + default: + break; } } if (inputIsValid) { switch (type) { - case "expense": - transactions.addExpense(description, amount, category, date); - break; - case "income": - transactions.addIncome(description, amount, category, date); - break; - default: - throw new AddTransactionUnknownTypeException(); + case "expense": + transactions.addExpense(description, amount, category, date); + break; + case "income": + transactions.addIncome(description, amount, category, date); + break; + default: + throw new AddTransactionUnknownTypeException(); } } } From 9eb49304513ff9919db46c996d2ec2b8b5cec7b4 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Oct 2022 22:29:52 +0800 Subject: [PATCH 056/416] Command Class errors fix --- src/main/java/seedu/duke/Duke.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 6ab64f9e0..bf4afdeca 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -29,9 +29,9 @@ public void run() { ui.showGreeting(); boolean isExit = false; String inData; // temp + Scanner scan = new Scanner(System.in); while (!isExit) { try { - Scanner scan = new Scanner(System.in); inData = scan.nextLine(); inData = inData.trim(); From 663b8ae73fd1c46c697015f2bc4bccd146b63e12 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Oct 2022 22:41:49 +0800 Subject: [PATCH 057/416] Fix Purge scanner --- src/main/java/seedu/duke/Duke.java | 5 +---- src/main/java/seedu/duke/Ui.java | 14 ++++++++++++++ src/main/java/seedu/duke/command/PurgeCommand.java | 5 +---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index bf4afdeca..d433b68f1 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -4,8 +4,6 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; -import java.util.Scanner; - public class Duke { private Storage storage; private TransactionList transactions; @@ -29,10 +27,9 @@ public void run() { ui.showGreeting(); boolean isExit = false; String inData; // temp - Scanner scan = new Scanner(System.in); while (!isExit) { try { - inData = scan.nextLine(); + inData = ui.readCommand(); inData = inData.trim(); Command c = Parser.parse(inData); diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 83b4e31c8..289649960 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -3,7 +3,12 @@ import seedu.duke.common.ErrorMessages; import seedu.duke.common.InfoMessages; +import java.util.Scanner; + public class Ui { + + private String input; + private Scanner in; /** * Prints each message from a variable messages string line by line into the output stream. * @@ -23,6 +28,15 @@ public static void printMessages(String... messages) { } //@@author + public Ui() { + in = new Scanner(System.in); + } + + public String readCommand() { + input = in.nextLine(); + return input.trim(); + } + public static void showGreeting() { printMessages(InfoMessages.MESSAGE_INFO_GREET.toString(), InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString()); } diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index a0ae77902..acfaff8c7 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -5,15 +5,12 @@ import seedu.duke.common.InfoMessages; import seedu.duke.data.TransactionList; -import java.util.Scanner; - public class PurgeCommand extends Command { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { // Shows confirmation prompt before deleting all transactions Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_WARNING.toString()); - Scanner confirmation = new Scanner(System.in); - String input = confirmation.nextLine(); + String input = ui.readCommand(); if (input.equals("Y")) { TransactionList.purgeEntries(transactions); } else { From fc64a6cb8b00ff61ef01c30c0fbf866501f7aa18 Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 6 Oct 2022 23:26:29 +0800 Subject: [PATCH 058/416] update expected.txt --- text-ui-test/EXPECTED.TXT | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 15b938785..860e04d84 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -6,15 +6,19 @@ ____________________________________________________________ There are no records of your transactions found. ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Expense Transaction: +Below are the records of your transactions: + +____________________________________________________________ +____________________________________________________________ +MOOOOOO... I have added the following Expense Transaction: [-][food] $20 at Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +MOOOOOO... I have added the following Income Transaction: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Expense Transaction: +MOOOOOO... I have added the following Expense Transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ @@ -25,7 +29,7 @@ Below are the records of your transactions: You have 3 transactions in your transaction history. ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +MOOOOOO... I have added the following Income Transaction: [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ @@ -38,7 +42,7 @@ ____________________________________________________________ Non-Numeric input detected! Please enter a numerical amount! ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +MOOOOOO... I have added the following Income Transaction: [+][bonus] $-1 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ @@ -65,3 +69,16 @@ ____________________________________________________________ ____________________________________________________________ MOOOOOO.... Are you sure you want to proceed with this command? Please enter Y to confirm. ____________________________________________________________ +____________________________________________________________ +MOOOOOO.... All of your transactions have been purged. +____________________________________________________________ +____________________________________________________________ +There are no records of your transactions found. +____________________________________________________________ +____________________________________________________________ +Below are the records of your transactions: + +____________________________________________________________ +____________________________________________________________ +Goodbye and see you soon. +____________________________________________________________ From a94b70b0f863b260e14da2d92569e1794e636a50 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 7 Oct 2022 00:02:41 +0800 Subject: [PATCH 059/416] Update tests --- gradle/wrapper/gradle-wrapper.properties | 2 +- text-ui-test/EXPECTED.TXT | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b7c8c5dbf..e750102e0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 860e04d84..f41a2b9c6 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -10,15 +10,15 @@ Below are the records of your transactions: ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Expense Transaction: +MOOOOOO... I have added the following Expense Transaction: [-][food] $20 at Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +MOOOOOO... I have added the following Income Transaction: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Expense Transaction: +MOOOOOO... I have added the following Expense Transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ @@ -29,7 +29,7 @@ Below are the records of your transactions: You have 3 transactions in your transaction history. ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +MOOOOOO... I have added the following Income Transaction: [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ @@ -42,7 +42,7 @@ ____________________________________________________________ Non-Numeric input detected! Please enter a numerical amount! ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +MOOOOOO... I have added the following Income Transaction: [+][bonus] $-1 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ From 2c0eb8c7539669c31dbf01065e1f441a426b5141 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 7 Oct 2022 00:07:29 +0800 Subject: [PATCH 060/416] Update gradle properties for team --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e0..b7c8c5dbf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 04f1d504c132701aaffdc17775d4848feaa098a2 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 06:08:52 +0800 Subject: [PATCH 061/416] Rename UI-related variables to comply to a specific format --- src/main/java/seedu/duke/Parser.java | 5 ++--- .../java/seedu/duke/command/ByeCommand.java | 2 +- .../java/seedu/duke/command/HelpCommand.java | 2 +- .../java/seedu/duke/command/ListCommand.java | 5 +++-- .../java/seedu/duke/command/PurgeCommand.java | 6 +++--- .../java/seedu/duke/common/ErrorMessages.java | 14 ++++++------- .../java/seedu/duke/common/InfoMessages.java | 18 ++++++++--------- .../java/seedu/duke/data/TransactionList.java | 20 +++++++++---------- .../seedu/duke/data/transaction/Expense.java | 4 ++-- .../seedu/duke/data/transaction/Income.java | 4 ++-- .../AddDeleteInvalidIndexException.java | 3 +-- ...ddTransactionInvalidCategoryException.java | 2 +- .../AddTransactionInvalidDateException.java | 2 +- .../AddTransactionMissingTagException.java | 3 +-- .../AddTransactionUnknownTypeException.java | 3 +-- 15 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index e6a2acc6e..ac463d33a 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -21,9 +21,8 @@ public class Parser { * @throws MoolahException Any command input exceptions captured by Moolah Manager. */ public static Command parse(String inData) throws MoolahException { - Command command; - command = null; - String[] splitInput = inData.split(" ", 2);; + Command command = null; + String[] splitInput = inData.split(" ", 2); // list commands duke to list all the tasks stored and their completion status // try at the start cos of the errors possibly diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index 3aa62f26f..af2399dee 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -7,7 +7,7 @@ public class ByeCommand extends Command { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { - Ui.showExitMessage(); + Ui.showExit(); //storage.writeToFile(transactions); } diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index ed2e1513f..2dbdb6d7b 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -7,7 +7,7 @@ public class HelpCommand extends Command { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { - Ui.showHelpList(); + Ui.showHelp(); } @Override diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index a466900c8..2dcac90a7 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -11,9 +11,10 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { // Prints all transactions if input is equal to "list" String transactionsList = transactions.listTransactions(); if (transactionsList.isEmpty()) { - Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_LIST_EMPTY.toString()); + Ui.showInfoMessage(InfoMessages.INFO_LIST_EMPTY.toString()); + return; } - Ui.showTransactionsList(transactionsList, InfoMessages.MESSAGE_INFO_LIST.toString()); + Ui.showTransactionsList(transactionsList, InfoMessages.INFO_LIST.toString()); } @Override diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index acfaff8c7..6b18fe5e3 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -9,14 +9,14 @@ public class PurgeCommand extends Command { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { // Shows confirmation prompt before deleting all transactions - Ui.showInfoMessage(InfoMessages.MESSAGE_INFO_WARNING.toString()); + Ui.showInfoMessage(InfoMessages.INFO_WARNING.toString()); String input = ui.readCommand(); if (input.equals("Y")) { TransactionList.purgeEntries(transactions); } else { - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); System.out.println("MOOOOOO.... Aborting Command, returning to Home."); - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); } } diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index a44f1f09b..f0200081a 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -4,13 +4,13 @@ * Provides enum variables for storing custom program error messages. */ public enum ErrorMessages { - MESSAGE_ERROR_INVALID_COMMAND("Oops, you have entered an invalid command."), - MESSAGE_ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), - MESSAGE_ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), - MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), - - MESSAGE_ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), - MESSAGE_ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"); + ERROR_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), + ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), + ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), + ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"), + ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), + ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), + ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"); public final String message; diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 6159aea1f..95c2c110b 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -4,16 +4,16 @@ * Provides enum variables for storing custom program information messages. */ public enum InfoMessages { - MESSAGE_INFO_DIVIDER("____________________________________________________________"), - MESSAGE_INFO_GREET("Hello! I'm your Moolah Manager!"), - MESSAGE_INFO_HELP_PROMPT("Enter if you need the list of commands."), - MESSAGE_INFO_HELP_GREET("I'm here to help. Here are the possible commands!"), - MESSAGE_INFO_LIST_EMPTY("There are no records of your transactions found."), - MESSAGE_INFO_LIST("Below are the records of your transactions:"), - MESSAGE_INFO_TRANSACTION_COUNT("You have %d transactions in your transaction history."), - MESSAGE_INFO_EXIT("Goodbye and see you soon."), + INFO_DIVIDER("____________________________________________________________"), + INFO_EXIT("Goodbye and see you soon."), + INFO_GREET("Hello! I'm your Moolah Manager!"), + INFO_HELP_GREET("I'm here to help. Here are the possible commands!"), + INFO_HELP_PROMPT("Enter if you need the list of commands."), + INFO_LIST("Below are the records of your transactions:"), + INFO_LIST_EMPTY("There are no records of your transactions found."), + INFO_TRANSACTION_COUNT("You have %d transactions in your transaction history."), + INFO_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."); - MESSAGE_INFO_WARNING("MOOOOOO.... Are you sure you want to proceed with this command? Please enter Y to confirm."); public final String message; /** diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 14b0797a0..da532b269 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -24,10 +24,10 @@ public TransactionList() { } public static void purgeEntries(TransactionList input) { - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); input.empty(); System.out.println("MOOOOOO.... All of your transactions have been purged."); - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); } private void empty() { @@ -43,30 +43,30 @@ private void removeEntry(int index) { } public static void deleteEntry(TransactionList input, int index) { - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); Transaction deleted = input.getEntry(index); String information = deleted.getDescription(); input.removeEntry(index); System.out.println("MOOOOOO.... I HAVE DELETED THE FOLLOWING TRANSACTION: " + information); - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); } public void addExpense(String description, int amount, String category, LocalDate date) { Expense expense = new Expense(description, amount, category, date); transactions.add(expense); - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); System.out.println("MOOOOOO... I have added the following Expense Transaction: "); System.out.println(expense); - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); } public void addIncome(String description, int amount, String category, LocalDate date) { Income income = new Income(description, amount, category, date); transactions.add(income); - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); System.out.println("MOOOOOO... I have added the following Income Transaction: "); System.out.println(income); - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER); + System.out.println(InfoMessages.INFO_DIVIDER); } public String listTransactions() { @@ -75,9 +75,9 @@ public String listTransactions() { for (Transaction transaction : transactions) { transactionsList += transaction.toString() + LINE_SEPARATOR; } - if (!transactionsList.equals(EMPTY_STRING)) { + if (!transactionsList.isEmpty()) { // Includes the count of the transactions with the transaction list - transactionsList += String.format(InfoMessages.MESSAGE_INFO_TRANSACTION_COUNT.toString(), + transactionsList += String.format(InfoMessages.INFO_TRANSACTION_COUNT.toString(), transactions.size()); } diff --git a/src/main/java/seedu/duke/data/transaction/Expense.java b/src/main/java/seedu/duke/data/transaction/Expense.java index 4ed21156c..61cb556a6 100644 --- a/src/main/java/seedu/duke/data/transaction/Expense.java +++ b/src/main/java/seedu/duke/data/transaction/Expense.java @@ -7,14 +7,14 @@ * Records the amount, category and the date of spending. */ public class Expense extends Transaction { - private static String ICON = "[-]"; + private static String ICON_EXPENSE = "[-]"; public Expense(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } public String getIcon() { - return ICON; + return ICON_EXPENSE; } @Override diff --git a/src/main/java/seedu/duke/data/transaction/Income.java b/src/main/java/seedu/duke/data/transaction/Income.java index cad8499d6..f7dff4a55 100644 --- a/src/main/java/seedu/duke/data/transaction/Income.java +++ b/src/main/java/seedu/duke/data/transaction/Income.java @@ -7,14 +7,14 @@ * Records the amount, category and date of the income. */ public class Income extends Transaction { - private static String ICON = "[+]"; + private static final String ICON_INCOME = "[+]"; public Income(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } public String getIcon() { - return ICON; + return ICON_INCOME; } @Override diff --git a/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java b/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java index cd875fe4b..3c77a34b7 100644 --- a/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java +++ b/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java @@ -8,9 +8,8 @@ public class AddDeleteInvalidIndexException extends MoolahException { * * @return A string containing the error message */ - @Override public String getMessage() { - return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_INDEX.toString(); + return ErrorMessages.ERROR_ADD_COMMAND_INVALID_INDEX.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java b/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java index 5fba79d07..94b07009e 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java @@ -10,7 +10,7 @@ public class AddTransactionInvalidCategoryException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY.toString(); + return ErrorMessages.ERROR_ADD_COMMAND_INVALID_CATEGORY.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java b/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java index e89285369..eb62fab77 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java @@ -11,6 +11,6 @@ public class AddTransactionInvalidDateException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_DATE.toString(); + return ErrorMessages.ERROR_ADD_COMMAND_INVALID_DATE.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java b/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java index 03a58e0fe..a0f59a0d5 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java @@ -3,7 +3,6 @@ import seedu.duke.common.ErrorMessages; public class AddTransactionMissingTagException extends MoolahException { - /** * Returns the error message of the exception to alert user of the exception. * @@ -11,6 +10,6 @@ public class AddTransactionMissingTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_MISSING_TAG.toString(); + return ErrorMessages.ERROR_ADD_COMMAND_MISSING_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java b/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java index f03987ddb..2351a938c 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java @@ -3,7 +3,6 @@ import seedu.duke.common.ErrorMessages; public class AddTransactionUnknownTypeException extends MoolahException { - /** * Returns the error message of the exception to alert user of the exception. * @@ -11,6 +10,6 @@ public class AddTransactionUnknownTypeException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_TYPE.toString(); + return ErrorMessages.ERROR_ADD_COMMAND_INVALID_TYPE.toString(); } } \ No newline at end of file From b4726b55bd1b9db9dae3d06cf690f10b8b84c939 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 06:59:56 +0800 Subject: [PATCH 062/416] Refractor source codes for the display of output, replace magic literals and adopt string formatter --- src/main/java/seedu/duke/Ui.java | 51 ++++++++++--------- .../java/seedu/duke/command/AddCommand.java | 12 +++-- src/main/java/seedu/duke/command/Command.java | 1 - .../seedu/duke/command/DeleteCommand.java | 9 ++-- .../java/seedu/duke/command/EditCommand.java | 1 - .../java/seedu/duke/command/ListCommand.java | 9 ++-- .../java/seedu/duke/command/PurgeCommand.java | 16 +++--- .../java/seedu/duke/common/InfoMessages.java | 13 +++-- .../java/seedu/duke/data/TransactionList.java | 26 +++------- .../duke/data/transaction/Transaction.java | 15 ++++-- 10 files changed, 81 insertions(+), 72 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 289649960..a1c4c40a4 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,30 +1,33 @@ package seedu.duke; -import seedu.duke.common.ErrorMessages; -import seedu.duke.common.InfoMessages; +import static seedu.duke.common.ErrorMessages.ERROR_INVALID_COMMAND; +import static seedu.duke.common.InfoMessages.INFO_DIVIDER; +import static seedu.duke.common.InfoMessages.INFO_EXIT; +import static seedu.duke.common.InfoMessages.INFO_GREET; +import static seedu.duke.common.InfoMessages.INFO_HELP_GREET; +import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; import java.util.Scanner; public class Ui { - private String input; private Scanner in; + /** * Prints each message from a variable messages string line by line into the output stream. * * @param messages A string of variable arguments. */ - //@@author chydarren-reused // Reused from https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/ui/TextUi.java // with minor modifications public static void printMessages(String... messages) { - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER.toString()); + System.out.println(INFO_DIVIDER); // Prints the string of arguments line by line in a loop for (String message : messages) { System.out.println(message); } - System.out.println(InfoMessages.MESSAGE_INFO_DIVIDER.toString()); + System.out.println(INFO_DIVIDER); } //@@author @@ -37,38 +40,36 @@ public String readCommand() { return input.trim(); } - public static void showGreeting() { - printMessages(InfoMessages.MESSAGE_INFO_GREET.toString(), InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString()); + public static void showErrorMessage(String errorMessage) { + printMessages(errorMessage); } - public static void showHelpList() { - // To include the other messages for commands - printMessages(InfoMessages.MESSAGE_INFO_HELP_GREET.toString()); + public static void showInfoMessage(String infoMessage) { + printMessages(infoMessage); } - public static void showTransactionsList(String transactionsList, String message) { - printMessages(message, transactionsList); + public static void showGreeting() { + printMessages(INFO_GREET.toString(), INFO_HELP_PROMPT.toString()); } - public static void showExitMessage() { - printMessages(InfoMessages.MESSAGE_INFO_EXIT.toString()); + public static void showHelp() { + // To include the other messages for commands + printMessages(INFO_HELP_GREET.toString()); } - public static void showErrorMessage(String errorMessage) { - printMessages(errorMessage); + public static void showExit() { + printMessages(INFO_EXIT.toString()); } - - public static void showNonNumericError() { - printMessages("Non-Numeric input detected! Please enter a numerical amount!"); + public static void showInvalidCommand() { + printMessages(ERROR_INVALID_COMMAND.toString(), INFO_HELP_PROMPT.toString()); } - public static void showInfoMessage(String infoMessage) { - printMessages(infoMessage); + public static void showTransactionAction(String infoMessage, String transactionDetails) { + printMessages(infoMessage, transactionDetails); } - public static void showInvalidCommand() { - printMessages(ErrorMessages.MESSAGE_ERROR_INVALID_COMMAND.toString(), - InfoMessages.MESSAGE_INFO_HELP_PROMPT.toString()); + public static void showTransactionsList(String transactionsList, String message) { + printMessages(message, transactionsList); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 8ee5adffe..67a09a144 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -3,6 +3,7 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.common.ErrorMessages; +import seedu.duke.common.InfoMessages; import seedu.duke.data.TransactionList; import seedu.duke.exception.AddTransactionInvalidCategoryException; import seedu.duke.exception.AddTransactionInvalidDateException; @@ -61,16 +62,15 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws parseCategoryTag(parameter); category = parameter; } catch (AddTransactionInvalidCategoryException e) { - Ui.printMessages(String.valueOf(ErrorMessages.MESSAGE_ERROR_ADD_COMMAND_INVALID_CATEGORY)); + Ui.printMessages(ErrorMessages.ERROR_ADD_COMMAND_INVALID_CATEGORY.toString()); inputIsValid = false; } - break; case "a/": try { amount = Integer.parseInt(parameter); } catch (NumberFormatException e) { - Ui.showNonNumericError(); + Ui.showErrorMessage(ErrorMessages.ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC.toString()); inputIsValid = false; } break; @@ -87,10 +87,12 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws if (inputIsValid) { switch (type) { case "expense": - transactions.addExpense(description, amount, category, date); + String expense = transactions.addExpense(description, amount, category, date); + Ui.showTransactionAction(InfoMessages.INFO_ADD_EXPENSE.toString(), expense); break; case "income": - transactions.addIncome(description, amount, category, date); + String income = transactions.addIncome(description, amount, category, date); + Ui.showTransactionAction(InfoMessages.INFO_ADD_INCOME.toString(), income); break; default: throw new AddTransactionUnknownTypeException(); diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 5c4552c56..abd1c0aec 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -6,7 +6,6 @@ import seedu.duke.exception.MoolahException; public abstract class Command { - public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; public abstract boolean isExit(); diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index ac3de3f0c..3173d0739 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -6,8 +6,10 @@ import seedu.duke.exception.AddDeleteInvalidIndexException; import seedu.duke.exception.MoolahException; -public class DeleteCommand extends Command { +import static seedu.duke.common.ErrorMessages.ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC; +import static seedu.duke.common.InfoMessages.INFO_DELETE; +public class DeleteCommand extends Command { private String input; public DeleteCommand(String input) { @@ -27,14 +29,15 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws try { index = Integer.parseInt(input); } catch (NumberFormatException e) { - Ui.showNonNumericError(); + Ui.showErrorMessage(ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC.toString()); return; } if ((index > numberOfTransactions) || (index <= 0)) { isInputValid = false; } if (isInputValid) { - TransactionList.deleteEntry(transactions, index); + String transaction = TransactionList.deleteTransaction(transactions, index); + Ui.showTransactionAction(INFO_DELETE.toString(), transaction); } else { throw new AddDeleteInvalidIndexException(); } diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 0a0398a51..6ed3b57a9 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -5,7 +5,6 @@ import seedu.duke.data.TransactionList; public class EditCommand extends Command { - private String input; public EditCommand(String input) { diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 2dcac90a7..4e0080fc9 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -2,19 +2,20 @@ import seedu.duke.Storage; import seedu.duke.Ui; -import seedu.duke.common.InfoMessages; import seedu.duke.data.TransactionList; +import static seedu.duke.common.InfoMessages.INFO_LIST; +import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; + public class ListCommand extends Command { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { - // Prints all transactions if input is equal to "list" String transactionsList = transactions.listTransactions(); if (transactionsList.isEmpty()) { - Ui.showInfoMessage(InfoMessages.INFO_LIST_EMPTY.toString()); + Ui.showInfoMessage(INFO_LIST_EMPTY.toString()); return; } - Ui.showTransactionsList(transactionsList, InfoMessages.INFO_LIST.toString()); + Ui.showTransactionsList(transactionsList, INFO_LIST.toString()); } @Override diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 6b18fe5e3..c6443384f 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -2,22 +2,26 @@ import seedu.duke.Storage; import seedu.duke.Ui; -import seedu.duke.common.InfoMessages; import seedu.duke.data.TransactionList; +import static seedu.duke.common.InfoMessages.INFO_PURGE; +import static seedu.duke.common.InfoMessages.INFO_PURGE_ABORT; +import static seedu.duke.common.InfoMessages.INFO_PURGE_WARNING; + public class PurgeCommand extends Command { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { // Shows confirmation prompt before deleting all transactions - Ui.showInfoMessage(InfoMessages.INFO_WARNING.toString()); + ui.showInfoMessage(INFO_PURGE_WARNING.toString()); String input = ui.readCommand(); + if (input.equals("Y")) { TransactionList.purgeEntries(transactions); - } else { - System.out.println(InfoMessages.INFO_DIVIDER); - System.out.println("MOOOOOO.... Aborting Command, returning to Home."); - System.out.println(InfoMessages.INFO_DIVIDER); + Ui.showInfoMessage(INFO_PURGE.toString()); + return; } + + Ui.showInfoMessage(INFO_PURGE_ABORT.toString()); } @Override diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 95c2c110b..98e73d8e4 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -5,14 +5,19 @@ */ public enum InfoMessages { INFO_DIVIDER("____________________________________________________________"), + INFO_ADD_EXPENSE("I have added the following Expense transaction: "), + INFO_ADD_INCOME("I have added the following Income transaction: "), + INFO_DELETE("I have deleted the following transaction: "), INFO_EXIT("Goodbye and see you soon."), - INFO_GREET("Hello! I'm your Moolah Manager!"), - INFO_HELP_GREET("I'm here to help. Here are the possible commands!"), + INFO_GREET("Hello! I'm Moo and I will help you to manage your finances."), + INFO_HELP_GREET("Gotcha! Here are the commands that you may use:"), INFO_HELP_PROMPT("Enter if you need the list of commands."), INFO_LIST("Below are the records of your transactions:"), INFO_LIST_EMPTY("There are no records of your transactions found."), - INFO_TRANSACTION_COUNT("You have %d transactions in your transaction history."), - INFO_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."); + INFO_PURGE("All your transactions have been purged."), + INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), + INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."), + INFO_TRANSACTION_COUNT("You have %d transactions in your transaction history."); public final String message; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index da532b269..d1284bd1c 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -24,10 +24,7 @@ public TransactionList() { } public static void purgeEntries(TransactionList input) { - System.out.println(InfoMessages.INFO_DIVIDER); input.empty(); - System.out.println("MOOOOOO.... All of your transactions have been purged."); - System.out.println(InfoMessages.INFO_DIVIDER); } private void empty() { @@ -42,31 +39,22 @@ private void removeEntry(int index) { transactions.remove(index - 1); } - public static void deleteEntry(TransactionList input, int index) { - System.out.println(InfoMessages.INFO_DIVIDER); - Transaction deleted = input.getEntry(index); - String information = deleted.getDescription(); + public static String deleteTransaction(TransactionList input, int index) { + Transaction transaction = input.getEntry(index); input.removeEntry(index); - System.out.println("MOOOOOO.... I HAVE DELETED THE FOLLOWING TRANSACTION: " + information); - System.out.println(InfoMessages.INFO_DIVIDER); + return transaction.getDescription(); } - public void addExpense(String description, int amount, String category, LocalDate date) { + public String addExpense(String description, int amount, String category, LocalDate date) { Expense expense = new Expense(description, amount, category, date); transactions.add(expense); - System.out.println(InfoMessages.INFO_DIVIDER); - System.out.println("MOOOOOO... I have added the following Expense Transaction: "); - System.out.println(expense); - System.out.println(InfoMessages.INFO_DIVIDER); + return expense.toString(); } - public void addIncome(String description, int amount, String category, LocalDate date) { + public String addIncome(String description, int amount, String category, LocalDate date) { Income income = new Income(description, amount, category, date); transactions.add(income); - System.out.println(InfoMessages.INFO_DIVIDER); - System.out.println("MOOOOOO... I have added the following Income Transaction: "); - System.out.println(income); - System.out.println(InfoMessages.INFO_DIVIDER); + return income.toString(); } public String listTransactions() { diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 8bf7e8f52..a606856f8 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -6,9 +6,16 @@ import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; public class Transaction { + private static final String PREFIX_CATEGORY = "["; + private static final String SUFFIX_CATEGORY = "]"; + private static final String SYMBOL_DOLLAR = "$"; + private static final String SYMBOL_PIPE = "|"; + private static final String TEXT_AT = "at"; + private static final String TEXT_DESCRIPTION = "Description:"; + + private String category; private String description; private int amount; - private String category; // category of income or expense private LocalDate date; public Transaction(String description, int amount, String category, LocalDate date) { @@ -48,12 +55,12 @@ public String printFormattedDate() { } public String printFormattedCategory() { - return "[" + category + "]"; + return PREFIX_CATEGORY + category + SUFFIX_CATEGORY; } @Override public String toString() { - return printFormattedCategory() + " $" + amount + " at " + printFormattedDate() - + " | Description: " + description; + return String.format("%s %s%d %s %s %s %s %s", printFormattedCategory(), SYMBOL_DOLLAR, + amount, TEXT_AT, printFormattedDate(), SYMBOL_PIPE, TEXT_DESCRIPTION, description); } } \ No newline at end of file From 6e22ac63612a36ded2e5d31cdf75acb060cf84b9 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 07:19:02 +0800 Subject: [PATCH 063/416] Replace redundant functions in TransactionList class --- src/main/java/seedu/duke/Ui.java | 4 --- .../java/seedu/duke/command/PurgeCommand.java | 2 +- .../java/seedu/duke/common/InfoMessages.java | 3 +- .../java/seedu/duke/data/TransactionList.java | 29 +++++-------------- 4 files changed, 9 insertions(+), 29 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index a1c4c40a4..6ab7bf611 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -18,9 +18,6 @@ public class Ui { * * @param messages A string of variable arguments. */ - //@@author chydarren-reused - // Reused from https://github.com/se-edu/addressbook-level2/blob/master/src/seedu/addressbook/ui/TextUi.java - // with minor modifications public static void printMessages(String... messages) { System.out.println(INFO_DIVIDER); // Prints the string of arguments line by line in a loop @@ -29,7 +26,6 @@ public static void printMessages(String... messages) { } System.out.println(INFO_DIVIDER); } - //@@author public Ui() { in = new Scanner(System.in); diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index c6443384f..2c3199609 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -16,7 +16,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { String input = ui.readCommand(); if (input.equals("Y")) { - TransactionList.purgeEntries(transactions); + TransactionList.purgeTransactions(); Ui.showInfoMessage(INFO_PURGE.toString()); return; } diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 98e73d8e4..e55491e7e 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -16,8 +16,7 @@ public enum InfoMessages { INFO_LIST_EMPTY("There are no records of your transactions found."), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), - INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."), - INFO_TRANSACTION_COUNT("You have %d transactions in your transaction history."); + INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."); public final String message; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index d1284bd1c..537bd467e 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -1,6 +1,5 @@ package seedu.duke.data; -import seedu.duke.common.InfoMessages; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; import seedu.duke.data.transaction.Transaction; @@ -17,32 +16,20 @@ public class TransactionList { private static final String EMPTY_STRING = ""; private static final String LINE_SEPARATOR = System.lineSeparator(); - private ArrayList transactions; + private static ArrayList transactions; public TransactionList() { this.transactions = new ArrayList<>(); } - public static void purgeEntries(TransactionList input) { - input.empty(); - } - - private void empty() { - transactions.clear(); - } - private Transaction getEntry(int index) { return transactions.get(index - 1); } - private void removeEntry(int index) { - transactions.remove(index - 1); - } - public static String deleteTransaction(TransactionList input, int index) { Transaction transaction = input.getEntry(index); - input.removeEntry(index); - return transaction.getDescription(); + transactions.remove(index); + return transaction.toString(); } public String addExpense(String description, int amount, String category, LocalDate date) { @@ -63,15 +50,13 @@ public String listTransactions() { for (Transaction transaction : transactions) { transactionsList += transaction.toString() + LINE_SEPARATOR; } - if (!transactionsList.isEmpty()) { - // Includes the count of the transactions with the transaction list - transactionsList += String.format(InfoMessages.INFO_TRANSACTION_COUNT.toString(), - transactions.size()); - } - return transactionsList; } + public static void purgeTransactions() { + transactions.clear(); + } + public int size() { return transactions.size(); } From 02a1bd91fc9a8ef6c3fa08151aef98a84acfd62f Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 07:20:02 +0800 Subject: [PATCH 064/416] Update EXPECTED.TXT and remove spacing from transaction action message to pass Gradle test --- .../java/seedu/duke/common/InfoMessages.java | 6 ++-- text-ui-test/EXPECTED.TXT | 35 ++++++++----------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index e55491e7e..1861d7db0 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -5,9 +5,9 @@ */ public enum InfoMessages { INFO_DIVIDER("____________________________________________________________"), - INFO_ADD_EXPENSE("I have added the following Expense transaction: "), - INFO_ADD_INCOME("I have added the following Income transaction: "), - INFO_DELETE("I have deleted the following transaction: "), + INFO_ADD_EXPENSE("I have added the following Expense transaction:"), + INFO_ADD_INCOME("I have added the following Income transaction:"), + INFO_DELETE("I have deleted the following transaction:"), INFO_EXIT("Goodbye and see you soon."), INFO_GREET("Hello! I'm Moo and I will help you to manage your finances."), INFO_HELP_GREET("Gotcha! Here are the commands that you may use:"), diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index f41a2b9c6..c9e4b8c31 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,24 +1,20 @@ ____________________________________________________________ -Hello! I'm your Moolah Manager! +Hello! I'm Moo and I will help you to manage your finances. Enter if you need the list of commands. ____________________________________________________________ ____________________________________________________________ There are no records of your transactions found. ____________________________________________________________ ____________________________________________________________ -Below are the records of your transactions: - -____________________________________________________________ -____________________________________________________________ -MOOOOOO... I have added the following Expense Transaction: +I have added the following Expense transaction: [-][food] $20 at Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +I have added the following Income transaction: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Expense Transaction: +I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ @@ -26,10 +22,10 @@ Below are the records of your transactions: [-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare -You have 3 transactions in your transaction history. + ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +I have added the following Income transaction: [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ @@ -39,10 +35,10 @@ ____________________________________________________________ Invalid category, please ensure your category is correct! ____________________________________________________________ ____________________________________________________________ -Non-Numeric input detected! Please enter a numerical amount! +Non-Numeric input detected, please enter a numerical amount! ____________________________________________________________ ____________________________________________________________ -MOOOOOO... I have added the following Income Transaction: +I have added the following Income transaction: [+][bonus] $-1 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ @@ -61,24 +57,21 @@ Below are the records of your transactions: [-][transport] $1 at Oct 02 2022 | Description: bus_fare [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss [+][bonus] $-1 at Oct 03 2022 | Description: thank_you_boss -You have 5 transactions in your transaction history. + ____________________________________________________________ ____________________________________________________________ -MOOOOOO.... I HAVE DELETED THE FOLLOWING TRANSACTION: NIL +I have deleted the following transaction: +[-][food] $20 at Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ -MOOOOOO.... Are you sure you want to proceed with this command? Please enter Y to confirm. +Are you sure you want to proceed with this command? Please enter 'Y' to confirm. ____________________________________________________________ ____________________________________________________________ -MOOOOOO.... All of your transactions have been purged. +All your transactions have been purged. ____________________________________________________________ ____________________________________________________________ There are no records of your transactions found. -____________________________________________________________ -____________________________________________________________ -Below are the records of your transactions: - ____________________________________________________________ ____________________________________________________________ Goodbye and see you soon. -____________________________________________________________ +____________________________________________________________ \ No newline at end of file From ae77b866060011348a8d568096a18b92a3b42a80 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 07:30:47 +0800 Subject: [PATCH 065/416] Attempt to fix newline issue --- text-ui-test/EXPECTED.TXT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index c9e4b8c31..8ad93933b 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -74,4 +74,4 @@ There are no records of your transactions found. ____________________________________________________________ ____________________________________________________________ Goodbye and see you soon. -____________________________________________________________ \ No newline at end of file +____________________________________________________________ From 552314f0c8abfb9826b08fdf1a492976461a46b9 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 09:30:37 +0800 Subject: [PATCH 066/416] Add ability to find specific transactions --- .../java/seedu/duke/command/FindCommand.java | 65 +++++++++++++++++++ .../java/seedu/duke/common/ErrorMessages.java | 3 +- .../java/seedu/duke/common/InfoMessages.java | 6 +- .../java/seedu/duke/data/TransactionList.java | 14 +++- ...ndTransactionMissingKeywordsException.java | 15 +++++ 5 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 src/main/java/seedu/duke/command/FindCommand.java create mode 100644 src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java new file mode 100644 index 000000000..5ec127b61 --- /dev/null +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -0,0 +1,65 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.FindTransactionMissingKeywordsException; + +import static seedu.duke.common.InfoMessages.INFO_LIST_FILTERED; +import static seedu.duke.common.InfoMessages.INFO_LIST_UNFILTERED; + +/** + * Represents a find command object that will execute the operations for Find command. + */ +public class FindCommand extends Command { + protected String keywords; + + /** + * Initialises the variables of the FindCommand class. + * + * @param keywords A string containing the keywords used in the search expression. + */ + public FindCommand(String keywords) { + this.keywords = keywords; + } + + /** + * Checks the format of find to ensure that it contains keywords used in the search expression. + * + * @param keywords A string containing the keywords used in the search expression. + * @throws FindTransactionMissingKeywordsException If a user does not enter a search expression for Find. + */ + public void checkFindFormat(String keywords) throws FindTransactionMissingKeywordsException { + if (keywords.isBlank()) { + throw new FindTransactionMissingKeywordsException(); + } + } + + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) { + try { + // Checks the format of find to ensure that it contains keywords used in the search expression + checkFindFormat(keywords); + String transactionsList = transactions.findTransactions(keywords); + if (transactionsList.isEmpty()) { + ui.showInfoMessage(INFO_LIST_UNFILTERED.toString()); + return; + } + ui.showTransactionsList(transactionsList, INFO_LIST_FILTERED.toString()); + } catch (FindTransactionMissingKeywordsException e) { + ui.showErrorMessage(e.getMessage()); + } + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index f0200081a..87017e2eb 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -10,7 +10,8 @@ public enum ErrorMessages { ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), - ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"); + ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), + ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"); public final String message; diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 1861d7db0..169689cdb 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -12,8 +12,10 @@ public enum InfoMessages { INFO_GREET("Hello! I'm Moo and I will help you to manage your finances."), INFO_HELP_GREET("Gotcha! Here are the commands that you may use:"), INFO_HELP_PROMPT("Enter if you need the list of commands."), - INFO_LIST("Below are the records of your transactions:"), - INFO_LIST_EMPTY("There are no records of your transactions found."), + INFO_LIST("Here are your transaction records:"), + INFO_LIST_EMPTY("There are no transaction records found."), + INFO_LIST_FILTERED("Here are the transaction records that match your search expression:"), + INFO_LIST_UNFILTERED("There are no transaction records that match your search expression."), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."); diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 537bd467e..86633e36e 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -46,13 +46,25 @@ public String addIncome(String description, int amount, String category, LocalDa public String listTransactions() { String transactionsList = EMPTY_STRING; - // Loops each task from the transactions list + // Loops each transaction from the transactions list for (Transaction transaction : transactions) { transactionsList += transaction.toString() + LINE_SEPARATOR; } return transactionsList; } + public String findTransactions(String keywords) { + String transactionsList = EMPTY_STRING; + // Loops each transaction from the transactions list + for (Transaction transaction : transactions) { + // Includes only transactions that contain the keywords used in the search expression + if (transaction.toString().contains(keywords)) { + transactionsList += transaction.toString() + LINE_SEPARATOR; + } + } + return transactionsList; + } + public static void purgeTransactions() { transactions.clear(); } diff --git a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java new file mode 100644 index 000000000..f622df300 --- /dev/null +++ b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class FindTransactionMissingKeywordsException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_FIND_COMMAND_MISSING_KEYWORDS.toString(); + } +} \ No newline at end of file From c140c671d78aa7e8282cc314db42c556bfae3493 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 09:34:56 +0800 Subject: [PATCH 067/416] Retrieve error messages from exception class --- src/main/java/seedu/duke/command/AddCommand.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 67a09a144..23107f4fd 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -2,8 +2,7 @@ import seedu.duke.Storage; import seedu.duke.Ui; -import seedu.duke.common.ErrorMessages; -import seedu.duke.common.InfoMessages; + import seedu.duke.data.TransactionList; import seedu.duke.exception.AddTransactionInvalidCategoryException; import seedu.duke.exception.AddTransactionInvalidDateException; @@ -16,6 +15,9 @@ import java.time.format.DateTimeParseException; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; +import static seedu.duke.common.ErrorMessages.ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC; +import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; +import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; public class AddCommand extends Command { @@ -62,7 +64,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws parseCategoryTag(parameter); category = parameter; } catch (AddTransactionInvalidCategoryException e) { - Ui.printMessages(ErrorMessages.ERROR_ADD_COMMAND_INVALID_CATEGORY.toString()); + Ui.printMessages(e.getMessage()); inputIsValid = false; } break; @@ -70,7 +72,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws try { amount = Integer.parseInt(parameter); } catch (NumberFormatException e) { - Ui.showErrorMessage(ErrorMessages.ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC.toString()); + Ui.showErrorMessage(ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC.toString()); inputIsValid = false; } break; @@ -88,11 +90,11 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws switch (type) { case "expense": String expense = transactions.addExpense(description, amount, category, date); - Ui.showTransactionAction(InfoMessages.INFO_ADD_EXPENSE.toString(), expense); + Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); break; case "income": String income = transactions.addIncome(description, amount, category, date); - Ui.showTransactionAction(InfoMessages.INFO_ADD_INCOME.toString(), income); + Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); break; default: throw new AddTransactionUnknownTypeException(); From cb29032f205acb40cb7347f3352e34db47651007 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 09:41:35 +0800 Subject: [PATCH 068/416] Fix bugs occured when a user input does not have additional fields on top of command --- src/main/java/seedu/duke/Parser.java | 36 ++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index ac463d33a..7a36d26a6 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -1,49 +1,71 @@ package seedu.duke; +import seedu.duke.command.Command; import seedu.duke.command.AddCommand; import seedu.duke.command.ByeCommand; -import seedu.duke.command.Command; import seedu.duke.command.DeleteCommand; import seedu.duke.command.EditCommand; +import seedu.duke.command.FindCommand; import seedu.duke.command.HelpCommand; import seedu.duke.command.ListCommand; import seedu.duke.command.PurgeCommand; + import seedu.duke.exception.MoolahException; import seedu.duke.exception.UnknownInputException; public class Parser { + private static final String EMPTY_STRING = ""; + private static final String DELIMITER = " "; + private static final int SPLIT_POSITION = 2; + + /** + * Splits the user input into two parts, i.e. the command and the description. + * + * @param inData A line of input entered by the user. + * @return A string array of input tokens. + */ + public static String[] splitInput(String inData) { + String[] inputTokens = inData.split(DELIMITER, SPLIT_POSITION); + if (!inData.contains(DELIMITER)) { + inputTokens = new String[]{inData, EMPTY_STRING}; + } + return inputTokens; + } /** * Parses the user input and deal with any input error returned. * - * @param inData The user input. + * @param inData A line of input entered by the user. * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. * @throws MoolahException Any command input exceptions captured by Moolah Manager. */ public static Command parse(String inData) throws MoolahException { Command command = null; - String[] splitInput = inData.split(" ", 2); + String[] inputTokens = splitInput(inData); // list commands duke to list all the tasks stored and their completion status // try at the start cos of the errors possibly - switch (splitInput[0]) { + switch (inputTokens[0]) { case "help": command = new HelpCommand(); break; case "list": command = new ListCommand(); break; + case "find": + command = new FindCommand(inputTokens[1]); + break; case "purge": command = new PurgeCommand(); break; case "delete": - command = new DeleteCommand(splitInput[1]); + command = new DeleteCommand(inputTokens[1]); break; case "add": - command = new AddCommand(splitInput[1]); + command = new AddCommand(inputTokens[1]); break; case "edit": - command = new EditCommand(splitInput[1]); + command = new EditCommand(inputTokens[1]); break; case "bye": command = new ByeCommand(); From 615fd95ae393a6813b7cdd8c8a68a14ed6215f81 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 09:45:49 +0800 Subject: [PATCH 069/416] Update EXPECTED.TXT and input.txt to test the Find command --- text-ui-test/EXPECTED.TXT | 16 ++++++++++++---- text-ui-test/input.txt | 2 ++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 8ad93933b..d6a133c9f 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -3,7 +3,7 @@ Hello! I'm Moo and I will help you to manage your finances. Enter if you need the list of commands. ____________________________________________________________ ____________________________________________________________ -There are no records of your transactions found. +There are no transaction records found. ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: @@ -18,7 +18,15 @@ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ -Below are the records of your transactions: +Keyword(s) for your search expression missing, please check your input! +____________________________________________________________ +____________________________________________________________ +Here are the transaction records that match your search expression: +[-][transport] $1 at Oct 02 2022 | Description: bus_fare + +____________________________________________________________ +____________________________________________________________ +Here are your transaction records: [-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare @@ -51,7 +59,7 @@ ____________________________________________________________ Invalid date, please ensure your date format is correct! ____________________________________________________________ ____________________________________________________________ -Below are the records of your transactions: +Here are your transaction records: [-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare @@ -70,7 +78,7 @@ ____________________________________________________________ All your transactions have been purged. ____________________________________________________________ ____________________________________________________________ -There are no records of your transactions found. +There are no transaction records found. ____________________________________________________________ ____________________________________________________________ Goodbye and see you soon. diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 2a933747c..2fe9621b2 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -2,6 +2,8 @@ list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary add t/expense c/transport a/1 d/02102022 i/bus_fare +find +find bus_fare list add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss add t/heheheh c/bonus a/1000000 d/03102022 i/thank_you_boss From 85504a86219830d05e403d033e10ef500e6d0031 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 09:49:30 +0800 Subject: [PATCH 070/416] Add header comments for the refractored command objects --- src/main/java/seedu/duke/command/AddCommand.java | 10 ++++++++++ src/main/java/seedu/duke/command/ByeCommand.java | 10 ++++++++++ src/main/java/seedu/duke/command/Command.java | 10 ++++++++++ .../java/seedu/duke/command/DeleteCommand.java | 15 +++++++++++++++ src/main/java/seedu/duke/command/EditCommand.java | 10 ++++++++++ src/main/java/seedu/duke/command/HelpCommand.java | 10 ++++++++++ src/main/java/seedu/duke/command/ListCommand.java | 10 ++++++++++ .../java/seedu/duke/command/PurgeCommand.java | 10 ++++++++++ 8 files changed, 85 insertions(+) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 23107f4fd..8cd04ebb0 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -19,6 +19,9 @@ import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; +/** + * Represents an add command object that will execute the operations for Add command. + */ public class AddCommand extends Command { private String input; @@ -27,6 +30,13 @@ public AddCommand(String input) { this.input = input; } + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { /* diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index af2399dee..93faa04aa 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -4,7 +4,17 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; +/** + * Represents a bye command object that will execute the operations for Bye command. + */ public class ByeCommand extends Command { + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { Ui.showExit(); diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index abd1c0aec..24c7d16a9 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -5,7 +5,17 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +/** + * Represents an object that can be inherited by other command objects. + */ public abstract class Command { + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; public abstract boolean isExit(); diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 3173d0739..5bbb72c55 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -9,13 +9,28 @@ import static seedu.duke.common.ErrorMessages.ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC; import static seedu.duke.common.InfoMessages.INFO_DELETE; +/** + * Represents a delete command object that will execute the operations for Delete command. + */ public class DeleteCommand extends Command { private String input; + /** + * Initialises the variables of the DeleteCommand class. + * + * @param input A string that represents the index of the task. + */ public DeleteCommand(String input) { this.input = input; } + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { /* diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 6ed3b57a9..b7c007959 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -4,6 +4,9 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; +/** + * Represents an edit command object that will execute the operations for Edit command. + */ public class EditCommand extends Command { private String input; @@ -11,6 +14,13 @@ public EditCommand(String input) { this.input = input; } + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { /* diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 2dbdb6d7b..96f10deb6 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -4,7 +4,17 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; +/** + * Represents a help command object that will execute the operations for Help command. + */ public class HelpCommand extends Command { + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { Ui.showHelp(); diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 4e0080fc9..96960b8b1 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -7,7 +7,17 @@ import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; +/** + * Represents a list command object that will execute the operations for List command. + */ public class ListCommand extends Command { + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { String transactionsList = transactions.listTransactions(); diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 2c3199609..6ffb18ee7 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -8,7 +8,17 @@ import static seedu.duke.common.InfoMessages.INFO_PURGE_ABORT; import static seedu.duke.common.InfoMessages.INFO_PURGE_WARNING; +/** + * Represents a purge command object that will execute the operations for Purge command. + */ public class PurgeCommand extends Command { + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { // Shows confirmation prompt before deleting all transactions From 780ddda0fac5811d2fce4b21fb8b96d69a48fe6d Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 10:13:50 +0800 Subject: [PATCH 071/416] Fix the exception handler for handling invalid or empty commands --- src/main/java/seedu/duke/Parser.java | 5 ++--- .../duke/exception/InvalidCommandException.java | 16 ++++++++++++++++ .../duke/exception/UnknownInputException.java | 15 --------------- 3 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/InvalidCommandException.java delete mode 100644 src/main/java/seedu/duke/exception/UnknownInputException.java diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 7a36d26a6..af413a6a2 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -11,7 +11,7 @@ import seedu.duke.command.PurgeCommand; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.UnknownInputException; +import seedu.duke.exception.InvalidCommandException; public class Parser { private static final String EMPTY_STRING = ""; @@ -71,8 +71,7 @@ public static Command parse(String inData) throws MoolahException { command = new ByeCommand(); break; default: - Ui.showInvalidCommand(); // if u still want this - throw new UnknownInputException(); + throw new InvalidCommandException(); } return command; } diff --git a/src/main/java/seedu/duke/exception/InvalidCommandException.java b/src/main/java/seedu/duke/exception/InvalidCommandException.java new file mode 100644 index 000000000..e9e75107e --- /dev/null +++ b/src/main/java/seedu/duke/exception/InvalidCommandException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import static seedu.duke.common.ErrorMessages.ERROR_INVALID_COMMAND; + +public class InvalidCommandException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ERROR_INVALID_COMMAND.toString(); + } + +} diff --git a/src/main/java/seedu/duke/exception/UnknownInputException.java b/src/main/java/seedu/duke/exception/UnknownInputException.java deleted file mode 100644 index 5982bb3cc..000000000 --- a/src/main/java/seedu/duke/exception/UnknownInputException.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.duke.exception; - -import seedu.duke.Ui; - -public class UnknownInputException extends MoolahException { - - // HELP ME CHANGE PLS - - @Override - public String getMessage() { - Ui.showInvalidCommand(); - return "OOPS!!! I'm sorry, but I don't know what that means :-( "; - } - -} From 87c852c18601c89c55a2e4a4e80bfe5a8c6049e2 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 10:17:47 +0800 Subject: [PATCH 072/416] Remove an extra new line at the InvalidCommandException class --- src/main/java/seedu/duke/exception/InvalidCommandException.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/seedu/duke/exception/InvalidCommandException.java b/src/main/java/seedu/duke/exception/InvalidCommandException.java index e9e75107e..97706c5b4 100644 --- a/src/main/java/seedu/duke/exception/InvalidCommandException.java +++ b/src/main/java/seedu/duke/exception/InvalidCommandException.java @@ -12,5 +12,4 @@ public class InvalidCommandException extends MoolahException { public String getMessage() { return ERROR_INVALID_COMMAND.toString(); } - } From 0e23aa842096afa2305001500161833713c72d65 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 10:52:52 +0800 Subject: [PATCH 073/416] Add JUnit test for Transaction class --- .../data/transaction/TransactionTest.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/test/java/seedu/duke/data/transaction/TransactionTest.java diff --git a/src/test/java/seedu/duke/data/transaction/TransactionTest.java b/src/test/java/seedu/duke/data/transaction/TransactionTest.java new file mode 100644 index 000000000..caf787db7 --- /dev/null +++ b/src/test/java/seedu/duke/data/transaction/TransactionTest.java @@ -0,0 +1,45 @@ +package seedu.duke.data.transaction; + +import org.junit.jupiter.api.Test; +import seedu.duke.data.transaction.Income; +import seedu.duke.data.transaction.Transaction; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TransactionTest { + Transaction transaction = new Income("Milked cows in the farm", 50, + "Salary", null); + + @Test + public void testGetDescription() { + assertEquals("Milked cows in the farm", transaction.getDescription()); + } + + @Test + public void testSetDescription() { + transaction.setDescription("Helped the cows to wash their ass"); + assertEquals("Helped the cows to wash their ass", transaction.getDescription()); + } + + @Test + public void testGetAmount() { + assertEquals(50, transaction.getAmount()); + } + + @Test + public void testSetAmount() { + transaction.setAmount(500); + assertEquals(500, transaction.getAmount()); + } + + @Test + public void testGetCategory() { + assertEquals("Salary", transaction.getCategory()); + } + + @Test + public void testSetCategory() { + transaction.setCategory("Love"); + assertEquals("Love", transaction.getCategory()); + } +} From 121ca09c33b12a1505a3ed3a9d30d3ab24f1ca58 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Fri, 7 Oct 2022 11:14:53 +0800 Subject: [PATCH 074/416] Resolving Errors --- src/main/java/seedu/duke/Duke.java | 2 +- src/test/java/seedu/duke/DukeTest.java | 12 ------------ src/test/java/seedu/duke/data/DukeTest.java | 13 +++++++++++++ 3 files changed, 14 insertions(+), 13 deletions(-) delete mode 100644 src/test/java/seedu/duke/DukeTest.java create mode 100644 src/test/java/seedu/duke/data/DukeTest.java diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index a3a1bec43..796b1126c 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -11,7 +11,7 @@ public static void main(String[] args) { Scanner scan = new Scanner(System.in); Ui.showGreeting(); - while (scan.hasNextLine() && true) { + while (scan.hasNextLine()) { // Receives user input continuously inData = scan.nextLine(); inData = inData.trim(); diff --git a/src/test/java/seedu/duke/DukeTest.java b/src/test/java/seedu/duke/DukeTest.java deleted file mode 100644 index 2dda5fd65..000000000 --- a/src/test/java/seedu/duke/DukeTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.duke; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.junit.jupiter.api.Test; - -class DukeTest { - @Test - public void sampleTest() { - assertTrue(true); - } -} diff --git a/src/test/java/seedu/duke/data/DukeTest.java b/src/test/java/seedu/duke/data/DukeTest.java new file mode 100644 index 000000000..11a65b960 --- /dev/null +++ b/src/test/java/seedu/duke/data/DukeTest.java @@ -0,0 +1,13 @@ +package seedu.duke.data; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +class DukeTest { + @Test + public void checkInsert() { + +; } +} + From d8c043f9f078c24cf7c246a0d34f3c7314d2e8c3 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 13:18:27 +0800 Subject: [PATCH 075/416] Add ability to list total savings (income and expenditure) for each category --- src/main/java/seedu/duke/Parser.java | 5 ++ .../java/seedu/duke/command/GetCommand.java | 35 ++++++++++++++ .../java/seedu/duke/common/InfoMessages.java | 1 + .../java/seedu/duke/data/CategoryList.java | 48 +++++++++++++++++++ .../java/seedu/duke/data/TransactionList.java | 4 +- .../seedu/duke/data/transaction/Category.java | 36 ++++++++++++++ .../duke/data/transaction/Transaction.java | 4 +- .../data/transaction/TransactionTest.java | 2 - 8 files changed, 129 insertions(+), 6 deletions(-) create mode 100644 src/main/java/seedu/duke/command/GetCommand.java create mode 100644 src/main/java/seedu/duke/data/CategoryList.java create mode 100644 src/main/java/seedu/duke/data/transaction/Category.java diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index af413a6a2..13798e9fc 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -6,6 +6,7 @@ import seedu.duke.command.DeleteCommand; import seedu.duke.command.EditCommand; import seedu.duke.command.FindCommand; +import seedu.duke.command.GetCommand; import seedu.duke.command.HelpCommand; import seedu.duke.command.ListCommand; import seedu.duke.command.PurgeCommand; @@ -55,6 +56,10 @@ public static Command parse(String inData) throws MoolahException { case "find": command = new FindCommand(inputTokens[1]); break; + case "get": + // Additional tokens will be allowed for get + command = new GetCommand(); + break; case "purge": command = new PurgeCommand(); break; diff --git a/src/main/java/seedu/duke/command/GetCommand.java b/src/main/java/seedu/duke/command/GetCommand.java new file mode 100644 index 000000000..ec9bde349 --- /dev/null +++ b/src/main/java/seedu/duke/command/GetCommand.java @@ -0,0 +1,35 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.CategoryList; +import seedu.duke.data.TransactionList; + +import static seedu.duke.common.InfoMessages.INFO_GET_CATEGORIES; + +/** + * Represents a get command object that will execute the operations for Get command. + */ +public class GetCommand extends Command { + CategoryList categories = new CategoryList(); + + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) { + // This will be a method within this method, command will be e.g. "get categories" + categories.calculateTotalAmount(transactions); + String categoriesList = categories.listCategories(); + Ui.showTransactionsList(categoriesList, INFO_GET_CATEGORIES.toString()); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 169689cdb..3d2b9165b 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -9,6 +9,7 @@ public enum InfoMessages { INFO_ADD_INCOME("I have added the following Income transaction:"), INFO_DELETE("I have deleted the following transaction:"), INFO_EXIT("Goodbye and see you soon."), + INFO_GET_CATEGORIES("Here are the total savings for each category:"), INFO_GREET("Hello! I'm Moo and I will help you to manage your finances."), INFO_HELP_GREET("Gotcha! Here are the commands that you may use:"), INFO_HELP_PROMPT("Enter if you need the list of commands."), diff --git a/src/main/java/seedu/duke/data/CategoryList.java b/src/main/java/seedu/duke/data/CategoryList.java new file mode 100644 index 000000000..313ac4938 --- /dev/null +++ b/src/main/java/seedu/duke/data/CategoryList.java @@ -0,0 +1,48 @@ +package seedu.duke.data; + +import seedu.duke.data.transaction.Category; + +import java.util.ArrayList; + +public class CategoryList { + private static final String EMPTY_STRING = ""; + private static final String LINE_SEPARATOR = System.lineSeparator(); + + private static ArrayList categories = new ArrayList<>(); + + public static void addCategories(TransactionList transactions) { + for (int i = 0; i < transactions.size(); i++) { + Category category = new Category(transactions.getEntry(i).getCategory()); + + // Adds a category in the list only if the category is not in list + if (!categories.contains(category)) { + categories.add(category); + } + } + } + + public static void calculateTotalAmount(TransactionList transactions) { + addCategories(transactions); + for (Category category : categories) { + int amount = category.getAmount(); + + // Gets each transaction and if belongs to category, compute to total amount + for (int i = 0; i < transactions.size(); i++) { + if (transactions.getEntry(i).getCategory().equals(category.getCategory())) { + amount += transactions.getEntry(i).getAmount(); + } + } + + category.setAmount(amount); + } + } + + public String listCategories() { + String categoriesList = EMPTY_STRING; + // Loops each category from the categories list + for (Category category : categories) { + categoriesList += category.toString() + LINE_SEPARATOR; + } + return categoriesList; + } +} diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 86633e36e..b18c6a227 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -22,8 +22,8 @@ public TransactionList() { this.transactions = new ArrayList<>(); } - private Transaction getEntry(int index) { - return transactions.get(index - 1); + public Transaction getEntry(int index) { + return transactions.get(index); } public static String deleteTransaction(TransactionList input, int index) { diff --git a/src/main/java/seedu/duke/data/transaction/Category.java b/src/main/java/seedu/duke/data/transaction/Category.java new file mode 100644 index 000000000..cf29927b7 --- /dev/null +++ b/src/main/java/seedu/duke/data/transaction/Category.java @@ -0,0 +1,36 @@ +package seedu.duke.data.transaction; + +public class Category { + private static final String PREFIX_CATEGORY = "["; + private static final String POSTFIX_CATEGORY = "]"; + private static final String SYMBOL_DOLLAR = "$"; + + private String category; + private int amount; + + public Category(String category) { + this.category = category; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + @Override + public String toString() { + return String.format("%s%s%s %s%d", PREFIX_CATEGORY, category, POSTFIX_CATEGORY, + SYMBOL_DOLLAR, amount); + } +} diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index a606856f8..254af320e 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -7,7 +7,7 @@ public class Transaction { private static final String PREFIX_CATEGORY = "["; - private static final String SUFFIX_CATEGORY = "]"; + private static final String POSTFIX_CATEGORY = "]"; private static final String SYMBOL_DOLLAR = "$"; private static final String SYMBOL_PIPE = "|"; private static final String TEXT_AT = "at"; @@ -55,7 +55,7 @@ public String printFormattedDate() { } public String printFormattedCategory() { - return PREFIX_CATEGORY + category + SUFFIX_CATEGORY; + return PREFIX_CATEGORY + category + POSTFIX_CATEGORY; } @Override diff --git a/src/test/java/seedu/duke/data/transaction/TransactionTest.java b/src/test/java/seedu/duke/data/transaction/TransactionTest.java index caf787db7..50fb68051 100644 --- a/src/test/java/seedu/duke/data/transaction/TransactionTest.java +++ b/src/test/java/seedu/duke/data/transaction/TransactionTest.java @@ -1,8 +1,6 @@ package seedu.duke.data.transaction; import org.junit.jupiter.api.Test; -import seedu.duke.data.transaction.Income; -import seedu.duke.data.transaction.Transaction; import static org.junit.jupiter.api.Assertions.assertEquals; From 62ceefa2d683ac8ae94ea1d04a84cc9b64803021 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 7 Oct 2022 13:22:12 +0800 Subject: [PATCH 076/416] Update EXPECTED.TXT and input.txt to test the Get command --- src/main/java/seedu/duke/data/TransactionList.java | 2 +- text-ui-test/EXPECTED.TXT | 7 +++++++ text-ui-test/input.txt | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b18c6a227..54e3c93c1 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -27,7 +27,7 @@ public Transaction getEntry(int index) { } public static String deleteTransaction(TransactionList input, int index) { - Transaction transaction = input.getEntry(index); + Transaction transaction = input.getEntry(index - 1); transactions.remove(index); return transaction.toString(); } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index d6a133c9f..9dd63210a 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -16,6 +16,13 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare +____________________________________________________________ +____________________________________________________________ +Here are the total savings for each category: +[food] $20 +[salary] $2000 +[transport] $1 + ____________________________________________________________ ____________________________________________________________ Keyword(s) for your search expression missing, please check your input! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 2fe9621b2..51146d0dd 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -2,6 +2,7 @@ list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary add t/expense c/transport a/1 d/02102022 i/bus_fare +get find find bus_fare list From 23030c6e578235554c5386bd1df83b4c6e6bd86e Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 14:22:04 +0800 Subject: [PATCH 077/416] Add testing for containNumeric() function --- .../java/seedu/duke/command/AddCommand.java | 4 ++-- .../duke/data/transaction/AddCommandTest.java | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/test/java/seedu/duke/data/transaction/AddCommandTest.java diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 8cd04ebb0..6e8c2d21d 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -128,7 +128,7 @@ public boolean isExit() { * @throws AddTransactionInvalidCategoryException Invalid category parameter exception. */ private static void parseCategoryTag(String parameter) throws AddTransactionInvalidCategoryException { - if (isNumeric(parameter)) { + if (containNumeric(parameter)) { throw new AddTransactionInvalidCategoryException(); } @@ -140,7 +140,7 @@ private static void parseCategoryTag(String parameter) throws AddTransactionInva * @param parameter The user input after the user tag. * @return true if there are numeric characters within the parameter. */ - public static boolean isNumeric(String parameter) { + public static boolean containNumeric(String parameter) { char[] characters = parameter.toCharArray(); for (char character : characters) { if (Character.isDigit(character)) { diff --git a/src/test/java/seedu/duke/data/transaction/AddCommandTest.java b/src/test/java/seedu/duke/data/transaction/AddCommandTest.java new file mode 100644 index 000000000..b1297a5b4 --- /dev/null +++ b/src/test/java/seedu/duke/data/transaction/AddCommandTest.java @@ -0,0 +1,22 @@ +package seedu.duke.data.transaction; + +import org.junit.jupiter.api.Test; +import seedu.duke.command.AddCommand; + +import static org.junit.jupiter.api.Assertions.*; + +public class AddCommandTest { + + boolean testOutputContainsNumber = AddCommand.containNumeric("Food1"); + boolean testOutputWithoutNumber = AddCommand.containNumeric("Food"); + + @Test + public void testCheckIfInputContainsNumeric() { + assertTrue(testOutputContainsNumber); + } + + @Test + public void testCheckIfInputContainsOnlyAlphabets() { + assertFalse(testOutputWithoutNumber); + } +} From d0c0099afe13fea12d082b4a8d25fac434a5a061 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 14:24:51 +0800 Subject: [PATCH 078/416] Reorganise AddCommandTest.java file location --- .../duke/{data/transaction => command}/AddCommandTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/seedu/duke/{data/transaction => command}/AddCommandTest.java (93%) diff --git a/src/test/java/seedu/duke/data/transaction/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java similarity index 93% rename from src/test/java/seedu/duke/data/transaction/AddCommandTest.java rename to src/test/java/seedu/duke/command/AddCommandTest.java index b1297a5b4..642b8ef38 100644 --- a/src/test/java/seedu/duke/data/transaction/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -1,4 +1,4 @@ -package seedu.duke.data.transaction; +package seedu.duke.command; import org.junit.jupiter.api.Test; import seedu.duke.command.AddCommand; From e6ca48921affd3d2a9b5a19f675640a1e0134a7f Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 14:30:36 +0800 Subject: [PATCH 079/416] Change import statement in AddCommandTest.java --- src/test/java/seedu/duke/command/AddCommandTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index 642b8ef38..443a9b1b0 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -1,9 +1,10 @@ package seedu.duke.command; import org.junit.jupiter.api.Test; -import seedu.duke.command.AddCommand; -import static org.junit.jupiter.api.Assertions.*; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; public class AddCommandTest { From cd6b73856fc4a2861f7af0032d09642e3765d3e5 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 14:40:00 +0800 Subject: [PATCH 080/416] Update test method names in AddCommandTest.java --- src/test/java/seedu/duke/command/AddCommandTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index 443a9b1b0..b39e3fa65 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -8,16 +8,15 @@ public class AddCommandTest { - boolean testOutputContainsNumber = AddCommand.containNumeric("Food1"); - boolean testOutputWithoutNumber = AddCommand.containNumeric("Food"); - @Test - public void testCheckIfInputContainsNumeric() { + public void containNumeric_IfContainsNumeric_ReturnTrue() { + boolean testOutputContainsNumber = AddCommand.containNumeric("Food1"); assertTrue(testOutputContainsNumber); } @Test - public void testCheckIfInputContainsOnlyAlphabets() { + public void containNumeric_IfDoesNotContainNumeric_ReturnFalse(){ + boolean testOutputWithoutNumber = AddCommand.containNumeric("Food"); assertFalse(testOutputWithoutNumber); } } From 14e5241dffda280722eefd048f3389ff7608e9ca Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 14:41:51 +0800 Subject: [PATCH 081/416] Add spacing before bracket in method --- src/test/java/seedu/duke/command/AddCommandTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index b39e3fa65..dec7c3ad2 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -15,7 +15,7 @@ public void containNumeric_IfContainsNumeric_ReturnTrue() { } @Test - public void containNumeric_IfDoesNotContainNumeric_ReturnFalse(){ + public void containNumeric_IfDoesNotContainNumeric_ReturnFalse() { boolean testOutputWithoutNumber = AddCommand.containNumeric("Food"); assertFalse(testOutputWithoutNumber); } From 89885541900ad2c7cd79492509388a07c8e62fcd Mon Sep 17 00:00:00 2001 From: brian-vb Date: Fri, 7 Oct 2022 15:25:17 +0800 Subject: [PATCH 082/416] Add more checks to Purge Command and a Junit Test. --- src/main/java/seedu/duke/Duke.java | 8 ------ .../java/seedu/duke/command/PurgeCommand.java | 17 +++++++++-- .../java/seedu/duke/common/InfoMessages.java | 2 ++ .../seedu/duke/command/PurgeCommandTest.java | 28 +++++++++++++++++++ src/test/java/seedu/duke/data/DukeTest.java | 13 --------- 5 files changed, 44 insertions(+), 24 deletions(-) create mode 100644 src/test/java/seedu/duke/command/PurgeCommandTest.java delete mode 100644 src/test/java/seedu/duke/data/DukeTest.java diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 2dfde6821..1cd657971 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -23,12 +23,6 @@ public Duke() { // NEED TO ADD FILE PATH }**/ } -<<<<<<< HEAD - while (scan.hasNextLine()) { - // Receives user input continuously - inData = scan.nextLine(); - inData = inData.trim(); -======= public void run() { ui.showGreeting(); boolean isExit = false; @@ -37,8 +31,6 @@ public void run() { try { inData = ui.readCommand(); inData = inData.trim(); ->>>>>>> 9f27496827dc51384187748e96daf45d2b37a005 - Command c = Parser.parse(inData); c.execute(transactions, ui, storage); isExit = c.isExit(); diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 6ffb18ee7..42525be5e 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -4,9 +4,7 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import static seedu.duke.common.InfoMessages.INFO_PURGE; -import static seedu.duke.common.InfoMessages.INFO_PURGE_ABORT; -import static seedu.duke.common.InfoMessages.INFO_PURGE_WARNING; +import static seedu.duke.common.InfoMessages.*; /** * Represents a purge command object that will execute the operations for Purge command. @@ -22,6 +20,11 @@ public class PurgeCommand extends Command { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { // Shows confirmation prompt before deleting all transactions + boolean check = isEmpty(transactions); + if (check) { + Ui.showInfoMessage(INFO_PURGE_EMPTY.toString()); + return; + } ui.showInfoMessage(INFO_PURGE_WARNING.toString()); String input = ui.readCommand(); @@ -34,6 +37,14 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { Ui.showInfoMessage(INFO_PURGE_ABORT.toString()); } + public static boolean isEmpty(TransactionList transactions) { + int size = transactions.size(); + if (size == 0) { + return true; + } + return false; + } + @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 3d2b9165b..fa216c092 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -19,6 +19,8 @@ public enum InfoMessages { INFO_LIST_UNFILTERED("There are no transaction records that match your search expression."), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), + + INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."); public final String message; diff --git a/src/test/java/seedu/duke/command/PurgeCommandTest.java b/src/test/java/seedu/duke/command/PurgeCommandTest.java new file mode 100644 index 000000000..a377eb750 --- /dev/null +++ b/src/test/java/seedu/duke/command/PurgeCommandTest.java @@ -0,0 +1,28 @@ +package seedu.duke.command; + +import org.junit.jupiter.api.Test; +import seedu.duke.data.TransactionList; + +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PurgeCommandTest { + + @Test + public void purge_IfEmpty_ReturnTrue() { + TransactionList transactions = new TransactionList(); + boolean testOutputWithoutEntries = PurgeCommand.isEmpty(transactions); + assertTrue(testOutputWithoutEntries); + } + + @Test + public void purge_IfNotEmpty_ReturnFalse() { + TransactionList transactions = new TransactionList(); + LocalDate date = LocalDate.of(2022, 1, 1); + transactions.addExpense("Maggi", 10, "Food", date); + boolean testOutputWithEntries = PurgeCommand.isEmpty(transactions); + assertFalse(testOutputWithEntries); + } +} diff --git a/src/test/java/seedu/duke/data/DukeTest.java b/src/test/java/seedu/duke/data/DukeTest.java deleted file mode 100644 index 11a65b960..000000000 --- a/src/test/java/seedu/duke/data/DukeTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.duke.data; - -import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; - - -class DukeTest { - @Test - public void checkInsert() { - -; } -} - From 5169c3d7bb079365c59421d60edeb67ed4d7c81d Mon Sep 17 00:00:00 2001 From: brian-vb Date: Fri, 7 Oct 2022 15:29:52 +0800 Subject: [PATCH 083/416] Edited TransactionTest to input a Date rather than a Null Variable. Checkstyle Changes for PurgeCommand. --- src/main/java/seedu/duke/command/PurgeCommand.java | 5 ++++- .../java/seedu/duke/data/transaction/TransactionTest.java | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 42525be5e..5f558be03 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -4,7 +4,10 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import static seedu.duke.common.InfoMessages.*; +import static seedu.duke.common.InfoMessages.INFO_PURGE; +import static seedu.duke.common.InfoMessages.INFO_PURGE_ABORT; +import static seedu.duke.common.InfoMessages.INFO_PURGE_EMPTY; +import static seedu.duke.common.InfoMessages.INFO_PURGE_WARNING; /** * Represents a purge command object that will execute the operations for Purge command. diff --git a/src/test/java/seedu/duke/data/transaction/TransactionTest.java b/src/test/java/seedu/duke/data/transaction/TransactionTest.java index 50fb68051..670214790 100644 --- a/src/test/java/seedu/duke/data/transaction/TransactionTest.java +++ b/src/test/java/seedu/duke/data/transaction/TransactionTest.java @@ -2,11 +2,15 @@ import org.junit.jupiter.api.Test; +import java.time.LocalDate; + import static org.junit.jupiter.api.Assertions.assertEquals; public class TransactionTest { + + LocalDate date = LocalDate.of(2022, 1, 1); Transaction transaction = new Income("Milked cows in the farm", 50, - "Salary", null); + "Salary", date); @Test public void testGetDescription() { From cf6908a2d87681093bd9d28b5edf6aec7adcff47 Mon Sep 17 00:00:00 2001 From: wcwy Date: Fri, 7 Oct 2022 15:42:50 +0800 Subject: [PATCH 084/416] Add JUnit test for AddCommand class for invalid date format To ensure that the empty date and not-supported date format will throw a correct AddTransactionInvalidDateException exception. --- .../seedu/duke/command/AddCommandTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index dec7c3ad2..7a16e6fd0 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -1,12 +1,40 @@ package seedu.duke.command; import org.junit.jupiter.api.Test; +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.AddTransactionInvalidDateException; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; public class AddCommandTest { + @Test + public void execute_EmptyDate_ExpectedException () { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + AddCommand addCommand = new AddCommand("t/income c/bonus a/-1 d/ i/thank_you_boss"); + + assertThrows(AddTransactionInvalidDateException.class, + () -> addCommand.execute(transactions, ui, storage) + ); + } + + @Test + public void execute_InvalidDateFormat_ExpectedException () { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + AddCommand addCommand = new AddCommand("t/income c/bonus a/-1 d/2020-01-01 i/thank_you_boss"); + + assertThrows(AddTransactionInvalidDateException.class, + () -> addCommand.execute(transactions, ui, storage) + ); + } @Test public void containNumeric_IfContainsNumeric_ReturnTrue() { From 2178506c77e40c239bc283b780d9bb691e01a629 Mon Sep 17 00:00:00 2001 From: wcwy Date: Fri, 7 Oct 2022 15:47:13 +0800 Subject: [PATCH 085/416] Reformat indentation for lambda functions in AddCommandTest --- .../java/seedu/duke/command/AddCommandTest.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index 7a16e6fd0..6ac3ef6e6 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -13,26 +13,28 @@ public class AddCommandTest { @Test - public void execute_EmptyDate_ExpectedException () { + public void execute_EmptyDate_ExpectedException() { TransactionList transactions = new TransactionList(); Ui ui = new Ui(); Storage storage = new Storage(); AddCommand addCommand = new AddCommand("t/income c/bonus a/-1 d/ i/thank_you_boss"); - assertThrows(AddTransactionInvalidDateException.class, - () -> addCommand.execute(transactions, ui, storage) + assertThrows( + AddTransactionInvalidDateException.class, + () -> addCommand.execute(transactions, ui, storage) ); } @Test - public void execute_InvalidDateFormat_ExpectedException () { + public void execute_InvalidDateFormat_ExpectedException() { TransactionList transactions = new TransactionList(); Ui ui = new Ui(); Storage storage = new Storage(); AddCommand addCommand = new AddCommand("t/income c/bonus a/-1 d/2020-01-01 i/thank_you_boss"); - assertThrows(AddTransactionInvalidDateException.class, - () -> addCommand.execute(transactions, ui, storage) + assertThrows( + AddTransactionInvalidDateException.class, + () -> addCommand.execute(transactions, ui, storage) ); } From e0a62475e61c0aa588cd3774b197d2ca559a912a Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 16:30:53 +0800 Subject: [PATCH 086/416] Update parsing methods for the Category and Amount tags --- .../java/seedu/duke/command/AddCommand.java | 89 ++++++++++++------- .../java/seedu/duke/common/ErrorMessages.java | 1 + .../AddTransactionInvalidAmountException.java | 16 ++++ 3 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 6e8c2d21d..2a466e7a7 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -4,15 +4,13 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.AddTransactionInvalidCategoryException; -import seedu.duke.exception.AddTransactionInvalidDateException; -import seedu.duke.exception.AddTransactionMissingTagException; -import seedu.duke.exception.AddTransactionUnknownTypeException; -import seedu.duke.exception.MoolahException; +import seedu.duke.exception.*; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; import static seedu.duke.common.ErrorMessages.ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC; @@ -33,9 +31,9 @@ public AddCommand(String input) { /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { @@ -70,21 +68,13 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws type = parameter; break; case "c/": - try { - parseCategoryTag(parameter); - category = parameter; - } catch (AddTransactionInvalidCategoryException e) { - Ui.printMessages(e.getMessage()); - inputIsValid = false; - } +// if (containNumeric(parameter)) { +// throw new AddTransactionInvalidCategoryException(); +// } + category = parseCategoryTag(parameter); break; case "a/": - try { - amount = Integer.parseInt(parameter); - } catch (NumberFormatException e) { - Ui.showErrorMessage(ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC.toString()); - inputIsValid = false; - } + amount = parseAmountTag(parameter); break; case "d/": date = parseDateTag(parameter); @@ -121,29 +111,33 @@ public boolean isExit() { // the other add functions + /** - * Processes the parameter. If it is an invalid parameter an exception error would be thrown. + * Checks if the parameter contains numeric characters. * * @param parameter The user input after the user tag. - * @throws AddTransactionInvalidCategoryException Invalid category parameter exception. + * @return true if there are numeric characters within the parameter. */ - private static void parseCategoryTag(String parameter) throws AddTransactionInvalidCategoryException { - if (containNumeric(parameter)) { - throw new AddTransactionInvalidCategoryException(); + public static boolean containNumeric(String parameter) { + char[] characters = parameter.toCharArray(); + for (char character : characters) { + if (Character.isDigit(character)) { + return true; + } } - + return false; } /** - * Checks if the parameter contains numeric characters. + * Checks if the parameter contains alphabetical characters. * * @param parameter The user input after the user tag. - * @return true if there are numeric characters within the parameter. + * @return true if there are alphabetical characters within the parameter. */ - public static boolean containNumeric(String parameter) { + public static boolean containAlphabet(String parameter) { char[] characters = parameter.toCharArray(); for (char character : characters) { - if (Character.isDigit(character)) { + if (Character.isAlphabetic(character)) { return true; } } @@ -151,6 +145,41 @@ public static boolean containNumeric(String parameter) { } + /** + * Parse the user parameter input for the description and returns it. + * + * @param parameter The user input after the user tag. + * @return The category parameter if no exceptions are thrown. + * @throws AddTransactionInvalidCategoryException Invalid category format exception. + */ + + private static String parseCategoryTag(String parameter) throws AddTransactionInvalidCategoryException { + Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); + Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); + if (containNumeric(parameter) || hasSpecialSymbols.find()) { + throw new AddTransactionInvalidCategoryException(); + } + return parameter; + + } + + /** + * Parse the user parameter input for the amount and returns it. + * + * @param parameter The user input after the user tag. + * @return The amount integer if no exceptions are thrown. + * @throws AddTransactionInvalidAmountException Invalid amount format exception. + */ + private static int parseAmountTag(String parameter) throws AddTransactionInvalidAmountException { + Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); + Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); + if (containAlphabet(parameter) || hasSpecialSymbols.find()) { + throw new AddTransactionInvalidAmountException(); + } else { + return Integer.parseInt(parameter); + } + } + /** * Parse the user parameter input for date into a LocalDate object and returns it. * diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 87017e2eb..8a71d7700 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -7,6 +7,7 @@ public enum ErrorMessages { ERROR_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), + ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, please ensure your amount is in positive numerals only!"), ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java b/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java new file mode 100644 index 000000000..f9acaa740 --- /dev/null +++ b/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class AddTransactionInvalidAmountException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_ADD_COMMAND_INVALID_AMOUNT.toString(); + } + +} From 50078fe5f9a59d55dfa75ba5e05a2738f0135331 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 16:36:18 +0800 Subject: [PATCH 087/416] Update import statements and removed comments in AddCommand.java --- src/main/java/seedu/duke/command/AddCommand.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 2a466e7a7..554b12a48 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -4,7 +4,13 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.*; +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.AddTransactionInvalidAmountException; +import seedu.duke.exception.AddTransactionInvalidCategoryException; +import seedu.duke.exception.AddTransactionUnknownTypeException; +import seedu.duke.exception.AddTransactionInvalidDateException; +import seedu.duke.exception.AddTransactionMissingTagException; + import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -68,9 +74,6 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws type = parameter; break; case "c/": -// if (containNumeric(parameter)) { -// throw new AddTransactionInvalidCategoryException(); -// } category = parseCategoryTag(parameter); break; case "a/": From a5982d592429e849fcb52386fa309deac21601b0 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 16:46:10 +0800 Subject: [PATCH 088/416] Update test cases in AddCommandTest.java --- src/test/java/seedu/duke/command/AddCommandTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index 6ac3ef6e6..eec0a582b 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -17,7 +17,7 @@ public void execute_EmptyDate_ExpectedException() { TransactionList transactions = new TransactionList(); Ui ui = new Ui(); Storage storage = new Storage(); - AddCommand addCommand = new AddCommand("t/income c/bonus a/-1 d/ i/thank_you_boss"); + AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/ i/thank_you_boss"); assertThrows( AddTransactionInvalidDateException.class, @@ -30,7 +30,7 @@ public void execute_InvalidDateFormat_ExpectedException() { TransactionList transactions = new TransactionList(); Ui ui = new Ui(); Storage storage = new Storage(); - AddCommand addCommand = new AddCommand("t/income c/bonus a/-1 d/2020-01-01 i/thank_you_boss"); + AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss"); assertThrows( AddTransactionInvalidDateException.class, From abd6a16048268c6e9ccfcc9669b4208613b213a3 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Fri, 7 Oct 2022 16:53:30 +0800 Subject: [PATCH 089/416] Edit Expected.txt file contents --- text-ui-test/EXPECTED.TXT | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 9dd63210a..1cef6ac67 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -50,11 +50,10 @@ ____________________________________________________________ Invalid category, please ensure your category is correct! ____________________________________________________________ ____________________________________________________________ -Non-Numeric input detected, please enter a numerical amount! +Invalid amount, please ensure your amount is in positive numerals only! ____________________________________________________________ ____________________________________________________________ -I have added the following Income transaction: -[+][bonus] $-1 at Oct 03 2022 | Description: thank_you_boss +Invalid amount, please ensure your amount is in positive numerals only! ____________________________________________________________ ____________________________________________________________ Invalid date, please ensure your date format is correct! @@ -71,7 +70,6 @@ Here are your transaction records: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss -[+][bonus] $-1 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ From 9b3498d65fc4ec7e6607719f56e7834247a3f78d Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 03:32:16 +0800 Subject: [PATCH 090/416] Refractor code for adding transaction --- .../java/seedu/duke/command/AddCommand.java | 82 ++++++++----------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 554b12a48..47fe32bf5 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -19,7 +19,6 @@ import java.util.regex.Pattern; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; -import static seedu.duke.common.ErrorMessages.ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC; import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; @@ -35,7 +34,7 @@ public AddCommand(String input) { } /** - * Executes the operations related to the command. + * Executes the "add" command. Check and parse the necessary parameters before adding transaction. * * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. @@ -47,24 +46,15 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist */ - /** - * Parses the add transaction command by checking if the compulsory tags exist followed by - * adding the transaction. - * Then executes the command to add the transaction into the list. - * - * @param userInput The user input after the "add" command word. - * @throws AddTransactionMissingTagException Exceptions related to "add" command. - */ + String[] splits = input.split(" "); checkTagsExist(splits); - // TODO: To check that each parameter is in correct format, e.g. amount should be valid integer/double - // TODO: To move the add transaction logic below to Command class in Command.execute() + String description = ""; int amount = 0; String category = ""; LocalDate date = null; String type = ""; - boolean inputIsValid = true; for (String split : splits) { String tag = split.substring(0, 2); @@ -89,31 +79,46 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws break; } } - if (inputIsValid) { - switch (type) { - case "expense": - String expense = transactions.addExpense(description, amount, category, date); - Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); - break; - case "income": - String income = transactions.addIncome(description, amount, category, date); - Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); - break; - default: - throw new AddTransactionUnknownTypeException(); - } - } + assert date != null; + addTransactionByType(transactions, type, description, amount, category, date); } - @Override public boolean isExit() { return false; } + private static void addTransactionByType(TransactionList transactions, String type, String description, int amount, String category, LocalDate date) throws AddTransactionUnknownTypeException { + switch (type) { + case "expense": + String expense = transactions.addExpense(description, amount, category, date); + Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); + break; + case "income": + String income = transactions.addIncome(description, amount, category, date); + Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); + break; + default: + throw new AddTransactionUnknownTypeException(); + } + } - // the other add functions - + /** + * Checks if the targeted tags exists in the split user inputs. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws AddTransactionMissingTagException Missing tag exception. + */ + private static void checkTagsExist(String[] splits) throws AddTransactionMissingTagException { + // TODO: To add the tags into Command class instead + String[] tags = new String[]{"t/", "c/", "a/", "d/", "i/"}; + for (String tag : tags) { + boolean found = findMatchingTagFromInputs(tag, splits); + if (!found) { + throw new AddTransactionMissingTagException(); + } + } + } /** * Checks if the parameter contains numeric characters. @@ -200,23 +205,6 @@ private static LocalDate parseDateTag(String parameter) throws AddTransactionInv } } - /** - * Checks if the targeted tags exists in the split user inputs. - * - * @param splits The user input after the command word, split into a list for every space found. - * @throws AddTransactionMissingTagException Missing tag exception. - */ - private static void checkTagsExist(String[] splits) throws AddTransactionMissingTagException { - // TODO: To add the tags into Command class instead - String[] tags = new String[]{"t/", "c/", "a/", "d/", "i/"}; - for (String tag : tags) { - boolean found = findMatchingTagFromInputs(tag, splits); - if (!found) { - throw new AddTransactionMissingTagException(); - } - } - } - /** * Returns a boolean value on whether a tag can be found among the split user inputs. * From d859728417776b01eff2f8c3728b588f3e9f850b Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 03:34:42 +0800 Subject: [PATCH 091/416] Reformat code to comply with Java coding standards --- src/main/java/seedu/duke/command/AddCommand.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 47fe32bf5..135c80825 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -88,7 +88,9 @@ public boolean isExit() { return false; } - private static void addTransactionByType(TransactionList transactions, String type, String description, int amount, String category, LocalDate date) throws AddTransactionUnknownTypeException { + private static void addTransactionByType(TransactionList transactions, String type, String description, + int amount, String category, LocalDate date) throws AddTransactionUnknownTypeException { + switch (type) { case "expense": String expense = transactions.addExpense(description, amount, category, date); From 6ef6c0fddecb2d6da4e222a05525647a207aebba Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 04:24:56 +0800 Subject: [PATCH 092/416] Set up the command word, description, usage and parameters info for each command. Change the command word used in Parser.java to use the static command word specified in each Command class. --- src/main/java/seedu/duke/Parser.java | 21 ++++++++++--------- src/main/java/seedu/duke/command/Command.java | 11 ++++++++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 13798e9fc..05a6bf368 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -11,6 +11,7 @@ import seedu.duke.command.ListCommand; import seedu.duke.command.PurgeCommand; + import seedu.duke.exception.MoolahException; import seedu.duke.exception.InvalidCommandException; @@ -46,33 +47,33 @@ public static Command parse(String inData) throws MoolahException { // list commands duke to list all the tasks stored and their completion status // try at the start cos of the errors possibly - switch (inputTokens[0]) { - case "help": + switch (inputTokens[0].toUpperCase()) { + case HelpCommand.COMMAND_WORD: command = new HelpCommand(); break; - case "list": + case ListCommand.COMMAND_WORD: command = new ListCommand(); break; - case "find": + case FindCommand.COMMAND_WORD: command = new FindCommand(inputTokens[1]); break; - case "get": + case GetCommand.COMMAND_WORD: // Additional tokens will be allowed for get command = new GetCommand(); break; - case "purge": + case PurgeCommand.COMMAND_WORD: command = new PurgeCommand(); break; - case "delete": + case DeleteCommand.COMMAND_WORD: command = new DeleteCommand(inputTokens[1]); break; - case "add": + case AddCommand.COMMAND_WORD: command = new AddCommand(inputTokens[1]); break; - case "edit": + case EditCommand.COMMAND_WORD: command = new EditCommand(inputTokens[1]); break; - case "bye": + case ByeCommand.COMMAND_WORD: command = new ByeCommand(); break; default: diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 24c7d16a9..2dbd83fe1 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -16,6 +16,17 @@ public abstract class Command { * @param transactions An instance of the TransactionList class. * @param storage An instance of the Storage class. */ + + // The command word used to trigger the execution of Moolah Manager's operations. + public static String COMMAND_WORD; + // The description for the usage of command. + public static String COMMAND_DESCRIPTION; + // The guiding information for the usage of command. + public static String COMMAND_USAGE; + // The formatting information for the parameters used by the command. + public static String COMMAND_PARAMETERS_INFO; + + public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; public abstract boolean isExit(); From 85b65337b99867c6e6b6a05fbea85bb1ceee9c8c Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 04:26:38 +0800 Subject: [PATCH 093/416] Add static strings on command help info for Add, Bye, Delete and Edit --- .../java/seedu/duke/command/AddCommand.java | 15 +++++++++++++ .../java/seedu/duke/command/ByeCommand.java | 14 ++++++++++-- .../seedu/duke/command/DeleteCommand.java | 16 ++++++++++++-- .../java/seedu/duke/command/EditCommand.java | 22 +++++++++++++++++-- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 135c80825..4e7027c41 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -27,6 +27,21 @@ */ public class AddCommand extends Command { + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "ADD"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " + + "either an \"income\" or an \"expense\" into the transaction list."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION"; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + + "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted.\n" + + "CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted.\n" + + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + + "DESCRIPTION: More information regarding the transaction, written without any space."; + private String input; public AddCommand(String input) { diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index 93faa04aa..153e5aeec 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -8,12 +8,22 @@ * Represents a bye command object that will execute the operations for Bye command. */ public class ByeCommand extends Command { + + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "BYE"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To exit the application."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: bye"; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; + /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 5bbb72c55..7f2d84e4e 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -13,6 +13,18 @@ * Represents a delete command object that will execute the operations for Delete command. */ public class DeleteCommand extends Command { + + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "DELETE"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To delete a specific entry in the list of transactions."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: delete e/ENTRY"; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + + "ENTRY: The entry number of the transaction. " + + "Type \"list\" to list all the entry numbers of transaction.\n"; + private String input; /** @@ -27,9 +39,9 @@ public DeleteCommand(String input) { /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index b7c007959..8858c247b 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -8,6 +8,24 @@ * Represents an edit command object that will execute the operations for Edit command. */ public class EditCommand extends Command { + + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "EDIT"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To edit a specific entry in the list of transactions."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: " + + "edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION]"; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + + "ENTRY: The entry number of the transaction. " + + "Type \"list\" to list all the entry numbers of transaction.\n" + + "(Optional) TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted.\n" + + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + + "(Optional) AMOUNT: Value of the transaction in numerical form. " + + "Only integer within 0 and 10000000 is accepted.\n" + + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + + "(Optional) DESCRIPTION: More information regarding the transaction, written without any space."; private String input; public EditCommand(String input) { @@ -17,9 +35,9 @@ public EditCommand(String input) { /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { From 98cab78e6ddd60ebe5dbb4c9f9e13f4e5776a286 Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 04:46:25 +0800 Subject: [PATCH 094/416] Add static strings on command help info for Help, List and Purge --- .../java/seedu/duke/command/HelpCommand.java | 17 +++++++++++++++-- .../java/seedu/duke/command/ListCommand.java | 17 +++++++++++++++-- .../java/seedu/duke/command/PurgeCommand.java | 14 ++++++++++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 96f10deb6..3ee947561 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -8,12 +8,25 @@ * Represents a help command object that will execute the operations for Help command. */ public class HelpCommand extends Command { + + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "HELP"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To display a list of available commands " + + "with their respective expected parameters.\n" + + "Type \"help o/detailed\" for a detailed version of all parameters used."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: help [o/detailed]"; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + + "(Optional) o/detailed - Detailed version of guide."; + /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 96960b8b1..b3151b16f 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -11,12 +11,25 @@ * Represents a list command object that will execute the operations for List command. */ public class ListCommand extends Command { + + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "LIST"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To list all or some transactions based on selection."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] "; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + + "(Optional) TYPE - The type of transaction. Only \"income\" or \"expense\" is accepted.\n" + + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; + /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 5f558be03..d48428e31 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -13,12 +13,22 @@ * Represents a purge command object that will execute the operations for Purge command. */ public class PurgeCommand extends Command { + + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "PURGE"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To purge a all entries in the list of transactions."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: purge"; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; + /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { From 9ed58f9346d7ef6207c52dccd94e342a0a12eb4b Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 04:46:47 +0800 Subject: [PATCH 095/416] Add static strings on command help info template for Find and Get --- src/main/java/seedu/duke/command/FindCommand.java | 15 +++++++++++++-- src/main/java/seedu/duke/command/GetCommand.java | 15 +++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 5ec127b61..acb42815d 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -12,6 +12,17 @@ * Represents a find command object that will execute the operations for Find command. */ public class FindCommand extends Command { + + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "FIND"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To find ..."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: find ..."; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + + "PARAMETERNAME: ... "; + protected String keywords; /** @@ -38,9 +49,9 @@ public void checkFindFormat(String keywords) throws FindTransactionMissingKeywor /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { diff --git a/src/main/java/seedu/duke/command/GetCommand.java b/src/main/java/seedu/duke/command/GetCommand.java index ec9bde349..2c43d9cca 100644 --- a/src/main/java/seedu/duke/command/GetCommand.java +++ b/src/main/java/seedu/duke/command/GetCommand.java @@ -11,14 +11,25 @@ * Represents a get command object that will execute the operations for Get command. */ public class GetCommand extends Command { + + // The command word used to trigger the execution of Moolah Manager's operations. + public static final String COMMAND_WORD = "GET"; + // The description for the usage of command. + public static final String COMMAND_DESCRIPTION = "To get ..."; + // The guiding information for the usage of command. + public static final String COMMAND_USAGE = "Usage: get ..."; + // The formatting information for the parameters used by the command. + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + + "PARAMETERNAME: ... "; + CategoryList categories = new CategoryList(); /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { From 480e135f3baf317791d421edfa4cad856cf50e37 Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 05:01:42 +0800 Subject: [PATCH 096/416] Add default empty constructor for all command classes --- src/main/java/seedu/duke/command/AddCommand.java | 3 +++ src/main/java/seedu/duke/command/ByeCommand.java | 4 +++- src/main/java/seedu/duke/command/DeleteCommand.java | 3 +++ src/main/java/seedu/duke/command/EditCommand.java | 3 +++ src/main/java/seedu/duke/command/FindCommand.java | 3 +++ src/main/java/seedu/duke/command/GetCommand.java | 3 +++ src/main/java/seedu/duke/command/HelpCommand.java | 2 ++ src/main/java/seedu/duke/command/ListCommand.java | 3 +++ src/main/java/seedu/duke/command/PurgeCommand.java | 3 +++ 9 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 4e7027c41..dab843aee 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -44,6 +44,9 @@ public class AddCommand extends Command { private String input; + public AddCommand() { + } + public AddCommand(String input) { this.input = input; } diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index 153e5aeec..8ae91d99c 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -18,6 +18,9 @@ public class ByeCommand extends Command { // The formatting information for the parameters used by the command. public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; + public ByeCommand() { + } + /** * Executes the operations related to the command. * @@ -28,7 +31,6 @@ public class ByeCommand extends Command { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { Ui.showExit(); - //storage.writeToFile(transactions); } @Override diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 7f2d84e4e..62e8f2811 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -27,6 +27,9 @@ public class DeleteCommand extends Command { private String input; + public DeleteCommand() { + } + /** * Initialises the variables of the DeleteCommand class. * diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 8858c247b..b05fddebc 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -28,6 +28,9 @@ public class EditCommand extends Command { + "(Optional) DESCRIPTION: More information regarding the transaction, written without any space."; private String input; + public EditCommand() { + } + public EditCommand(String input) { this.input = input; } diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index acb42815d..a6bf87144 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -25,6 +25,9 @@ public class FindCommand extends Command { protected String keywords; + public FindCommand() { + } + /** * Initialises the variables of the FindCommand class. * diff --git a/src/main/java/seedu/duke/command/GetCommand.java b/src/main/java/seedu/duke/command/GetCommand.java index 2c43d9cca..a813439ed 100644 --- a/src/main/java/seedu/duke/command/GetCommand.java +++ b/src/main/java/seedu/duke/command/GetCommand.java @@ -22,6 +22,9 @@ public class GetCommand extends Command { public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + "PARAMETERNAME: ... "; + public GetCommand() { + } + CategoryList categories = new CategoryList(); /** diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 3ee947561..c5f3b9916 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -21,6 +21,8 @@ public class HelpCommand extends Command { public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + "(Optional) o/detailed - Detailed version of guide."; + public HelpCommand() { + } /** * Executes the operations related to the command. * diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index b3151b16f..47ce94410 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -24,6 +24,9 @@ public class ListCommand extends Command { + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; + public ListCommand() { + } + /** * Executes the operations related to the command. * diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index d48428e31..bc56f5f75 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -23,6 +23,9 @@ public class PurgeCommand extends Command { // The formatting information for the parameters used by the command. public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; + public PurgeCommand() { + } + /** * Executes the operations related to the command. * From ba02e21d8c0f765f19c8f9e1426d6becdd6eca13 Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 05:37:42 +0800 Subject: [PATCH 097/416] Implement help command feature Two versions of help are available: 1. Basic help containing command word, description and usage only. 2. Detailed help containing basic help and parameters information. --- src/main/java/seedu/duke/Parser.java | 2 +- src/main/java/seedu/duke/Ui.java | 4 +- .../java/seedu/duke/command/AddCommand.java | 7 +++ .../java/seedu/duke/command/ByeCommand.java | 7 +++ .../seedu/duke/command/DeleteCommand.java | 7 +++ .../java/seedu/duke/command/EditCommand.java | 8 +++ .../java/seedu/duke/command/FindCommand.java | 7 +++ .../java/seedu/duke/command/GetCommand.java | 7 +++ .../java/seedu/duke/command/HelpCommand.java | 51 ++++++++++++++++++- .../java/seedu/duke/command/ListCommand.java | 8 +++ .../java/seedu/duke/command/PurgeCommand.java | 7 +++ 11 files changed, 111 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 05a6bf368..8c473ca12 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -49,7 +49,7 @@ public static Command parse(String inData) throws MoolahException { // try at the start cos of the errors possibly switch (inputTokens[0].toUpperCase()) { case HelpCommand.COMMAND_WORD: - command = new HelpCommand(); + command = new HelpCommand(inputTokens[1]); break; case ListCommand.COMMAND_WORD: command = new ListCommand(); diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 6ab7bf611..831e17d79 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -48,9 +48,9 @@ public static void showGreeting() { printMessages(INFO_GREET.toString(), INFO_HELP_PROMPT.toString()); } - public static void showHelp() { + public static void showHelp(String helpMessage) { // To include the other messages for commands - printMessages(INFO_HELP_GREET.toString()); + printMessages(INFO_HELP_GREET.toString(), helpMessage); } public static void showExit() { diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index dab843aee..7e8db70ca 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -42,6 +42,13 @@ public class AddCommand extends Command { + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + "DESCRIPTION: More information regarding the transaction, written without any space."; + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + private String input; public AddCommand() { diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index 8ae91d99c..ab3d165f1 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -18,6 +18,13 @@ public class ByeCommand extends Command { // The formatting information for the parameters used by the command. public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public ByeCommand() { } diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 62e8f2811..c1aa5fae3 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -25,6 +25,13 @@ public class DeleteCommand extends Command { + "ENTRY: The entry number of the transaction. " + "Type \"list\" to list all the entry numbers of transaction.\n"; + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + private String input; public DeleteCommand() { diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index b05fddebc..d7dc337cd 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -26,6 +26,14 @@ public class EditCommand extends Command { + "Only integer within 0 and 10000000 is accepted.\n" + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + "(Optional) DESCRIPTION: More information regarding the transaction, written without any space."; + + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + private String input; public EditCommand() { diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index a6bf87144..169c3b6fe 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -23,6 +23,13 @@ public class FindCommand extends Command { public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + "PARAMETERNAME: ... "; + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + protected String keywords; public FindCommand() { diff --git a/src/main/java/seedu/duke/command/GetCommand.java b/src/main/java/seedu/duke/command/GetCommand.java index a813439ed..e5fe08a84 100644 --- a/src/main/java/seedu/duke/command/GetCommand.java +++ b/src/main/java/seedu/duke/command/GetCommand.java @@ -22,6 +22,13 @@ public class GetCommand extends Command { public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + "PARAMETERNAME: ... "; + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public GetCommand() { } diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index c5f3b9916..16a7817a7 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -21,8 +21,22 @@ public class HelpCommand extends Command { public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + "(Optional) o/detailed - Detailed version of guide."; + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + + private String input; + public HelpCommand() { } + + public HelpCommand(String input) { + this.input = input; + } + /** * Executes the operations related to the command. * @@ -32,7 +46,42 @@ public HelpCommand() { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { - Ui.showHelp(); + String helpMessage = ""; + if (input.contains("o/detailed")) { + helpMessage = generateDetailedHelp(); + } else { + helpMessage = generateBasicHelp(); + } + + Ui.showHelp(helpMessage); + } + + private String generateBasicHelp() { + String helpMessage = HelpCommand.COMMAND_HELP + "\n" + + AddCommand.COMMAND_HELP + "\n" + + ListCommand.COMMAND_HELP + "\n" + + FindCommand.COMMAND_HELP + "\n" + + GetCommand.COMMAND_HELP + "\n" + + EditCommand.COMMAND_HELP + "\n" + + DeleteCommand.COMMAND_HELP + "\n" + + PurgeCommand.COMMAND_HELP + "\n" + + ByeCommand.COMMAND_HELP; + + return helpMessage; + } + + private String generateDetailedHelp() { + String helpMessage = HelpCommand.COMMAND_DETAILED_HELP + "\n" + + AddCommand.COMMAND_DETAILED_HELP + "\n" + + ListCommand.COMMAND_DETAILED_HELP + "\n" + + FindCommand.COMMAND_DETAILED_HELP + "\n" + + GetCommand.COMMAND_DETAILED_HELP + "\n" + + EditCommand.COMMAND_DETAILED_HELP + "\n" + + DeleteCommand.COMMAND_DETAILED_HELP + "\n" + + PurgeCommand.COMMAND_DETAILED_HELP + "\n" + + ByeCommand.COMMAND_DETAILED_HELP; + + return helpMessage; } @Override diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 47ce94410..5c067e56b 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -24,6 +24,14 @@ public class ListCommand extends Command { + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; + + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public ListCommand() { } diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index bc56f5f75..2ce4621d4 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -23,6 +23,13 @@ public class PurgeCommand extends Command { // The formatting information for the parameters used by the command. public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public PurgeCommand() { } From 0e747af525de32767d778bd88559ff432524c00d Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 8 Oct 2022 05:38:13 +0800 Subject: [PATCH 098/416] Update test cases on implemented help command for regression testing --- text-ui-test/EXPECTED.TXT | 216 ++++++++++++++++++++++++++++++++++++++ text-ui-test/input.txt | 4 + 2 files changed, 220 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 1cef6ac67..1446f4c3d 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,6 +1,222 @@ ____________________________________________________________ Hello! I'm Moo and I will help you to manage your finances. Enter if you need the list of commands. +____________________________________________________________ +____________________________________________________________ +Gotcha! Here are the commands that you may use: +Command Word: HELP +To display a list of available commands with their respective expected parameters. +Type "help o/detailed" for a detailed version of all parameters used. +Usage: help [o/detailed] + +Command Word: ADD +To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. +Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION + +Command Word: LIST +To list all or some transactions based on selection. +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] + +Command Word: FIND +To find ... +Usage: find ... + +Command Word: GET +To get ... +Usage: get ... + +Command Word: EDIT +To edit a specific entry in the list of transactions. +Usage: edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION] + +Command Word: DELETE +To delete a specific entry in the list of transactions. +Usage: delete e/ENTRY + +Command Word: PURGE +To purge a all entries in the list of transactions. +Usage: purge + +Command Word: BYE +To exit the application. +Usage: bye + +____________________________________________________________ +____________________________________________________________ +Gotcha! Here are the commands that you may use: +Command Word: HELP +To display a list of available commands with their respective expected parameters. +Type "help o/detailed" for a detailed version of all parameters used. +Usage: help [o/detailed] +Parameters information: +(Optional) o/detailed - Detailed version of guide. + +Command Word: ADD +To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. +Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION +Parameters information: +TYPE: The type of transaction. Only "income" or "expense" is accepted. +CATEGORY: A category for the transaction. Only string containing alphabets is accepted. +AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted. +DATE: Date of the transaction. The format must be in "yyyyMMdd". +DESCRIPTION: More information regarding the transaction, written without any space. + +Command Word: LIST +To list all or some transactions based on selection. +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] +Parameters information: +(Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. +(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. +(Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". + +Command Word: FIND +To find ... +Usage: find ... +Parameters information: +PARAMETERNAME: ... + +Command Word: GET +To get ... +Usage: get ... +Parameters information: +PARAMETERNAME: ... + +Command Word: EDIT +To edit a specific entry in the list of transactions. +Usage: edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION] +Parameters information: +ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. +(Optional) TYPE: The type of transaction. Only "income" or "expense" is accepted. +(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. +(Optional) AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted. +(Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". +(Optional) DESCRIPTION: More information regarding the transaction, written without any space. + +Command Word: DELETE +To delete a specific entry in the list of transactions. +Usage: delete e/ENTRY +Parameters information: +ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. + + +Command Word: PURGE +To purge a all entries in the list of transactions. +Usage: purge +Parameters information: -NIL- + +Command Word: BYE +To exit the application. +Usage: bye +Parameters information: -NIL- + +____________________________________________________________ +____________________________________________________________ +Gotcha! Here are the commands that you may use: +Command Word: HELP +To display a list of available commands with their respective expected parameters. +Type "help o/detailed" for a detailed version of all parameters used. +Usage: help [o/detailed] + +Command Word: ADD +To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. +Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION + +Command Word: LIST +To list all or some transactions based on selection. +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] + +Command Word: FIND +To find ... +Usage: find ... + +Command Word: GET +To get ... +Usage: get ... + +Command Word: EDIT +To edit a specific entry in the list of transactions. +Usage: edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION] + +Command Word: DELETE +To delete a specific entry in the list of transactions. +Usage: delete e/ENTRY + +Command Word: PURGE +To purge a all entries in the list of transactions. +Usage: purge + +Command Word: BYE +To exit the application. +Usage: bye + +____________________________________________________________ +____________________________________________________________ +Gotcha! Here are the commands that you may use: +Command Word: HELP +To display a list of available commands with their respective expected parameters. +Type "help o/detailed" for a detailed version of all parameters used. +Usage: help [o/detailed] +Parameters information: +(Optional) o/detailed - Detailed version of guide. + +Command Word: ADD +To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. +Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION +Parameters information: +TYPE: The type of transaction. Only "income" or "expense" is accepted. +CATEGORY: A category for the transaction. Only string containing alphabets is accepted. +AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted. +DATE: Date of the transaction. The format must be in "yyyyMMdd". +DESCRIPTION: More information regarding the transaction, written without any space. + +Command Word: LIST +To list all or some transactions based on selection. +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] +Parameters information: +(Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. +(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. +(Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". + +Command Word: FIND +To find ... +Usage: find ... +Parameters information: +PARAMETERNAME: ... + +Command Word: GET +To get ... +Usage: get ... +Parameters information: +PARAMETERNAME: ... + +Command Word: EDIT +To edit a specific entry in the list of transactions. +Usage: edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION] +Parameters information: +ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. +(Optional) TYPE: The type of transaction. Only "income" or "expense" is accepted. +(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. +(Optional) AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted. +(Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". +(Optional) DESCRIPTION: More information regarding the transaction, written without any space. + +Command Word: DELETE +To delete a specific entry in the list of transactions. +Usage: delete e/ENTRY +Parameters information: +ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. + + +Command Word: PURGE +To purge a all entries in the list of transactions. +Usage: purge +Parameters information: -NIL- + +Command Word: BYE +To exit the application. +Usage: bye +Parameters information: -NIL- + ____________________________________________________________ ____________________________________________________________ There are no transaction records found. diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 51146d0dd..04db03085 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -1,3 +1,7 @@ +help +help o/detailed +help blablabla +help o/detailed blablabla list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary From f7fea0aea5218b86828856793b4a0709025f1a0e Mon Sep 17 00:00:00 2001 From: chydarren Date: Sat, 8 Oct 2022 09:18:14 +0800 Subject: [PATCH 099/416] Add help for Find and Stats --- src/main/java/seedu/duke/Parser.java | 8 +- .../java/seedu/duke/command/FindCommand.java | 33 ++++--- .../java/seedu/duke/command/GetCommand.java | 56 ----------- .../java/seedu/duke/command/HelpCommand.java | 57 ++++++------ .../java/seedu/duke/command/StatsCommand.java | 66 +++++++++++++ .../java/seedu/duke/common/InfoMessages.java | 3 +- text-ui-test/EXPECTED.TXT | 93 +++++++++---------- text-ui-test/input.txt | 1 - 8 files changed, 165 insertions(+), 152 deletions(-) delete mode 100644 src/main/java/seedu/duke/command/GetCommand.java create mode 100644 src/main/java/seedu/duke/command/StatsCommand.java diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 8c473ca12..07a4fa59d 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -6,7 +6,7 @@ import seedu.duke.command.DeleteCommand; import seedu.duke.command.EditCommand; import seedu.duke.command.FindCommand; -import seedu.duke.command.GetCommand; +import seedu.duke.command.StatsCommand; import seedu.duke.command.HelpCommand; import seedu.duke.command.ListCommand; import seedu.duke.command.PurgeCommand; @@ -57,9 +57,9 @@ public static Command parse(String inData) throws MoolahException { case FindCommand.COMMAND_WORD: command = new FindCommand(inputTokens[1]); break; - case GetCommand.COMMAND_WORD: - // Additional tokens will be allowed for get - command = new GetCommand(); + case StatsCommand.COMMAND_WORD: + // Additional tokens will be allowed for liststats + command = new StatsCommand(inputTokens[1]); break; case PurgeCommand.COMMAND_WORD: command = new PurgeCommand(); diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 169c3b6fe..0b09aaaf9 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -12,23 +12,30 @@ * Represents a find command object that will execute the operations for Find command. */ public class FindCommand extends Command { - - // The command word used to trigger the execution of Moolah Manager's operations. + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "FIND"; - // The description for the usage of command. - public static final String COMMAND_DESCRIPTION = "To find ..."; - // The guiding information for the usage of command. - public static final String COMMAND_USAGE = "Usage: find ..."; - // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" - + "PARAMETERNAME: ... "; + // The description for the usage of command + public static final String COMMAND_DESCRIPTION = "To find specific transaction(s) based " + + "on any keywords inputted by the user."; + // The guiding information for the usage of command + public static final String COMMAND_USAGE = "Usage: find KEYWORDS"; + // The formatting information for the parameters used by the command + public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + + LINE_SEPARATOR + + "KEYWORDS: Any partial or full keyword(s) that matches the details of the transaction, " + + "such as type, category, amount, date or description."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + + LINE_SEPARATOR + + COMMAND_DESCRIPTION + + LINE_SEPARATOR + + COMMAND_USAGE + + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + + LINE_SEPARATOR; protected String keywords; diff --git a/src/main/java/seedu/duke/command/GetCommand.java b/src/main/java/seedu/duke/command/GetCommand.java deleted file mode 100644 index e5fe08a84..000000000 --- a/src/main/java/seedu/duke/command/GetCommand.java +++ /dev/null @@ -1,56 +0,0 @@ -package seedu.duke.command; - -import seedu.duke.Storage; -import seedu.duke.Ui; -import seedu.duke.data.CategoryList; -import seedu.duke.data.TransactionList; - -import static seedu.duke.common.InfoMessages.INFO_GET_CATEGORIES; - -/** - * Represents a get command object that will execute the operations for Get command. - */ -public class GetCommand extends Command { - - // The command word used to trigger the execution of Moolah Manager's operations. - public static final String COMMAND_WORD = "GET"; - // The description for the usage of command. - public static final String COMMAND_DESCRIPTION = "To get ..."; - // The guiding information for the usage of command. - public static final String COMMAND_USAGE = "Usage: get ..."; - // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" - + "PARAMETERNAME: ... "; - - // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; - // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; - - public GetCommand() { - } - - CategoryList categories = new CategoryList(); - - /** - * Executes the operations related to the command. - * - * @param ui An instance of the Ui class. - * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. - */ - @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) { - // This will be a method within this method, command will be e.g. "get categories" - categories.calculateTotalAmount(transactions); - String categoriesList = categories.listCategories(); - Ui.showTransactionsList(categoriesList, INFO_GET_CATEGORIES.toString()); - } - - @Override - public boolean isExit() { - return false; - } -} diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 16a7817a7..0c93a60bc 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -8,23 +8,28 @@ * Represents a help command object that will execute the operations for Help command. */ public class HelpCommand extends Command { - - // The command word used to trigger the execution of Moolah Manager's operations. + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "HELP"; - // The description for the usage of command. + // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To display a list of available commands " - + "with their respective expected parameters.\n" + + "with their respective expected parameters." + + LINE_SEPARATOR + "Type \"help o/detailed\" for a detailed version of all parameters used."; - // The guiding information for the usage of command. + // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: help [o/detailed]"; - // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + // The formatting information for the parameters used by the command + public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + + LINE_SEPARATOR + "(Optional) o/detailed - Detailed version of guide."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + + LINE_SEPARATOR + + COMMAND_DESCRIPTION + + LINE_SEPARATOR + + COMMAND_USAGE + + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; @@ -57,28 +62,28 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { } private String generateBasicHelp() { - String helpMessage = HelpCommand.COMMAND_HELP + "\n" - + AddCommand.COMMAND_HELP + "\n" - + ListCommand.COMMAND_HELP + "\n" - + FindCommand.COMMAND_HELP + "\n" - + GetCommand.COMMAND_HELP + "\n" - + EditCommand.COMMAND_HELP + "\n" - + DeleteCommand.COMMAND_HELP + "\n" - + PurgeCommand.COMMAND_HELP + "\n" + String helpMessage = HelpCommand.COMMAND_HELP + LINE_SEPARATOR + + AddCommand.COMMAND_HELP + LINE_SEPARATOR + + ListCommand.COMMAND_HELP + LINE_SEPARATOR + + FindCommand.COMMAND_HELP + LINE_SEPARATOR + + StatsCommand.COMMAND_HELP + LINE_SEPARATOR + + EditCommand.COMMAND_HELP + LINE_SEPARATOR + + DeleteCommand.COMMAND_HELP + LINE_SEPARATOR + + PurgeCommand.COMMAND_HELP + LINE_SEPARATOR + ByeCommand.COMMAND_HELP; return helpMessage; } private String generateDetailedHelp() { - String helpMessage = HelpCommand.COMMAND_DETAILED_HELP + "\n" - + AddCommand.COMMAND_DETAILED_HELP + "\n" - + ListCommand.COMMAND_DETAILED_HELP + "\n" - + FindCommand.COMMAND_DETAILED_HELP + "\n" - + GetCommand.COMMAND_DETAILED_HELP + "\n" - + EditCommand.COMMAND_DETAILED_HELP + "\n" - + DeleteCommand.COMMAND_DETAILED_HELP + "\n" - + PurgeCommand.COMMAND_DETAILED_HELP + "\n" + String helpMessage = HelpCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + + AddCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + + ListCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + + FindCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + + StatsCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + + EditCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + + DeleteCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + + PurgeCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + ByeCommand.COMMAND_DETAILED_HELP; return helpMessage; diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java new file mode 100644 index 000000000..fb6e14722 --- /dev/null +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -0,0 +1,66 @@ +package seedu.duke.command; + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.CategoryList; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.ListStatisticsInvalidStatsTypeException; +import seedu.duke.exception.ListStatisticsMissingTagException; +import seedu.duke.exception.MoolahException; + +import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; +import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; + +/** + * Represents a get command object that will execute the operations for Get command. + */ +public class StatsCommand extends Command { + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations + public static final String COMMAND_WORD = "STATS"; + // The description for the usage of command + public static final String COMMAND_DESCRIPTION = "To get statistics of the transactions such " + + "as the total savings for each category, summary of expenditure, etc."; + // The guiding information for the usage of command + public static final String COMMAND_USAGE = "Usage: stats s/STATISTICS_TYPE"; + // The formatting information for the parameters used by the command + public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + + LINE_SEPARATOR + + "STATISTICS_TYPE: The type of statistics to be generated. Only \"categories\" is accepted."; + + // Basic help description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + + COMMAND_USAGE + LINE_SEPARATOR; + // Detailed help description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + + LINE_SEPARATOR; + + private String input; + + public StatsCommand(String input) { + this.input = input; + } + + CategoryList categories = new CategoryList(); + + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) { + // This will be a method within this method, command will be e.g. "get categories" + categories.calculateTotalAmount(transactions); + String categoriesList = categories.listCategories(); + Ui.showTransactionsList(categoriesList, INFO_STATS_CATEGORIES.toString()); + } + + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index fa216c092..898d56eec 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -9,7 +9,6 @@ public enum InfoMessages { INFO_ADD_INCOME("I have added the following Income transaction:"), INFO_DELETE("I have deleted the following transaction:"), INFO_EXIT("Goodbye and see you soon."), - INFO_GET_CATEGORIES("Here are the total savings for each category:"), INFO_GREET("Hello! I'm Moo and I will help you to manage your finances."), INFO_HELP_GREET("Gotcha! Here are the commands that you may use:"), INFO_HELP_PROMPT("Enter if you need the list of commands."), @@ -17,6 +16,8 @@ public enum InfoMessages { INFO_LIST_EMPTY("There are no transaction records found."), INFO_LIST_FILTERED("Here are the transaction records that match your search expression:"), INFO_LIST_UNFILTERED("There are no transaction records that match your search expression."), + INFO_STATS_EMPTY("There are no statistics available yet for the given statistics type."), + INFO_STATS_CATEGORIES("Here are the total savings for each category:"), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 1446f4c3d..afe20bda7 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -15,15 +15,15 @@ Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION Command Word: LIST To list all or some transactions based on selection. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] Command Word: FIND -To find ... -Usage: find ... +To find specific transaction(s) based on any keywords inputted by the user. +Usage: find KEYWORDS -Command Word: GET -To get ... -Usage: get ... +Command Word: STATS +To get statistics of the transactions such as the total savings for each category, summary of expenditure, etc. +Usage: stats s/STATISTICS_TYPE Command Word: EDIT To edit a specific entry in the list of transactions. @@ -48,13 +48,13 @@ Command Word: HELP To display a list of available commands with their respective expected parameters. Type "help o/detailed" for a detailed version of all parameters used. Usage: help [o/detailed] -Parameters information: +Parameters information: (Optional) o/detailed - Detailed version of guide. Command Word: ADD To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION -Parameters information: +Parameters information: TYPE: The type of transaction. Only "income" or "expense" is accepted. CATEGORY: A category for the transaction. Only string containing alphabets is accepted. AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted. @@ -63,28 +63,28 @@ DESCRIPTION: More information regarding the transaction, written without any spa Command Word: LIST To list all or some transactions based on selection. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] -Parameters information: +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] +Parameters information: (Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. (Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. (Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". Command Word: FIND -To find ... -Usage: find ... -Parameters information: -PARAMETERNAME: ... +To find specific transaction(s) based on any keywords inputted by the user. +Usage: find KEYWORDS +Parameters information: +KEYWORDS: Any partial or full keyword(s) that matches the details of the transaction, such as type, category, amount, date or description. -Command Word: GET -To get ... -Usage: get ... -Parameters information: -PARAMETERNAME: ... +Command Word: STATS +To get statistics of the transactions such as the total savings for each category, summary of expenditure, etc. +Usage: stats s/STATISTICS_TYPE +Parameters information: +STATISTICS_TYPE: The type of statistics to be generated. Only "categories" is accepted. Command Word: EDIT To edit a specific entry in the list of transactions. Usage: edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION] -Parameters information: +Parameters information: ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. (Optional) TYPE: The type of transaction. Only "income" or "expense" is accepted. (Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. @@ -95,10 +95,9 @@ ENTRY: The entry number of the transaction. Type "list" to list all the entry nu Command Word: DELETE To delete a specific entry in the list of transactions. Usage: delete e/ENTRY -Parameters information: +Parameters information: ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. - Command Word: PURGE To purge a all entries in the list of transactions. Usage: purge @@ -123,15 +122,15 @@ Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION Command Word: LIST To list all or some transactions based on selection. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] Command Word: FIND -To find ... -Usage: find ... +To find specific transaction(s) based on any keywords inputted by the user. +Usage: find KEYWORDS -Command Word: GET -To get ... -Usage: get ... +Command Word: STATS +To get statistics of the transactions such as the total savings for each category, summary of expenditure, etc. +Usage: stats s/STATISTICS_TYPE Command Word: EDIT To edit a specific entry in the list of transactions. @@ -156,13 +155,13 @@ Command Word: HELP To display a list of available commands with their respective expected parameters. Type "help o/detailed" for a detailed version of all parameters used. Usage: help [o/detailed] -Parameters information: +Parameters information: (Optional) o/detailed - Detailed version of guide. Command Word: ADD To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION -Parameters information: +Parameters information: TYPE: The type of transaction. Only "income" or "expense" is accepted. CATEGORY: A category for the transaction. Only string containing alphabets is accepted. AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted. @@ -171,28 +170,28 @@ DESCRIPTION: More information regarding the transaction, written without any spa Command Word: LIST To list all or some transactions based on selection. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] -Parameters information: +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] +Parameters information: (Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. (Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. (Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". Command Word: FIND -To find ... -Usage: find ... -Parameters information: -PARAMETERNAME: ... +To find specific transaction(s) based on any keywords inputted by the user. +Usage: find KEYWORDS +Parameters information: +KEYWORDS: Any partial or full keyword(s) that matches the details of the transaction, such as type, category, amount, date or description. -Command Word: GET -To get ... -Usage: get ... -Parameters information: -PARAMETERNAME: ... +Command Word: STATS +To get statistics of the transactions such as the total savings for each category, summary of expenditure, etc. +Usage: stats s/STATISTICS_TYPE +Parameters information: +STATISTICS_TYPE: The type of statistics to be generated. Only "categories" is accepted. Command Word: EDIT To edit a specific entry in the list of transactions. Usage: edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION] -Parameters information: +Parameters information: ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. (Optional) TYPE: The type of transaction. Only "income" or "expense" is accepted. (Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. @@ -203,10 +202,9 @@ ENTRY: The entry number of the transaction. Type "list" to list all the entry nu Command Word: DELETE To delete a specific entry in the list of transactions. Usage: delete e/ENTRY -Parameters information: +Parameters information: ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. - Command Word: PURGE To purge a all entries in the list of transactions. Usage: purge @@ -232,13 +230,6 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare -____________________________________________________________ -____________________________________________________________ -Here are the total savings for each category: -[food] $20 -[salary] $2000 -[transport] $1 - ____________________________________________________________ ____________________________________________________________ Keyword(s) for your search expression missing, please check your input! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 04db03085..03fe8f5a1 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -6,7 +6,6 @@ list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary add t/expense c/transport a/1 d/02102022 i/bus_fare -get find find bus_fare list From 0afb30759260403b2b017a4b0ecfde1da1b55d4f Mon Sep 17 00:00:00 2001 From: chydarren Date: Sat, 8 Oct 2022 09:19:53 +0800 Subject: [PATCH 100/416] Fix CR/LF line break and spacing issues for compatibility with UI-test on Windows --- .../java/seedu/duke/command/AddCommand.java | 29 +++++++------- .../java/seedu/duke/command/ByeCommand.java | 14 ++++--- src/main/java/seedu/duke/command/Command.java | 8 ++-- .../seedu/duke/command/DeleteCommand.java | 27 ++++++++----- .../java/seedu/duke/command/EditCommand.java | 40 ++++++++++++------- .../java/seedu/duke/command/ListCommand.java | 33 +++++++++------ .../java/seedu/duke/command/PurgeCommand.java | 19 ++++----- 7 files changed, 99 insertions(+), 71 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 7e8db70ca..684f4cca8 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -26,20 +26,25 @@ * Represents an add command object that will execute the operations for Add command. */ public class AddCommand extends Command { - - // The command word used to trigger the execution of Moolah Manager's operations. + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "ADD"; - // The description for the usage of command. + // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " + "either an \"income\" or an \"expense\" into the transaction list."; - // The guiding information for the usage of command. + // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION"; - // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" - + "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted.\n" - + "CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" - + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted.\n" - + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + // The formatting information for the parameters used by the command + public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + + LINE_SEPARATOR + + "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted." + + LINE_SEPARATOR + + "CATEGORY: A category for the transaction. Only string containing alphabets is accepted." + + LINE_SEPARATOR + + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted." + + LINE_SEPARATOR + + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + + LINE_SEPARATOR + "DESCRIPTION: More information regarding the transaction, written without any space."; // Basic help description @@ -51,9 +56,6 @@ public class AddCommand extends Command { private String input; - public AddCommand() { - } - public AddCommand(String input) { this.input = input; } @@ -71,7 +73,6 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist */ - String[] splits = input.split(" "); checkTagsExist(splits); diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index ab3d165f1..64af3925f 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -8,7 +8,7 @@ * Represents a bye command object that will execute the operations for Bye command. */ public class ByeCommand extends Command { - + private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations. public static final String COMMAND_WORD = "BYE"; // The description for the usage of command. @@ -19,11 +19,15 @@ public class ByeCommand extends Command { public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + + LINE_SEPARATOR + + COMMAND_DESCRIPTION + + LINE_SEPARATOR + + COMMAND_USAGE + + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + + LINE_SEPARATOR; public ByeCommand() { } diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 2dbd83fe1..70142cd66 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -17,13 +17,13 @@ public abstract class Command { * @param storage An instance of the Storage class. */ - // The command word used to trigger the execution of Moolah Manager's operations. + // The command word used to trigger the execution of Moolah Manager's operations public static String COMMAND_WORD; - // The description for the usage of command. + // The description for the usage of command public static String COMMAND_DESCRIPTION; - // The guiding information for the usage of command. + // The guiding information for the usage of command public static String COMMAND_USAGE; - // The formatting information for the parameters used by the command. + // The formatting information for the parameters used by the command public static String COMMAND_PARAMETERS_INFO; diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index c1aa5fae3..f83cea118 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -13,24 +13,29 @@ * Represents a delete command object that will execute the operations for Delete command. */ public class DeleteCommand extends Command { - - // The command word used to trigger the execution of Moolah Manager's operations. + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "DELETE"; - // The description for the usage of command. + // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To delete a specific entry in the list of transactions."; - // The guiding information for the usage of command. + // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: delete e/ENTRY"; - // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + // The formatting information for the parameters used by the command + public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + + LINE_SEPARATOR + "ENTRY: The entry number of the transaction. " - + "Type \"list\" to list all the entry numbers of transaction.\n"; + + "Type \"list\" to list all the entry numbers of transaction."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + + LINE_SEPARATOR + + COMMAND_DESCRIPTION + + LINE_SEPARATOR + + COMMAND_USAGE + + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + + LINE_SEPARATOR; private String input; diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index d7dc337cd..76d9369aa 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -8,31 +8,41 @@ * Represents an edit command object that will execute the operations for Edit command. */ public class EditCommand extends Command { - - // The command word used to trigger the execution of Moolah Manager's operations. + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "EDIT"; - // The description for the usage of command. + // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To edit a specific entry in the list of transactions."; - // The guiding information for the usage of command. + // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: " + "edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION]"; - // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + // The formatting information for the parameters used by the command + public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + + LINE_SEPARATOR + "ENTRY: The entry number of the transaction. " - + "Type \"list\" to list all the entry numbers of transaction.\n" - + "(Optional) TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted.\n" - + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + + "Type \"list\" to list all the entry numbers of transaction." + + LINE_SEPARATOR + + "(Optional) TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted." + + LINE_SEPARATOR + + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted." + + LINE_SEPARATOR + "(Optional) AMOUNT: Value of the transaction in numerical form. " - + "Only integer within 0 and 10000000 is accepted.\n" - + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + + "Only integer within 0 and 10000000 is accepted." + + LINE_SEPARATOR + + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + + LINE_SEPARATOR + "(Optional) DESCRIPTION: More information regarding the transaction, written without any space."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + + LINE_SEPARATOR + + COMMAND_DESCRIPTION + + LINE_SEPARATOR + + COMMAND_USAGE + + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + + LINE_SEPARATOR; private String input; diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 5c067e56b..9d1bcbb76 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -11,26 +11,33 @@ * Represents a list command object that will execute the operations for List command. */ public class ListCommand extends Command { - - // The command word used to trigger the execution of Moolah Manager's operations. + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "LIST"; - // The description for the usage of command. + // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To list all or some transactions based on selection."; - // The guiding information for the usage of command. - public static final String COMMAND_USAGE = "Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] "; - // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" - + "(Optional) TYPE - The type of transaction. Only \"income\" or \"expense\" is accepted.\n" - + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + // The guiding information for the usage of command + public static final String COMMAND_USAGE = "Usage: list [t/TYPE] [c/CATEGORY] [d/DATE]"; + // The formatting information for the parameters used by the command + public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + + LINE_SEPARATOR + + "(Optional) TYPE - The type of transaction. Only \"income\" or \"expense\" is accepted." + + LINE_SEPARATOR + + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted." + + LINE_SEPARATOR + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + + LINE_SEPARATOR + + COMMAND_DESCRIPTION + + LINE_SEPARATOR + + COMMAND_USAGE + + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + + LINE_SEPARATOR; public ListCommand() { } diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 2ce4621d4..85d9c8110 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -13,22 +13,23 @@ * Represents a purge command object that will execute the operations for Purge command. */ public class PurgeCommand extends Command { - - // The command word used to trigger the execution of Moolah Manager's operations. + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "PURGE"; - // The description for the usage of command. + // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To purge a all entries in the list of transactions."; - // The guiding information for the usage of command. + // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: purge"; - // The formatting information for the parameters used by the command. + // The formatting information for the parameters used by the command public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + + LINE_SEPARATOR; public PurgeCommand() { } From 2df20db3607e57a33cb83e8b617b621437422157 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sat, 8 Oct 2022 09:36:35 +0800 Subject: [PATCH 101/416] Add StatsType tag to StatsCommand --- .../java/seedu/duke/command/StatsCommand.java | 90 +++++++++++++++++-- .../java/seedu/duke/common/ErrorMessages.java | 5 +- .../AddTransactionMissingTagException.java | 2 +- ...stStatisticsInvalidStatsTypeException.java | 16 ++++ .../ListStatisticsMissingTagException.java | 15 ++++ 5 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/ListStatisticsInvalidStatsTypeException.java create mode 100644 src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index fb6e14722..ee03e7dca 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -42,8 +42,6 @@ public StatsCommand(String input) { this.input = input; } - CategoryList categories = new CategoryList(); - /** * Executes the operations related to the command. * @@ -52,11 +50,89 @@ public StatsCommand(String input) { * @param storage An instance of the Storage class. */ @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) { - // This will be a method within this method, command will be e.g. "get categories" - categories.calculateTotalAmount(transactions); - String categoriesList = categories.listCategories(); - Ui.showTransactionsList(categoriesList, INFO_STATS_CATEGORIES.toString()); + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + /* + Checks if userInput is in the correct input format by further parsing, + before adding entry to arraylist + */ + String[] splits = input.split(" "); + checkTagsExist(splits); + + String statsType = ""; + + for (String split : splits) { + String tag = split.substring(0, 2); + String parameter = split.substring(2); + switch (tag) { + case "s/": + listStatisticsByStatsType(parameter, transactions); + break; + /*case "t/": + break; + case "n/": + break;*/ + default: + break; + } + } + } + + private static void listStatisticsByStatsType(String statsType, TransactionList transactions) + throws ListStatisticsInvalidStatsTypeException { + /* + Known issue; currently each repeat use of command will generate more classes, need + to probably add into constructor and pass in categories + */ + CategoryList categories = new CategoryList(); + + switch (statsType) { + case "categories": + categories.calculateTotalAmount(transactions); + String categoriesList = categories.listCategories(); + if (categoriesList.isEmpty()) { + Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); + return; + } + Ui.showTransactionsList(categoriesList, INFO_STATS_CATEGORIES.toString()); + break; + default: + throw new ListStatisticsInvalidStatsTypeException(); + } + } + + /** + * Checks if the targeted tags exists in the split user inputs. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws ListStatisticsMissingTagException Missing tag exception. + */ + private static void checkTagsExist(String[] splits) throws ListStatisticsMissingTagException { + // TODO: To add the tags into Command class instead + String[] tags = new String[]{"s/"}; + for (String tag : tags) { + boolean found = findMatchingTagFromInputs(tag, splits); + if (!found) { + throw new ListStatisticsMissingTagException(); + } + } + } + + /** + * Returns a boolean value on whether a tag can be found among the split user inputs. + * + * @param tag A specific tag used to locate the command parameter. + * @param splits The user input after the command word, split into a list for every space found. + * @return Whether the tag is found within the split inputs. + */ + private static boolean findMatchingTagFromInputs(String tag, String[] splits) { + boolean found = false; + for (String split : splits) { + if (split.startsWith(tag)) { + found = true; + break; + } + } + return found; } @Override diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 8a71d7700..4359b70b2 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -11,8 +11,9 @@ public enum ErrorMessages { ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), - ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), - ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"); + ERROR_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), + ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), + ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"); public final String message; diff --git a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java b/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java index a0f59a0d5..2b619bb73 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java @@ -10,6 +10,6 @@ public class AddTransactionMissingTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_MISSING_TAG.toString(); + return ErrorMessages.ERROR_COMMAND_MISSING_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/ListStatisticsInvalidStatsTypeException.java b/src/main/java/seedu/duke/exception/ListStatisticsInvalidStatsTypeException.java new file mode 100644 index 000000000..d4dc39427 --- /dev/null +++ b/src/main/java/seedu/duke/exception/ListStatisticsInvalidStatsTypeException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class ListStatisticsInvalidStatsTypeException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STATS_COMMAND_INVALID_STATSTYPE.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java b/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java new file mode 100644 index 000000000..371298b76 --- /dev/null +++ b/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class ListStatisticsMissingTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_COMMAND_MISSING_TAG.toString(); + } +} From 138029e51d3d80aa5c2fac7be22337c9fe49c32b Mon Sep 17 00:00:00 2001 From: chydarren Date: Sat, 8 Oct 2022 09:36:59 +0800 Subject: [PATCH 102/416] Update EXPECTED.TXT and input.txt to test the Stats command for listing statistics --- text-ui-test/EXPECTED.TXT | 16 ++++++++++++++++ text-ui-test/input.txt | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index afe20bda7..9e053f405 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -215,6 +215,15 @@ To exit the application. Usage: bye Parameters information: -NIL- +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! +____________________________________________________________ +____________________________________________________________ +Type of statistics given is invalid, please check your input! +____________________________________________________________ +____________________________________________________________ +There are no statistics available yet for the given statistics type. ____________________________________________________________ ____________________________________________________________ There are no transaction records found. @@ -230,6 +239,13 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare +____________________________________________________________ +____________________________________________________________ +Here are the total savings for each category: +[food] $20 +[salary] $2000 +[transport] $1 + ____________________________________________________________ ____________________________________________________________ Keyword(s) for your search expression missing, please check your input! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 03fe8f5a1..1c592041b 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -2,10 +2,14 @@ help help o/detailed help blablabla help o/detailed blablabla +stats +stats s/invalid +stats s/categories list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary add t/expense c/transport a/1 d/02102022 i/bus_fare +stats s/categories find find bus_fare list From 08218853e66f4c1db07c7a49e287ba495b78065d Mon Sep 17 00:00:00 2001 From: Chua Han Yong Darren Date: Sat, 8 Oct 2022 09:43:24 +0800 Subject: [PATCH 103/416] Update StatsCommand.java --- src/main/java/seedu/duke/command/StatsCommand.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index fb6e14722..2537fc1ae 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -4,12 +4,9 @@ import seedu.duke.Ui; import seedu.duke.data.CategoryList; import seedu.duke.data.TransactionList; -import seedu.duke.exception.ListStatisticsInvalidStatsTypeException; -import seedu.duke.exception.ListStatisticsMissingTagException; import seedu.duke.exception.MoolahException; import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; -import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; /** * Represents a get command object that will execute the operations for Get command. From 24bb1efdc7c4d52dc9f3f20935a088870541acc7 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 9 Oct 2022 00:20:00 +0800 Subject: [PATCH 104/416] Add tag and parameter handling with exceptions --- .../java/seedu/duke/command/AddCommand.java | 67 +++++++++++++------ .../java/seedu/duke/common/ErrorMessages.java | 3 + .../AddTransactionExtraTagException.java | 15 +++++ ...dTransactionMissingParameterException.java | 15 +++++ text-ui-test/EXPECTED.TXT | 5 +- text-ui-test/input.txt | 1 + 6 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/AddTransactionExtraTagException.java create mode 100644 src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 7e8db70ca..f154052f2 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -5,11 +5,13 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.AddTransactionInvalidAmountException; +import seedu.duke.exception.AddTransactionMissingTagException; +import seedu.duke.exception.AddTransactionMissingParameterException; +import seedu.duke.exception.AddTransactionExtraTagException; +import seedu.duke.exception.AddTransactionInvalidDateException; import seedu.duke.exception.AddTransactionInvalidCategoryException; +import seedu.duke.exception.AddTransactionInvalidAmountException; import seedu.duke.exception.AddTransactionUnknownTypeException; -import seedu.duke.exception.AddTransactionInvalidDateException; -import seedu.duke.exception.AddTransactionMissingTagException; import java.time.LocalDate; @@ -30,29 +32,19 @@ public class AddCommand extends Command { // The command word used to trigger the execution of Moolah Manager's operations. public static final String COMMAND_WORD = "ADD"; // The description for the usage of command. - public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " - + "either an \"income\" or an \"expense\" into the transaction list."; + public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " + "either an \"income\" or an \"expense\" into the transaction list."; // The guiding information for the usage of command. public static final String COMMAND_USAGE = "Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION"; // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" - + "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted.\n" - + "CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" - + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted.\n" - + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" - + "DESCRIPTION: More information regarding the transaction, written without any space."; + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted.\n" + "CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted.\n" + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + "DESCRIPTION: More information regarding the transaction, written without any space."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" - + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + COMMAND_DESCRIPTION + "\n" + COMMAND_USAGE + "\n"; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; private String input; - public AddCommand() { - } public AddCommand(String input) { this.input = input; @@ -73,7 +65,10 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws */ String[] splits = input.split(" "); - checkTagsExist(splits); + + checkExtraTagExist(splits); // if more than 5 tags exist in input, throw exception + checkTagsExist(splits); // if the mandatory tags does not exist, throw exception + checkTagStringExist(splits); // if the tag string (parameter) does not exist, throw exception. String description = ""; int amount = 0; @@ -113,8 +108,7 @@ public boolean isExit() { return false; } - private static void addTransactionByType(TransactionList transactions, String type, String description, - int amount, String category, LocalDate date) throws AddTransactionUnknownTypeException { + private static void addTransactionByType(TransactionList transactions, String type, String description, int amount, String category, LocalDate date) throws AddTransactionUnknownTypeException { switch (type) { case "expense": @@ -130,6 +124,41 @@ private static void addTransactionByType(TransactionList transactions, String ty } } + + /** + * Checks if there are extra tag(s) within the user input. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws AddTransactionExtraTagException Extra tag exception. + */ + + private static void checkExtraTagExist(String[] splits) throws AddTransactionExtraTagException { + int countNumberOfTags = 0; + for (String split : splits) { + countNumberOfTags += 1; + } + if (countNumberOfTags > 5) { + throw new AddTransactionExtraTagException(); + } + } + + + /** + * Checks if there are missing parameter within the user input. + * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws AddTransactionMissingParameterException Extra tag exception. + */ + private static void checkTagStringExist(String[] splits) throws AddTransactionMissingParameterException { + for (String split : splits) { + if (split.length() <= 2) { + throw new AddTransactionMissingParameterException(); + } + } + } + + /** * Checks if the targeted tags exists in the split user inputs. * diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 8a71d7700..ad681cab4 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -12,6 +12,9 @@ public enum ErrorMessages { ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), + ERROR_ADD_COMMAND_EXTRA_TAG("Additional tag(s) detected, please check your input!"), + ERROR_ADD_COMMAND_MISSING_PARAMETER("Mandatory parameter(s) missing, please check your input!"), + ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"); public final String message; diff --git a/src/main/java/seedu/duke/exception/AddTransactionExtraTagException.java b/src/main/java/seedu/duke/exception/AddTransactionExtraTagException.java new file mode 100644 index 000000000..75e7d63e2 --- /dev/null +++ b/src/main/java/seedu/duke/exception/AddTransactionExtraTagException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class AddTransactionExtraTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_ADD_COMMAND_EXTRA_TAG.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java b/src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java new file mode 100644 index 000000000..acc3fa569 --- /dev/null +++ b/src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class AddTransactionMissingParameterException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_ADD_COMMAND_MISSING_PARAMETER.toString(); + } +} diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 1446f4c3d..03e4f175c 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -278,7 +278,10 @@ ____________________________________________________________ Invalid date, please ensure your date format is correct! ____________________________________________________________ ____________________________________________________________ -Invalid date, please ensure your date format is correct! +Mandatory parameter(s) missing, please check your input! +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 04db03085..52c55d424 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -18,6 +18,7 @@ add t/income c/bonus a/-1 d/03102022 i/thank_you_boss add t/income c/bonus a/10000000 d/20220101 i/thank_you_boss add t/income c/bonus a/10000000 d/obviouslyFalseDate i/thank_you_boss add t/income c/bonus a/10000000 d/ i/thank_you_boss +add t/income c/bonus a/10000000 list delete 1 purge From ce27c93c8104d2a33169249b7965bd0ba7ab105b Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 9 Oct 2022 00:27:47 +0800 Subject: [PATCH 105/416] Format indentation in AddCommand.java --- .../java/seedu/duke/command/AddCommand.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index f154052f2..2b6f2ec9e 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -32,14 +32,21 @@ public class AddCommand extends Command { // The command word used to trigger the execution of Moolah Manager's operations. public static final String COMMAND_WORD = "ADD"; // The description for the usage of command. - public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " + "either an \"income\" or an \"expense\" into the transaction list."; + public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " + + "either an \"income\" or an \"expense\" into the transaction list."; // The guiding information for the usage of command. public static final String COMMAND_USAGE = "Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION"; // The formatting information for the parameters used by the command. - public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted.\n" + "CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted.\n" + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + "DESCRIPTION: More information regarding the transaction, written without any space."; + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: \n" + + "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted.\n" + + "CATEGORY: A category for the transaction. Only string containing alphabets is accepted.\n" + + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted.\n" + + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\".\n" + + "DESCRIPTION: More information regarding the transaction, written without any space."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + COMMAND_DESCRIPTION + "\n" + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + + COMMAND_DESCRIPTION + "\n" + COMMAND_USAGE + "\n"; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; @@ -108,7 +115,9 @@ public boolean isExit() { return false; } - private static void addTransactionByType(TransactionList transactions, String type, String description, int amount, String category, LocalDate date) throws AddTransactionUnknownTypeException { + private static void addTransactionByType(TransactionList transactions, String type, String description, + int amount, String category, LocalDate date) + throws AddTransactionUnknownTypeException { switch (type) { case "expense": From 9f3de15a23c9e1f05fa3b563249b2df1b6603063 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 9 Oct 2022 01:18:32 +0800 Subject: [PATCH 106/416] Update amount checking method when parsed --- .../java/seedu/duke/command/AddCommand.java | 37 ++++++++++++------- .../java/seedu/duke/common/ErrorMessages.java | 3 +- text-ui-test/EXPECTED.TXT | 7 +++- text-ui-test/input.txt | 1 + 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 95a3b4be4..89f52b0cb 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -33,22 +33,25 @@ public class AddCommand extends Command { public static final String COMMAND_WORD = "ADD"; // The description for the usage of command - public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " + + public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " + + "either an \"income\" or an \"expense\" into the transaction list."; // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION"; // The formatting information for the parameters used by the command - public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + LINE_SEPARATOR + - "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted." + LINE_SEPARATOR + - "CATEGORY: A category for the transaction. Only string containing alphabets is accepted." + LINE_SEPARATOR + - "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted." + - LINE_SEPARATOR + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + LINE_SEPARATOR + - "DESCRIPTION: More information regarding the transaction, written without any space."; + public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + + LINE_SEPARATOR + + "TYPE: The type of transaction. Only \"income\" or \"expense\" is accepted." + LINE_SEPARATOR + + "CATEGORY: A category for the transaction. Only string containing alphabets is accepted." + + LINE_SEPARATOR + + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted." + + LINE_SEPARATOR + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + LINE_SEPARATOR + + "DESCRIPTION: More information regarding the transaction, written without any space."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + COMMAND_DESCRIPTION + "\n" + - COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + COMMAND_DESCRIPTION + "\n" + + COMMAND_USAGE + "\n"; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; @@ -244,13 +247,21 @@ private static String parseCategoryTag(String parameter) throws AddTransactionIn * @return The amount integer if no exceptions are thrown. * @throws AddTransactionInvalidAmountException Invalid amount format exception. */ - private static int parseAmountTag(String parameter) throws AddTransactionInvalidAmountException { + private static int parseAmountTag(String parameter) throws MoolahException { Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); - if (containAlphabet(parameter) || hasSpecialSymbols.find()) { + try { + if (containAlphabet(parameter) || hasSpecialSymbols.find()) { + throw new AddTransactionInvalidAmountException(); + } + int amount = Integer.parseInt(parameter); + if (amount < 0 || amount > 10000000) { + throw new AddTransactionInvalidAmountException(); + } + return amount; + + } catch (NumberFormatException e) { throw new AddTransactionInvalidAmountException(); - } else { - return Integer.parseInt(parameter); } } diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index b07de06da..b3cd654b5 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -7,7 +7,8 @@ public enum ErrorMessages { ERROR_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), - ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, please ensure your amount is in positive numerals only!"), + ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, " + + "please ensure your amount is in positive numerals ($10000000 or less) only!"), ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 698b6291b..1ca603960 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -273,10 +273,13 @@ ____________________________________________________________ Invalid category, please ensure your category is correct! ____________________________________________________________ ____________________________________________________________ -Invalid amount, please ensure your amount is in positive numerals only! +Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! ____________________________________________________________ ____________________________________________________________ -Invalid amount, please ensure your amount is in positive numerals only! +Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +____________________________________________________________ +____________________________________________________________ +Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! ____________________________________________________________ ____________________________________________________________ Invalid date, please ensure your date format is correct! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 8cca89b31..d47d9a3e8 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -18,6 +18,7 @@ add t/heheheh c/bonus a/1000000 d/03102022 i/thank_you_boss add t/income c/123 a/10000000 d/03102022 i/thank_you_boss add t/income c/bonus a/abc d/03102022 i/thank_you_boss add t/income c/bonus a/-1 d/03102022 i/thank_you_boss +add t/income c/bonus a/9999999999999 d/03102022 i/thank_you_boss add t/income c/bonus a/10000000 d/20220101 i/thank_you_boss add t/income c/bonus a/10000000 d/obviouslyFalseDate i/thank_you_boss add t/income c/bonus a/10000000 d/ i/thank_you_boss From ae55ccd144c501d8517ad08a376cacab7fdd753b Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 9 Oct 2022 01:21:33 +0800 Subject: [PATCH 107/416] Update indentation in ErrorMessage.java --- src/main/java/seedu/duke/common/ErrorMessages.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index b3cd654b5..c6207ca49 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -7,8 +7,8 @@ public enum ErrorMessages { ERROR_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), - ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, " + - "please ensure your amount is in positive numerals ($10000000 or less) only!"), + ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, " + + "please ensure your amount is in positive numerals ($10000000 or less) only!"), ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), From 0f9216b7bdabdb2b5d2fdc414a85e4be3aba06a1 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 9 Oct 2022 01:28:15 +0800 Subject: [PATCH 108/416] Update expected exception for AddCommandTest.java --- src/test/java/seedu/duke/command/AddCommandTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index eec0a582b..1d3a36b70 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -5,6 +5,7 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.exception.AddTransactionInvalidDateException; +import seedu.duke.exception.AddTransactionMissingParameterException; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -20,7 +21,7 @@ public void execute_EmptyDate_ExpectedException() { AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/ i/thank_you_boss"); assertThrows( - AddTransactionInvalidDateException.class, + AddTransactionMissingParameterException.class, () -> addCommand.execute(transactions, ui, storage) ); } From 651d9ff295f520804f5380e8eef5416edb56d81b Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 10:22:22 +0800 Subject: [PATCH 109/416] Move commonly-used string and tag parsers into Utilities class --- src/main/java/seedu/duke/Parser.java | 47 ++----- .../java/seedu/duke/common/ErrorMessages.java | 13 +- .../java/seedu/duke/common/Utilities.java | 122 ++++++++++++++++++ .../AddTransactionMissingTagException.java | 2 +- ...=> InputTransactionExtraTagException.java} | 4 +- ...tTransactionInvalidCategoryException.java} | 4 +- ...InputTransactionInvalidDateException.java} | 4 +- ...InputTransactionUnknownTypeException.java} | 4 +- .../ListStatisticsMissingTagException.java | 2 +- 9 files changed, 152 insertions(+), 50 deletions(-) create mode 100644 src/main/java/seedu/duke/common/Utilities.java rename src/main/java/seedu/duke/exception/{AddTransactionExtraTagException.java => InputTransactionExtraTagException.java} (67%) rename src/main/java/seedu/duke/exception/{AddTransactionInvalidCategoryException.java => InputTransactionInvalidCategoryException.java} (65%) rename src/main/java/seedu/duke/exception/{AddTransactionInvalidDateException.java => InputTransactionInvalidDateException.java} (66%) rename src/main/java/seedu/duke/exception/{AddTransactionUnknownTypeException.java => InputTransactionUnknownTypeException.java} (66%) diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java index 07a4fa59d..fd5978fec 100644 --- a/src/main/java/seedu/duke/Parser.java +++ b/src/main/java/seedu/duke/Parser.java @@ -11,29 +11,12 @@ import seedu.duke.command.ListCommand; import seedu.duke.command.PurgeCommand; - import seedu.duke.exception.MoolahException; import seedu.duke.exception.InvalidCommandException; -public class Parser { - private static final String EMPTY_STRING = ""; - private static final String DELIMITER = " "; - private static final int SPLIT_POSITION = 2; - - /** - * Splits the user input into two parts, i.e. the command and the description. - * - * @param inData A line of input entered by the user. - * @return A string array of input tokens. - */ - public static String[] splitInput(String inData) { - String[] inputTokens = inData.split(DELIMITER, SPLIT_POSITION); - if (!inData.contains(DELIMITER)) { - inputTokens = new String[]{inData, EMPTY_STRING}; - } - return inputTokens; - } +import static seedu.duke.common.Utilities.splitInput; +public class Parser { /** * Parses the user input and deal with any input error returned. * @@ -44,34 +27,32 @@ public static String[] splitInput(String inData) { public static Command parse(String inData) throws MoolahException { Command command = null; String[] inputTokens = splitInput(inData); + String input = inputTokens[1]; - // list commands duke to list all the tasks stored and their completion status - // try at the start cos of the errors possibly switch (inputTokens[0].toUpperCase()) { case HelpCommand.COMMAND_WORD: - command = new HelpCommand(inputTokens[1]); + command = new HelpCommand(input); + break; + case AddCommand.COMMAND_WORD: + command = new AddCommand(input); + break; + case EditCommand.COMMAND_WORD: + command = new EditCommand(input); break; case ListCommand.COMMAND_WORD: - command = new ListCommand(); + command = new ListCommand(input); break; case FindCommand.COMMAND_WORD: - command = new FindCommand(inputTokens[1]); + command = new FindCommand(input); break; case StatsCommand.COMMAND_WORD: - // Additional tokens will be allowed for liststats - command = new StatsCommand(inputTokens[1]); + command = new StatsCommand(input); break; case PurgeCommand.COMMAND_WORD: command = new PurgeCommand(); break; case DeleteCommand.COMMAND_WORD: - command = new DeleteCommand(inputTokens[1]); - break; - case AddCommand.COMMAND_WORD: - command = new AddCommand(inputTokens[1]); - break; - case EditCommand.COMMAND_WORD: - command = new EditCommand(inputTokens[1]); + command = new DeleteCommand(input); break; case ByeCommand.COMMAND_WORD: command = new ByeCommand(); diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index c6207ca49..158bb1238 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -5,21 +5,20 @@ */ public enum ErrorMessages { ERROR_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), + ERROR_INPUT_EXTRA_TAG("Additional tag(s) detected, please check your input!"), + ERROR_INPUT_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), + ERROR_INPUT_INVALID_DATE("Invalid date, please ensure your date format is correct!"), + ERROR_INPUT_INVALID_TAG("Invalid tag(s) detected, please check your input!"), + ERROR_INPUT_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), - ERROR_ADD_COMMAND_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, " + "please ensure your amount is in positive numerals ($10000000 or less) only!"), - ERROR_ADD_COMMAND_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), - ERROR_ADD_COMMAND_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), - - ERROR_ADD_COMMAND_EXTRA_TAG("Additional tag(s) detected, please check your input!"), ERROR_ADD_COMMAND_MISSING_PARAMETER("Mandatory parameter(s) missing, please check your input!"), - ERROR_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), + ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"); - public final String message; /** diff --git a/src/main/java/seedu/duke/common/Utilities.java b/src/main/java/seedu/duke/common/Utilities.java new file mode 100644 index 000000000..18ffe3c75 --- /dev/null +++ b/src/main/java/seedu/duke/common/Utilities.java @@ -0,0 +1,122 @@ +package seedu.duke.common; + +import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.InputTransactionExtraTagException; +import seedu.duke.exception.InputTransactionInvalidCategoryException; +import seedu.duke.exception.InputTransactionInvalidDateException; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; + +/** + * Provides a set of extra utilities for processing of different inputs in various commands. + */ +public class Utilities { + private static final String EMPTY_STRING = ""; + private static final String DELIMITER = " "; + private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; + private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; + private static final int SPLIT_POSITION = 2; + + /** + * Splits the user input into two parts, i.e. the command and the description. + * + * @param inData A line of input entered by the user. + * @return A string array of input tokens. + */ + public static String[] splitInput(String inData) { + String[] inputTokens = inData.split(DELIMITER, SPLIT_POSITION); + if (!inData.contains(DELIMITER)) { + inputTokens = new String[]{inData, EMPTY_STRING}; + } + return inputTokens; + } + + /** + * Checks if there are extra tag(s) within the user input. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws InputTransactionExtraTagException Extra tag exception. + */ + public static void checkExtraTagExist(String[] splits, int tagLimit) throws InputTransactionExtraTagException { + int countNumberOfTags = 0; + for (String split : splits) { + countNumberOfTags += 1; + } + if (countNumberOfTags > tagLimit) { + throw new InputTransactionExtraTagException(); + } + } + + /** + * Checks if the parameter contains numeric characters. + * + * @param parameter The user input after the user tag. + * @return A boolean value indicating whether there are numeric characters within the parameter. + */ + public static boolean containNumeric(String parameter) { + char[] characters = parameter.toCharArray(); + for (char character : characters) { + if (Character.isDigit(character)) { + return true; + } + } + return false; + } + + /** + * Parses the user parameter input for the description and returns it. + * + * @param parameter The user input after the user tag. + * @return The class type if no exceptions are thrown. + * @throws InputTransactionUnknownTypeException Invalid type format exception. + */ + public static String parseTypeTag(String parameter) throws InputTransactionUnknownTypeException { + switch (parameter) { + case "expense": + return CLASS_TYPE_EXPENSE; + case "income": + return CLASS_TYPE_INCOME; + default: + throw new InputTransactionUnknownTypeException(); + } + } + + /** + * Parses the user parameter input for the description and returns it. + * + * @param parameter The user input after the user tag. + * @return The category parameter if no exceptions are thrown. + * @throws InputTransactionInvalidCategoryException Invalid category format exception. + */ + public static String parseCategoryTag(String parameter) throws InputTransactionInvalidCategoryException { + Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); + Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); + if (containNumeric(parameter) || hasSpecialSymbols.find()) { + throw new InputTransactionInvalidCategoryException(); + } + return parameter; + } + + /** + * Parses the user parameter input for date into a LocalDate object and returns it. + * + * @param parameter The user input after the user tag. + * @return The LocalDate object parsed from user input given. + * @throws InputTransactionInvalidDateException Invalid date format exception. + */ + public static LocalDate parseDateTag(String parameter) throws InputTransactionInvalidDateException { + try { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_INPUT_PATTERN.toString()); + LocalDate date = LocalDate.parse(parameter, formatter); + return date; + } catch (DateTimeParseException exception) { + throw new InputTransactionInvalidDateException(); + } + } +} diff --git a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java b/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java index 2b619bb73..a0f59a0d5 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java @@ -10,6 +10,6 @@ public class AddTransactionMissingTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_COMMAND_MISSING_TAG.toString(); + return ErrorMessages.ERROR_ADD_COMMAND_MISSING_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionExtraTagException.java b/src/main/java/seedu/duke/exception/InputTransactionExtraTagException.java similarity index 67% rename from src/main/java/seedu/duke/exception/AddTransactionExtraTagException.java rename to src/main/java/seedu/duke/exception/InputTransactionExtraTagException.java index 75e7d63e2..89f93da3c 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionExtraTagException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionExtraTagException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddTransactionExtraTagException extends MoolahException { +public class InputTransactionExtraTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +10,6 @@ public class AddTransactionExtraTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_EXTRA_TAG.toString(); + return ErrorMessages.ERROR_INPUT_EXTRA_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java similarity index 65% rename from src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java rename to src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java index 94b07009e..41eadcabb 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionInvalidCategoryException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddTransactionInvalidCategoryException extends MoolahException { +public class InputTransactionInvalidCategoryException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,7 +10,7 @@ public class AddTransactionInvalidCategoryException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_INVALID_CATEGORY.toString(); + return ErrorMessages.ERROR_INPUT_INVALID_CATEGORY.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java similarity index 66% rename from src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java rename to src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java index eb62fab77..fb3355b39 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionInvalidDateException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddTransactionInvalidDateException extends MoolahException { +public class InputTransactionInvalidDateException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. @@ -11,6 +11,6 @@ public class AddTransactionInvalidDateException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_INVALID_DATE.toString(); + return ErrorMessages.ERROR_INPUT_INVALID_DATE.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java b/src/main/java/seedu/duke/exception/InputTransactionUnknownTypeException.java similarity index 66% rename from src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java rename to src/main/java/seedu/duke/exception/InputTransactionUnknownTypeException.java index 2351a938c..4605f4c7a 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionUnknownTypeException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionUnknownTypeException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddTransactionUnknownTypeException extends MoolahException { +public class InputTransactionUnknownTypeException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +10,6 @@ public class AddTransactionUnknownTypeException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_INVALID_TYPE.toString(); + return ErrorMessages.ERROR_INPUT_INVALID_TYPE.toString(); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java b/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java index 371298b76..caa69c7e5 100644 --- a/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java +++ b/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java @@ -10,6 +10,6 @@ public class ListStatisticsMissingTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_COMMAND_MISSING_TAG.toString(); + return ErrorMessages.ERROR_ADD_COMMAND_MISSING_TAG.toString(); } } From 49400be3e4f05710dcc43167b1c749cefc14e484 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 10:23:28 +0800 Subject: [PATCH 110/416] Import tag handlers from Utilities class into AddCommand class --- .../java/seedu/duke/command/AddCommand.java | 112 ++++-------------- 1 file changed, 20 insertions(+), 92 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 89f52b0cb..95e3588dd 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -5,24 +5,21 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.AddTransactionMissingTagException; -import seedu.duke.exception.AddTransactionMissingParameterException; -import seedu.duke.exception.AddTransactionExtraTagException; -import seedu.duke.exception.AddTransactionInvalidDateException; -import seedu.duke.exception.AddTransactionInvalidCategoryException; import seedu.duke.exception.AddTransactionInvalidAmountException; -import seedu.duke.exception.AddTransactionUnknownTypeException; - +import seedu.duke.exception.AddTransactionMissingParameterException; +import seedu.duke.exception.AddTransactionMissingTagException; +import seedu.duke.exception.InputTransactionInvalidTagException; +import seedu.duke.exception.InputTransactionUnknownTypeException; import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; +import static seedu.duke.common.Utilities.checkExtraTagExist; +import static seedu.duke.common.Utilities.parseCategoryTag; +import static seedu.duke.common.Utilities.parseDateTag; /** * Represents an add command object that will execute the operations for Add command. @@ -34,8 +31,7 @@ public class AddCommand extends Command { // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To add a new transaction entry, which could be " - + - "either an \"income\" or an \"expense\" into the transaction list."; + + "either an \"income\" or an \"expense\" into the transaction list."; // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION"; // The formatting information for the parameters used by the command @@ -55,8 +51,9 @@ public class AddCommand extends Command { // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; - private String input; + private static final int TAG_LIMIT = 5; + private String input; public AddCommand(String input) { this.input = input; @@ -77,9 +74,12 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws */ String[] splits = input.split(" "); - checkExtraTagExist(splits); // if more than 5 tags exist in input, throw exception - checkTagsExist(splits); // if the mandatory tags does not exist, throw exception - checkParameterExist(splits); // if the parameter does not exist, throw exception. + // Throws exception if there are more than five tags + checkExtraTagExist(splits, TAG_LIMIT); + // Throws exception if there are no mandatory tags + checkTagsExist(splits); + // Throws exception if there are no parameters + checkParameterExist(splits); String description = ""; int amount = 0; @@ -107,7 +107,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws description = parameter; break; default: - break; + throw new InputTransactionInvalidTagException(); } } assert date != null; @@ -121,7 +121,7 @@ public boolean isExit() { private static void addTransactionByType(TransactionList transactions, String type, String description, int amount, String category, LocalDate date) - throws AddTransactionUnknownTypeException { + throws InputTransactionUnknownTypeException { switch (type) { case "expense": @@ -133,29 +133,10 @@ private static void addTransactionByType(TransactionList transactions, String ty Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); break; default: - throw new AddTransactionUnknownTypeException(); + throw new InputTransactionUnknownTypeException(); } } - - /** - * Checks if there are extra tag(s) within the user input. - * - * @param splits The user input after the command word, split into a list for every space found. - * @throws AddTransactionExtraTagException Extra tag exception. - */ - - private static void checkExtraTagExist(String[] splits) throws AddTransactionExtraTagException { - int countNumberOfTags = 0; - for (String split : splits) { - countNumberOfTags += 1; - } - if (countNumberOfTags > 5) { - throw new AddTransactionExtraTagException(); - } - } - - /** * Checks if there are missing parameter within the user input. * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. @@ -171,7 +152,6 @@ private static void checkParameterExist(String[] splits) throws AddTransactionMi } } - /** * Checks if the targeted tags exists in the split user inputs. * @@ -189,22 +169,6 @@ private static void checkTagsExist(String[] splits) throws AddTransactionMissing } } - /** - * Checks if the parameter contains numeric characters. - * - * @param parameter The user input after the user tag. - * @return true if there are numeric characters within the parameter. - */ - public static boolean containNumeric(String parameter) { - char[] characters = parameter.toCharArray(); - for (char character : characters) { - if (Character.isDigit(character)) { - return true; - } - } - return false; - } - /** * Checks if the parameter contains alphabetical characters. * @@ -221,27 +185,8 @@ public static boolean containAlphabet(String parameter) { return false; } - - /** - * Parse the user parameter input for the description and returns it. - * - * @param parameter The user input after the user tag. - * @return The category parameter if no exceptions are thrown. - * @throws AddTransactionInvalidCategoryException Invalid category format exception. - */ - - private static String parseCategoryTag(String parameter) throws AddTransactionInvalidCategoryException { - Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); - Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); - if (containNumeric(parameter) || hasSpecialSymbols.find()) { - throw new AddTransactionInvalidCategoryException(); - } - return parameter; - - } - /** - * Parse the user parameter input for the amount and returns it. + * Parses the user parameter input for the amount and returns it. * * @param parameter The user input after the user tag. * @return The amount integer if no exceptions are thrown. @@ -265,23 +210,6 @@ private static int parseAmountTag(String parameter) throws MoolahException { } } - /** - * Parse the user parameter input for date into a LocalDate object and returns it. - * - * @param parameter The user input after the user tag. - * @return The LocalDate object parsed from user input given. - * @throws AddTransactionInvalidDateException Invalid date format exception. - */ - private static LocalDate parseDateTag(String parameter) throws AddTransactionInvalidDateException { - try { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_INPUT_PATTERN.toString()); - LocalDate date = LocalDate.parse(parameter, formatter); - return date; - } catch (DateTimeParseException exception) { - throw new AddTransactionInvalidDateException(); - } - } - /** * Returns a boolean value on whether a tag can be found among the split user inputs. * From ddd7807c9d83b295aa35e3d9123c41d4622ecbbb Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 10:25:19 +0800 Subject: [PATCH 111/416] Add ability to list transactions by filtered tags and import tag handlers from Utilities class into ListCommand class --- .../java/seedu/duke/command/ListCommand.java | 86 ++++++++++++++++--- .../java/seedu/duke/data/TransactionList.java | 43 +++++++++- .../duke/data/transaction/Transaction.java | 4 + .../InputTransactionInvalidTagException.java | 15 ++++ 4 files changed, 134 insertions(+), 14 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/InputTransactionInvalidTagException.java diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 9d1bcbb76..260fa4865 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -3,9 +3,18 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.exception.InputTransactionInvalidTagException; +import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.MoolahException; + +import java.time.LocalDate; import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; +import static seedu.duke.common.Utilities.checkExtraTagExist; +import static seedu.duke.common.Utilities.parseCategoryTag; +import static seedu.duke.common.Utilities.parseDateTag; +import static seedu.duke.common.Utilities.parseTypeTag; /** * Represents a list command object that will execute the operations for List command. @@ -27,19 +36,20 @@ public class ListCommand extends Command { + LINE_SEPARATOR + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; - // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD - + LINE_SEPARATOR - + COMMAND_DESCRIPTION - + LINE_SEPARATOR - + COMMAND_USAGE - + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - - public ListCommand() { + + private static final int TAG_LIMIT = 3; + + private String input; + + public ListCommand(String input) { + this.input = input; } /** @@ -50,8 +60,62 @@ public ListCommand() { * @param storage An instance of the Storage class. */ @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) { - String transactionsList = transactions.listTransactions(); + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + /* + Checks if userInput is in the correct input format by further parsing, + before passing any tags to the filter for transaction list. + */ + String[] splits = {}; + if (!input.isEmpty()) { + splits = input.split(" "); + } + + // Throws exception if there are more than three tags + checkExtraTagExist(splits, TAG_LIMIT); + + String category = ""; + LocalDate date = null; + String type = ""; + + for (String split : splits) { + // Throws exception if the tag cannot split + if (split.length() < 2) { + throw new InputTransactionInvalidTagException(); + } + String tag = split.substring(0, 2); + String parameter = split.substring(2); + switch (tag) { + case "t/": + type = parseTypeTag(parameter); + break; + case "c/": + category = parseCategoryTag(parameter); + break; + case "d/": + date = parseDateTag(parameter); + assert date != null; + break; + default: + // Throws exception if the tag is invalid + throw new InputTransactionInvalidTagException(); + } + } + listTransactions(transactions, type, category, date); + } + + /** + * List all or some transactions based on selection. + * + * @param transactions An instance of the TransactionList class. + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + */ + private static void listTransactions(TransactionList transactions, String type, + String category, LocalDate date) + throws InputTransactionUnknownTypeException { + String transactionsList = transactions.listTransactions(type, category, date); if (transactionsList.isEmpty()) { Ui.showInfoMessage(INFO_LIST_EMPTY.toString()); return; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 54e3c93c1..62f7769a7 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -3,6 +3,7 @@ import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.InputTransactionUnknownTypeException; import java.time.LocalDate; import java.util.ArrayList; @@ -44,15 +45,51 @@ public String addIncome(String description, int amount, String category, LocalDa return income.toString(); } - public String listTransactions() { + /** + * Checks whether the transaction belongs to the Income or Expense class type. + * + * @param transaction The transaction record from the transactions list. + * @param classType The transaction class type that is either Income or Expense. + * @return A boolean value indicating whether transaction record belongs to the given class type. + * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + */ + public boolean checkTransactionInstance(Object transaction, String classType) throws ClassNotFoundException { + return Class.forName(classType).isInstance(transaction); + } + + /** + * List all or some transactions based on selection. + * + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @return A string containing the formatted transaction list. + * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + */ + public String listTransactions(String type, String category, LocalDate date) + throws InputTransactionUnknownTypeException { String transactionsList = EMPTY_STRING; // Loops each transaction from the transactions list - for (Transaction transaction : transactions) { - transactionsList += transaction.toString() + LINE_SEPARATOR; + try { + for (Transaction transaction : transactions) { + if (((type.isEmpty() || checkTransactionInstance(transaction, type))) + && (category.isEmpty() || transaction.getCategory().equals(category)) + && (date == null || transaction.getDate().equals(date))) { + transactionsList += transaction.toString() + LINE_SEPARATOR; + } + } + } catch (ClassNotFoundException e) { + throw new InputTransactionUnknownTypeException(); } return transactionsList; } + /** + * Find specific transaction(s) based on any keywords inputted by the user. + * + * @param keywords Any partial or full keyword(s) that matches the details of the transaction. + * @return A string containing the formatted transaction list. + */ public String findTransactions(String keywords) { String transactionsList = EMPTY_STRING; // Loops each transaction from the transactions list diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 254af320e..04bee2436 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -49,6 +49,10 @@ public void setCategory(String category) { this.category = category; } + public LocalDate getDate() { + return date; + } + public String printFormattedDate() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_OUTPUT_PATTERN.toString()); return date.format(formatter); diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidTagException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidTagException.java new file mode 100644 index 000000000..7e7b92d61 --- /dev/null +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidTagException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class InputTransactionInvalidTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_INPUT_INVALID_TAG.toString(); + } +} From 7e7b0d74be1eb2bf90b9912d576b561583c001ed Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 10:26:34 +0800 Subject: [PATCH 112/416] Update EXPECTED.TXT and input.txt to test the List command with filter tags --- text-ui-test/EXPECTED.TXT | 33 +++++++++++++++++++++++++++++++++ text-ui-test/input.txt | 8 ++++++++ 2 files changed, 41 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 1ca603960..4837fc6db 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -239,6 +239,39 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare +____________________________________________________________ +____________________________________________________________ +Invalid tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ +Type of transaction given is invalid, please check your input! +____________________________________________________________ +____________________________________________________________ +Here are your transaction records: +[-][food] $20 at Sep 13 2022 | Description: NIL +[-][transport] $1 at Oct 02 2022 | Description: bus_fare + +____________________________________________________________ +____________________________________________________________ +Here are your transaction records: +[-][food] $20 at Sep 13 2022 | Description: NIL + +____________________________________________________________ +____________________________________________________________ +There are no transaction records found. +____________________________________________________________ +____________________________________________________________ +Here are your transaction records: +[-][food] $20 at Sep 13 2022 | Description: NIL + +____________________________________________________________ +____________________________________________________________ +Invalid date, please ensure your date format is correct! +____________________________________________________________ +____________________________________________________________ +Here are your transaction records: +[+][salary] $2000 at Sep 30 2022 | Description: jan_salary + ____________________________________________________________ ____________________________________________________________ Here are the total savings for each category: diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index d47d9a3e8..dd3bd5f38 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -9,6 +9,14 @@ list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary add t/expense c/transport a/1 d/02102022 i/bus_fare +list / +list t/Expense +list t/expense +list c/food +list c/food d/30092022 +list c/food d/13092022 +list t/income d/ +list t/income d/30092022 stats s/categories find find bus_fare From f15a8426805eb6c6805789bed4456bd64076639d Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 10:31:27 +0800 Subject: [PATCH 113/416] Move containNumeric JUnit test into UtilitiesTest class from AddCommandTest class --- .../seedu/duke/command/AddCommandTest.java | 19 ++--------------- .../java/seedu/duke/common/UtilitiesTest.java | 21 +++++++++++++++++++ .../data/transaction/TransactionTest.java | 1 - 3 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 src/test/java/seedu/duke/common/UtilitiesTest.java diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index 1d3a36b70..4065cf090 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -4,12 +4,9 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.AddTransactionInvalidDateException; +import seedu.duke.exception.InputTransactionInvalidDateException; import seedu.duke.exception.AddTransactionMissingParameterException; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; public class AddCommandTest { @@ -34,20 +31,8 @@ public void execute_InvalidDateFormat_ExpectedException() { AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss"); assertThrows( - AddTransactionInvalidDateException.class, + InputTransactionInvalidDateException.class, () -> addCommand.execute(transactions, ui, storage) ); } - - @Test - public void containNumeric_IfContainsNumeric_ReturnTrue() { - boolean testOutputContainsNumber = AddCommand.containNumeric("Food1"); - assertTrue(testOutputContainsNumber); - } - - @Test - public void containNumeric_IfDoesNotContainNumeric_ReturnFalse() { - boolean testOutputWithoutNumber = AddCommand.containNumeric("Food"); - assertFalse(testOutputWithoutNumber); - } } diff --git a/src/test/java/seedu/duke/common/UtilitiesTest.java b/src/test/java/seedu/duke/common/UtilitiesTest.java new file mode 100644 index 000000000..e33775d1e --- /dev/null +++ b/src/test/java/seedu/duke/common/UtilitiesTest.java @@ -0,0 +1,21 @@ +package seedu.duke.common; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.duke.common.Utilities.containNumeric; + +public class UtilitiesTest { + @Test + public void containNumeric_IfContainsNumeric_ReturnTrue() { + boolean testOutputContainsNumber = containNumeric("Food1"); + assertTrue(testOutputContainsNumber); + } + + @Test + public void containNumeric_IfDoesNotContainNumeric_ReturnFalse() { + boolean testOutputWithoutNumber = containNumeric("Food"); + assertFalse(testOutputWithoutNumber); + } +} diff --git a/src/test/java/seedu/duke/data/transaction/TransactionTest.java b/src/test/java/seedu/duke/data/transaction/TransactionTest.java index 670214790..feebfaf5c 100644 --- a/src/test/java/seedu/duke/data/transaction/TransactionTest.java +++ b/src/test/java/seedu/duke/data/transaction/TransactionTest.java @@ -7,7 +7,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class TransactionTest { - LocalDate date = LocalDate.of(2022, 1, 1); Transaction transaction = new Income("Milked cows in the farm", 50, "Salary", date); From 1305a058ec8b80e3280a4b81bfd251139b5b0343 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 14:59:41 +0800 Subject: [PATCH 114/416] Extract if statement into a separate method based on PR feedback by Thin Hong --- .../java/seedu/duke/data/TransactionList.java | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 62f7769a7..5e00e8086 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -53,10 +53,33 @@ public String addIncome(String description, int amount, String category, LocalDa * @return A boolean value indicating whether transaction record belongs to the given class type. * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ - public boolean checkTransactionInstance(Object transaction, String classType) throws ClassNotFoundException { + public boolean isTransactionInstance(Object transaction, String classType) throws ClassNotFoundException { return Class.forName(classType).isInstance(transaction); } + /** + * Checks whether a transaction fulfills the given filter criteria. + * + * @param transaction The transaction record from the transactions list. + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @return A string containing the formatted transaction list. + * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + */ + public boolean isMatchListFilters(Transaction transaction, String type, String category, + LocalDate date) throws InputTransactionUnknownTypeException { + boolean isMatch; + try { + isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) + && (category.isEmpty() || transaction.getCategory().equals(category)) + && (date == null || transaction.getDate().equals(date))); + } catch (ClassNotFoundException e) { + throw new InputTransactionUnknownTypeException(); + } + return isMatch; + } + /** * List all or some transactions based on selection. * @@ -70,16 +93,10 @@ public String listTransactions(String type, String category, LocalDate date) throws InputTransactionUnknownTypeException { String transactionsList = EMPTY_STRING; // Loops each transaction from the transactions list - try { - for (Transaction transaction : transactions) { - if (((type.isEmpty() || checkTransactionInstance(transaction, type))) - && (category.isEmpty() || transaction.getCategory().equals(category)) - && (date == null || transaction.getDate().equals(date))) { - transactionsList += transaction.toString() + LINE_SEPARATOR; - } + for (Transaction transaction : transactions) { + if (isMatchListFilters(transaction, type, category, date)) { + transactionsList += transaction.toString() + LINE_SEPARATOR; } - } catch (ClassNotFoundException e) { - throw new InputTransactionUnknownTypeException(); } return transactionsList; } From f52a488d60acd7a5bba984bee804f3677e63040f Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 17:56:16 +0800 Subject: [PATCH 115/416] Add more assertions into ListCommand class --- src/main/java/seedu/duke/command/ListCommand.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 260fa4865..52f352bae 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -45,6 +45,9 @@ public class ListCommand extends Command { + LINE_SEPARATOR; private static final int TAG_LIMIT = 3; + private static final int MINIMUM_TAG_LENGTH = 2; + private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; + private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; private String input; @@ -79,14 +82,17 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws for (String split : splits) { // Throws exception if the tag cannot split - if (split.length() < 2) { + if (split.length() < MINIMUM_TAG_LENGTH) { throw new InputTransactionInvalidTagException(); } + assert split.length() >= MINIMUM_TAG_LENGTH; + String tag = split.substring(0, 2); String parameter = split.substring(2); switch (tag) { case "t/": type = parseTypeTag(parameter); + assert type.equals(CLASS_TYPE_EXPENSE) || type.equals(CLASS_TYPE_INCOME); break; case "c/": category = parseCategoryTag(parameter); @@ -120,6 +126,7 @@ private static void listTransactions(TransactionList transactions, String type, Ui.showInfoMessage(INFO_LIST_EMPTY.toString()); return; } + assert !transactionsList.isEmpty(); Ui.showTransactionsList(transactionsList, INFO_LIST.toString()); } From 44e97106f8d0db9d5523c9b3c2909d0098e276d8 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 18:19:39 +0800 Subject: [PATCH 116/416] Add assertions for Find and Stats command classes, and some redundant lines --- src/main/java/seedu/duke/command/FindCommand.java | 14 +++++--------- src/main/java/seedu/duke/command/StatsCommand.java | 9 ++------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 0b09aaaf9..faa1ba09b 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -27,21 +27,15 @@ public class FindCommand extends Command { + "such as type, category, amount, date or description."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD - + LINE_SEPARATOR - + COMMAND_DESCRIPTION - + LINE_SEPARATOR - + COMMAND_USAGE - + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; protected String keywords; - public FindCommand() { - } - /** * Initialises the variables of the FindCommand class. * @@ -75,11 +69,13 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { try { // Checks the format of find to ensure that it contains keywords used in the search expression checkFindFormat(keywords); + assert !keywords.isBlank(); String transactionsList = transactions.findTransactions(keywords); if (transactionsList.isEmpty()) { ui.showInfoMessage(INFO_LIST_UNFILTERED.toString()); return; } + assert !transactionsList.isEmpty(); ui.showTransactionsList(transactionsList, INFO_LIST_FILTERED.toString()); } catch (FindTransactionMissingKeywordsException e) { ui.showErrorMessage(e.getMessage()); diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index ee03e7dca..98feb56e0 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -53,13 +53,11 @@ public StatsCommand(String input) { public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { /* Checks if userInput is in the correct input format by further parsing, - before adding entry to arraylist + before passing any tags to the filter for statistics. */ String[] splits = input.split(" "); checkTagsExist(splits); - String statsType = ""; - for (String split : splits) { String tag = split.substring(0, 2); String parameter = split.substring(2); @@ -67,10 +65,6 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws case "s/": listStatisticsByStatsType(parameter, transactions); break; - /*case "t/": - break; - case "n/": - break;*/ default: break; } @@ -93,6 +87,7 @@ private static void listStatisticsByStatsType(String statsType, TransactionList Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); return; } + assert !categoriesList.isEmpty(); Ui.showTransactionsList(categoriesList, INFO_STATS_CATEGORIES.toString()); break; default: From 22b1aa37cb5a2f81f0a0b9d7273f2572cc29febf Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 10 Oct 2022 18:19:39 +0800 Subject: [PATCH 117/416] Add assertions for Find and Stats command classes, and remove some redundant lines --- src/main/java/seedu/duke/command/FindCommand.java | 14 +++++--------- src/main/java/seedu/duke/command/StatsCommand.java | 9 ++------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 0b09aaaf9..faa1ba09b 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -27,21 +27,15 @@ public class FindCommand extends Command { + "such as type, category, amount, date or description."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD - + LINE_SEPARATOR - + COMMAND_DESCRIPTION - + LINE_SEPARATOR - + COMMAND_USAGE - + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; protected String keywords; - public FindCommand() { - } - /** * Initialises the variables of the FindCommand class. * @@ -75,11 +69,13 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { try { // Checks the format of find to ensure that it contains keywords used in the search expression checkFindFormat(keywords); + assert !keywords.isBlank(); String transactionsList = transactions.findTransactions(keywords); if (transactionsList.isEmpty()) { ui.showInfoMessage(INFO_LIST_UNFILTERED.toString()); return; } + assert !transactionsList.isEmpty(); ui.showTransactionsList(transactionsList, INFO_LIST_FILTERED.toString()); } catch (FindTransactionMissingKeywordsException e) { ui.showErrorMessage(e.getMessage()); diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index ee03e7dca..98feb56e0 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -53,13 +53,11 @@ public StatsCommand(String input) { public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { /* Checks if userInput is in the correct input format by further parsing, - before adding entry to arraylist + before passing any tags to the filter for statistics. */ String[] splits = input.split(" "); checkTagsExist(splits); - String statsType = ""; - for (String split : splits) { String tag = split.substring(0, 2); String parameter = split.substring(2); @@ -67,10 +65,6 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws case "s/": listStatisticsByStatsType(parameter, transactions); break; - /*case "t/": - break; - case "n/": - break;*/ default: break; } @@ -93,6 +87,7 @@ private static void listStatisticsByStatsType(String statsType, TransactionList Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); return; } + assert !categoriesList.isEmpty(); Ui.showTransactionsList(categoriesList, INFO_STATS_CATEGORIES.toString()); break; default: From fbab58b46f8bfe340e83992ac743573988e3da11 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 10 Oct 2022 19:08:15 +0800 Subject: [PATCH 118/416] Move Utilities.java and Parser.java into Parser package Utilities.java is renamed to ParameterParser.java. Parser.java is renamed to CommandParser.java. Purpose of this change is to group all the classes of parsing nature into the same package to increase cohesion for the parsers. --- src/main/java/seedu/duke/Duke.java | 3 +- src/main/java/seedu/duke/Parser.java | 65 ----------- .../java/seedu/duke/command/AddCommand.java | 6 +- .../java/seedu/duke/command/ListCommand.java | 8 +- .../java/seedu/duke/parser/CommandParser.java | 107 ++++++++++++++++++ .../ParameterParser.java} | 30 ++--- .../java/seedu/duke/common/UtilitiesTest.java | 2 +- 7 files changed, 129 insertions(+), 92 deletions(-) delete mode 100644 src/main/java/seedu/duke/Parser.java create mode 100644 src/main/java/seedu/duke/parser/CommandParser.java rename src/main/java/seedu/duke/{common/Utilities.java => parser/ParameterParser.java} (80%) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 1cd657971..2883ef78c 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -3,6 +3,7 @@ import seedu.duke.command.Command; import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +import seedu.duke.parser.CommandParser; public class Duke { private Storage storage; @@ -31,7 +32,7 @@ public void run() { try { inData = ui.readCommand(); inData = inData.trim(); - Command c = Parser.parse(inData); + Command c = CommandParser.parse(inData); c.execute(transactions, ui, storage); isExit = c.isExit(); } catch (MoolahException e) { diff --git a/src/main/java/seedu/duke/Parser.java b/src/main/java/seedu/duke/Parser.java deleted file mode 100644 index fd5978fec..000000000 --- a/src/main/java/seedu/duke/Parser.java +++ /dev/null @@ -1,65 +0,0 @@ -package seedu.duke; - -import seedu.duke.command.Command; -import seedu.duke.command.AddCommand; -import seedu.duke.command.ByeCommand; -import seedu.duke.command.DeleteCommand; -import seedu.duke.command.EditCommand; -import seedu.duke.command.FindCommand; -import seedu.duke.command.StatsCommand; -import seedu.duke.command.HelpCommand; -import seedu.duke.command.ListCommand; -import seedu.duke.command.PurgeCommand; - -import seedu.duke.exception.MoolahException; -import seedu.duke.exception.InvalidCommandException; - -import static seedu.duke.common.Utilities.splitInput; - -public class Parser { - /** - * Parses the user input and deal with any input error returned. - * - * @param inData A line of input entered by the user. - * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. - * @throws MoolahException Any command input exceptions captured by Moolah Manager. - */ - public static Command parse(String inData) throws MoolahException { - Command command = null; - String[] inputTokens = splitInput(inData); - String input = inputTokens[1]; - - switch (inputTokens[0].toUpperCase()) { - case HelpCommand.COMMAND_WORD: - command = new HelpCommand(input); - break; - case AddCommand.COMMAND_WORD: - command = new AddCommand(input); - break; - case EditCommand.COMMAND_WORD: - command = new EditCommand(input); - break; - case ListCommand.COMMAND_WORD: - command = new ListCommand(input); - break; - case FindCommand.COMMAND_WORD: - command = new FindCommand(input); - break; - case StatsCommand.COMMAND_WORD: - command = new StatsCommand(input); - break; - case PurgeCommand.COMMAND_WORD: - command = new PurgeCommand(); - break; - case DeleteCommand.COMMAND_WORD: - command = new DeleteCommand(input); - break; - case ByeCommand.COMMAND_WORD: - command = new ByeCommand(); - break; - default: - throw new InvalidCommandException(); - } - return command; - } -} diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 95e3588dd..762e5d4c1 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -17,9 +17,9 @@ import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; -import static seedu.duke.common.Utilities.checkExtraTagExist; -import static seedu.duke.common.Utilities.parseCategoryTag; -import static seedu.duke.common.Utilities.parseDateTag; +import static seedu.duke.parser.ParameterParser.checkExtraTagExist; +import static seedu.duke.parser.ParameterParser.parseCategoryTag; +import static seedu.duke.parser.ParameterParser.parseDateTag; /** * Represents an add command object that will execute the operations for Add command. diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 52f352bae..6e51d76e3 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -11,10 +11,10 @@ import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; -import static seedu.duke.common.Utilities.checkExtraTagExist; -import static seedu.duke.common.Utilities.parseCategoryTag; -import static seedu.duke.common.Utilities.parseDateTag; -import static seedu.duke.common.Utilities.parseTypeTag; +import static seedu.duke.parser.ParameterParser.checkExtraTagExist; +import static seedu.duke.parser.ParameterParser.parseCategoryTag; +import static seedu.duke.parser.ParameterParser.parseDateTag; +import static seedu.duke.parser.ParameterParser.parseTypeTag; /** * Represents a list command object that will execute the operations for List command. diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java new file mode 100644 index 000000000..6fd6e3c63 --- /dev/null +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -0,0 +1,107 @@ +package seedu.duke.parser; + +import seedu.duke.command.Command; +import seedu.duke.command.AddCommand; +import seedu.duke.command.ByeCommand; +import seedu.duke.command.DeleteCommand; +import seedu.duke.command.EditCommand; +import seedu.duke.command.FindCommand; +import seedu.duke.command.StatsCommand; +import seedu.duke.command.HelpCommand; +import seedu.duke.command.ListCommand; +import seedu.duke.command.PurgeCommand; + +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.InvalidCommandException; + +public class CommandParser { + private static final String EMPTY_STRING = ""; + private static final String DELIMITER = " "; + private static final int SPLIT_POSITION = 2; + + /** + * Parses the user input into Command class based on the command word. + * + * @param fullCommandInput A line of input entered by the user. + * @return IS_EXIT If input equals "bye", else return IS_CONTINUE. + * @throws MoolahException Any command input exceptions captured by Moolah Manager. + */ + public static Command parse(String fullCommandInput) throws MoolahException { + Command command = null; + String[] inputTokens = splitInput(fullCommandInput); + + assert inputTokens.length == 2; + String commandWordInput = inputTokens[0]; + String parametersInput = inputTokens[1]; + + // Parse the command word from user input + command = getCommand(commandWordInput); + + // Parse the parameters from user input to set up the parameters for the command + assert command != null; + ParameterParser.parse(command, parametersInput); + + return command; + } + + /** + * Splits the user input into two parts, i.e. the command word and the parameter(s). + * + * @param fullCommandInput A line of input entered by the user. + * @return A string array of input tokens. + */ + public static String[] splitInput(String fullCommandInput) { + String[] inputTokens; + // Separate the command word and the parameters into an array of size two + if (fullCommandInput.contains(DELIMITER)) { + inputTokens = fullCommandInput.split(DELIMITER, SPLIT_POSITION); + } else { + inputTokens = new String[]{fullCommandInput, EMPTY_STRING}; + } + return inputTokens; + } + + /** + * Creates a Command object based on the command word entered by user. + * + * @param commandWordInput The command word entered by user. + * @return Command object created + * @throws InvalidCommandException Exception when the command word is not supported by the application. + */ + private static Command getCommand(String commandWordInput) throws InvalidCommandException{ + Command command = null; + switch (commandWordInput.toUpperCase()) { + case HelpCommand.COMMAND_WORD: + command = new HelpCommand(); + break; + case AddCommand.COMMAND_WORD: + command = new AddCommand(); + break; + case EditCommand.COMMAND_WORD: + command = new EditCommand(); + break; + case ListCommand.COMMAND_WORD: + command = new ListCommand(); + break; + case FindCommand.COMMAND_WORD: + command = new FindCommand(); + break; + case StatsCommand.COMMAND_WORD: + command = new StatsCommand(); + break; + case PurgeCommand.COMMAND_WORD: + command = new PurgeCommand(); + break; + case DeleteCommand.COMMAND_WORD: + command = new DeleteCommand(); + break; + case ByeCommand.COMMAND_WORD: + command = new ByeCommand(); + break; + default: + throw new InvalidCommandException(); + } + return command; + + } +} diff --git a/src/main/java/seedu/duke/common/Utilities.java b/src/main/java/seedu/duke/parser/ParameterParser.java similarity index 80% rename from src/main/java/seedu/duke/common/Utilities.java rename to src/main/java/seedu/duke/parser/ParameterParser.java index 18ffe3c75..16891c4e1 100644 --- a/src/main/java/seedu/duke/common/Utilities.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -1,9 +1,7 @@ -package seedu.duke.common; +package seedu.duke.parser; -import seedu.duke.exception.InputTransactionUnknownTypeException; -import seedu.duke.exception.InputTransactionExtraTagException; -import seedu.duke.exception.InputTransactionInvalidCategoryException; -import seedu.duke.exception.InputTransactionInvalidDateException; +import seedu.duke.command.Command; +import seedu.duke.exception.*; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -16,27 +14,23 @@ /** * Provides a set of extra utilities for processing of different inputs in various commands. */ -public class Utilities { - private static final String EMPTY_STRING = ""; - private static final String DELIMITER = " "; +public class ParameterParser { private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; - private static final int SPLIT_POSITION = 2; /** - * Splits the user input into two parts, i.e. the command and the description. + * To parse the parameters input into proper parameters of the command object * - * @param inData A line of input entered by the user. - * @return A string array of input tokens. + * @param command A command object created based on the command word given by user. + * @throws MoolahException Any command input exceptions captured by Moolah Manager. */ - public static String[] splitInput(String inData) { - String[] inputTokens = inData.split(DELIMITER, SPLIT_POSITION); - if (!inData.contains(DELIMITER)) { - inputTokens = new String[]{inData, EMPTY_STRING}; - } - return inputTokens; + public static void parse(Command command, String parametersInput) throws MoolahException { + assert command != null; + + } + /** * Checks if there are extra tag(s) within the user input. * diff --git a/src/test/java/seedu/duke/common/UtilitiesTest.java b/src/test/java/seedu/duke/common/UtilitiesTest.java index e33775d1e..fd4207be0 100644 --- a/src/test/java/seedu/duke/common/UtilitiesTest.java +++ b/src/test/java/seedu/duke/common/UtilitiesTest.java @@ -4,7 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.duke.common.Utilities.containNumeric; +import static seedu.duke.parser.ParameterParser.containNumeric; public class UtilitiesTest { @Test From ac07bd7a0c87d54eea6eac1bb2539514c6feab85 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Mon, 10 Oct 2022 19:22:27 +0800 Subject: [PATCH 119/416] Add exception handling for list command --- .../java/seedu/duke/command/AddCommand.java | 25 ++---- .../java/seedu/duke/command/ListCommand.java | 85 +++++++++++++++---- .../java/seedu/duke/common/Utilities.java | 21 ++++- .../seedu/duke/command/AddCommandTest.java | 44 ++++++++-- text-ui-test/EXPECTED.TXT | 11 ++- text-ui-test/input.txt | 3 + 6 files changed, 145 insertions(+), 44 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 95e3588dd..55bddc886 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -6,7 +6,6 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; import seedu.duke.exception.AddTransactionInvalidAmountException; -import seedu.duke.exception.AddTransactionMissingParameterException; import seedu.duke.exception.AddTransactionMissingTagException; import seedu.duke.exception.InputTransactionInvalidTagException; import seedu.duke.exception.InputTransactionUnknownTypeException; @@ -18,8 +17,10 @@ import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; import static seedu.duke.common.Utilities.checkExtraTagExist; -import static seedu.duke.common.Utilities.parseCategoryTag; +import static seedu.duke.common.Utilities.checkParameterExist; import static seedu.duke.common.Utilities.parseDateTag; +import static seedu.duke.common.Utilities.parseCategoryTag; + /** * Represents an add command object that will execute the operations for Add command. @@ -81,6 +82,11 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws // Throws exception if there are no parameters checkParameterExist(splits); + assert (splits.length == TAG_LIMIT) : "Number of split inputs must equal 5."; + // At this stage, the number of split inputs will be == 5 as all the 5 tags must exist + // Any additional tags would throw an exception before this assertion. + + String description = ""; int amount = 0; String category = ""; @@ -137,20 +143,7 @@ private static void addTransactionByType(TransactionList transactions, String ty } } - /** - * Checks if there are missing parameter within the user input. - * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. - * - * @param splits The user input after the command word, split into a list for every space found. - * @throws AddTransactionMissingParameterException Extra tag exception. - */ - private static void checkParameterExist(String[] splits) throws AddTransactionMissingParameterException { - for (String split : splits) { - if (split.length() <= 2) { - throw new AddTransactionMissingParameterException(); - } - } - } + /** * Checks if the targeted tags exists in the split user inputs. diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 52f352bae..f1055c2d5 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -11,11 +11,13 @@ import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; +import static seedu.duke.common.Utilities.checkParameterExist; import static seedu.duke.common.Utilities.checkExtraTagExist; import static seedu.duke.common.Utilities.parseCategoryTag; import static seedu.duke.common.Utilities.parseDateTag; import static seedu.duke.common.Utilities.parseTypeTag; + /** * Represents a list command object that will execute the operations for List command. */ @@ -33,16 +35,13 @@ public class ListCommand extends Command { + "(Optional) TYPE - The type of transaction. Only \"income\" or \"expense\" is accepted." + LINE_SEPARATOR + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted." - + LINE_SEPARATOR - + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; + + LINE_SEPARATOR + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR - + COMMAND_DESCRIPTION + LINE_SEPARATOR - + COMMAND_USAGE + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + COMMAND_DESCRIPTION + + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO - + LINE_SEPARATOR; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; private static final int TAG_LIMIT = 3; private static final int MINIMUM_TAG_LENGTH = 2; @@ -73,18 +72,21 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws splits = input.split(" "); } - // Throws exception if there are more than three tags + checkExtraTagExist(splits, TAG_LIMIT); + // Throws exception if there are more than three tags + checkForInvalidTags(splits); + // Throws exception if there are any invalid tags + checkParameterExist(splits); + // Throws exception if there are tags with empty parameters + String category = ""; LocalDate date = null; String type = ""; for (String split : splits) { - // Throws exception if the tag cannot split - if (split.length() < MINIMUM_TAG_LENGTH) { - throw new InputTransactionInvalidTagException(); - } + assert split.length() >= MINIMUM_TAG_LENGTH; String tag = split.substring(0, 2); @@ -113,13 +115,12 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * List all or some transactions based on selection. * * @param transactions An instance of the TransactionList class. - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ - private static void listTransactions(TransactionList transactions, String type, - String category, LocalDate date) + private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date) throws InputTransactionUnknownTypeException { String transactionsList = transactions.listTransactions(type, category, date); if (transactionsList.isEmpty()) { @@ -130,6 +131,56 @@ private static void listTransactions(TransactionList transactions, String type, Ui.showTransactionsList(transactionsList, INFO_LIST.toString()); } + /** + * Checks if the tag inputs are valid. + * If the tags are invalid or if there are duplicated tags, exception would be thrown. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws InputTransactionInvalidTagException Invalid Tag exception + */ + private static void checkForInvalidTags(String[] splits) throws InputTransactionInvalidTagException { + // TODO: To add the tags into Command class instead + String[] tags = new String[]{"t/", "c/", "d/"}; + boolean isDuplicatedTypeTag = false; + boolean isDuplicatedCategoryTag = false; + boolean isDuplicatedDateTag = false; + + + for (String split : splits) { + if (split.length() < MINIMUM_TAG_LENGTH) { + throw new InputTransactionInvalidTagException(); + } + String tag = split.substring(0, 2); + switch (tag) { + case "t/": + if (isDuplicatedTypeTag) { + throw new InputTransactionInvalidTagException(); + } + isDuplicatedTypeTag = true; + break; + + case "c/": + if (isDuplicatedCategoryTag) { + throw new InputTransactionInvalidTagException(); + } + isDuplicatedCategoryTag = true; + break; + + case "d/": + if (isDuplicatedDateTag) { + throw new InputTransactionInvalidTagException(); + } + isDuplicatedDateTag = true; + break; + + default: + throw new InputTransactionInvalidTagException(); + + } + } + } + + @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/common/Utilities.java b/src/main/java/seedu/duke/common/Utilities.java index 18ffe3c75..cc96ab457 100644 --- a/src/main/java/seedu/duke/common/Utilities.java +++ b/src/main/java/seedu/duke/common/Utilities.java @@ -1,9 +1,10 @@ package seedu.duke.common; -import seedu.duke.exception.InputTransactionUnknownTypeException; import seedu.duke.exception.InputTransactionExtraTagException; -import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; +import seedu.duke.exception.InputTransactionInvalidCategoryException; +import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.AddTransactionMissingParameterException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -53,6 +54,22 @@ public static void checkExtraTagExist(String[] splits, int tagLimit) throws Inpu } } + + /** + * Checks if there are missing parameter within the user input. + * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws AddTransactionMissingParameterException Extra tag exception. + */ + public static void checkParameterExist(String[] splits) throws AddTransactionMissingParameterException { + for (String split : splits) { + if (split.length() <= 2) { + throw new AddTransactionMissingParameterException(); + } + } + } + /** * Checks if the parameter contains numeric characters. * diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index 4065cf090..b7ff0c66a 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -4,12 +4,45 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.exception.AddTransactionInvalidAmountException; +import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; import seedu.duke.exception.AddTransactionMissingParameterException; import static org.junit.jupiter.api.Assertions.assertThrows; public class AddCommandTest { + + + @Test + public void execute_InvalidCategory_ExpectedException() { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + AddCommand addCommand = new AddCommand("t/expense c/f0od a/20 d/13092022 i/yummy"); + assertThrows(InputTransactionInvalidCategoryException.class, () -> addCommand.execute(transactions, + ui, storage)); + } + + + @Test + public void execute_InvalidNegativeAmount_ExpectedException() { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + AddCommand addCommand = new AddCommand("t/expense c/food a/-20 d/13092022 i/isThisAnIncome"); + assertThrows(AddTransactionInvalidAmountException.class, () -> addCommand.execute(transactions, ui, storage)); + } + + @Test + public void execute_InvalidAmountInput_ExpectedException() { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + AddCommand addCommand = new AddCommand("t/income c/bonus a/one_dollar d/13092022 i/thank_you_boss"); + assertThrows(AddTransactionInvalidAmountException.class, () -> addCommand.execute(transactions, ui, storage)); + } + @Test public void execute_EmptyDate_ExpectedException() { TransactionList transactions = new TransactionList(); @@ -17,10 +50,8 @@ public void execute_EmptyDate_ExpectedException() { Storage storage = new Storage(); AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/ i/thank_you_boss"); - assertThrows( - AddTransactionMissingParameterException.class, - () -> addCommand.execute(transactions, ui, storage) - ); + assertThrows(AddTransactionMissingParameterException.class, () -> addCommand.execute(transactions, + ui, storage)); } @Test @@ -30,9 +61,6 @@ public void execute_InvalidDateFormat_ExpectedException() { Storage storage = new Storage(); AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss"); - assertThrows( - InputTransactionInvalidDateException.class, - () -> addCommand.execute(transactions, ui, storage) - ); + assertThrows(InputTransactionInvalidDateException.class, () -> addCommand.execute(transactions, ui, storage)); } } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 4837fc6db..53cf3a87c 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -244,6 +244,12 @@ ____________________________________________________________ Invalid tag(s) detected, please check your input! ____________________________________________________________ ____________________________________________________________ +Mandatory parameter(s) missing, please check your input! +____________________________________________________________ +____________________________________________________________ +Invalid tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ Type of transaction given is invalid, please check your input! ____________________________________________________________ ____________________________________________________________ @@ -266,7 +272,7 @@ Here are your transaction records: ____________________________________________________________ ____________________________________________________________ -Invalid date, please ensure your date format is correct! +Mandatory parameter(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: @@ -294,6 +300,9 @@ Here are your transaction records: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index dd3bd5f38..bfd222a01 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -10,6 +10,8 @@ add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary add t/expense c/transport a/1 d/02102022 i/bus_fare list / +list t/ +list T/ list t/Expense list t/expense list c/food @@ -21,6 +23,7 @@ stats s/categories find find bus_fare list +add / c/bonus a/10000000 d/03102022 i/thank_you_boss add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss add t/heheheh c/bonus a/1000000 d/03102022 i/thank_you_boss add t/income c/123 a/10000000 d/03102022 i/thank_you_boss From 6aaca5de2d37788fa81488c05b681d17369223b2 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Mon, 10 Oct 2022 19:32:29 +0800 Subject: [PATCH 120/416] Update files with changes from commit 22b1aa37cb5a2f81f0a0b9d7273f2572cc29febf --- .../java/seedu/duke/command/FindCommand.java | 16 ++++++---------- .../java/seedu/duke/command/StatsCommand.java | 11 +++-------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 0b09aaaf9..d2cd572bc 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -27,21 +27,15 @@ public class FindCommand extends Command { + "such as type, category, amount, date or description."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD - + LINE_SEPARATOR - + COMMAND_DESCRIPTION - + LINE_SEPARATOR - + COMMAND_USAGE - + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; protected String keywords; - public FindCommand() { - } - /** * Initialises the variables of the FindCommand class. * @@ -75,11 +69,13 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { try { // Checks the format of find to ensure that it contains keywords used in the search expression checkFindFormat(keywords); + assert !keywords.isBlank(); String transactionsList = transactions.findTransactions(keywords); if (transactionsList.isEmpty()) { ui.showInfoMessage(INFO_LIST_UNFILTERED.toString()); return; } + assert !transactionsList.isEmpty(); ui.showTransactionsList(transactionsList, INFO_LIST_FILTERED.toString()); } catch (FindTransactionMissingKeywordsException e) { ui.showErrorMessage(e.getMessage()); @@ -90,4 +86,4 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { public boolean isExit() { return false; } -} +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index ee03e7dca..17a8e2677 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -53,13 +53,11 @@ public StatsCommand(String input) { public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { /* Checks if userInput is in the correct input format by further parsing, - before adding entry to arraylist + before passing any tags to the filter for statistics. */ String[] splits = input.split(" "); checkTagsExist(splits); - String statsType = ""; - for (String split : splits) { String tag = split.substring(0, 2); String parameter = split.substring(2); @@ -67,10 +65,6 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws case "s/": listStatisticsByStatsType(parameter, transactions); break; - /*case "t/": - break; - case "n/": - break;*/ default: break; } @@ -93,6 +87,7 @@ private static void listStatisticsByStatsType(String statsType, TransactionList Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); return; } + assert !categoriesList.isEmpty(); Ui.showTransactionsList(categoriesList, INFO_STATS_CATEGORIES.toString()); break; default: @@ -139,4 +134,4 @@ private static boolean findMatchingTagFromInputs(String tag, String[] splits) { public boolean isExit() { return false; } -} +} \ No newline at end of file From 2a623ff26339a01ba672eb91ce3ff6852086bd0c Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Mon, 10 Oct 2022 19:43:02 +0800 Subject: [PATCH 121/416] Rename exception for missing parameters --- src/main/java/seedu/duke/command/AddCommand.java | 2 +- src/main/java/seedu/duke/common/Utilities.java | 8 ++++---- ...Exception.java => InputMissingParameterException.java} | 2 +- src/test/java/seedu/duke/command/AddCommandTest.java | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) rename src/main/java/seedu/duke/exception/{AddTransactionMissingParameterException.java => InputMissingParameterException.java} (82%) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 55bddc886..48cc925a9 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -84,7 +84,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws assert (splits.length == TAG_LIMIT) : "Number of split inputs must equal 5."; // At this stage, the number of split inputs will be == 5 as all the 5 tags must exist - // Any additional tags would throw an exception before this assertion. + // Any additional tags would throw an exception before this assertion String description = ""; diff --git a/src/main/java/seedu/duke/common/Utilities.java b/src/main/java/seedu/duke/common/Utilities.java index cc96ab457..9521e33c5 100644 --- a/src/main/java/seedu/duke/common/Utilities.java +++ b/src/main/java/seedu/duke/common/Utilities.java @@ -4,7 +4,7 @@ import seedu.duke.exception.InputTransactionInvalidDateException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionUnknownTypeException; -import seedu.duke.exception.AddTransactionMissingParameterException; +import seedu.duke.exception.InputMissingParameterException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -60,12 +60,12 @@ public static void checkExtraTagExist(String[] splits, int tagLimit) throws Inpu * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. * * @param splits The user input after the command word, split into a list for every space found. - * @throws AddTransactionMissingParameterException Extra tag exception. + * @throws InputMissingParameterException Extra tag exception. */ - public static void checkParameterExist(String[] splits) throws AddTransactionMissingParameterException { + public static void checkParameterExist(String[] splits) throws InputMissingParameterException { for (String split : splits) { if (split.length() <= 2) { - throw new AddTransactionMissingParameterException(); + throw new InputMissingParameterException(); } } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java b/src/main/java/seedu/duke/exception/InputMissingParameterException.java similarity index 82% rename from src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java rename to src/main/java/seedu/duke/exception/InputMissingParameterException.java index acc3fa569..d97b60e97 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java +++ b/src/main/java/seedu/duke/exception/InputMissingParameterException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddTransactionMissingParameterException extends MoolahException { +public class InputMissingParameterException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index b7ff0c66a..575c62411 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -7,7 +7,7 @@ import seedu.duke.exception.AddTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; -import seedu.duke.exception.AddTransactionMissingParameterException; +import seedu.duke.exception.InputMissingParameterException; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -50,7 +50,7 @@ public void execute_EmptyDate_ExpectedException() { Storage storage = new Storage(); AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/ i/thank_you_boss"); - assertThrows(AddTransactionMissingParameterException.class, () -> addCommand.execute(transactions, + assertThrows(InputMissingParameterException.class, () -> addCommand.execute(transactions, ui, storage)); } From 0cbcfe49f8195477c734741dec093f49e69080f1 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:09:46 +0800 Subject: [PATCH 122/416] Add exceptions and rename exceptions to be more generic Exceptions added to handle unsupported tag during parameter parsing, invalid index for out of bound transaction list index selected, and unknown help option to handle unknown parameter to "o/" tag. Rename 4 other exception files to a more generic name. --- ...ception.java => EmptyParameterException.java} | 4 ++-- ....java => EntryNumberNotNumericException.java} | 4 ++-- ...tion.java => InputDuplicateTagException.java} | 4 ++-- ...eption.java => InputMissingTagException.java} | 2 +- .../exception/InputUnsupportedTagException.java | 15 +++++++++++++++ .../duke/exception/InvalidIndexException.java | 15 +++++++++++++++ .../exception/UnknownHelpOptionException.java | 16 ++++++++++++++++ 7 files changed, 53 insertions(+), 7 deletions(-) rename src/main/java/seedu/duke/exception/{InputTransactionExtraTagException.java => EmptyParameterException.java} (67%) rename src/main/java/seedu/duke/exception/{AddDeleteInvalidIndexException.java => EntryNumberNotNumericException.java} (67%) rename src/main/java/seedu/duke/exception/{AddTransactionMissingParameterException.java => InputDuplicateTagException.java} (64%) rename src/main/java/seedu/duke/exception/{AddTransactionMissingTagException.java => InputMissingTagException.java} (82%) create mode 100644 src/main/java/seedu/duke/exception/InputUnsupportedTagException.java create mode 100644 src/main/java/seedu/duke/exception/InvalidIndexException.java create mode 100644 src/main/java/seedu/duke/exception/UnknownHelpOptionException.java diff --git a/src/main/java/seedu/duke/exception/InputTransactionExtraTagException.java b/src/main/java/seedu/duke/exception/EmptyParameterException.java similarity index 67% rename from src/main/java/seedu/duke/exception/InputTransactionExtraTagException.java rename to src/main/java/seedu/duke/exception/EmptyParameterException.java index 89f93da3c..128ca5bf0 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionExtraTagException.java +++ b/src/main/java/seedu/duke/exception/EmptyParameterException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class InputTransactionExtraTagException extends MoolahException { +public class EmptyParameterException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +10,6 @@ public class InputTransactionExtraTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_EXTRA_TAG.toString(); + return ErrorMessages.ERROR_INPUT_MISSING_PARAMETER.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java b/src/main/java/seedu/duke/exception/EntryNumberNotNumericException.java similarity index 67% rename from src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java rename to src/main/java/seedu/duke/exception/EntryNumberNotNumericException.java index 3c77a34b7..d7c4b49e0 100644 --- a/src/main/java/seedu/duke/exception/AddDeleteInvalidIndexException.java +++ b/src/main/java/seedu/duke/exception/EntryNumberNotNumericException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddDeleteInvalidIndexException extends MoolahException { +public class EntryNumberNotNumericException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +10,6 @@ public class AddDeleteInvalidIndexException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_INVALID_INDEX.toString(); + return ErrorMessages.ERROR_ENTRY_NUMBER_NOT_NUMERIC.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java b/src/main/java/seedu/duke/exception/InputDuplicateTagException.java similarity index 64% rename from src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java rename to src/main/java/seedu/duke/exception/InputDuplicateTagException.java index acc3fa569..7f8866a31 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionMissingParameterException.java +++ b/src/main/java/seedu/duke/exception/InputDuplicateTagException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddTransactionMissingParameterException extends MoolahException { +public class InputDuplicateTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +10,6 @@ public class AddTransactionMissingParameterException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_MISSING_PARAMETER.toString(); + return ErrorMessages.ERROR_INPUT_DUPLICATE_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java b/src/main/java/seedu/duke/exception/InputMissingTagException.java similarity index 82% rename from src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java rename to src/main/java/seedu/duke/exception/InputMissingTagException.java index a0f59a0d5..4983dfe44 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionMissingTagException.java +++ b/src/main/java/seedu/duke/exception/InputMissingTagException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class AddTransactionMissingTagException extends MoolahException { +public class InputMissingTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * diff --git a/src/main/java/seedu/duke/exception/InputUnsupportedTagException.java b/src/main/java/seedu/duke/exception/InputUnsupportedTagException.java new file mode 100644 index 000000000..715fef4e6 --- /dev/null +++ b/src/main/java/seedu/duke/exception/InputUnsupportedTagException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class InputUnsupportedTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_INPUT_UNSUPPORTED_TAG.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/InvalidIndexException.java b/src/main/java/seedu/duke/exception/InvalidIndexException.java new file mode 100644 index 000000000..f2a036af9 --- /dev/null +++ b/src/main/java/seedu/duke/exception/InvalidIndexException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class InvalidIndexException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_INVALID_INDEX.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/UnknownHelpOptionException.java b/src/main/java/seedu/duke/exception/UnknownHelpOptionException.java new file mode 100644 index 000000000..a97827fab --- /dev/null +++ b/src/main/java/seedu/duke/exception/UnknownHelpOptionException.java @@ -0,0 +1,16 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class UnknownHelpOptionException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_UNKNOWN_HELP_OPTION.toString(); + } + +} From ed46fbb920d7c0fa0b164b78ad57e9b15de4f837 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:11:25 +0800 Subject: [PATCH 123/416] Add error messages for the newly added exceptions Some error messages enum is also renamed to a more generic name. --- src/main/java/seedu/duke/common/ErrorMessages.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 158bb1238..937f9eb35 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -5,19 +5,21 @@ */ public enum ErrorMessages { ERROR_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), - ERROR_INPUT_EXTRA_TAG("Additional tag(s) detected, please check your input!"), + ERROR_INPUT_UNSUPPORTED_TAG("Not supported tag(s) detected, please check your input!"), + ERROR_INPUT_DUPLICATE_TAG("Duplicate tag(s) detected, please check your input!"), ERROR_INPUT_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), ERROR_INPUT_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_INPUT_INVALID_TAG("Invalid tag(s) detected, please check your input!"), ERROR_INPUT_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), - ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), + ERROR_ENTRY_NUMBER_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, " + "please ensure your amount is in positive numerals ($10000000 or less) only!"), - ERROR_ADD_COMMAND_INVALID_INDEX("Invalid index, please ensure your index is correct!"), - ERROR_ADD_COMMAND_MISSING_PARAMETER("Mandatory parameter(s) missing, please check your input!"), + ERROR_INVALID_INDEX("Invalid index, please ensure your index is correct!"), + ERROR_INPUT_MISSING_PARAMETER("Mandatory parameter(s) is empty, please check your input!"), ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), - ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"); + ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), + ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"); public final String message; From 0d8ae34f41d58d5af5f9315ae0087cd64270ee54 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:14:04 +0800 Subject: [PATCH 124/416] Add backward compatibility for find due to different command format --- .../java/seedu/duke/command/FindCommand.java | 3 --- .../java/seedu/duke/parser/CommandParser.java | 23 +++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 0b09aaaf9..1bd8f256f 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -39,9 +39,6 @@ public class FindCommand extends Command { protected String keywords; - public FindCommand() { - } - /** * Initialises the variables of the FindCommand class. * diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index 6fd6e3c63..4657685e6 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -14,6 +14,14 @@ import seedu.duke.exception.MoolahException; import seedu.duke.exception.InvalidCommandException; +/** + * Represents a parser that parses the user input into a Command object ready for execution + * + *

The CommandParser will check that the user input calls a valid command, + * then create the corresponding command object and call ParameterParser to further parse the parameter portion + * of the user input and set the parameters inside the command object, + * such that the command object will be ready for an execution. + */ public class CommandParser { private static final String EMPTY_STRING = ""; private static final String DELIMITER = " "; @@ -30,12 +38,18 @@ public static Command parse(String fullCommandInput) throws MoolahException { Command command = null; String[] inputTokens = splitInput(fullCommandInput); - assert inputTokens.length == 2; + assert inputTokens.length == 2; String commandWordInput = inputTokens[0]; String parametersInput = inputTokens[1]; // Parse the command word from user input - command = getCommand(commandWordInput); + command = getCommand(commandWordInput, parametersInput); + + + // TODO: To remove this if statement once a solution is found for managing parameter that allows space + if (command instanceof FindCommand) { + return command; + } // Parse the parameters from user input to set up the parameters for the command assert command != null; @@ -68,7 +82,8 @@ public static String[] splitInput(String fullCommandInput) { * @return Command object created * @throws InvalidCommandException Exception when the command word is not supported by the application. */ - private static Command getCommand(String commandWordInput) throws InvalidCommandException{ + private static Command getCommand(String commandWordInput, String parameterInput) throws InvalidCommandException { + // TODO: Remove parameter input once a solution is found for managing parameter that allows space Command command = null; switch (commandWordInput.toUpperCase()) { case HelpCommand.COMMAND_WORD: @@ -84,7 +99,7 @@ private static Command getCommand(String commandWordInput) throws InvalidCommand command = new ListCommand(); break; case FindCommand.COMMAND_WORD: - command = new FindCommand(); + command = new FindCommand(parameterInput); break; case StatsCommand.COMMAND_WORD: command = new StatsCommand(); From eb5d3d429b596eed3967d9e14d2cb80a8ca506cd Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:17:21 +0800 Subject: [PATCH 125/416] Declare static strings for command tags and transaction name --- src/main/java/seedu/duke/command/CommandTag.java | 12 ++++++++++++ .../java/seedu/duke/data/transaction/Expense.java | 1 + .../java/seedu/duke/data/transaction/Income.java | 2 ++ 3 files changed, 15 insertions(+) create mode 100644 src/main/java/seedu/duke/command/CommandTag.java diff --git a/src/main/java/seedu/duke/command/CommandTag.java b/src/main/java/seedu/duke/command/CommandTag.java new file mode 100644 index 000000000..49a74882f --- /dev/null +++ b/src/main/java/seedu/duke/command/CommandTag.java @@ -0,0 +1,12 @@ +package seedu.duke.command; + +public class CommandTag { + public static final String COMMAND_TAG_TRANSACTION_TYPE = "t/"; + public static final String COMMAND_TAG_TRANSACTION_CATEGORY = "c/"; + public static final String COMMAND_TAG_TRANSACTION_AMOUNT = "a/"; + public static final String COMMAND_TAG_TRANSACTION_DATE = "d/"; + public static final String COMMAND_TAG_TRANSACTION_DESCRIPTION = "i/"; + public static final String COMMAND_TAG_LIST_ENTRY_NUMBER = "e/"; + public static final String COMMAND_TAG_HELP_OPTION = "o/"; + public static final String COMMAND_TAG_STATISTICS_TYPE = "s/"; +} diff --git a/src/main/java/seedu/duke/data/transaction/Expense.java b/src/main/java/seedu/duke/data/transaction/Expense.java index 61cb556a6..0e107be86 100644 --- a/src/main/java/seedu/duke/data/transaction/Expense.java +++ b/src/main/java/seedu/duke/data/transaction/Expense.java @@ -7,6 +7,7 @@ * Records the amount, category and the date of spending. */ public class Expense extends Transaction { + public static final String TRANSACTION_NAME = "expense"; private static String ICON_EXPENSE = "[-]"; public Expense(String description, int amount, String category, LocalDate date) { diff --git a/src/main/java/seedu/duke/data/transaction/Income.java b/src/main/java/seedu/duke/data/transaction/Income.java index f7dff4a55..2df6b9891 100644 --- a/src/main/java/seedu/duke/data/transaction/Income.java +++ b/src/main/java/seedu/duke/data/transaction/Income.java @@ -7,8 +7,10 @@ * Records the amount, category and date of the income. */ public class Income extends Transaction { + public static final String TRANSACTION_NAME = "income"; private static final String ICON_INCOME = "[+]"; + public Income(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } From 727b613cbde483e4a46fdf8377ac6af1a3266562 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:21:22 +0800 Subject: [PATCH 126/416] Setup Command.java file to prepare for methods refactoring Two default methods, getMandatoryTags() and getOptionalTags() is declared for the commands that do not have mandatory tags or optional tags. The commands that have either of each will override the method to return a string array of their corresponding tags. A number of setters are also created to allow polymorphism. --- src/main/java/seedu/duke/command/Command.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 70142cd66..8b7eb0d18 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -5,6 +5,8 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +import java.time.LocalDate; + /** * Represents an object that can be inherited by other command objects. */ @@ -26,8 +28,56 @@ public abstract class Command { // The formatting information for the parameters used by the command public static String COMMAND_PARAMETERS_INFO; + /** + * Get the default mandatory tags of the command (no mandatory tag). + * To be overridden by subclasses which the command requires mandatory tag + * + * @return A string array containing all mandatory tags + */ + public String[] getMandatoryTags() { + String[] mandatoryTags = new String[0]; + return mandatoryTags; + } + + /** + * Get the default optional tags of the command (no optional tag). + * To be overridden by subclasses which the command requires optional tag + * + * @return A string array containing all optional tags + */ + public String[] getOptionalTags() { + String[] optionalTags = new String[0]; + return optionalTags; + } public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; public abstract boolean isExit(); + + // Methods below are to be overridden by the subclass whenever applicable + public void setType(String type) { + } + + public void setDescription(String description) { + } + + public void setAmount(int amount) { + } + + public void setCategory(String category) { + } + + public void setDate(LocalDate date) { + } + + public void setEntryNumber(int entryNumber) { + } + + public void setIsDetailedOption(boolean isDetailed) { + } + + public void setStatsType(String statsType) { + + } + } From 904c1928b1b0dee3703d5f1a8305e3c3fd1da626 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:28:12 +0800 Subject: [PATCH 127/416] Refactor all parsing related methods in Command package into parser Except for find commands, all the commands now do not have any parsing-related method inside Command package anymore. All the methods are now extracted into ParameterParser.java which will conduct the following checks and parse the parameter into a correct format for the command to execute: * Check that the input contains all mandatory tags of the command * Check that the input does not contain tags unsupported by the command * Check that the input does not contain the same tag more than once * Check that the input does not contain a tag without parameter * For each parameter, check that the format of the parameter is correct --- .../java/seedu/duke/command/AddCommand.java | 226 +++-------- .../seedu/duke/command/DeleteCommand.java | 40 +- .../java/seedu/duke/command/EditCommand.java | 86 +++- .../java/seedu/duke/command/HelpCommand.java | 25 +- .../java/seedu/duke/command/ListCommand.java | 99 +++-- .../java/seedu/duke/command/StatsCommand.java | 81 +--- .../seedu/duke/parser/ParameterParser.java | 377 +++++++++++++++++- 7 files changed, 612 insertions(+), 322 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 762e5d4c1..247b8e57a 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -4,22 +4,20 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Expense; +import seedu.duke.data.transaction.Income; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.AddTransactionInvalidAmountException; -import seedu.duke.exception.AddTransactionMissingParameterException; -import seedu.duke.exception.AddTransactionMissingTagException; -import seedu.duke.exception.InputTransactionInvalidTagException; import seedu.duke.exception.InputTransactionUnknownTypeException; import java.time.LocalDate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; -import static seedu.duke.parser.ParameterParser.checkExtraTagExist; -import static seedu.duke.parser.ParameterParser.parseCategoryTag; -import static seedu.duke.parser.ParameterParser.parseDateTag; /** * Represents an add command object that will execute the operations for Add command. @@ -43,189 +41,97 @@ public class AddCommand extends Command { + "AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted." + LINE_SEPARATOR + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + LINE_SEPARATOR + "DESCRIPTION: More information regarding the transaction, written without any space."; - - // Basic help description public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + COMMAND_DESCRIPTION + "\n" + COMMAND_USAGE + "\n"; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; - private static final int TAG_LIMIT = 5; + private String type; + private String description; + private int amount; + private String category; + private LocalDate date; - private String input; + public AddCommand() { + } - public AddCommand(String input) { - this.input = input; + public AddCommand(String type, String description, int amount, String category, LocalDate date) { + this.type = type; + this.description = description; + this.amount = amount; + this.category = category; + this.date = date; } /** - * Executes the "add" command. Check and parse the necessary parameters before adding transaction. + * Gets the mandatory tags of the command. * - * @param ui An instance of the Ui class. - * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @return A string array containing all mandatory tags */ @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - /* - Checks if userInput is in the correct input format by further parsing, - before adding entry to arraylist - */ - String[] splits = input.split(" "); - - // Throws exception if there are more than five tags - checkExtraTagExist(splits, TAG_LIMIT); - // Throws exception if there are no mandatory tags - checkTagsExist(splits); - // Throws exception if there are no parameters - checkParameterExist(splits); - - String description = ""; - int amount = 0; - String category = ""; - LocalDate date = null; - String type = ""; - - for (String split : splits) { - String tag = split.substring(0, 2); - String parameter = split.substring(2); - switch (tag) { - case "t/": - type = parameter; - break; - case "c/": - category = parseCategoryTag(parameter); - break; - case "a/": - amount = parseAmountTag(parameter); - break; - case "d/": - date = parseDateTag(parameter); - break; - case "i/": - description = parameter; - break; - default: - throw new InputTransactionInvalidTagException(); - } - } - assert date != null; - addTransactionByType(transactions, type, description, amount, category, date); + public String[] getMandatoryTags() { + String[] mandatoryTags = new String[]{ + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_AMOUNT, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_TRANSACTION_DESCRIPTION + }; + return mandatoryTags; } @Override - public boolean isExit() { - return false; + public void setType(String type) { + this.type = type; } - private static void addTransactionByType(TransactionList transactions, String type, String description, int amount, - String category, LocalDate date) - throws InputTransactionUnknownTypeException { - - switch (type) { - case "expense": - String expense = transactions.addExpense(description, amount, category, date); - Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); - break; - case "income": - String income = transactions.addIncome(description, amount, category, date); - Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); - break; - default: - throw new InputTransactionUnknownTypeException(); - } + @Override + public void setDescription(String description) { + this.description = description; } - /** - * Checks if there are missing parameter within the user input. - * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. - * - * @param splits The user input after the command word, split into a list for every space found. - * @throws AddTransactionMissingParameterException Extra tag exception. - */ - private static void checkParameterExist(String[] splits) throws AddTransactionMissingParameterException { - for (String split : splits) { - if (split.length() <= 2) { - throw new AddTransactionMissingParameterException(); - } - } + @Override + public void setAmount(int amount) { + this.amount = amount; } - /** - * Checks if the targeted tags exists in the split user inputs. - * - * @param splits The user input after the command word, split into a list for every space found. - * @throws AddTransactionMissingTagException Missing tag exception. - */ - private static void checkTagsExist(String[] splits) throws AddTransactionMissingTagException { - // TODO: To add the tags into Command class instead - String[] tags = new String[]{"t/", "c/", "a/", "d/", "i/"}; - for (String tag : tags) { - boolean found = findMatchingTagFromInputs(tag, splits); - if (!found) { - throw new AddTransactionMissingTagException(); - } - } + @Override + public void setCategory(String category) { + this.category = category; } - /** - * Checks if the parameter contains alphabetical characters. - * - * @param parameter The user input after the user tag. - * @return true if there are alphabetical characters within the parameter. - */ - public static boolean containAlphabet(String parameter) { - char[] characters = parameter.toCharArray(); - for (char character : characters) { - if (Character.isAlphabetic(character)) { - return true; - } - } - return false; + @Override + public void setDate(LocalDate date) { + this.date = date; } /** - * Parses the user parameter input for the amount and returns it. + * Executes the "add" command. Check and parse the necessary parameters before adding transaction. * - * @param parameter The user input after the user tag. - * @return The amount integer if no exceptions are thrown. - * @throws AddTransactionInvalidAmountException Invalid amount format exception. + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. */ - private static int parseAmountTag(String parameter) throws MoolahException { - Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); - Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); - try { - if (containAlphabet(parameter) || hasSpecialSymbols.find()) { - throw new AddTransactionInvalidAmountException(); - } - int amount = Integer.parseInt(parameter); - if (amount < 0 || amount > 10000000) { - throw new AddTransactionInvalidAmountException(); - } - return amount; - - } catch (NumberFormatException e) { - throw new AddTransactionInvalidAmountException(); + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + assert date != null; + switch (type) { + case Expense.TRANSACTION_NAME: + String expense = transactions.addExpense(description, amount, category, date); + Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); + break; + case Income.TRANSACTION_NAME: + String income = transactions.addIncome(description, amount, category, date); + Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); + break; + default: + throw new InputTransactionUnknownTypeException(); } } - /** - * Returns a boolean value on whether a tag can be found among the split user inputs. - * - * @param tag A specific tag used to locate the command parameter. - * @param splits The user input after the command word, split into a list for every space found. - * @return Whether the tag is found within the split inputs. - */ - private static boolean findMatchingTagFromInputs(String tag, String[] splits) { - boolean found = false; - for (String split : splits) { - if (split.startsWith(tag)) { - found = true; - break; - } - } - return found; + @Override + public boolean isExit() { + return false; } - } diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index f83cea118..1d1980e99 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -3,10 +3,10 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.AddDeleteInvalidIndexException; +import seedu.duke.exception.InvalidIndexException; import seedu.duke.exception.MoolahException; -import static seedu.duke.common.ErrorMessages.ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC; +import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; import static seedu.duke.common.InfoMessages.INFO_DELETE; /** @@ -37,18 +37,34 @@ public class DeleteCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - private String input; + // The optional tags that may exist in the user input + + + private int entryNumber; + + /** + * Initialises the variables of the DeleteCommand class. + */ public DeleteCommand() { } /** - * Initialises the variables of the DeleteCommand class. + * Gets the mandatory tags of the command. * - * @param input A string that represents the index of the task. + * @return A string array containing all mandatory tags */ - public DeleteCommand(String input) { - this.input = input; + @Override + public String[] getMandatoryTags() { + // The mandatory tags that must exist in the user input + String[] mandatoryTags = new String[]{COMMAND_TAG_LIST_ENTRY_NUMBER}; + + return mandatoryTags; + } + + @Override + public void setEntryNumber(int entryNumber) { + this.entryNumber = entryNumber; } /** @@ -65,15 +81,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws before adding entry to arraylist */ boolean isInputValid = true; - int index; + int index = entryNumber; int numberOfTransactions; numberOfTransactions = transactions.size(); - try { - index = Integer.parseInt(input); - } catch (NumberFormatException e) { - Ui.showErrorMessage(ERROR_ADD_COMMAND_AMOUNT_NOT_NUMERIC.toString()); - return; - } if ((index > numberOfTransactions) || (index <= 0)) { isInputValid = false; } @@ -81,7 +91,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws String transaction = TransactionList.deleteTransaction(transactions, index); Ui.showTransactionAction(INFO_DELETE.toString(), transaction); } else { - throw new AddDeleteInvalidIndexException(); + throw new InvalidIndexException(); } } diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 76d9369aa..8f02954b2 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -4,6 +4,15 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import java.time.LocalDate; + +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; + /** * Represents an edit command object that will execute the operations for Edit command. */ @@ -44,13 +53,72 @@ public class EditCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - private String input; + private int entryNumber; + private String type; + private String description; + private int amount; + private String category; + private LocalDate date; public EditCommand() { } - public EditCommand(String input) { - this.input = input; + /** + * Gets the mandatory tags of the command. + * + * @return A string array containing all mandatory tags + */ + @Override + public String[] getMandatoryTags() { + String[] mandatoryTags = new String[]{COMMAND_TAG_LIST_ENTRY_NUMBER}; + return mandatoryTags; + } + + /** + * Gets the optional tags of the command. + * + * @return A string array containing all optional tags + */ + @Override + public String[] getOptionalTags() { + String[] optionalTags = new String[]{ + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_AMOUNT, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_TRANSACTION_DESCRIPTION + }; + return optionalTags; + } + + @Override + public void setEntryNumber(int entryNumber) { + this.entryNumber = entryNumber; + } + + @Override + public void setType(String type) { + this.type = type; + } + + @Override + public void setDescription(String description) { + this.description = description; + } + + @Override + public void setAmount(int amount) { + this.amount = amount; + } + + @Override + public void setCategory(String category) { + this.category = category; + } + + @Override + public void setDate(LocalDate date) { + this.date = date; } /** @@ -62,10 +130,14 @@ public EditCommand(String input) { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { - /* - Checks if userInput is in the correct input format by further parsing, - before the entry in the arraylist - */ + // Dummy output for test + System.out.println(String.format("Entry number: %d\nType: %s\nDesc: %s\n$: %d\nCat: %s, Date: %s", + entryNumber, + type, + description, + amount, + category, + date.toString())); } @Override diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 0c93a60bc..2fffe6df4 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -4,6 +4,8 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; + /** * Represents a help command object that will execute the operations for Help command. */ @@ -33,13 +35,28 @@ public class HelpCommand extends Command { // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; - private String input; + private boolean isDetailed; public HelpCommand() { + this.isDetailed = false; + } + + @Override + public void setIsDetailedOption(boolean isDetailed) { + this.isDetailed = isDetailed; } - public HelpCommand(String input) { - this.input = input; + /** + * Gets the optional tags of the command. + * + * @return A string array containing all optional tags + */ + @Override + public String[] getOptionalTags() { + String[] optionalTags = new String[]{ + COMMAND_TAG_HELP_OPTION + }; + return optionalTags; } /** @@ -52,7 +69,7 @@ public HelpCommand(String input) { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { String helpMessage = ""; - if (input.contains("o/detailed")) { + if (isDetailed) { helpMessage = generateDetailedHelp(); } else { helpMessage = generateBasicHelp(); diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 6e51d76e3..0cd23362d 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -3,18 +3,16 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.InputTransactionInvalidTagException; import seedu.duke.exception.InputTransactionUnknownTypeException; import seedu.duke.exception.MoolahException; import java.time.LocalDate; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; -import static seedu.duke.parser.ParameterParser.checkExtraTagExist; -import static seedu.duke.parser.ParameterParser.parseCategoryTag; -import static seedu.duke.parser.ParameterParser.parseDateTag; -import static seedu.duke.parser.ParameterParser.parseTypeTag; /** * Represents a list command object that will execute the operations for List command. @@ -49,10 +47,44 @@ public class ListCommand extends Command { private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; - private String input; + private String category; + private LocalDate date; + private String type; - public ListCommand(String input) { - this.input = input; + public ListCommand() { + category = ""; + date = null; + type = ""; + } + + /** + * Gets the optional tags of the command. + * + * @return A string array containing all optional tags + */ + @Override + public String[] getOptionalTags() { + String[] optionalTags = new String[]{ + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_DATE + }; + return optionalTags; + } + + @Override + public void setType(String type) { + this.type = type; + } + + @Override + public void setCategory(String category) { + this.category = category; + } + + @Override + public void setDate(LocalDate date) { + this.date = date; } /** @@ -64,48 +96,7 @@ public ListCommand(String input) { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - /* - Checks if userInput is in the correct input format by further parsing, - before passing any tags to the filter for transaction list. - */ - String[] splits = {}; - if (!input.isEmpty()) { - splits = input.split(" "); - } - - // Throws exception if there are more than three tags - checkExtraTagExist(splits, TAG_LIMIT); - - String category = ""; - LocalDate date = null; - String type = ""; - - for (String split : splits) { - // Throws exception if the tag cannot split - if (split.length() < MINIMUM_TAG_LENGTH) { - throw new InputTransactionInvalidTagException(); - } - assert split.length() >= MINIMUM_TAG_LENGTH; - - String tag = split.substring(0, 2); - String parameter = split.substring(2); - switch (tag) { - case "t/": - type = parseTypeTag(parameter); - assert type.equals(CLASS_TYPE_EXPENSE) || type.equals(CLASS_TYPE_INCOME); - break; - case "c/": - category = parseCategoryTag(parameter); - break; - case "d/": - date = parseDateTag(parameter); - assert date != null; - break; - default: - // Throws exception if the tag is invalid - throw new InputTransactionInvalidTagException(); - } - } + // Pass the tags to the filter for transaction list. listTransactions(transactions, type, category, date); } @@ -113,13 +104,13 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * List all or some transactions based on selection. * * @param transactions An instance of the TransactionList class. - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ private static void listTransactions(TransactionList transactions, String type, - String category, LocalDate date) + String category, LocalDate date) throws InputTransactionUnknownTypeException { String transactionsList = transactions.listTransactions(type, category, date); if (transactionsList.isEmpty()) { diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index ee03e7dca..73d94d51f 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -5,9 +5,9 @@ import seedu.duke.data.CategoryList; import seedu.duke.data.TransactionList; import seedu.duke.exception.ListStatisticsInvalidStatsTypeException; -import seedu.duke.exception.ListStatisticsMissingTagException; import seedu.duke.exception.MoolahException; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; @@ -36,10 +36,20 @@ public class StatsCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - private String input; + private String statsType; - public StatsCommand(String input) { - this.input = input; + public StatsCommand() { + } + + /** + * Gets the mandatory tags of the command. + * + * @return A string array containing all mandatory tags + */ + @Override + public String[] getMandatoryTags() { + String[] mandatoryTags = new String[]{COMMAND_TAG_STATISTICS_TYPE}; + return mandatoryTags; } /** @@ -51,30 +61,12 @@ public StatsCommand(String input) { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - /* - Checks if userInput is in the correct input format by further parsing, - before adding entry to arraylist - */ - String[] splits = input.split(" "); - checkTagsExist(splits); - - String statsType = ""; + listStatisticsByStatsType(statsType, transactions); + } - for (String split : splits) { - String tag = split.substring(0, 2); - String parameter = split.substring(2); - switch (tag) { - case "s/": - listStatisticsByStatsType(parameter, transactions); - break; - /*case "t/": - break; - case "n/": - break;*/ - default: - break; - } - } + @Override + public void setStatsType(String statsType) { + this.statsType = statsType; } private static void listStatisticsByStatsType(String statsType, TransactionList transactions) @@ -100,41 +92,6 @@ private static void listStatisticsByStatsType(String statsType, TransactionList } } - /** - * Checks if the targeted tags exists in the split user inputs. - * - * @param splits The user input after the command word, split into a list for every space found. - * @throws ListStatisticsMissingTagException Missing tag exception. - */ - private static void checkTagsExist(String[] splits) throws ListStatisticsMissingTagException { - // TODO: To add the tags into Command class instead - String[] tags = new String[]{"s/"}; - for (String tag : tags) { - boolean found = findMatchingTagFromInputs(tag, splits); - if (!found) { - throw new ListStatisticsMissingTagException(); - } - } - } - - /** - * Returns a boolean value on whether a tag can be found among the split user inputs. - * - * @param tag A specific tag used to locate the command parameter. - * @param splits The user input after the command word, split into a list for every space found. - * @return Whether the tag is found within the split inputs. - */ - private static boolean findMatchingTagFromInputs(String tag, String[] splits) { - boolean found = false; - for (String split : splits) { - if (split.startsWith(tag)) { - found = true; - break; - } - } - return found; - } - @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 16891c4e1..b299b7106 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -1,66 +1,264 @@ package seedu.duke.parser; import seedu.duke.command.Command; -import seedu.duke.exception.*; +import seedu.duke.command.ListCommand; +import seedu.duke.data.transaction.Expense; +import seedu.duke.data.transaction.Income; +import seedu.duke.exception.InputDuplicateTagException; +import seedu.duke.exception.InputMissingTagException; +import seedu.duke.exception.InputUnsupportedTagException; +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.EmptyParameterException; +import seedu.duke.exception.UnknownHelpOptionException; +import seedu.duke.exception.InputTransactionInvalidDateException; +import seedu.duke.exception.AddTransactionInvalidAmountException; +import seedu.duke.exception.InputTransactionInvalidCategoryException; +import seedu.duke.exception.EntryNumberNotNumericException; +import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.ListStatisticsInvalidStatsTypeException; + import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; +import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; +import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; + import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; /** - * Provides a set of extra utilities for processing of different inputs in various commands. + * Parses the parameter portion of the user input and set the parameters into the Command object + * + *

The ParameterParser will check that the parameter input portion contains only the supported tags, + * for each of the supported tag, parses the parameter into the valid form required by the Command object + * and to store the parsed value inside the command object. */ public class ParameterParser { + private static final String EMPTY_STRING = ""; + private static final String DELIMITER = " "; + private static final int SPLIT_POSITION = 2; private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; /** - * To parse the parameters input into proper parameters of the command object + * To parse the parameters input into proper parameters of the command object. + * + *

The parameters will go through the following checks during the parsing: + * 1. Check that the user input contains all mandatory tags of the command + * 2. Check that the user input does not contain tags not supported by the command + * 3. Check that the user input does not contain a same tag more than once + * 4. Check that the user input does not contain a tag without parameter + * 5. For each parameter, check that the format of the parameter is correct * * @param command A command object created based on the command word given by user. * @throws MoolahException Any command input exceptions captured by Moolah Manager. */ public static void parse(Command command, String parametersInput) throws MoolahException { assert command != null; + String[] splits = parametersInput.split(DELIMITER); + + // Might throw InputMissingTagException + checkMandatoryTagsExist(command, splits); + + /* + An empty parameterInput suggests that no tag is provided by user. Since it passes the check + for mandatory check, it also means that the command does not have a mandatory tag. Therefore, + there is no more need to further check and set the parameters for empty parameters input. + */ + if (!parametersInput.isEmpty()) { + + // Might throw InputUnsupportedTagException + checkUnsupportedTagsNotExist(command, splits); + // Might throw InputDuplicateTagException + checkDuplicateTagsNotExist(splits); + // Might throw InputMissingParameterException + checkParameterNotEmpty(splits); + + // The parameters input contains only the supported tags. + // For each tag, check that the parameter is correct and set it inside the command. + setCommand(command, splits); + } } + /** + * Checks if all the mandatory tags exists in the split user inputs. + * + * @param command A command object created based on the command word given by user. + * @param splits The user input after the command word, split into a list for every space found. + * @throws InputMissingTagException Missing mandatory tag exception. + */ + private static void checkMandatoryTagsExist(Command command, String[] splits) throws InputMissingTagException { + String[] tags = command.getMandatoryTags(); + for (String tag : tags) { + boolean found = findMatchingTagAmongInputs(tag, splits); + if (!found) { + throw new InputMissingTagException(); + } + } + } + + /** + * Checks if the split user inputs contains any unsupported tag. + * + * @param command A command object created based on the command word given by user. + * @param splits The user input after the command word, split into a list for every space found. + * @throws InputUnsupportedTagException Extra tag exception. + */ + private static void checkUnsupportedTagsNotExist(Command command, String[] splits) + throws InputUnsupportedTagException { + String[] mandatoryTags = command.getMandatoryTags(); + String[] optionalTags = command.getOptionalTags(); + + for (String split : splits) { + if (split.length() < 2) { + // None of the tag is shorter than two characters + throw new InputUnsupportedTagException(); + } + boolean hasFoundAmongMandatoryTag = findIfParameterTagAmongTags(split, mandatoryTags); + boolean hasFoundAmongOptionalTag = findIfParameterTagAmongTags(split, optionalTags); + if (hasFoundAmongMandatoryTag || hasFoundAmongOptionalTag) { + continue; + } + + // Found a tag entered by the user but does not exist in the supported tag for the command + throw new InputUnsupportedTagException(); + } + } /** - * Checks if there are extra tag(s) within the user input. + * Checks if the split user inputs contains a tag multiple times. * * @param splits The user input after the command word, split into a list for every space found. - * @throws InputTransactionExtraTagException Extra tag exception. + * @throws InputDuplicateTagException Extra tag exception. */ - public static void checkExtraTagExist(String[] splits, int tagLimit) throws InputTransactionExtraTagException { - int countNumberOfTags = 0; + private static void checkDuplicateTagsNotExist(String[] splits) throws InputDuplicateTagException { + HashMap tagOccurenceMap = new HashMap<>(); for (String split : splits) { - countNumberOfTags += 1; + String tag = split.substring(0, SPLIT_POSITION); + + // The duplicated tag can be found in the hash map + if (tagOccurenceMap.containsKey(tag)) { + throw new InputDuplicateTagException(); + } + tagOccurenceMap.put(tag, 1); } - if (countNumberOfTags > tagLimit) { - throw new InputTransactionExtraTagException(); + } + + /** + * Checks if there are missing parameter within the user input. + * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. + * + * @param splits The user input after the command word, split into a list for every space found. + * @throws EmptyParameterException Extra tag exception. + * @author chinhan99 + */ + private static void checkParameterNotEmpty(String[] splits) throws EmptyParameterException { + for (String split : splits) { + if (split.length() <= 2) { + throw new EmptyParameterException(); + } } } /** - * Checks if the parameter contains numeric characters. + * Returns a boolean value on whether a tag can be found among the split user inputs. * - * @param parameter The user input after the user tag. - * @return A boolean value indicating whether there are numeric characters within the parameter. + * @param tag A specific tag used to locate the command parameter. + * @param splits The user input after the command word, split into a list for every space found. + * @return Whether the tag is found within the split inputs. */ - public static boolean containNumeric(String parameter) { - char[] characters = parameter.toCharArray(); - for (char character : characters) { - if (Character.isDigit(character)) { - return true; + private static boolean findMatchingTagAmongInputs(String tag, String[] splits) { + boolean hasFound = false; + for (String split : splits) { + hasFound = split.startsWith(tag); + if (hasFound) { + break; } } - return false; + return hasFound; + } + + /** + * Returns a boolean value on whether a tag can be found among the split user inputs. + * + * @param parameter A user parameter input entered after the command word. + * @param tags An array of tags. + * @return Whether the tag is found within the split inputs. + */ + private static boolean findIfParameterTagAmongTags(String parameter, String[] tags) { + boolean hasFound = false; + + String parameterTag = parameter.substring(0, SPLIT_POSITION); + for (String tag : tags) { + hasFound = tag.equals(parameterTag); + if (hasFound) { + break; + } + } + return hasFound; + } + + /** + * For each split parameters, split it into tag and parameter, then check and set the parameters into the Command. + * + * @param command A command object created based on the command word given by user. + * @param splits The user input after the command word, split into a list for every space found. + * @throws MoolahException Any command input exceptions captured by Moolah Manager. + */ + private static void setCommand(Command command, String[] splits) throws MoolahException { + for (String split : splits) { + String tag = split.substring(0, SPLIT_POSITION); + String parameter = split.substring(SPLIT_POSITION); + setParameter(command, tag, parameter); + } + } + + private static void setParameter(Command command, String tag, String parameter) throws MoolahException { + switch (tag) { + case COMMAND_TAG_TRANSACTION_TYPE: + // TODO: To standardise the format for transaction type for add and list + if (command instanceof ListCommand) { + command.setType(parseTypeTagForListing(parameter)); + } else { + command.setType(parseTypeTagForAdding(parameter)); + } + break; + case COMMAND_TAG_TRANSACTION_CATEGORY: + command.setCategory(parseCategoryTag(parameter)); + break; + case COMMAND_TAG_TRANSACTION_AMOUNT: + command.setAmount(parseAmountTag(parameter)); + break; + case COMMAND_TAG_TRANSACTION_DATE: + command.setDate(parseDateTag(parameter)); + break; + case COMMAND_TAG_TRANSACTION_DESCRIPTION: + command.setDescription(parameter); + break; + case COMMAND_TAG_LIST_ENTRY_NUMBER: + command.setEntryNumber(parseEntryTag(parameter)); + break; + case COMMAND_TAG_HELP_OPTION: + command.setIsDetailedOption(parseHelpOptionTag(parameter)); + break; + case COMMAND_TAG_STATISTICS_TYPE: + command.setStatsType(parseStatsTypeTag(parameter)); + break; + default: + throw new InputMissingTagException(); + } } /** @@ -69,8 +267,9 @@ public static boolean containNumeric(String parameter) { * @param parameter The user input after the user tag. * @return The class type if no exceptions are thrown. * @throws InputTransactionUnknownTypeException Invalid type format exception. + * @author chydarren */ - public static String parseTypeTag(String parameter) throws InputTransactionUnknownTypeException { + public static String parseTypeTagForListing(String parameter) throws InputTransactionUnknownTypeException { switch (parameter) { case "expense": return CLASS_TYPE_EXPENSE; @@ -81,12 +280,32 @@ public static String parseTypeTag(String parameter) throws InputTransactionUnkno } } + /** + * Check if the type parameter is a valid transaction type and returns the parameter if it is valid. + * + * @param parameter The user input after the user tag. + * @return The user input after the user tag. + * @throws InputTransactionUnknownTypeException Invalid type format exception. + * @author wcwy + */ + public static String parseTypeTagForAdding(String parameter) throws InputTransactionUnknownTypeException { + boolean isExpense = parameter.equals(Expense.TRANSACTION_NAME); + boolean isIncome = parameter.equals(Income.TRANSACTION_NAME); + + if (!isExpense && !isIncome) { + throw new InputTransactionUnknownTypeException(); + } + + return parameter; + } + /** * Parses the user parameter input for the description and returns it. * * @param parameter The user input after the user tag. * @return The category parameter if no exceptions are thrown. * @throws InputTransactionInvalidCategoryException Invalid category format exception. + * @author chinhan99 */ public static String parseCategoryTag(String parameter) throws InputTransactionInvalidCategoryException { Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); @@ -97,12 +316,39 @@ public static String parseCategoryTag(String parameter) throws InputTransactionI return parameter; } + /** + * Parses the user parameter input for the amount and returns it. + * + * @param parameter The user input after the user tag. + * @return The amount integer if no exceptions are thrown. + * @throws AddTransactionInvalidAmountException Invalid amount format exception. + * @author chinhan99 + */ + private static int parseAmountTag(String parameter) throws AddTransactionInvalidAmountException { + Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); + Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); + try { + if (containAlphabet(parameter) || hasSpecialSymbols.find()) { + throw new AddTransactionInvalidAmountException(); + } + int amount = Integer.parseInt(parameter); + if (amount < 0 || amount > 10000000) { + throw new AddTransactionInvalidAmountException(); + } + return amount; + + } catch (NumberFormatException e) { + throw new AddTransactionInvalidAmountException(); + } + } + /** * Parses the user parameter input for date into a LocalDate object and returns it. * * @param parameter The user input after the user tag. * @return The LocalDate object parsed from user input given. * @throws InputTransactionInvalidDateException Invalid date format exception. + * @author wcwy */ public static LocalDate parseDateTag(String parameter) throws InputTransactionInvalidDateException { try { @@ -113,4 +359,95 @@ public static LocalDate parseDateTag(String parameter) throws InputTransactionIn throw new InputTransactionInvalidDateException(); } } + + /** + * Parses the user parameter input for entry number into an integer and returns it. + * + * @param parameter The user input after the user tag. + * @return The valid integer for list index parsed from user input given. + * @throws MoolahException Either invalid index exception or amount not numeric exception. + * @author brian-vb + */ + public static int parseEntryTag(String parameter) throws MoolahException { + int index; + try { + index = Integer.parseInt(parameter); + } catch (NumberFormatException e) { + throw new EntryNumberNotNumericException(); + } + + return index; + } + + /** + * Return a boolean value indicating if the option selected by user is "detailed". + * + * @param parameter The user input after the user tag. + * @return A boolean value indicating if the option selected by user is "detailed" + * @throws UnknownHelpOptionException An unknown help option exception. + * @author wcwy + */ + public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOptionException { + boolean isValidHelpOption = parameter.equals("detailed"); + if (isValidHelpOption) { + return true; + } else { + throw new UnknownHelpOptionException(); + } + } + + /** + * Check if the type parameter is a valid statistic type and returns the parameter if it is valid. + * + * @param parameter The user input after the user tag. + * @return The statistic type. + * @throws ListStatisticsInvalidStatsTypeException Invalid statistic type exception. + * @author brian-vb + */ + public static String parseStatsTypeTag(String parameter) throws ListStatisticsInvalidStatsTypeException { + String statsType = EMPTY_STRING; + switch (parameter) { + case "categories": + statsType = "categories"; + break; + default: + throw new ListStatisticsInvalidStatsTypeException(); + } + return statsType; + } + + + /** + * Checks if the parameter contains numeric characters. + * + * @param parameter The user input after the user tag. + * @return A boolean value indicating whether there are numeric characters within the parameter. + * @author chinhan99 + */ + public static boolean containNumeric(String parameter) { + char[] characters = parameter.toCharArray(); + for (char character : characters) { + if (Character.isDigit(character)) { + return true; + } + } + return false; + } + + /** + * Checks if the parameter contains alphabetical characters. + * + * @param parameter The user input after the user tag. + * @return true if there are alphabetical characters within the parameter. + * @author chinhan99 + */ + public static boolean containAlphabet(String parameter) { + char[] characters = parameter.toCharArray(); + for (char character : characters) { + if (Character.isAlphabetic(character)) { + return true; + } + } + return false; + } } From 087f125f0d50ac8a7a2c114f4e42b97c96616af2 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:28:40 +0800 Subject: [PATCH 128/416] Fix delete command index error --- src/main/java/seedu/duke/data/TransactionList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 5e00e8086..1bbe8ed37 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -29,7 +29,7 @@ public Transaction getEntry(int index) { public static String deleteTransaction(TransactionList input, int index) { Transaction transaction = input.getEntry(index - 1); - transactions.remove(index); + transactions.remove(index - 1); return transaction.toString(); } From fcb4f7dbbfc49ad1cb2f84f072ca5d611914ef54 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:29:33 +0800 Subject: [PATCH 129/416] Move testing method from AddCommandTest to ParameterParserTest As they were affected by the parsing-related methods refactoring. --- .../seedu/duke/command/AddCommandTest.java | 26 +------------- .../duke/parser/ParameterParserTest.java | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 25 deletions(-) create mode 100644 src/test/java/seedu/duke/parser/ParameterParserTest.java diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index 4065cf090..639dd3371 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -5,34 +5,10 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.exception.InputTransactionInvalidDateException; -import seedu.duke.exception.AddTransactionMissingParameterException; +import seedu.duke.exception.EmptyParameterException; import static org.junit.jupiter.api.Assertions.assertThrows; public class AddCommandTest { - @Test - public void execute_EmptyDate_ExpectedException() { - TransactionList transactions = new TransactionList(); - Ui ui = new Ui(); - Storage storage = new Storage(); - AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/ i/thank_you_boss"); - assertThrows( - AddTransactionMissingParameterException.class, - () -> addCommand.execute(transactions, ui, storage) - ); - } - - @Test - public void execute_InvalidDateFormat_ExpectedException() { - TransactionList transactions = new TransactionList(); - Ui ui = new Ui(); - Storage storage = new Storage(); - AddCommand addCommand = new AddCommand("t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss"); - - assertThrows( - InputTransactionInvalidDateException.class, - () -> addCommand.execute(transactions, ui, storage) - ); - } } diff --git a/src/test/java/seedu/duke/parser/ParameterParserTest.java b/src/test/java/seedu/duke/parser/ParameterParserTest.java new file mode 100644 index 000000000..729ad67ac --- /dev/null +++ b/src/test/java/seedu/duke/parser/ParameterParserTest.java @@ -0,0 +1,35 @@ +package seedu.duke.parser; + +import org.junit.jupiter.api.Test; +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.command.AddCommand; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.InputTransactionInvalidDateException; +import seedu.duke.exception.EmptyParameterException; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ParameterParserTest { + @Test + public void parse_AddCommandEmptyDate_ExpectedException() { + AddCommand addCommand = new AddCommand(); + String parametersInput = "t/income c/bonus a/1 d/ i/thank_you_boss"; + + assertThrows( + EmptyParameterException.class, + () -> ParameterParser.parse(addCommand, parametersInput) + ); + } + + @Test + public void parse_AddCommandInvalidDateFormat_ExpectedException() { + AddCommand addCommand = new AddCommand(); + String parametersInput = "t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss"; + + assertThrows( + InputTransactionInvalidDateException.class, + () -> ParameterParser.parse(addCommand, parametersInput) + ); + } +} From 5c26b414352e55f0df9a05ead99482f68ee36c83 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 03:29:50 +0800 Subject: [PATCH 130/416] Update test cases for regression testing --- text-ui-test/EXPECTED.TXT | 114 +++----------------------------------- text-ui-test/input.txt | 1 + 2 files changed, 9 insertions(+), 106 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 4837fc6db..c338aa9a5 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -110,111 +110,10 @@ Parameters information: -NIL- ____________________________________________________________ ____________________________________________________________ -Gotcha! Here are the commands that you may use: -Command Word: HELP -To display a list of available commands with their respective expected parameters. -Type "help o/detailed" for a detailed version of all parameters used. -Usage: help [o/detailed] - -Command Word: ADD -To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. -Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION - -Command Word: LIST -To list all or some transactions based on selection. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] - -Command Word: FIND -To find specific transaction(s) based on any keywords inputted by the user. -Usage: find KEYWORDS - -Command Word: STATS -To get statistics of the transactions such as the total savings for each category, summary of expenditure, etc. -Usage: stats s/STATISTICS_TYPE - -Command Word: EDIT -To edit a specific entry in the list of transactions. -Usage: edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION] - -Command Word: DELETE -To delete a specific entry in the list of transactions. -Usage: delete e/ENTRY - -Command Word: PURGE -To purge a all entries in the list of transactions. -Usage: purge - -Command Word: BYE -To exit the application. -Usage: bye - +Not supported tag(s) detected, please check your input! ____________________________________________________________ ____________________________________________________________ -Gotcha! Here are the commands that you may use: -Command Word: HELP -To display a list of available commands with their respective expected parameters. -Type "help o/detailed" for a detailed version of all parameters used. -Usage: help [o/detailed] -Parameters information: -(Optional) o/detailed - Detailed version of guide. - -Command Word: ADD -To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. -Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION -Parameters information: -TYPE: The type of transaction. Only "income" or "expense" is accepted. -CATEGORY: A category for the transaction. Only string containing alphabets is accepted. -AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted. -DATE: Date of the transaction. The format must be in "yyyyMMdd". -DESCRIPTION: More information regarding the transaction, written without any space. - -Command Word: LIST -To list all or some transactions based on selection. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] -Parameters information: -(Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. -(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. -(Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". - -Command Word: FIND -To find specific transaction(s) based on any keywords inputted by the user. -Usage: find KEYWORDS -Parameters information: -KEYWORDS: Any partial or full keyword(s) that matches the details of the transaction, such as type, category, amount, date or description. - -Command Word: STATS -To get statistics of the transactions such as the total savings for each category, summary of expenditure, etc. -Usage: stats s/STATISTICS_TYPE -Parameters information: -STATISTICS_TYPE: The type of statistics to be generated. Only "categories" is accepted. - -Command Word: EDIT -To edit a specific entry in the list of transactions. -Usage: edit e/ENTRY [t/TYPE] [c/CATEGORY] [a/AMOUNT] [d/DATE] [i/DESCRIPTION] -Parameters information: -ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. -(Optional) TYPE: The type of transaction. Only "income" or "expense" is accepted. -(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. -(Optional) AMOUNT: Value of the transaction in numerical form. Only integer within 0 and 10000000 is accepted. -(Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". -(Optional) DESCRIPTION: More information regarding the transaction, written without any space. - -Command Word: DELETE -To delete a specific entry in the list of transactions. -Usage: delete e/ENTRY -Parameters information: -ENTRY: The entry number of the transaction. Type "list" to list all the entry numbers of transaction. - -Command Word: PURGE -To purge a all entries in the list of transactions. -Usage: purge -Parameters information: -NIL- - -Command Word: BYE -To exit the application. -Usage: bye -Parameters information: -NIL- - +Not supported tag(s) detected, please check your input! ____________________________________________________________ ____________________________________________________________ Mandatory tag(s) missing, please check your input! @@ -241,7 +140,7 @@ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ -Invalid tag(s) detected, please check your input! +Not supported tag(s) detected, please check your input! ____________________________________________________________ ____________________________________________________________ Type of transaction given is invalid, please check your input! @@ -266,7 +165,7 @@ Here are your transaction records: ____________________________________________________________ ____________________________________________________________ -Invalid date, please ensure your date format is correct! +Mandatory parameter(s) is empty, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: @@ -321,7 +220,7 @@ ____________________________________________________________ Invalid date, please ensure your date format is correct! ____________________________________________________________ ____________________________________________________________ -Mandatory parameter(s) missing, please check your input! +Mandatory parameter(s) is empty, please check your input! ____________________________________________________________ ____________________________________________________________ Mandatory tag(s) missing, please check your input! @@ -333,6 +232,9 @@ Here are your transaction records: [-][transport] $1 at Oct 02 2022 | Description: bus_fare [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index dd3bd5f38..7b1b154b3 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -33,6 +33,7 @@ add t/income c/bonus a/10000000 d/ i/thank_you_boss add t/income c/bonus a/10000000 list delete 1 +delete e/1 purge Y list From a7e931ce52ab93e224f19be4fb0a9112484b7751 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 04:56:10 +0800 Subject: [PATCH 131/416] Change error message for missing parameter and add more test cases --- .../java/seedu/duke/common/ErrorMessages.java | 2 +- text-ui-test/EXPECTED.TXT | 124 +++++++++++++++++- text-ui-test/input.txt | 37 +++++- 3 files changed, 156 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 937f9eb35..904a6f1ad 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -15,7 +15,7 @@ public enum ErrorMessages { ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, " + "please ensure your amount is in positive numerals ($10000000 or less) only!"), ERROR_INVALID_INDEX("Invalid index, please ensure your index is correct!"), - ERROR_INPUT_MISSING_PARAMETER("Mandatory parameter(s) is empty, please check your input!"), + ERROR_INPUT_MISSING_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 05eeb3e22..d1ca9609f 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -116,12 +116,36 @@ ____________________________________________________________ Not supported tag(s) detected, please check your input! ____________________________________________________________ ____________________________________________________________ +Not supported tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ +Parameter behind tag(s) is found to be empty, please check your input! +____________________________________________________________ +____________________________________________________________ +Duplicate tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! +____________________________________________________________ +____________________________________________________________ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ +Parameter behind tag(s) is found to be empty, please check your input! +____________________________________________________________ +____________________________________________________________ Type of statistics given is invalid, please check your input! ____________________________________________________________ ____________________________________________________________ +Not supported tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ +Duplicate tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ There are no statistics available yet for the given statistics type. ____________________________________________________________ ____________________________________________________________ @@ -140,10 +164,28 @@ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ +Parameter behind tag(s) is found to be empty, please check your input! +____________________________________________________________ +____________________________________________________________ +Parameter behind tag(s) is found to be empty, please check your input! +____________________________________________________________ +____________________________________________________________ +Parameter behind tag(s) is found to be empty, please check your input! +____________________________________________________________ +____________________________________________________________ +Parameter behind tag(s) is found to be empty, please check your input! +____________________________________________________________ +____________________________________________________________ +Parameter behind tag(s) is found to be empty, please check your input! +____________________________________________________________ +____________________________________________________________ +Duplicate tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ Not supported tag(s) detected, please check your input! ____________________________________________________________ ____________________________________________________________ -Mandatory parameter(s) is empty, please check your input! +Parameter behind tag(s) is found to be empty, please check your input! ____________________________________________________________ ____________________________________________________________ Not supported tag(s) detected, please check your input! @@ -171,7 +213,7 @@ Here are your transaction records: ____________________________________________________________ ____________________________________________________________ -Mandatory parameter(s) is empty, please check your input! +Parameter behind tag(s) is found to be empty, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: @@ -192,6 +234,9 @@ ____________________________________________________________ Here are the transaction records that match your search expression: [-][transport] $1 at Oct 02 2022 | Description: bus_fare +____________________________________________________________ +____________________________________________________________ +There are no transaction records that match your search expression. ____________________________________________________________ ____________________________________________________________ Here are your transaction records: @@ -229,9 +274,6 @@ ____________________________________________________________ Invalid date, please ensure your date format is correct! ____________________________________________________________ ____________________________________________________________ -Mandatory parameter(s) is empty, please check your input! -____________________________________________________________ -____________________________________________________________ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ @@ -250,14 +292,86 @@ I have deleted the following transaction: [-][food] $20 at Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ +Here are your transaction records: +[+][salary] $2000 at Sep 30 2022 | Description: jan_salary +[-][transport] $1 at Oct 02 2022 | Description: bus_fare +[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss + +____________________________________________________________ +____________________________________________________________ +Invalid index, please ensure your index is correct! +____________________________________________________________ +____________________________________________________________ +Invalid index, please ensure your index is correct! +____________________________________________________________ +____________________________________________________________ +Invalid index, please ensure your index is correct! +____________________________________________________________ +____________________________________________________________ +I have deleted the following transaction: +[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +____________________________________________________________ +____________________________________________________________ +Here are your transaction records: +[+][salary] $2000 at Sep 30 2022 | Description: jan_salary +[-][transport] $1 at Oct 02 2022 | Description: bus_fare + +____________________________________________________________ +____________________________________________________________ +Not supported tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ +Not supported tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ +Not supported tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ +Are you sure you want to proceed with this command? Please enter 'Y' to confirm. +____________________________________________________________ +____________________________________________________________ +Purging has been aborted. All transactions records are retained. +____________________________________________________________ +____________________________________________________________ +Are you sure you want to proceed with this command? Please enter 'Y' to confirm. +____________________________________________________________ +____________________________________________________________ +Purging has been aborted. All transactions records are retained. +____________________________________________________________ +____________________________________________________________ +Are you sure you want to proceed with this command? Please enter 'Y' to confirm. +____________________________________________________________ +____________________________________________________________ +Purging has been aborted. All transactions records are retained. +____________________________________________________________ +____________________________________________________________ +Are you sure you want to proceed with this command? Please enter 'Y' to confirm. +____________________________________________________________ +____________________________________________________________ +Purging has been aborted. All transactions records are retained. +____________________________________________________________ +____________________________________________________________ Are you sure you want to proceed with this command? Please enter 'Y' to confirm. ____________________________________________________________ ____________________________________________________________ All your transactions have been purged. ____________________________________________________________ ____________________________________________________________ +The command is aborted as the transactions list is empty. +____________________________________________________________ +____________________________________________________________ There are no transaction records found. ____________________________________________________________ ____________________________________________________________ +I have added the following Expense transaction: +[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ +____________________________________________________________ +____________________________________________________________ +Not supported tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ +Not supported tag(s) detected, please check your input! +____________________________________________________________ +____________________________________________________________ Goodbye and see you soon. ____________________________________________________________ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 3f27b702c..63bce7157 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -2,13 +2,27 @@ help help o/detailed help blablabla help o/detailed blablabla +help / +help o/ +help o/detailed o/detailed stats +stats blablabla +stats / +stats s/ stats s/invalid +stats s/categories blablabla +stats s/categories s/categories stats s/categories list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary add t/expense c/transport a/1 d/02102022 i/bus_fare +add t/ c/transport a/1 d/02102022 i/bus_fare +add t/expense c/ a/1 d/02102022 i/bus_fare +add t/expense c/transport a/ d/02102022 i/bus_fare +add t/expense c/transport a/1 d/ i/bus_fare +add t/expense c/transport a/1 d/02102022 i/ +add t/expense c/transport a/1 d/02102022 i/bus_fare i/mrt_fare list / list t/ list T/ @@ -22,6 +36,7 @@ list t/income d/30092022 stats s/categories find find bus_fare +find TOTALLY_NON_EXISTING_STRING list add / c/bonus a/10000000 d/03102022 i/thank_you_boss add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss @@ -32,12 +47,32 @@ add t/income c/bonus a/-1 d/03102022 i/thank_you_boss add t/income c/bonus a/9999999999999 d/03102022 i/thank_you_boss add t/income c/bonus a/10000000 d/20220101 i/thank_you_boss add t/income c/bonus a/10000000 d/obviouslyFalseDate i/thank_you_boss -add t/income c/bonus a/10000000 d/ i/thank_you_boss add t/income c/bonus a/10000000 list delete 1 delete e/1 +list +delete e/-1 +delete e/0 +delete e/4 +delete e/3 +list +purge 123123 +purge o/ +purge o/detailed +purge +N +purge +NoNONONOONOno +purge +YouJokingWithMe? +purge +y purge Y +purge list +add t/expense c/transport a/1 d/02102022 i/i/i/i/i/ +bye world +bye c/transport bye From c5926ee2a3da5a2700940fdc3968c37bd03bb3a9 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 11 Oct 2022 07:59:00 +0800 Subject: [PATCH 132/416] Update the About Us page to include Darren's and Thin Hong's profile pictures, and also rectify all links to portfolio pages --- docs/AboutUs.md | 10 +++++----- docs/team/chiathinhong.png | Bin 0 -> 24458 bytes docs/team/chuahanyongdarren.png | Bin 0 -> 21124 bytes 3 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 docs/team/chiathinhong.png create mode 100644 docs/team/chuahanyongdarren.png diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 50758f489..ed4dbbec2 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -2,9 +2,9 @@ Display | Name | Github Profile | Portfolio --------|:--------------------:|:--------------------------------------:|:---------: -![](https://via.placeholder.com/100.png?text=Photo) | Chua Han Yong Darren | [Github](https://github.com/chydarren) | [Portfolio](docs/team/chuahanyongdarren.md) -![](https://via.placeholder.com/100.png?text=Photo) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](docs/team/chiathinhong.md) -![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](docs/team/yongchinhan.md) -![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](docs/team/paullow.md) -![](https://via.placeholder.com/100.png?text=Photo) | Brian Wong Yun Long | [Github](https://github.com/brian-vb) | [Portfolio](docs/team/brianwongyunlong.md) +![](team/chuahanyongdarren.png) | Chua Han Yong Darren | [Github](https://github.com/chydarren) | [Portfolio](team/chuahanyongdarren.md) +![](team/chiathinhong.png) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](team/chiathinhong.md) +![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](team/yongchinhan.md) +![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](team/paullow.md) +![](https://via.placeholder.com/100.png?text=Photo) | Brian Wong Yun Long | [Github](https://github.com/brian-vb) | [Portfolio](team/brianwongyunlong.md) diff --git a/docs/team/chiathinhong.png b/docs/team/chiathinhong.png new file mode 100644 index 0000000000000000000000000000000000000000..d3b42b61794e722b7aa9613dbef61e1e7ba0aa43 GIT binary patch literal 24458 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 literal 0 HcmV?d00001 diff --git a/docs/team/chuahanyongdarren.png b/docs/team/chuahanyongdarren.png new file mode 100644 index 0000000000000000000000000000000000000000..a62ff38b05aa6dadee40d0df7d8d2a4df71d2007 GIT binary patch literal 21124 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 Date: Tue, 11 Oct 2022 08:01:09 +0800 Subject: [PATCH 133/416] Remove lines that are not needed in the Duke main class --- src/main/java/seedu/duke/Duke.java | 28 +++++-------------- .../java/seedu/duke/common/ErrorMessages.java | 4 +-- .../java/seedu/duke/common/InfoMessages.java | 1 - 3 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 2883ef78c..5455f47a4 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -10,11 +10,11 @@ public class Duke { private TransactionList transactions; private Ui ui; - public Duke() { // NEED TO ADD FILE PATH + public Duke() { // TODO: Add a file path when implementing storage feature ui = new Ui(); transactions = new TransactionList(); - // ideal code after u add all the storage stuff + // TODO: Ideal code after adding the storage feature /**storage = new Storage(filePath); try { tasks = new TaskList(storage.load()); @@ -27,33 +27,19 @@ public Duke() { // NEED TO ADD FILE PATH public void run() { ui.showGreeting(); boolean isExit = false; - String inData; // temp while (!isExit) { try { - inData = ui.readCommand(); - inData = inData.trim(); - Command c = CommandParser.parse(inData); - c.execute(transactions, ui, storage); - isExit = c.isExit(); + String fullCommand = ui.readCommand(); + Command command = CommandParser.parse(fullCommand); + command.execute(transactions, ui, storage); + isExit = command.isExit(); } catch (MoolahException e) { Ui.showErrorMessage(e.getMessage()); } - //ideal code - /***try { - String fullCommand = ui.readCommand(); - ui.showLine(); // divider line - Command c = Parser.parse(fullCommand); - c.execute(tasks, ui, storage); - isExit = c.isExit(); - } catch (DukeException e) { - ui.showError(e.getErrorMessage()); - } finally { - ui.showLine(); - }**/ } } public static void main(String[] args) { - new Duke().run(); // NEED TO ADD FILE PATH + new Duke().run(); // TODO: Add a file path when implementing storage feature } } diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 904a6f1ad..cacdf004b 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -11,11 +11,11 @@ public enum ErrorMessages { ERROR_INPUT_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_INPUT_INVALID_TAG("Invalid tag(s) detected, please check your input!"), ERROR_INPUT_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), + ERROR_INPUT_MISSING_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), + ERROR_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_ENTRY_NUMBER_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, " + "please ensure your amount is in positive numerals ($10000000 or less) only!"), - ERROR_INVALID_INDEX("Invalid index, please ensure your index is correct!"), - ERROR_INPUT_MISSING_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 898d56eec..11fcb9fce 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -20,7 +20,6 @@ public enum InfoMessages { INFO_STATS_CATEGORIES("Here are the total savings for each category:"), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), - INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."); From 63ecae74e87fec74c1cd6340054c04a794b4fa0c Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 11 Oct 2022 08:02:59 +0800 Subject: [PATCH 134/416] Remove the passing of the transactions list when calling Delete command and add some header comments --- src/main/java/seedu/duke/Ui.java | 55 +++++++++++--- .../seedu/duke/command/DeleteCommand.java | 22 +++--- .../java/seedu/duke/data/TransactionList.java | 71 +++++++++++++++---- 3 files changed, 112 insertions(+), 36 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 831e17d79..e9f554d0e 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,6 +1,5 @@ package seedu.duke; -import static seedu.duke.common.ErrorMessages.ERROR_INVALID_COMMAND; import static seedu.duke.common.InfoMessages.INFO_DIVIDER; import static seedu.duke.common.InfoMessages.INFO_EXIT; import static seedu.duke.common.InfoMessages.INFO_GREET; @@ -27,45 +26,83 @@ public static void printMessages(String... messages) { System.out.println(INFO_DIVIDER); } + /** + * Initialises the variables of the Ui class. + */ public Ui() { in = new Scanner(System.in); } + /** + * Reads the command from the user. + * + * @return The command from the user. + */ public String readCommand() { input = in.nextLine(); - return input.trim(); + return input; } + /** + * Prepares the error message to be displayed to the user. + * + * @param errorMessage An error message when an exception is handled by the program. + */ public static void showErrorMessage(String errorMessage) { printMessages(errorMessage); } + /** + * Prepares the information message to be displayed to the user. + * + * @param infoMessage An information message that describes the functionality of the program. + */ public static void showInfoMessage(String infoMessage) { printMessages(infoMessage); } + /** + * Prepares the greeting messages to be displayed to the user. + */ public static void showGreeting() { printMessages(INFO_GREET.toString(), INFO_HELP_PROMPT.toString()); } + /** + * Prepares the help messages to be displayed to the user. + * + * @param helpMessage A help message that specifies the details of how to use the program. + */ public static void showHelp(String helpMessage) { - // To include the other messages for commands printMessages(INFO_HELP_GREET.toString(), helpMessage); } + /** + * Prepares the exit message to be displayed to the user. + */ public static void showExit() { printMessages(INFO_EXIT.toString()); } - public static void showInvalidCommand() { - printMessages(ERROR_INVALID_COMMAND.toString(), INFO_HELP_PROMPT.toString()); - } - + /** + * Prepares the messages to be displayed to the user when add or delete has been performed on + * the transaction list. + * + * @param infoMessage An information message that describes the functionality of + * the program. + * @param transactionDetails Details of the action that has been performed on the transaction. + */ public static void showTransactionAction(String infoMessage, String transactionDetails) { printMessages(infoMessage, transactionDetails); } - public static void showTransactionsList(String transactionsList, String message) { - printMessages(message, transactionsList); + /** + * Prepares the transaction list messages to be displayed to the user. + * + * @param transactionsList A string containing the formatted transaction list. + * @param listMessage A list message that complements with the transactions list. + */ + public static void showTransactionsList(String transactionsList, String listMessage) { + printMessages(listMessage, transactionsList); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 1d1980e99..58ec49925 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -27,12 +27,8 @@ public class DeleteCommand extends Command { + "Type \"list\" to list all the entry numbers of transaction."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD - + LINE_SEPARATOR - + COMMAND_DESCRIPTION - + LINE_SEPARATOR - + COMMAND_USAGE - + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; @@ -43,16 +39,13 @@ public class DeleteCommand extends Command { private int entryNumber; - /** - * Initialises the variables of the DeleteCommand class. - */ public DeleteCommand() { } /** * Gets the mandatory tags of the command. * - * @return A string array containing all mandatory tags + * @return A string array containing all mandatory tags. */ @Override public String[] getMandatoryTags() { @@ -68,7 +61,7 @@ public void setEntryNumber(int entryNumber) { } /** - * Executes the operations related to the command. + * Executes the "delete" command. Checks and parses the necessary parameters before deleting transaction. * * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. @@ -88,13 +81,18 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws isInputValid = false; } if (isInputValid) { - String transaction = TransactionList.deleteTransaction(transactions, index); + String transaction = TransactionList.deleteTransaction(index); Ui.showTransactionAction(INFO_DELETE.toString(), transaction); } else { throw new InvalidIndexException(); } } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 1bbe8ed37..9ed72f18d 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -19,26 +19,68 @@ public class TransactionList { private static ArrayList transactions; + /** + * Initialises the variables of the TransactionList class. + */ public TransactionList() { this.transactions = new ArrayList<>(); } + /** + * Gets a specific entry from the transactions list, to be used by other classes. + * + * @param index An index of the transaction that is to be retrieved. + * @return The transaction entry from the transactions list. + */ public Transaction getEntry(int index) { return transactions.get(index); } - public static String deleteTransaction(TransactionList input, int index) { - Transaction transaction = input.getEntry(index - 1); + /** + * Gets the number of transactions in the transactions list. + * + * @return An integer value that indicates the number of transactions. + */ + public int size() { + return transactions.size(); + } + + /** + * Deletes a transaction from the transactions list based on the specified index. + * + * @param index An index of the transaction that is to be retrieved. + * @return A string tht states the details of the deleted transaction. + */ + public static String deleteTransaction(int index) { + Transaction transaction = transactions.get(index - 1); transactions.remove(index - 1); return transaction.toString(); } + /** + * Adds a transaction of class type Expense into the transactions list. + * + * @param description More information regarding the transaction, written without any space. + * @param amount Value of the transaction in numerical form. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @return A string that states the details of the added expense transaction. + */ public String addExpense(String description, int amount, String category, LocalDate date) { Expense expense = new Expense(description, amount, category, date); transactions.add(expense); return expense.toString(); } + /** + * Adds a transaction of class type Income into the transactions list. + * + * @param description More information regarding the transaction, written without any space. + * @param amount Value of the transaction in numerical form. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @return A string that states the details of the added income transaction. + */ public String addIncome(String description, int amount, String category, LocalDate date) { Income income = new Income(description, amount, category, date); transactions.add(income); @@ -48,8 +90,8 @@ public String addIncome(String description, int amount, String category, LocalDa /** * Checks whether the transaction belongs to the Income or Expense class type. * - * @param transaction The transaction record from the transactions list. - * @param classType The transaction class type that is either Income or Expense. + * @param transaction The transaction record from the transactions list. + * @param classType The transaction class type that is either Income or Expense. * @return A boolean value indicating whether transaction record belongs to the given class type. * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ @@ -60,10 +102,10 @@ public boolean isTransactionInstance(Object transaction, String classType) throw /** * Checks whether a transaction fulfills the given filter criteria. * - * @param transaction The transaction record from the transactions list. - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param transaction The transaction record from the transactions list. + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @return A string containing the formatted transaction list. * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ @@ -83,9 +125,9 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c /** * List all or some transactions based on selection. * - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @return A string containing the formatted transaction list. * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ @@ -119,11 +161,10 @@ public String findTransactions(String keywords) { return transactionsList; } + /** + * Purges all records in the transactions list. + */ public static void purgeTransactions() { transactions.clear(); } - - public int size() { - return transactions.size(); - } } From 3bab1e1b91b0dd0fdce6b45d2df84c404732653c Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 11 Oct 2022 08:05:56 +0800 Subject: [PATCH 135/416] Fix documentation issues during refractoring of parser to comply with JavaDoc standards --- .../java/seedu/duke/command/AddCommand.java | 13 +++++-- .../java/seedu/duke/command/ByeCommand.java | 23 ++++++------ src/main/java/seedu/duke/command/Command.java | 29 ++++++++------- .../java/seedu/duke/command/EditCommand.java | 19 +++++----- .../java/seedu/duke/command/HelpCommand.java | 18 ++++++---- .../java/seedu/duke/command/ListCommand.java | 29 ++++++++------- .../java/seedu/duke/command/PurgeCommand.java | 5 +++ .../java/seedu/duke/parser/CommandParser.java | 14 ++++---- .../seedu/duke/parser/ParameterParser.java | 35 +++++++++---------- 9 files changed, 104 insertions(+), 81 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 0a9d91cc4..c1ea4cce3 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -56,6 +56,9 @@ public class AddCommand extends Command { public AddCommand() { } + /** + * Initialises the variables of the AddCommand class. + */ public AddCommand(String type, String description, int amount, String category, LocalDate date) { this.type = type; this.description = description; @@ -67,7 +70,7 @@ public AddCommand(String type, String description, int amount, String category, /** * Gets the mandatory tags of the command. * - * @return A string array containing all mandatory tags + * @return A string array containing all mandatory tags. */ @Override public String[] getMandatoryTags() { @@ -107,11 +110,12 @@ public void setDate(LocalDate date) { } /** - * Executes the "add" command. Check and parse the necessary parameters before adding transaction. + * Executes the "add" command. Checks and parses the necessary parameters before adding transaction. * * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. * @param storage An instance of the Storage class. + * @throws InputTransactionUnknownTypeException If the type of transaction is not recognised. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { @@ -131,6 +135,11 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws } } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index 64af3925f..e7b99d16a 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -9,22 +9,18 @@ */ public class ByeCommand extends Command { private static final String LINE_SEPARATOR = System.lineSeparator(); - // The command word used to trigger the execution of Moolah Manager's operations. + // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "BYE"; - // The description for the usage of command. + // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To exit the application."; - // The guiding information for the usage of command. + // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: bye"; - // The formatting information for the parameters used by the command. + // The formatting information for the parameters used by the command public static final String COMMAND_PARAMETERS_INFO = "Parameters information: -NIL-"; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD - + LINE_SEPARATOR - + COMMAND_DESCRIPTION - + LINE_SEPARATOR - + COMMAND_USAGE - + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; @@ -33,7 +29,7 @@ public ByeCommand() { } /** - * Executes the operations related to the command. + * Executes the "bye" command. * * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. @@ -44,6 +40,11 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { Ui.showExit(); } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return true; diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 8b7eb0d18..6ee442bf2 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -11,13 +11,6 @@ * Represents an object that can be inherited by other command objects. */ public abstract class Command { - /** - * Executes the operations related to the command. - * - * @param ui An instance of the Ui class. - * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. - */ // The command word used to trigger the execution of Moolah Manager's operations public static String COMMAND_WORD; @@ -30,9 +23,9 @@ public abstract class Command { /** * Get the default mandatory tags of the command (no mandatory tag). - * To be overridden by subclasses which the command requires mandatory tag + * To be overridden by subclasses which the command requires mandatory tag. * - * @return A string array containing all mandatory tags + * @return A string array containing all mandatory tags. */ public String[] getMandatoryTags() { String[] mandatoryTags = new String[0]; @@ -40,18 +33,30 @@ public String[] getMandatoryTags() { } /** - * Get the default optional tags of the command (no optional tag). - * To be overridden by subclasses which the command requires optional tag + * Gets the default optional tags of the command (no optional tag). + * To be overridden by subclasses which the command requires optional tag. * - * @return A string array containing all optional tags + * @return A string array containing all optional tags. */ public String[] getOptionalTags() { String[] optionalTags = new String[0]; return optionalTags; } + /** + * Executes the operations related to the command. + * + * @param ui An instance of the Ui class. + * @param transactions An instance of the TransactionList class. + * @param storage An instance of the Storage class. + */ public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ public abstract boolean isExit(); // Methods below are to be overridden by the subclass whenever applicable diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 8f02954b2..83c831d7a 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -43,12 +43,8 @@ public class EditCommand extends Command { + "(Optional) DESCRIPTION: More information regarding the transaction, written without any space."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD - + LINE_SEPARATOR - + COMMAND_DESCRIPTION - + LINE_SEPARATOR - + COMMAND_USAGE - + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; @@ -66,7 +62,7 @@ public EditCommand() { /** * Gets the mandatory tags of the command. * - * @return A string array containing all mandatory tags + * @return A string array containing all mandatory tags. */ @Override public String[] getMandatoryTags() { @@ -77,7 +73,7 @@ public String[] getMandatoryTags() { /** * Gets the optional tags of the command. * - * @return A string array containing all optional tags + * @return A string array containing all optional tags. */ @Override public String[] getOptionalTags() { @@ -122,7 +118,7 @@ public void setDate(LocalDate date) { } /** - * Executes the operations related to the command. + * Executes the "edit" command. * * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. @@ -140,6 +136,11 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { date.toString())); } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 2fffe6df4..47c3a3f93 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -26,17 +26,16 @@ public class HelpCommand extends Command { + "(Optional) o/detailed - Detailed version of guide."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD - + LINE_SEPARATOR - + COMMAND_DESCRIPTION - + LINE_SEPARATOR - + COMMAND_USAGE - + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; private boolean isDetailed; + /** + * Instantiates the ListCommand class with required variables. + */ public HelpCommand() { this.isDetailed = false; } @@ -49,7 +48,7 @@ public void setIsDetailedOption(boolean isDetailed) { /** * Gets the optional tags of the command. * - * @return A string array containing all optional tags + * @return A string array containing all optional tags. */ @Override public String[] getOptionalTags() { @@ -106,6 +105,11 @@ private String generateDetailedHelp() { return helpMessage; } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index a278d04f4..9f4af1d61 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -34,20 +34,18 @@ public class ListCommand extends Command { + LINE_SEPARATOR + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + COMMAND_DESCRIPTION - + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - private static final int TAG_LIMIT = 3; - private static final int MINIMUM_TAG_LENGTH = 2; - private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; - private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; - private String category; private LocalDate date; private String type; + /** + * Initialises the variables of the ListCommand class. + */ public ListCommand() { category = ""; date = null; @@ -57,7 +55,7 @@ public ListCommand() { /** * Gets the optional tags of the command. * - * @return A string array containing all optional tags + * @return A string array containing all optional tags. */ @Override public String[] getOptionalTags() { @@ -93,17 +91,17 @@ public void setDate(LocalDate date) { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - // Pass the tags to the filter for transaction list. + // Passes the tags to the filter for transaction list listTransactions(transactions, type, category, date); } /** * List all or some transactions based on selection. * - * @param transactions An instance of the TransactionList class. - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param transactions An instance of the TransactionList class. + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date) @@ -117,6 +115,11 @@ private static void listTransactions(TransactionList transactions, String type, Ui.showTransactionsList(transactionsList, INFO_LIST.toString()); } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 85d9c8110..e45dc5d1f 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -69,6 +69,11 @@ public static boolean isEmpty(TransactionList transactions) { return false; } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index 4657685e6..e1e3b0cd3 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -15,7 +15,7 @@ import seedu.duke.exception.InvalidCommandException; /** - * Represents a parser that parses the user input into a Command object ready for execution + * Represents a parser that parses the user input into a Command object ready for execution. * *

The CommandParser will check that the user input calls a valid command, * then create the corresponding command object and call ParameterParser to further parse the parameter portion @@ -42,16 +42,15 @@ public static Command parse(String fullCommandInput) throws MoolahException { String commandWordInput = inputTokens[0]; String parametersInput = inputTokens[1]; - // Parse the command word from user input + // Parses the command word from user input command = getCommand(commandWordInput, parametersInput); - // TODO: To remove this if statement once a solution is found for managing parameter that allows space if (command instanceof FindCommand) { return command; } - // Parse the parameters from user input to set up the parameters for the command + // Parses the parameters from user input to set up the parameters for the command assert command != null; ParameterParser.parse(command, parametersInput); @@ -66,7 +65,7 @@ public static Command parse(String fullCommandInput) throws MoolahException { */ public static String[] splitInput(String fullCommandInput) { String[] inputTokens; - // Separate the command word and the parameters into an array of size two + // Separates the command word and the parameters into an array of size two if (fullCommandInput.contains(DELIMITER)) { inputTokens = fullCommandInput.split(DELIMITER, SPLIT_POSITION); } else { @@ -79,8 +78,8 @@ public static String[] splitInput(String fullCommandInput) { * Creates a Command object based on the command word entered by user. * * @param commandWordInput The command word entered by user. - * @return Command object created - * @throws InvalidCommandException Exception when the command word is not supported by the application. + * @return Command object created. + * @throws InvalidCommandException If the command word is not supported by the application. */ private static Command getCommand(String commandWordInput, String parameterInput) throws InvalidCommandException { // TODO: Remove parameter input once a solution is found for managing parameter that allows space @@ -117,6 +116,5 @@ private static Command getCommand(String commandWordInput, String parameterInput throw new InvalidCommandException(); } return command; - } } diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 5ae2b6630..4f693f1c8 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -4,6 +4,7 @@ import seedu.duke.command.ListCommand; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; + import seedu.duke.exception.InputDuplicateTagException; import seedu.duke.exception.InputMissingTagException; import seedu.duke.exception.InputUnsupportedTagException; @@ -17,7 +18,6 @@ import seedu.duke.exception.InputTransactionUnknownTypeException; import seedu.duke.exception.ListStatisticsInvalidStatsTypeException; - import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -37,7 +37,7 @@ import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; /** - * Parses the parameter portion of the user input and set the parameters into the Command object + * Parses the parameter portion of the user input and set the parameters into the Command object. * *

The ParameterParser will check that the parameter input portion contains only the supported tags, * for each of the supported tag, parses the parameter into the valid form required by the Command object @@ -53,17 +53,17 @@ public class ParameterParser { private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; /** - * To parse the parameters input into proper parameters of the command object. + * Parses the parameters input into proper parameters of the command object. * *

The parameters will go through the following checks during the parsing: - * 1. Check that the user input contains all mandatory tags of the command - * 2. Check that the user input does not contain tags not supported by the command - * 3. Check that the user input does not contain a same tag more than once - * 4. Check that the user input does not contain a tag without parameter - * 5. For each parameter, check that the format of the parameter is correct + * 1. Check that the user input contains all mandatory tags of the command. + * 2. Check that the user input does not contain tags not supported by the command. + * 3. Check that the user input does not contain a same tag more than once. + * 4. Check that the user input does not contain a tag without parameter. + * 5. For each parameter, check that the format of the parameter is correct. * * @param command A command object created based on the command word given by user. - * @throws MoolahException Any command input exceptions captured by Moolah Manager. + * @throws MoolahException If Moolah Manager captures any command input exceptions. */ public static void parse(Command command, String parametersInput) throws MoolahException { assert command != null; @@ -78,7 +78,6 @@ public static void parse(Command command, String parametersInput) throws MoolahE there is no more need to further check and set the parameters for empty parameters input. */ if (!parametersInput.isEmpty()) { - // Might throw InputUnsupportedTagException checkUnsupportedTagsNotExist(command, splits); // Might throw InputDuplicateTagException @@ -90,8 +89,6 @@ public static void parse(Command command, String parametersInput) throws MoolahE // For each tag, check that the parameter is correct and set it inside the command. setCommand(command, splits); } - - } /** @@ -99,7 +96,7 @@ public static void parse(Command command, String parametersInput) throws MoolahE * * @param command A command object created based on the command word given by user. * @param splits The user input after the command word, split into a list for every space found. - * @throws InputMissingTagException Missing mandatory tag exception. + * @throws InputMissingTagException If there is a missing mandatory tag. */ private static void checkMandatoryTagsExist(Command command, String[] splits) throws InputMissingTagException { String[] tags = command.getMandatoryTags(); @@ -116,7 +113,7 @@ private static void checkMandatoryTagsExist(Command command, String[] splits) th * * @param command A command object created based on the command word given by user. * @param splits The user input after the command word, split into a list for every space found. - * @throws InputUnsupportedTagException Extra tag exception. + * @throws InputUnsupportedTagException If there is an extra tag that is not recognised. */ private static void checkUnsupportedTagsNotExist(Command command, String[] splits) throws InputUnsupportedTagException { @@ -125,7 +122,7 @@ private static void checkUnsupportedTagsNotExist(Command command, String[] split for (String split : splits) { if (split.length() < MINIMUM_TAG_LENGTH) { - // None of the tag is shorter than two characters + // None of the tags is shorter than two characters throw new InputUnsupportedTagException(); } boolean hasFoundAmongMandatoryTag = findIfParameterTagAmongTags(split, mandatoryTags); @@ -143,7 +140,7 @@ private static void checkUnsupportedTagsNotExist(Command command, String[] split * Checks if the split user inputs contains a tag multiple times. * * @param splits The user input after the command word, split into a list for every space found. - * @throws InputDuplicateTagException Extra tag exception. + * @throws InputDuplicateTagException If there is an extra of the same tag. */ private static void checkDuplicateTagsNotExist(String[] splits) throws InputDuplicateTagException { HashMap tagOccurenceMap = new HashMap<>(); @@ -219,7 +216,7 @@ private static boolean findIfParameterTagAmongTags(String parameter, String[] ta * * @param command A command object created based on the command word given by user. * @param splits The user input after the command word, split into a list for every space found. - * @throws MoolahException Any command input exceptions captured by Moolah Manager. + * @throws MoolahException If Moolah Manager captures any command input exceptions. */ private static void setCommand(Command command, String[] splits) throws MoolahException { for (String split : splits) { @@ -406,10 +403,10 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt * @param parameter The user input after the user tag. * @return The statistic type. * @throws ListStatisticsInvalidStatsTypeException Invalid statistic type exception. - * @author brian-vb + * @author chydarren */ public static String parseStatsTypeTag(String parameter) throws ListStatisticsInvalidStatsTypeException { - String statsType = EMPTY_STRING; + String statsType; switch (parameter) { case "categories": statsType = "categories"; From 34fa20d967a34f96bd14a18ba86c4144f162cdaf Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 11 Oct 2022 08:07:09 +0800 Subject: [PATCH 136/416] Remove the try-catch block from FindCommand and throw Moolah Exception --- .../java/seedu/duke/command/FindCommand.java | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index d2cd572bc..91f4bffe4 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -4,6 +4,7 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.exception.FindTransactionMissingKeywordsException; +import seedu.duke.exception.MoolahException; import static seedu.duke.common.InfoMessages.INFO_LIST_FILTERED; import static seedu.duke.common.InfoMessages.INFO_LIST_UNFILTERED; @@ -28,8 +29,7 @@ public class FindCommand extends Command { // Basic help description public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR - + COMMAND_DESCRIPTION + LINE_SEPARATOR - + COMMAND_USAGE + LINE_SEPARATOR; + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; @@ -58,30 +58,32 @@ public void checkFindFormat(String keywords) throws FindTransactionMissingKeywor } /** - * Executes the operations related to the command. + * Executes the "Find" command. Checks that the input contain a search expression, i.e. keywords + * before performing any search on the transactions list. * * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. * @param storage An instance of the Storage class. */ @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) { - try { - // Checks the format of find to ensure that it contains keywords used in the search expression - checkFindFormat(keywords); - assert !keywords.isBlank(); - String transactionsList = transactions.findTransactions(keywords); - if (transactionsList.isEmpty()) { - ui.showInfoMessage(INFO_LIST_UNFILTERED.toString()); - return; - } - assert !transactionsList.isEmpty(); - ui.showTransactionsList(transactionsList, INFO_LIST_FILTERED.toString()); - } catch (FindTransactionMissingKeywordsException e) { - ui.showErrorMessage(e.getMessage()); + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + // Checks the format of find to ensure that it contains keywords used in the search expression + checkFindFormat(keywords); + assert !keywords.isBlank(); + String transactionsList = transactions.findTransactions(keywords); + if (transactionsList.isEmpty()) { + ui.showInfoMessage(INFO_LIST_UNFILTERED.toString()); + return; } + assert !transactionsList.isEmpty(); + ui.showTransactionsList(transactionsList, INFO_LIST_FILTERED.toString()); } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return false; From 7a765ab011fd56af9903677b0756b39bf0a7cc6f Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 11 Oct 2022 08:08:19 +0800 Subject: [PATCH 137/416] Fix the severe bug of duplicated output of "stats/s categories" command when used multiple times --- .../java/seedu/duke/command/StatsCommand.java | 22 +++++++++++++------ .../java/seedu/duke/data/CategoryList.java | 16 ++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 2c2cccb55..85e80968b 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -30,8 +30,7 @@ public class StatsCommand extends Command { // Basic help description public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR - + COMMAND_DESCRIPTION + LINE_SEPARATOR - + COMMAND_USAGE + LINE_SEPARATOR; + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; @@ -44,7 +43,7 @@ public StatsCommand() { /** * Gets the mandatory tags of the command. * - * @return A string array containing all mandatory tags + * @return A string array containing all mandatory tags. */ @Override public String[] getMandatoryTags() { @@ -69,18 +68,22 @@ public void setStatsType(String statsType) { this.statsType = statsType; } + /** + * Lists the statistics depending on the type of statistics requested. + * + * @param statsType The type of statistics that is needed, e.g. categories. + * @param transactions An instance of the TransactionList class. + * @throws ListStatisticsInvalidStatsTypeException If the type of statistics is not recognised. + */ private static void listStatisticsByStatsType(String statsType, TransactionList transactions) throws ListStatisticsInvalidStatsTypeException { - /* - Known issue; currently each repeat use of command will generate more classes, need - to probably add into constructor and pass in categories - */ CategoryList categories = new CategoryList(); switch (statsType) { case "categories": categories.calculateTotalAmount(transactions); String categoriesList = categories.listCategories(); + if (categoriesList.isEmpty()) { Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); return; @@ -93,6 +96,11 @@ private static void listStatisticsByStatsType(String statsType, TransactionList } } + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ @Override public boolean isExit() { return false; diff --git a/src/main/java/seedu/duke/data/CategoryList.java b/src/main/java/seedu/duke/data/CategoryList.java index 313ac4938..165370741 100644 --- a/src/main/java/seedu/duke/data/CategoryList.java +++ b/src/main/java/seedu/duke/data/CategoryList.java @@ -10,6 +10,11 @@ public class CategoryList { private static ArrayList categories = new ArrayList<>(); + /** + * Browses the list of transactions for unique categories and adds them into a category list. + * + * @param transactions An instance of the TransactionList class. + */ public static void addCategories(TransactionList transactions) { for (int i = 0; i < transactions.size(); i++) { Category category = new Category(transactions.getEntry(i).getCategory()); @@ -21,6 +26,11 @@ public static void addCategories(TransactionList transactions) { } } + /** + * Calculates the total amount of transactions belonging to each category in the category list. + * + * @param transactions An instance of the TransactionList class. + */ public static void calculateTotalAmount(TransactionList transactions) { addCategories(transactions); for (Category category : categories) { @@ -37,12 +47,18 @@ public static void calculateTotalAmount(TransactionList transactions) { } } + /** + * List the statistics for the total amount of savings in each category. + * + * @return A string containing the formatted categories list. + */ public String listCategories() { String categoriesList = EMPTY_STRING; // Loops each category from the categories list for (Category category : categories) { categoriesList += category.toString() + LINE_SEPARATOR; } + categories.clear(); return categoriesList; } } From bdb390be872c4d81ae6adcc535a3a8fe88ff0ab1 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 11 Oct 2022 10:44:48 +0800 Subject: [PATCH 138/416] Add some logging statements for ListCommand and StatsCommand classes --- .../java/seedu/duke/command/ListCommand.java | 16 +++++++++++++++ .../java/seedu/duke/command/StatsCommand.java | 20 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 9f4af1d61..ab880212c 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -7,6 +7,8 @@ import seedu.duke.exception.MoolahException; import java.time.LocalDate; +import java.util.logging.Logger; +import java.util.logging.Level; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; @@ -39,6 +41,8 @@ public class ListCommand extends Command { // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; + private static final Logger listLogger = Logger.getLogger(ListCommand.class.getName()); + private String category; private LocalDate date; private String type; @@ -91,6 +95,10 @@ public void setDate(LocalDate date) { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + listLogger.setLevel(Level.WARNING); + listLogger.log(Level.INFO, "List command starts passing the tags for filter, if any," + + " into the listTransactions method."); + // Passes the tags to the filter for transaction list listTransactions(transactions, type, category, date); } @@ -106,13 +114,21 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws */ private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date) throws InputTransactionUnknownTypeException { + listLogger.log(Level.INFO, "Listing of transactions is being processed into a" + + " transaction list variable."); String transactionsList = transactions.listTransactions(type, category, date); if (transactionsList.isEmpty()) { + listLogger.log(Level.INFO, "Transactions list is empty, so UI should display that" + + " there are no transaction records available."); Ui.showInfoMessage(INFO_LIST_EMPTY.toString()); + listLogger.log(Level.INFO, "End of List command."); return; } assert !transactionsList.isEmpty(); + listLogger.log(Level.INFO, "Transactions list is available, so UI should display the" + + " transaction records."); Ui.showTransactionsList(transactionsList, INFO_LIST.toString()); + listLogger.log(Level.INFO, "End of List command."); } /** diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 85e80968b..46e88932b 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -7,6 +7,9 @@ import seedu.duke.exception.ListStatisticsInvalidStatsTypeException; import seedu.duke.exception.MoolahException; +import java.util.logging.Level; +import java.util.logging.Logger; + import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; @@ -35,6 +38,8 @@ public class StatsCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; + private static final Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); + private String statsType; public StatsCommand() { @@ -60,6 +65,10 @@ public String[] getMandatoryTags() { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + statsLogger.setLevel(Level.WARNING); + statsLogger.log(Level.INFO, "Stats command starts passing the type of statistics" + + " and transactions list into the listStatisticsByStatsType method."); + listStatisticsByStatsType(statsType, transactions); } @@ -77,23 +86,34 @@ public void setStatsType(String statsType) { */ private static void listStatisticsByStatsType(String statsType, TransactionList transactions) throws ListStatisticsInvalidStatsTypeException { + statsLogger.log(Level.INFO, "A new instance of CategoryList is created."); CategoryList categories = new CategoryList(); switch (statsType) { case "categories": + statsLogger.log(Level.INFO, "The categories and amount for each category are " + + " being tallied and computed."); categories.calculateTotalAmount(transactions); String categoriesList = categories.listCategories(); if (categoriesList.isEmpty()) { + statsLogger.log(Level.INFO, "Categories list is empty, so UI should display that" + + " there are no statistics available."); Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); + statsLogger.log(Level.INFO, "End of Stats command."); return; } assert !categoriesList.isEmpty(); + statsLogger.log(Level.INFO, "Categories list is available, so UI should display the" + + " categories and amount of savings per category."); Ui.showTransactionsList(categoriesList, INFO_STATS_CATEGORIES.toString()); break; default: + statsLogger.log(Level.WARNING, "An exception has been caught due to an invalid statistics type."); throw new ListStatisticsInvalidStatsTypeException(); } + + statsLogger.log(Level.INFO, "End of Stats command."); } /** From 3bcb693e4baf7e324b9c245f6a2e197aa5fb2d5d Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Tue, 11 Oct 2022 13:46:00 +0800 Subject: [PATCH 139/416] Add logging in AddCommand.java and profile picture --- docs/AboutUs.md | 2 +- docs/team/yongchinhan.jpg | Bin 0 -> 115181 bytes .../java/seedu/duke/command/AddCommand.java | 24 +++++++++++++----- 3 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 docs/team/yongchinhan.jpg diff --git a/docs/AboutUs.md b/docs/AboutUs.md index ed4dbbec2..8d3c56a14 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -4,7 +4,7 @@ Display | Name | Github Profile | Portfo --------|:--------------------:|:--------------------------------------:|:---------: ![](team/chuahanyongdarren.png) | Chua Han Yong Darren | [Github](https://github.com/chydarren) | [Portfolio](team/chuahanyongdarren.md) ![](team/chiathinhong.png) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](team/chiathinhong.md) -![](https://via.placeholder.com/100.png?text=Photo) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](team/yongchinhan.md) +![](team/yongchinhan.jpg) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](team/yongchinhan.md) ![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](team/paullow.md) ![](https://via.placeholder.com/100.png?text=Photo) | Brian Wong Yun Long | [Github](https://github.com/brian-vb) | [Portfolio](team/brianwongyunlong.md) diff --git a/docs/team/yongchinhan.jpg b/docs/team/yongchinhan.jpg new file mode 100644 index 0000000000000000000000000000000000000000..887bde5fd7b6545d43851fd8536fd414d9acb558 GIT binary patch literal 115181 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%0A@j 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%0DIU{kpWwGMpnrfK>c?)ElHBRV3MFZw)Oe0jqSA4 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 WDH>Nwy->(Pcd?@B8YTUI8~+Ea+foDo literal 0 HcmV?d00001 diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index c1ea4cce3..83500217d 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -11,6 +11,10 @@ import java.time.LocalDate; +import java.util.logging.Level; +import java.util.logging.Logger; + + import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; @@ -47,6 +51,8 @@ public class AddCommand extends Command { // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + private static final Logger statsLogger = Logger.getLogger(AddCommand.class.getName()); + private String type; private String description; private int amount; @@ -74,12 +80,10 @@ public AddCommand(String type, String description, int amount, String category, */ @Override public String[] getMandatoryTags() { + String[] mandatoryTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_AMOUNT, - COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_TRANSACTION_DESCRIPTION + COMMAND_TAG_TRANSACTION_TYPE, COMMAND_TAG_TRANSACTION_CATEGORY, COMMAND_TAG_TRANSACTION_AMOUNT, + COMMAND_TAG_TRANSACTION_DATE, COMMAND_TAG_TRANSACTION_DESCRIPTION }; return mandatoryTags; } @@ -119,19 +123,27 @@ public void setDate(LocalDate date) { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - + statsLogger.setLevel(Level.WARNING); + statsLogger.log(Level.INFO, "Add Command checks the type of the transaction " + + "before adding into the transaction class."); assert date != null; switch (type) { case Expense.TRANSACTION_NAME: String expense = transactions.addExpense(description, amount, category, date); Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); + statsLogger.log(Level.INFO, "New expense transaction has been added " + + "and the UI should display respectively "); break; case Income.TRANSACTION_NAME: String income = transactions.addIncome(description, amount, category, date); Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); + statsLogger.log(Level.INFO, "New income transaction has been added " + + "and the UI should display respectively "); break; default: + statsLogger.log(Level.INFO, "Exception thrown when the ransaction type is unknown"); throw new InputTransactionUnknownTypeException(); + } } From 348679e2c756e2c0fbb783d8c3a758136c453c55 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Tue, 11 Oct 2022 14:05:30 +0800 Subject: [PATCH 140/416] Edit logging message and profile picture --- docs/team/yongchinhan.jpg | Bin 115181 -> 5456 bytes .../java/seedu/duke/command/AddCommand.java | 14 +++++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/team/yongchinhan.jpg b/docs/team/yongchinhan.jpg index 887bde5fd7b6545d43851fd8536fd414d9acb558..d5a864339399f4ce429c931fa9a6f0e9d91d4cb7 100644 GIT binary patch delta 4789 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 P+mafUDpf_;J3s%~hWs1{ literal 115181 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%0A@j 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%0DIU{kpWwGMpnrfK>c?)ElHBRV3MFZw)Oe0jqSA4 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 WDH>Nwy->(Pcd?@B8YTUI8~+Ea+foDo diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 83500217d..5014b0711 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -82,8 +82,11 @@ public AddCommand(String type, String description, int amount, String category, public String[] getMandatoryTags() { String[] mandatoryTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, COMMAND_TAG_TRANSACTION_CATEGORY, COMMAND_TAG_TRANSACTION_AMOUNT, - COMMAND_TAG_TRANSACTION_DATE, COMMAND_TAG_TRANSACTION_DESCRIPTION + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_AMOUNT, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_TRANSACTION_DESCRIPTION }; return mandatoryTags; } @@ -132,19 +135,20 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws String expense = transactions.addExpense(description, amount, category, date); Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); statsLogger.log(Level.INFO, "New expense transaction has been added " - + "and the UI should display respectively "); + + "and the UI should display acknowledgment message respectively."); break; case Income.TRANSACTION_NAME: String income = transactions.addIncome(description, amount, category, date); Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); statsLogger.log(Level.INFO, "New income transaction has been added " - + "and the UI should display respectively "); + + "and the UI should display acknowledgment message respectively."); break; default: - statsLogger.log(Level.INFO, "Exception thrown when the ransaction type is unknown"); + statsLogger.log(Level.INFO, "Exception thrown when the transaction type is unknown."); throw new InputTransactionUnknownTypeException(); } + statsLogger.log(Level.INFO, "End of Add command."); } /** From 20e3cfcabad2929fc659a632268b1d69e4dff206 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Tue, 11 Oct 2022 14:17:57 +0800 Subject: [PATCH 141/416] Update log level from info to warning --- src/main/java/seedu/duke/command/AddCommand.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 5014b0711..9d398bbd3 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -144,7 +144,8 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws + "and the UI should display acknowledgment message respectively."); break; default: - statsLogger.log(Level.INFO, "Exception thrown when the transaction type is unknown."); + statsLogger.log(Level.WARNING, "InputTransactionUnknownTypeException thrown when the transaction type" + + " is unknown."); throw new InputTransactionUnknownTypeException(); } From acbcddd3b93b961ee14a10d4bcbc1ea85f4f035b Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 15:42:42 +0800 Subject: [PATCH 142/416] Modify the description of throws tags in JavaDocs for ParameterParser As per suggestions by Darren in pr#81. --- .../seedu/duke/parser/ParameterParser.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 4f693f1c8..99df882a7 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -161,7 +161,7 @@ private static void checkDuplicateTagsNotExist(String[] splits) throws InputDupl * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. * * @param splits The user input after the command word, split into a list for every space found. - * @throws EmptyParameterException Extra tag exception. + * @throws EmptyParameterException If there exists a tag without parameter. * @author chinhan99 */ private static void checkParameterNotEmpty(String[] splits) throws EmptyParameterException { @@ -267,7 +267,7 @@ private static void setParameter(Command command, String tag, String parameter) * * @param parameter The user input after the user tag. * @return The class type if no exceptions are thrown. - * @throws InputTransactionUnknownTypeException Invalid type format exception. + * @throws InputTransactionUnknownTypeException If the transaction type provided is not supported. * @author chydarren */ public static String parseTypeTagForListing(String parameter) throws InputTransactionUnknownTypeException { @@ -286,7 +286,7 @@ public static String parseTypeTagForListing(String parameter) throws InputTransa * * @param parameter The user input after the user tag. * @return The user input after the user tag. - * @throws InputTransactionUnknownTypeException Invalid type format exception. + * @throws InputTransactionUnknownTypeException If the transaction type provided is not supported. * @author wcwy */ public static String parseTypeTagForAdding(String parameter) throws InputTransactionUnknownTypeException { @@ -305,7 +305,7 @@ public static String parseTypeTagForAdding(String parameter) throws InputTransac * * @param parameter The user input after the user tag. * @return The category parameter if no exceptions are thrown. - * @throws InputTransactionInvalidCategoryException Invalid category format exception. + * @throws InputTransactionInvalidCategoryException If the category provided contains numeric or symbols. * @author chinhan99 */ public static String parseCategoryTag(String parameter) throws InputTransactionInvalidCategoryException { @@ -322,7 +322,7 @@ public static String parseCategoryTag(String parameter) throws InputTransactionI * * @param parameter The user input after the user tag. * @return The amount integer if no exceptions are thrown. - * @throws AddTransactionInvalidAmountException Invalid amount format exception. + * @throws AddTransactionInvalidAmountException If the transaction amount provided is not a valid accepted integer. * @author chinhan99 */ private static int parseAmountTag(String parameter) throws AddTransactionInvalidAmountException { @@ -348,7 +348,7 @@ private static int parseAmountTag(String parameter) throws AddTransactionInvalid * * @param parameter The user input after the user tag. * @return The LocalDate object parsed from user input given. - * @throws InputTransactionInvalidDateException Invalid date format exception. + * @throws InputTransactionInvalidDateException If the format of the transaction date provided is incorrect. * @author wcwy */ public static LocalDate parseDateTag(String parameter) throws InputTransactionInvalidDateException { @@ -366,7 +366,7 @@ public static LocalDate parseDateTag(String parameter) throws InputTransactionIn * * @param parameter The user input after the user tag. * @return The valid integer for list index parsed from user input given. - * @throws MoolahException Either invalid index exception or amount not numeric exception. + * @throws MoolahException If the entry number provided is not parsable into integer. * @author brian-vb */ public static int parseEntryTag(String parameter) throws MoolahException { @@ -385,7 +385,7 @@ public static int parseEntryTag(String parameter) throws MoolahException { * * @param parameter The user input after the user tag. * @return A boolean value indicating if the option selected by user is "detailed" - * @throws UnknownHelpOptionException An unknown help option exception. + * @throws UnknownHelpOptionException If the help option parameter selected is not 'detailed'. * @author wcwy */ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOptionException { @@ -402,7 +402,7 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt * * @param parameter The user input after the user tag. * @return The statistic type. - * @throws ListStatisticsInvalidStatsTypeException Invalid statistic type exception. + * @throws ListStatisticsInvalidStatsTypeException If the statistic type given is not supported. * @author chydarren */ public static String parseStatsTypeTag(String parameter) throws ListStatisticsInvalidStatsTypeException { From 46bcbd340d0c63a75f636decff8d17cf9c28e73c Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 16:29:42 +0800 Subject: [PATCH 143/416] Add logging message for ParameterParser.java --- .../seedu/duke/parser/ParameterParser.java | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 99df882a7..f9d387eef 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -22,6 +22,8 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.HashMap; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,6 +54,9 @@ public class ParameterParser { private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; + private static final Logger parserLogger = Logger.getLogger(ParameterParser.class.getName()); + + /** * Parses the parameters input into proper parameters of the command object. * @@ -66,6 +71,7 @@ public class ParameterParser { * @throws MoolahException If Moolah Manager captures any command input exceptions. */ public static void parse(Command command, String parametersInput) throws MoolahException { + parserLogger.setLevel(Level.WARNING); assert command != null; String[] splits = parametersInput.split(DELIMITER); @@ -89,6 +95,7 @@ public static void parse(Command command, String parametersInput) throws MoolahE // For each tag, check that the parameter is correct and set it inside the command. setCommand(command, splits); } + parserLogger.log(Level.INFO, "Parameter parsed successfully: " + command + parametersInput); } /** @@ -103,6 +110,7 @@ private static void checkMandatoryTagsExist(Command command, String[] splits) th for (String tag : tags) { boolean found = findMatchingTagAmongInputs(tag, splits); if (!found) { + parserLogger.log(Level.WARNING, "A missing tag error is caught for the given tag: " + tag); throw new InputMissingTagException(); } } @@ -123,6 +131,7 @@ private static void checkUnsupportedTagsNotExist(Command command, String[] split for (String split : splits) { if (split.length() < MINIMUM_TAG_LENGTH) { // None of the tags is shorter than two characters + parserLogger.log(Level.WARNING, "An unsupported tag error is caught for the given tag: " + split); throw new InputUnsupportedTagException(); } boolean hasFoundAmongMandatoryTag = findIfParameterTagAmongTags(split, mandatoryTags); @@ -132,6 +141,7 @@ private static void checkUnsupportedTagsNotExist(Command command, String[] split } // Found a tag entered by the user but does not exist in the supported tag for the command + parserLogger.log(Level.WARNING, "An unsupported tag error is caught for the given tag: " + split); throw new InputUnsupportedTagException(); } } @@ -150,6 +160,7 @@ private static void checkDuplicateTagsNotExist(String[] splits) throws InputDupl // The duplicated tag can be found in the hash map if (tagOccurenceMap.containsKey(tag)) { + parserLogger.log(Level.WARNING, "An duplicate tag error is caught for the given tag: " + tag); throw new InputDuplicateTagException(); } tagOccurenceMap.put(tag, 1); @@ -167,6 +178,7 @@ private static void checkDuplicateTagsNotExist(String[] splits) throws InputDupl private static void checkParameterNotEmpty(String[] splits) throws EmptyParameterException { for (String split : splits) { if (split.length() == 2) { + parserLogger.log(Level.WARNING, "An empty parameter error is caught for the given tag input: " + split); throw new EmptyParameterException(); } } @@ -258,7 +270,8 @@ private static void setParameter(Command command, String tag, String parameter) command.setStatsType(parseStatsTypeTag(parameter)); break; default: - throw new InputMissingTagException(); + parserLogger.log(Level.WARNING, "An unsupported tag exception is caught: " + tag); + throw new InputUnsupportedTagException(); } } @@ -277,6 +290,7 @@ public static String parseTypeTagForListing(String parameter) throws InputTransa case "income": return CLASS_TYPE_INCOME; default: + parserLogger.log(Level.WARNING, "An invalid type error is caught for the given parameter: " + parameter); throw new InputTransactionUnknownTypeException(); } } @@ -294,6 +308,7 @@ public static String parseTypeTagForAdding(String parameter) throws InputTransac boolean isIncome = parameter.equals(Income.TRANSACTION_NAME); if (!isExpense && !isIncome) { + parserLogger.log(Level.WARNING, "An invalid type error is caught for the given parameter: " + parameter); throw new InputTransactionUnknownTypeException(); } @@ -312,6 +327,8 @@ public static String parseCategoryTag(String parameter) throws InputTransactionI Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); if (containNumeric(parameter) || hasSpecialSymbols.find()) { + parserLogger.log(Level.WARNING, "An invalid category error is caught for the given parameter: " + + parameter); throw new InputTransactionInvalidCategoryException(); } return parameter; @@ -330,15 +347,20 @@ private static int parseAmountTag(String parameter) throws AddTransactionInvalid Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); try { if (containAlphabet(parameter) || hasSpecialSymbols.find()) { + parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + + parameter); throw new AddTransactionInvalidAmountException(); } int amount = Integer.parseInt(parameter); if (amount < 0 || amount > 10000000) { + parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + + parameter); throw new AddTransactionInvalidAmountException(); } return amount; } catch (NumberFormatException e) { + parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + parameter); throw new AddTransactionInvalidAmountException(); } } @@ -357,6 +379,7 @@ public static LocalDate parseDateTag(String parameter) throws InputTransactionIn LocalDate date = LocalDate.parse(parameter, formatter); return date; } catch (DateTimeParseException exception) { + parserLogger.log(Level.WARNING, "An invalid date error is caught for the given parameter: " + parameter); throw new InputTransactionInvalidDateException(); } } @@ -374,6 +397,9 @@ public static int parseEntryTag(String parameter) throws MoolahException { try { index = Integer.parseInt(parameter); } catch (NumberFormatException e) { + + parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + + parameter); throw new EntryNumberNotNumericException(); } @@ -393,6 +419,8 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt if (isValidHelpOption) { return true; } else { + parserLogger.log(Level.WARNING, "An invalid help option error is caught for the given parameter: " + + parameter); throw new UnknownHelpOptionException(); } } @@ -412,6 +440,8 @@ public static String parseStatsTypeTag(String parameter) throws ListStatisticsIn statsType = "categories"; break; default: + parserLogger.log(Level.WARNING, "An invalid statstic type error is caught for the given parameter: " + + parameter); throw new ListStatisticsInvalidStatsTypeException(); } return statsType; From 5d23b09f67af8dcf30fca30e90d8293de5ef7919 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 11 Oct 2022 16:43:50 +0800 Subject: [PATCH 144/416] Update build.gradle to enable assertions --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index b0c5528fb..4c456b4da 100644 --- a/build.gradle +++ b/build.gradle @@ -42,5 +42,6 @@ checkstyle { } run{ + enableAssertions = true standardInput = System.in } From c7797b88db9b9938db86d63dd7d7c94a69ada3b5 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Tue, 11 Oct 2022 23:06:46 +0800 Subject: [PATCH 145/416] Merge branch to main --- META-INF/MANIFEST.MF | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 META-INF/MANIFEST.MF diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 000000000..19e86fe56 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: seedu.duke.Duke + From a20c6653d7aef55dfd8c66c843fc4cb33a0d11b2 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 12 Oct 2022 17:02:52 +0800 Subject: [PATCH 146/416] Add Assertions and Logger --- src/main/java/seedu/duke/parser/CommandParser.java | 6 ++++++ src/main/java/seedu/duke/parser/ParameterParser.java | 1 + 2 files changed, 7 insertions(+) diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index e1e3b0cd3..d6c7c9ca8 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -14,6 +14,9 @@ import seedu.duke.exception.MoolahException; import seedu.duke.exception.InvalidCommandException; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * Represents a parser that parses the user input into a Command object ready for execution. * @@ -27,6 +30,8 @@ public class CommandParser { private static final String DELIMITER = " "; private static final int SPLIT_POSITION = 2; + private static final Logger parserLogger = Logger.getLogger(ParameterParser.class.getName()); + /** * Parses the user input into Command class based on the command word. * @@ -113,6 +118,7 @@ private static Command getCommand(String commandWordInput, String parameterInput command = new ByeCommand(); break; default: + parserLogger.log(Level.WARNING, "An invalid command error is caught in this command"); throw new InvalidCommandException(); } return command; diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index f9d387eef..1555fb758 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -231,6 +231,7 @@ private static boolean findIfParameterTagAmongTags(String parameter, String[] ta * @throws MoolahException If Moolah Manager captures any command input exceptions. */ private static void setCommand(Command command, String[] splits) throws MoolahException { + assert command != null; for (String split : splits) { String tag = split.substring(0, SPLIT_POSITION); String parameter = split.substring(SPLIT_POSITION); From 5ff8713156bcb52fe28993be8e33e7a6195c4f6e Mon Sep 17 00:00:00 2001 From: brian-vb Date: Thu, 13 Oct 2022 14:12:51 +0800 Subject: [PATCH 147/416] Add Picture to AboutUs Add Assertions and Loggings to Purge and Delete. --- docs/AboutUs.md | 2 +- docs/team/brianwongyunlong.png | Bin 0 -> 30995 bytes .../seedu/duke/command/DeleteCommand.java | 14 ++++++++++++ .../java/seedu/duke/command/PurgeCommand.java | 21 +++++++++++++++++- 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 docs/team/brianwongyunlong.png diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 8d3c56a14..7615523e3 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -6,5 +6,5 @@ Display | Name | Github Profile | Portfo ![](team/chiathinhong.png) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](team/chiathinhong.md) ![](team/yongchinhan.jpg) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](team/yongchinhan.md) ![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](team/paullow.md) -![](https://via.placeholder.com/100.png?text=Photo) | Brian Wong Yun Long | [Github](https://github.com/brian-vb) | [Portfolio](team/brianwongyunlong.md) +![](team/brianwongyunlong.png) | Brian Wong Yun Long | [Github](https://github.com/brian-vb) | [Portfolio](team/brianwongyunlong.md) diff --git a/docs/team/brianwongyunlong.png b/docs/team/brianwongyunlong.png new file mode 100644 index 0000000000000000000000000000000000000000..7d104de2c8b91809f66d35a78b436f29e996c818 GIT binary patch literal 30995 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%0DdY=WDTUxaU!w? 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 numberOfTransactions) || (index <= 0)) { isInputValid = false; } + assert index > 0; if (isInputValid) { String transaction = TransactionList.deleteTransaction(index); Ui.showTransactionAction(INFO_DELETE.toString(), transaction); + logging.log(Level.INFO, "The requested transaction has been deleted " + + "and the UI should display the confirmation message respectively."); } else { + logging.log(Level.WARNING, "InvalidIndexException thrown when the index " + + "is invalid."); throw new InvalidIndexException(); } + logging.log(Level.INFO, "This is the end of the delete command."); } /** diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index e45dc5d1f..485d39b5d 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -4,6 +4,9 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import java.util.logging.Level; +import java.util.logging.Logger; + import static seedu.duke.common.InfoMessages.INFO_PURGE; import static seedu.duke.common.InfoMessages.INFO_PURGE_ABORT; import static seedu.duke.common.InfoMessages.INFO_PURGE_EMPTY; @@ -31,6 +34,8 @@ public class PurgeCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; + private static final Logger theLogger = Logger.getLogger(AddCommand.class.getName()); + public PurgeCommand() { } @@ -44,21 +49,35 @@ public PurgeCommand() { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { // Shows confirmation prompt before deleting all transactions + theLogger.setLevel(Level.WARNING); + theLogger.log(Level.INFO, "Purge Command checks if there are no transactions" + + " to be purged. If so, the command is aborted."); boolean check = isEmpty(transactions); if (check) { Ui.showInfoMessage(INFO_PURGE_EMPTY.toString()); + theLogger.log(Level.INFO, "The transactions list is found to be empty" + + " and the UI should display that information to the user respectively."); return; } - ui.showInfoMessage(INFO_PURGE_WARNING.toString()); + Ui.showInfoMessage(INFO_PURGE_WARNING.toString()); + theLogger.log(Level.INFO, "The UI should display a confirmation prompt" + + " for which the User would need to respond."); String input = ui.readCommand(); if (input.equals("Y")) { TransactionList.purgeTransactions(); + assert PurgeCommand.isEmpty(transactions); Ui.showInfoMessage(INFO_PURGE.toString()); + theLogger.log(Level.INFO, "The transactions list is now empty" + + " and the UI should display that information to the user respectively."); + theLogger.log(Level.INFO, "The end of the Purge Command."); return; } Ui.showInfoMessage(INFO_PURGE_ABORT.toString()); + theLogger.log(Level.INFO, "The user would have responded to not proceed with the command" + + " and the UI should display that information to abort the command respectively."); + theLogger.log(Level.INFO, "The end of the Purge Command."); } public static boolean isEmpty(TransactionList transactions) { From b2191320e2c2e09e9e7c31f487185107b54226f6 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sat, 15 Oct 2022 21:35:28 +0800 Subject: [PATCH 148/416] Reclaim code for team members to ensure RepoSense compatibility --- src/main/java/seedu/duke/Duke.java | 3 ++ src/main/java/seedu/duke/Storage.java | 1 + src/main/java/seedu/duke/Ui.java | 5 +++ .../java/seedu/duke/command/AddCommand.java | 23 +++++++----- .../java/seedu/duke/command/ByeCommand.java | 3 +- src/main/java/seedu/duke/command/Command.java | 2 +- .../java/seedu/duke/command/CommandTag.java | 3 +- .../seedu/duke/command/DeleteCommand.java | 18 +++++----- .../java/seedu/duke/command/EditCommand.java | 3 +- .../java/seedu/duke/command/FindCommand.java | 2 ++ .../java/seedu/duke/command/HelpCommand.java | 2 ++ .../java/seedu/duke/command/ListCommand.java | 4 +++ .../java/seedu/duke/command/PurgeCommand.java | 24 +++++++------ .../java/seedu/duke/command/StatsCommand.java | 4 +++ .../java/seedu/duke/common/DateFormats.java | 1 + .../java/seedu/duke/common/ErrorMessages.java | 1 + .../java/seedu/duke/common/InfoMessages.java | 1 + .../java/seedu/duke/data/CategoryList.java | 1 + .../java/seedu/duke/data/TransactionList.java | 7 ++++ .../seedu/duke/data/transaction/Category.java | 3 +- .../seedu/duke/data/transaction/Expense.java | 1 + .../seedu/duke/data/transaction/Income.java | 2 +- .../duke/data/transaction/Transaction.java | 3 ++ .../AddTransactionInvalidAmountException.java | 2 ++ .../exception/EmptyParameterException.java | 2 +- .../java/seedu/duke/parser/CommandParser.java | 4 +++ .../seedu/duke/parser/ParameterParser.java | 35 ++++++++++--------- .../seedu/duke/command/AddCommandTest.java | 6 ---- .../seedu/duke/command/PurgeCommandTest.java | 2 +- .../java/seedu/duke/common/UtilitiesTest.java | 3 +- .../data/transaction/TransactionTest.java | 3 +- .../duke/parser/ParameterParserTest.java | 2 ++ 32 files changed, 117 insertions(+), 59 deletions(-) delete mode 100644 src/test/java/seedu/duke/command/AddCommandTest.java diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 5455f47a4..d070d7c65 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -6,10 +6,12 @@ import seedu.duke.parser.CommandParser; public class Duke { + //@@author paullowse private Storage storage; private TransactionList transactions; private Ui ui; + //@@author chinhan99 public Duke() { // TODO: Add a file path when implementing storage feature ui = new Ui(); transactions = new TransactionList(); @@ -24,6 +26,7 @@ public Duke() { // TODO: Add a file path when implementing storage feature }**/ } + //@@author paullowse public void run() { ui.showGreeting(); boolean isExit = false; diff --git a/src/main/java/seedu/duke/Storage.java b/src/main/java/seedu/duke/Storage.java index 854361793..fffda8276 100644 --- a/src/main/java/seedu/duke/Storage.java +++ b/src/main/java/seedu/duke/Storage.java @@ -3,6 +3,7 @@ import seedu.duke.data.TransactionList; public class Storage { + //@@author chinhan99 public Storage() { diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index e9f554d0e..f91e6aa0f 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -9,6 +9,7 @@ import java.util.Scanner; public class Ui { + //@@author chydarren private String input; private Scanner in; @@ -26,6 +27,7 @@ public static void printMessages(String... messages) { System.out.println(INFO_DIVIDER); } + //@@author paullowse /** * Initialises the variables of the Ui class. */ @@ -43,6 +45,7 @@ public String readCommand() { return input; } + //@@author chydarren /** * Prepares the error message to be displayed to the user. * @@ -68,6 +71,7 @@ public static void showGreeting() { printMessages(INFO_GREET.toString(), INFO_HELP_PROMPT.toString()); } + //@@author chinhan99 /** * Prepares the help messages to be displayed to the user. * @@ -77,6 +81,7 @@ public static void showHelp(String helpMessage) { printMessages(INFO_HELP_GREET.toString(), helpMessage); } + //@@author chydarren /** * Prepares the exit message to be displayed to the user. */ diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 9d398bbd3..db8d23fc7 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -14,7 +14,6 @@ import java.util.logging.Level; import java.util.logging.Logger; - import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; @@ -51,8 +50,10 @@ public class AddCommand extends Command { // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; - private static final Logger statsLogger = Logger.getLogger(AddCommand.class.getName()); + //@@author chinhan99 + private static final Logger addLogger = Logger.getLogger(AddCommand.class.getName()); + //@@author paullowse private String type; private String description; private int amount; @@ -91,6 +92,7 @@ public String[] getMandatoryTags() { return mandatoryTags; } + //@@author wcwy @Override public void setType(String type) { this.type = type; @@ -126,32 +128,35 @@ public void setDate(LocalDate date) { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - statsLogger.setLevel(Level.WARNING); - statsLogger.log(Level.INFO, "Add Command checks the type of the transaction " + //@@author chinhan99 + addLogger.setLevel(Level.WARNING); + addLogger.log(Level.INFO, "Add Command checks the type of the transaction " + "before adding into the transaction class."); assert date != null; + //@@author wcwy switch (type) { case Expense.TRANSACTION_NAME: String expense = transactions.addExpense(description, amount, category, date); Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); - statsLogger.log(Level.INFO, "New expense transaction has been added " + addLogger.log(Level.INFO, "New expense transaction has been added " + "and the UI should display acknowledgment message respectively."); break; case Income.TRANSACTION_NAME: String income = transactions.addIncome(description, amount, category, date); Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); - statsLogger.log(Level.INFO, "New income transaction has been added " + addLogger.log(Level.INFO, "New income transaction has been added " + "and the UI should display acknowledgment message respectively."); break; default: - statsLogger.log(Level.WARNING, "InputTransactionUnknownTypeException thrown when the transaction type" + addLogger.log(Level.WARNING, "InputTransactionUnknownTypeException thrown when the transaction type" + " is unknown."); throw new InputTransactionUnknownTypeException(); - } - statsLogger.log(Level.INFO, "End of Add command."); + //@@author chinhan99 + addLogger.log(Level.INFO, "End of Add command."); } + //@@author paullowse /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index e7b99d16a..44d7b396b 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -25,6 +25,7 @@ public class ByeCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; + //@@author paullowse public ByeCommand() { } @@ -49,4 +50,4 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { public boolean isExit() { return true; } -} +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 6ee442bf2..db04e6d2d 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -11,7 +11,6 @@ * Represents an object that can be inherited by other command objects. */ public abstract class Command { - // The command word used to trigger the execution of Moolah Manager's operations public static String COMMAND_WORD; // The description for the usage of command @@ -21,6 +20,7 @@ public abstract class Command { // The formatting information for the parameters used by the command public static String COMMAND_PARAMETERS_INFO; + //@@author paullowse /** * Get the default mandatory tags of the command (no mandatory tag). * To be overridden by subclasses which the command requires mandatory tag. diff --git a/src/main/java/seedu/duke/command/CommandTag.java b/src/main/java/seedu/duke/command/CommandTag.java index 49a74882f..490df5eaa 100644 --- a/src/main/java/seedu/duke/command/CommandTag.java +++ b/src/main/java/seedu/duke/command/CommandTag.java @@ -1,6 +1,7 @@ package seedu.duke.command; public class CommandTag { + //@@author wcwy public static final String COMMAND_TAG_TRANSACTION_TYPE = "t/"; public static final String COMMAND_TAG_TRANSACTION_CATEGORY = "c/"; public static final String COMMAND_TAG_TRANSACTION_AMOUNT = "a/"; @@ -9,4 +10,4 @@ public class CommandTag { public static final String COMMAND_TAG_LIST_ENTRY_NUMBER = "e/"; public static final String COMMAND_TAG_HELP_OPTION = "o/"; public static final String COMMAND_TAG_STATISTICS_TYPE = "s/"; -} +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index c10b2059b..42e4ae215 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -36,13 +36,13 @@ public class DeleteCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - + //@@author brian-vb // The optional tags that may exist in the user input - - private static final Logger logging = Logger.getLogger(DeleteCommand.class.getName()); + private static final Logger deleteLogger = Logger.getLogger(DeleteCommand.class.getName()); private int entryNumber; + //@@author paullowse public DeleteCommand() { } @@ -64,6 +64,7 @@ public void setEntryNumber(int entryNumber) { this.entryNumber = entryNumber; } + //@@author brian-vb /** * Executes the "delete" command. Checks and parses the necessary parameters before deleting transaction. * @@ -78,8 +79,8 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist */ - logging.setLevel(Level.WARNING); - logging.log(Level.INFO, "Delete Command checks whether the index is valid " + deleteLogger.setLevel(Level.WARNING); + deleteLogger.log(Level.INFO, "Delete Command checks whether the index is valid " + "before executing the command."); boolean isInputValid = true; int index = entryNumber; @@ -92,16 +93,17 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws if (isInputValid) { String transaction = TransactionList.deleteTransaction(index); Ui.showTransactionAction(INFO_DELETE.toString(), transaction); - logging.log(Level.INFO, "The requested transaction has been deleted " + deleteLogger.log(Level.INFO, "The requested transaction has been deleted " + "and the UI should display the confirmation message respectively."); } else { - logging.log(Level.WARNING, "InvalidIndexException thrown when the index " + deleteLogger.log(Level.WARNING, "InvalidIndexException thrown when the index " + "is invalid."); throw new InvalidIndexException(); } - logging.log(Level.INFO, "This is the end of the delete command."); + deleteLogger.log(Level.INFO, "This is the end of the delete command."); } + //@@author paullowse /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 83c831d7a..cb97ccc12 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -49,6 +49,7 @@ public class EditCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; + //@@author paullowse private int entryNumber; private String type; private String description; @@ -145,4 +146,4 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { public boolean isExit() { return false; } -} +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 91f4bffe4..42da90947 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -34,6 +34,7 @@ public class FindCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; + //@@author chydarren protected String keywords; /** @@ -79,6 +80,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws ui.showTransactionsList(transactionsList, INFO_LIST_FILTERED.toString()); } + //@@author paullowse /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 47c3a3f93..8db91b298 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -31,6 +31,7 @@ public class HelpCommand extends Command { // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + //@@author wcwy private boolean isDetailed; /** @@ -105,6 +106,7 @@ private String generateDetailedHelp() { return helpMessage; } + //@@author paullowse /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index ab880212c..b7d8cee76 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -41,8 +41,10 @@ public class ListCommand extends Command { // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; + //@@author chydarren private static final Logger listLogger = Logger.getLogger(ListCommand.class.getName()); + //@@author paullowse private String category; private LocalDate date; private String type; @@ -86,6 +88,7 @@ public void setDate(LocalDate date) { this.date = date; } + //@@author chydarren /** * Executes the operations related to the command. * @@ -131,6 +134,7 @@ private static void listTransactions(TransactionList transactions, String type, listLogger.log(Level.INFO, "End of List command."); } + //@@author paullowse /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 485d39b5d..a160c695c 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -34,11 +34,14 @@ public class PurgeCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - private static final Logger theLogger = Logger.getLogger(AddCommand.class.getName()); + //@@author brian-vb + private static final Logger purgeLogger = Logger.getLogger(AddCommand.class.getName()); + //@@author paullowse public PurgeCommand() { } + //@@author brian-vb /** * Executes the operations related to the command. * @@ -49,18 +52,18 @@ public PurgeCommand() { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { // Shows confirmation prompt before deleting all transactions - theLogger.setLevel(Level.WARNING); - theLogger.log(Level.INFO, "Purge Command checks if there are no transactions" + purgeLogger.setLevel(Level.WARNING); + purgeLogger.log(Level.INFO, "Purge Command checks if there are no transactions" + " to be purged. If so, the command is aborted."); boolean check = isEmpty(transactions); if (check) { Ui.showInfoMessage(INFO_PURGE_EMPTY.toString()); - theLogger.log(Level.INFO, "The transactions list is found to be empty" + purgeLogger.log(Level.INFO, "The transactions list is found to be empty" + " and the UI should display that information to the user respectively."); return; } Ui.showInfoMessage(INFO_PURGE_WARNING.toString()); - theLogger.log(Level.INFO, "The UI should display a confirmation prompt" + purgeLogger.log(Level.INFO, "The UI should display a confirmation prompt" + " for which the User would need to respond."); String input = ui.readCommand(); @@ -68,16 +71,16 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { TransactionList.purgeTransactions(); assert PurgeCommand.isEmpty(transactions); Ui.showInfoMessage(INFO_PURGE.toString()); - theLogger.log(Level.INFO, "The transactions list is now empty" + purgeLogger.log(Level.INFO, "The transactions list is now empty" + " and the UI should display that information to the user respectively."); - theLogger.log(Level.INFO, "The end of the Purge Command."); + purgeLogger.log(Level.INFO, "The end of the Purge Command."); return; } Ui.showInfoMessage(INFO_PURGE_ABORT.toString()); - theLogger.log(Level.INFO, "The user would have responded to not proceed with the command" + purgeLogger.log(Level.INFO, "The user would have responded to not proceed with the command" + " and the UI should display that information to abort the command respectively."); - theLogger.log(Level.INFO, "The end of the Purge Command."); + purgeLogger.log(Level.INFO, "The end of the Purge Command."); } public static boolean isEmpty(TransactionList transactions) { @@ -88,6 +91,7 @@ public static boolean isEmpty(TransactionList transactions) { return false; } + //@@author paullowse /** * Enables the program to exit when the Bye command is issued. * @@ -97,4 +101,4 @@ public static boolean isEmpty(TransactionList transactions) { public boolean isExit() { return false; } -} +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 46e88932b..ab96582d7 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -38,10 +38,12 @@ public class StatsCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; + //@@author chydarren private static final Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); private String statsType; + //@@author paullowse public StatsCommand() { } @@ -56,6 +58,7 @@ public String[] getMandatoryTags() { return mandatoryTags; } + //@@author chydarren /** * Executes the operations related to the command. * @@ -116,6 +119,7 @@ private static void listStatisticsByStatsType(String statsType, TransactionList statsLogger.log(Level.INFO, "End of Stats command."); } + //@@author paullowse /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/common/DateFormats.java b/src/main/java/seedu/duke/common/DateFormats.java index 73ad808c8..686314ffc 100644 --- a/src/main/java/seedu/duke/common/DateFormats.java +++ b/src/main/java/seedu/duke/common/DateFormats.java @@ -4,6 +4,7 @@ * Provides enum variables for the approved date formats for input and output. */ public enum DateFormats { + //@@author wcwy DATE_INPUT_PATTERN("ddMMyyyy"), DATE_OUTPUT_PATTERN("MMM dd yyyy"); diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index cacdf004b..a142b6645 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -21,6 +21,7 @@ public enum ErrorMessages { ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"); + //@@author chydarren public final String message; /** diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 11fcb9fce..8b7d6bd0b 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -23,6 +23,7 @@ public enum InfoMessages { INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."); + //@@author chydarren public final String message; /** diff --git a/src/main/java/seedu/duke/data/CategoryList.java b/src/main/java/seedu/duke/data/CategoryList.java index 165370741..0ff21fe35 100644 --- a/src/main/java/seedu/duke/data/CategoryList.java +++ b/src/main/java/seedu/duke/data/CategoryList.java @@ -5,6 +5,7 @@ import java.util.ArrayList; public class CategoryList { + //@@author chydarren private static final String EMPTY_STRING = ""; private static final String LINE_SEPARATOR = System.lineSeparator(); diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 9ed72f18d..94076c384 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -14,11 +14,14 @@ * These operations include adding, listing, modifying, deleting and purging. */ public class TransactionList { + //@@author chydarren private static final String EMPTY_STRING = ""; private static final String LINE_SEPARATOR = System.lineSeparator(); + //@@author chinhan99 private static ArrayList transactions; + //@@author wcwy /** * Initialises the variables of the TransactionList class. */ @@ -26,6 +29,7 @@ public TransactionList() { this.transactions = new ArrayList<>(); } + //@@author brian-vb /** * Gets a specific entry from the transactions list, to be used by other classes. * @@ -57,6 +61,7 @@ public static String deleteTransaction(int index) { return transaction.toString(); } + //@@author wcwy /** * Adds a transaction of class type Expense into the transactions list. * @@ -87,6 +92,7 @@ public String addIncome(String description, int amount, String category, LocalDa return income.toString(); } + //@@author chydarren /** * Checks whether the transaction belongs to the Income or Expense class type. * @@ -161,6 +167,7 @@ public String findTransactions(String keywords) { return transactionsList; } + //@@author brian-vb /** * Purges all records in the transactions list. */ diff --git a/src/main/java/seedu/duke/data/transaction/Category.java b/src/main/java/seedu/duke/data/transaction/Category.java index cf29927b7..0bdc9d19e 100644 --- a/src/main/java/seedu/duke/data/transaction/Category.java +++ b/src/main/java/seedu/duke/data/transaction/Category.java @@ -1,6 +1,7 @@ package seedu.duke.data.transaction; public class Category { + //@@author chydarren private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; private static final String SYMBOL_DOLLAR = "$"; @@ -33,4 +34,4 @@ public String toString() { return String.format("%s%s%s %s%d", PREFIX_CATEGORY, category, POSTFIX_CATEGORY, SYMBOL_DOLLAR, amount); } -} +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/data/transaction/Expense.java b/src/main/java/seedu/duke/data/transaction/Expense.java index 0e107be86..8a2ed2115 100644 --- a/src/main/java/seedu/duke/data/transaction/Expense.java +++ b/src/main/java/seedu/duke/data/transaction/Expense.java @@ -7,6 +7,7 @@ * Records the amount, category and the date of spending. */ public class Expense extends Transaction { + //@@author wcwy public static final String TRANSACTION_NAME = "expense"; private static String ICON_EXPENSE = "[-]"; diff --git a/src/main/java/seedu/duke/data/transaction/Income.java b/src/main/java/seedu/duke/data/transaction/Income.java index 2df6b9891..b1b892aab 100644 --- a/src/main/java/seedu/duke/data/transaction/Income.java +++ b/src/main/java/seedu/duke/data/transaction/Income.java @@ -7,10 +7,10 @@ * Records the amount, category and date of the income. */ public class Income extends Transaction { + //@@author wcwy public static final String TRANSACTION_NAME = "income"; private static final String ICON_INCOME = "[+]"; - public Income(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 04bee2436..9e07ac4ac 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -6,6 +6,7 @@ import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; public class Transaction { + //@@author chydarren private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; private static final String SYMBOL_DOLLAR = "$"; @@ -13,6 +14,7 @@ public class Transaction { private static final String TEXT_AT = "at"; private static final String TEXT_DESCRIPTION = "Description:"; + //@@author chinhan99 private String category; private String description; private int amount; @@ -53,6 +55,7 @@ public LocalDate getDate() { return date; } + //@@author wcwy public String printFormattedDate() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_OUTPUT_PATTERN.toString()); return date.format(formatter); diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java b/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java index f9acaa740..80d97c3e6 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java +++ b/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java @@ -2,6 +2,7 @@ import seedu.duke.common.ErrorMessages; +//@@author chinhan99 public class AddTransactionInvalidAmountException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. @@ -14,3 +15,4 @@ public String getMessage() { } } +//@@author \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/EmptyParameterException.java b/src/main/java/seedu/duke/exception/EmptyParameterException.java index 128ca5bf0..47c85536b 100644 --- a/src/main/java/seedu/duke/exception/EmptyParameterException.java +++ b/src/main/java/seedu/duke/exception/EmptyParameterException.java @@ -12,4 +12,4 @@ public class EmptyParameterException extends MoolahException { public String getMessage() { return ErrorMessages.ERROR_INPUT_MISSING_PARAMETER.toString(); } -} +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index d6c7c9ca8..4fc2e06a8 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -26,12 +26,14 @@ * such that the command object will be ready for an execution. */ public class CommandParser { + //@@author chydarren private static final String EMPTY_STRING = ""; private static final String DELIMITER = " "; private static final int SPLIT_POSITION = 2; private static final Logger parserLogger = Logger.getLogger(ParameterParser.class.getName()); + //@@author wcwy /** * Parses the user input into Command class based on the command word. * @@ -62,6 +64,7 @@ public static Command parse(String fullCommandInput) throws MoolahException { return command; } + //@@author chydarren /** * Splits the user input into two parts, i.e. the command word and the parameter(s). * @@ -79,6 +82,7 @@ public static String[] splitInput(String fullCommandInput) { return inputTokens; } + //@@author paullowse /** * Creates a Command object based on the command word entered by user. * diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 1555fb758..b438817e2 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -46,17 +46,17 @@ * and to store the parsed value inside the command object. */ public class ParameterParser { + //@@author chydarren private static final String EMPTY_STRING = ""; private static final String DELIMITER = " "; private static final int SPLIT_POSITION = 2; private static final int MINIMUM_TAG_LENGTH = 2; - private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; - + //@@author wcwy private static final Logger parserLogger = Logger.getLogger(ParameterParser.class.getName()); - + //@@author chinhan99 /** * Parses the parameters input into proper parameters of the command object. * @@ -98,6 +98,7 @@ public static void parse(Command command, String parametersInput) throws MoolahE parserLogger.log(Level.INFO, "Parameter parsed successfully: " + command + parametersInput); } + //@@author wcwy /** * Checks if all the mandatory tags exists in the split user inputs. * @@ -116,6 +117,7 @@ private static void checkMandatoryTagsExist(Command command, String[] splits) th } } + //@@author chinhan99 /** * Checks if the split user inputs contains any unsupported tag. * @@ -146,6 +148,7 @@ private static void checkUnsupportedTagsNotExist(Command command, String[] split } } + //@@author wcwy /** * Checks if the split user inputs contains a tag multiple times. * @@ -167,13 +170,13 @@ private static void checkDuplicateTagsNotExist(String[] splits) throws InputDupl } } + //@@author chinhan99 /** * Checks if there are missing parameter within the user input. * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. * * @param splits The user input after the command word, split into a list for every space found. * @throws EmptyParameterException If there exists a tag without parameter. - * @author chinhan99 */ private static void checkParameterNotEmpty(String[] splits) throws EmptyParameterException { for (String split : splits) { @@ -184,6 +187,7 @@ private static void checkParameterNotEmpty(String[] splits) throws EmptyParamete } } + //@@author wcwy /** * Returns a boolean value on whether a tag can be found among the split user inputs. * @@ -202,6 +206,7 @@ private static boolean findMatchingTagAmongInputs(String tag, String[] splits) { return hasFound; } + //@@author chinhan99 /** * Returns a boolean value on whether a tag can be found among the split user inputs. * @@ -223,6 +228,7 @@ private static boolean findIfParameterTagAmongTags(String parameter, String[] ta return hasFound; } + //@@author paullowse /** * For each split parameters, split it into tag and parameter, then check and set the parameters into the Command. * @@ -239,6 +245,7 @@ private static void setCommand(Command command, String[] splits) throws MoolahEx } } + //@@author wcwy private static void setParameter(Command command, String tag, String parameter) throws MoolahException { switch (tag) { case COMMAND_TAG_TRANSACTION_TYPE: @@ -276,13 +283,13 @@ private static void setParameter(Command command, String tag, String parameter) } } + //@@author chydarren /** * Parses the user parameter input for the description and returns it. * * @param parameter The user input after the user tag. * @return The class type if no exceptions are thrown. * @throws InputTransactionUnknownTypeException If the transaction type provided is not supported. - * @author chydarren */ public static String parseTypeTagForListing(String parameter) throws InputTransactionUnknownTypeException { switch (parameter) { @@ -296,13 +303,13 @@ public static String parseTypeTagForListing(String parameter) throws InputTransa } } + //@@author wcwy /** * Check if the type parameter is a valid transaction type and returns the parameter if it is valid. * * @param parameter The user input after the user tag. * @return The user input after the user tag. * @throws InputTransactionUnknownTypeException If the transaction type provided is not supported. - * @author wcwy */ public static String parseTypeTagForAdding(String parameter) throws InputTransactionUnknownTypeException { boolean isExpense = parameter.equals(Expense.TRANSACTION_NAME); @@ -316,13 +323,13 @@ public static String parseTypeTagForAdding(String parameter) throws InputTransac return parameter; } + //@@author chinhan99 /** * Parses the user parameter input for the description and returns it. * * @param parameter The user input after the user tag. * @return The category parameter if no exceptions are thrown. * @throws InputTransactionInvalidCategoryException If the category provided contains numeric or symbols. - * @author chinhan99 */ public static String parseCategoryTag(String parameter) throws InputTransactionInvalidCategoryException { Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); @@ -341,7 +348,6 @@ public static String parseCategoryTag(String parameter) throws InputTransactionI * @param parameter The user input after the user tag. * @return The amount integer if no exceptions are thrown. * @throws AddTransactionInvalidAmountException If the transaction amount provided is not a valid accepted integer. - * @author chinhan99 */ private static int parseAmountTag(String parameter) throws AddTransactionInvalidAmountException { Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); @@ -366,13 +372,13 @@ private static int parseAmountTag(String parameter) throws AddTransactionInvalid } } + //@@author wcwy /** * Parses the user parameter input for date into a LocalDate object and returns it. * * @param parameter The user input after the user tag. * @return The LocalDate object parsed from user input given. * @throws InputTransactionInvalidDateException If the format of the transaction date provided is incorrect. - * @author wcwy */ public static LocalDate parseDateTag(String parameter) throws InputTransactionInvalidDateException { try { @@ -385,20 +391,19 @@ public static LocalDate parseDateTag(String parameter) throws InputTransactionIn } } + //@@author brian-vb /** * Parses the user parameter input for entry number into an integer and returns it. * * @param parameter The user input after the user tag. * @return The valid integer for list index parsed from user input given. * @throws MoolahException If the entry number provided is not parsable into integer. - * @author brian-vb */ public static int parseEntryTag(String parameter) throws MoolahException { int index; try { index = Integer.parseInt(parameter); } catch (NumberFormatException e) { - parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + parameter); throw new EntryNumberNotNumericException(); @@ -407,13 +412,13 @@ public static int parseEntryTag(String parameter) throws MoolahException { return index; } + //@@author wcwy /** * Return a boolean value indicating if the option selected by user is "detailed". * * @param parameter The user input after the user tag. * @return A boolean value indicating if the option selected by user is "detailed" * @throws UnknownHelpOptionException If the help option parameter selected is not 'detailed'. - * @author wcwy */ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOptionException { boolean isValidHelpOption = parameter.equals("detailed"); @@ -426,13 +431,13 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt } } + //@@author chydarren /** * Check if the type parameter is a valid statistic type and returns the parameter if it is valid. * * @param parameter The user input after the user tag. * @return The statistic type. * @throws ListStatisticsInvalidStatsTypeException If the statistic type given is not supported. - * @author chydarren */ public static String parseStatsTypeTag(String parameter) throws ListStatisticsInvalidStatsTypeException { String statsType; @@ -448,13 +453,12 @@ public static String parseStatsTypeTag(String parameter) throws ListStatisticsIn return statsType; } - + //@@author chinhan99 /** * Checks if the parameter contains numeric characters. * * @param parameter The user input after the user tag. * @return A boolean value indicating whether there are numeric characters within the parameter. - * @author chinhan99 */ public static boolean containNumeric(String parameter) { char[] characters = parameter.toCharArray(); @@ -471,7 +475,6 @@ public static boolean containNumeric(String parameter) { * * @param parameter The user input after the user tag. * @return true if there are alphabetical characters within the parameter. - * @author chinhan99 */ public static boolean containAlphabet(String parameter) { char[] characters = parameter.toCharArray(); diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java deleted file mode 100644 index 21f53be8b..000000000 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.duke.command; - -import org.junit.jupiter.api.Test; - -public class AddCommandTest { -} diff --git a/src/test/java/seedu/duke/command/PurgeCommandTest.java b/src/test/java/seedu/duke/command/PurgeCommandTest.java index a377eb750..c91309643 100644 --- a/src/test/java/seedu/duke/command/PurgeCommandTest.java +++ b/src/test/java/seedu/duke/command/PurgeCommandTest.java @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class PurgeCommandTest { - + //@@author brian-vb @Test public void purge_IfEmpty_ReturnTrue() { TransactionList transactions = new TransactionList(); diff --git a/src/test/java/seedu/duke/common/UtilitiesTest.java b/src/test/java/seedu/duke/common/UtilitiesTest.java index fd4207be0..d55756c5f 100644 --- a/src/test/java/seedu/duke/common/UtilitiesTest.java +++ b/src/test/java/seedu/duke/common/UtilitiesTest.java @@ -7,6 +7,7 @@ import static seedu.duke.parser.ParameterParser.containNumeric; public class UtilitiesTest { + //@@author chinhan99 @Test public void containNumeric_IfContainsNumeric_ReturnTrue() { boolean testOutputContainsNumber = containNumeric("Food1"); @@ -18,4 +19,4 @@ public void containNumeric_IfDoesNotContainNumeric_ReturnFalse() { boolean testOutputWithoutNumber = containNumeric("Food"); assertFalse(testOutputWithoutNumber); } -} +} \ No newline at end of file diff --git a/src/test/java/seedu/duke/data/transaction/TransactionTest.java b/src/test/java/seedu/duke/data/transaction/TransactionTest.java index feebfaf5c..4f6d7e5ff 100644 --- a/src/test/java/seedu/duke/data/transaction/TransactionTest.java +++ b/src/test/java/seedu/duke/data/transaction/TransactionTest.java @@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class TransactionTest { + //@@author chydarren LocalDate date = LocalDate.of(2022, 1, 1); Transaction transaction = new Income("Milked cows in the farm", 50, "Salary", date); @@ -43,4 +44,4 @@ public void testSetCategory() { transaction.setCategory("Love"); assertEquals("Love", transaction.getCategory()); } -} +} \ No newline at end of file diff --git a/src/test/java/seedu/duke/parser/ParameterParserTest.java b/src/test/java/seedu/duke/parser/ParameterParserTest.java index 745742940..f77425e42 100644 --- a/src/test/java/seedu/duke/parser/ParameterParserTest.java +++ b/src/test/java/seedu/duke/parser/ParameterParserTest.java @@ -11,6 +11,7 @@ public class ParameterParserTest { + //@@author chinhan99 @Test public void execute_InvalidCategory_ExpectedException() { AddCommand addCommand = new AddCommand(); @@ -45,6 +46,7 @@ public void execute_InvalidAmountInput_ExpectedException() { ); } + //@@author wcwy @Test public void parse_AddCommandEmptyDate_ExpectedException() { AddCommand addCommand = new AddCommand(); From 3e146faf61a690f0eb0fcd00d9307bfaecace722 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Sun, 16 Oct 2022 22:21:15 +0800 Subject: [PATCH 149/416] Add storage feature and exception handling --- .gitignore | 1 + META-INF/MANIFEST.MF | 3 + src/main/java/seedu/duke/Duke.java | 18 +- src/main/java/seedu/duke/Storage.java | 184 +++++++++++++++++- .../java/seedu/duke/command/AddCommand.java | 54 ++--- src/main/java/seedu/duke/command/Command.java | 1 + .../seedu/duke/command/DeleteCommand.java | 49 +++-- .../java/seedu/duke/command/PurgeCommand.java | 24 ++- .../java/seedu/duke/common/DateFormats.java | 3 +- .../java/seedu/duke/common/ErrorMessages.java | 5 +- .../java/seedu/duke/data/TransactionList.java | 63 ++++-- .../seedu/duke/data/transaction/Expense.java | 4 + .../seedu/duke/data/transaction/Income.java | 4 + .../duke/data/transaction/Transaction.java | 4 + .../StorageInputCorruptedException.java | 17 ++ .../exception/StorageWriteErrorException.java | 15 ++ .../java/seedu/duke/parser/CommandParser.java | 2 +- .../seedu/duke/parser/ParameterParser.java | 10 +- text-ui-test/EXPECTED.TXT | 3 + 19 files changed, 380 insertions(+), 84 deletions(-) create mode 100644 META-INF/MANIFEST.MF create mode 100644 src/main/java/seedu/duke/exception/StorageInputCorruptedException.java create mode 100644 src/main/java/seedu/duke/exception/StorageWriteErrorException.java diff --git a/.gitignore b/.gitignore index f69985ef1..0c5ba6d7d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,6 @@ src/main/resources/docs/ *.iml bin/ +/data/duke.txt /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 000000000..19e86fe56 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: seedu.duke.Duke + diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index d070d7c65..7b09b1621 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -3,8 +3,11 @@ import seedu.duke.command.Command; import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageWriteErrorException; import seedu.duke.parser.CommandParser; +import java.io.IOException; + public class Duke { //@@author paullowse private Storage storage; @@ -14,16 +17,14 @@ public class Duke { //@@author chinhan99 public Duke() { // TODO: Add a file path when implementing storage feature ui = new Ui(); - transactions = new TransactionList(); - // TODO: Ideal code after adding the storage feature - /**storage = new Storage(filePath); + storage = new Storage(); try { - tasks = new TaskList(storage.load()); - } catch (DukeException e) { - ui.showLoadingError(); - tasks = new TaskList(); - }**/ + transactions = new TransactionList(storage.initializeFile()); + } catch (MoolahException e) { + Ui.showErrorMessage(e.getMessage()); + transactions = new TransactionList(); + } } //@@author paullowse @@ -39,6 +40,7 @@ public void run() { } catch (MoolahException e) { Ui.showErrorMessage(e.getMessage()); } + } } diff --git a/src/main/java/seedu/duke/Storage.java b/src/main/java/seedu/duke/Storage.java index fffda8276..4f24c0978 100644 --- a/src/main/java/seedu/duke/Storage.java +++ b/src/main/java/seedu/duke/Storage.java @@ -1,14 +1,196 @@ package seedu.duke; +import seedu.duke.command.Command; +import seedu.duke.common.DateFormats; import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageInputCorruptedException; +import seedu.duke.exception.StorageWriteErrorException; +import seedu.duke.parser.ParameterParser; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.ArrayList; +import java.util.Scanner; + +import static seedu.duke.parser.CommandParser.getCommand; public class Storage { //@@author chinhan99 + private static final String DIRECTORY_PATH = "data"; + private static final String FILE_PATH = "data/duke.txt"; + private static final String DELIMITER = " \\| "; + + private TransactionList storedTransactions; public Storage() { + this.storedTransactions = new TransactionList(); + } + + + /** + * Checks if duke.txt and the directory exists. If any of these does not exist, it would be created. + * + * @return The duke.txt file which existed / is created. + * @throws IOException When there are issues creating the file. + */ + + private static File checkIfFileExist() throws IOException { + File directory = new File(DIRECTORY_PATH); + File file = new File(FILE_PATH); + + if (!directory.exists()) { + directory.mkdir(); + System.out.println("* Created new directory *"); + } + if (!file.exists()) { + file.createNewFile(); + System.out.println("* Created new file for use *"); + } else { + System.out.println("* Existing file detected *"); + + } + return file; } - public void writeToFile(TransactionList transactions) { + /** + * Initializes the duke.txt by checking its existence, then store the data values to the program. + * + * @return The TransactionList storing entries which would be used in the program. + * @throws StorageInputCorruptedException If there are errors due to corrupted duke.txt data. + * @throws StorageWriteErrorException If the file could not be created or could not be written. + */ + public TransactionList initializeFile() throws MoolahException { + try { + File file = checkIfFileExist(); + Scanner input = new Scanner(file); + storeFileValuesLocally(storedTransactions, input); + System.out.println("* duke.txt loaded successfully! *"); + + } catch (MoolahException e) { + //Catch any parsing errors and throw the default StorageInputCorruptedException + throw new StorageInputCorruptedException(); + } catch (IOException e) { + throw new StorageWriteErrorException(); + } + return storedTransactions; } + + /** + * Stores values from duke.txt to the program by parsing each line in the file. + * + * @param storedTransactions The TransactionList to hold the stored values from the file. + * @param input The input from duke.txt to be processed. + * @throws MoolahException WHen there are parsing errors, due to corrupted data. + */ + + private void storeFileValuesLocally(TransactionList storedTransactions, Scanner input) throws MoolahException { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateFormats.DATE_STORAGE_OUTPUT_PATTERN.toString()); + + Command command = null; + while (input.hasNext()) { + String line = input.nextLine(); + String[] splits = line.split(DELIMITER); + if (splits.length != 5) { + throw new StorageInputCorruptedException(); + } + + String type = splits[0]; + String category = splits[1]; + String amountString = splits[2]; + //Date has been formatted in duke.txt and must be synthesized into the correct string format before parsing. + try { + LocalDate date = LocalDate.parse(splits[3], formatter); + String dateString = synthesizeDateString(date); + + + String description = splits[4]; + + String parametersInput = "t/" + type + " c/" + category + " a/" + amountString + " d/" + dateString + + " i/" + description; + command = getCommand("add", parametersInput); + + ParameterParser.parse(command, parametersInput); + + // amount would be converted into an integer before being used in the addition of transaction locally + int amount = Integer.parseInt(splits[2]); + + switch (type) { + case "expense": + storedTransactions.addExpenseDuringStorage(description, amount, category, date); + break; + case "income": + storedTransactions.addIncomeDuringStorage(description, amount, category, date); + break; + default: + throw new StorageInputCorruptedException(); + } + } catch (DateTimeParseException e) { + // If the date format is incorrect, which is due to corrupted date information + throw new StorageInputCorruptedException(); + } + + } + + } + + + /** + * Synthesizes the date into dateString , which would be in the correct format to process. + * + * @param date which would be processed into a string. + * @return dateString, in the correct format to be parsed. + */ + + private String synthesizeDateString(LocalDate date) { + + String dateOfMonth = String.valueOf(date.getDayOfMonth()); + String month = String.valueOf(date.getMonthValue()); + String year = String.valueOf(date.getYear()); + // 10, 100 , 1000 would be renamed to constant once an appropriate name is discussed. + if (date.getMonthValue() < 10) { + month = "0" + month; + } + if (date.getDayOfMonth() < 10) { + dateOfMonth = "0" + dateOfMonth; + } + if (date.getYear() < 10) { + year = "000" + year; + } else if (date.getYear() < 100) { + year = "00" + year; + } else if (date.getYear() < 1000) { + year = "0" + year; + } + + String dateString = dateOfMonth + month + year; + return dateString; + } + + /** + * The function called each time there are changes to the transactions Arraylist. + * + * @param transactions The updated Arraylist. + * @throws IOException If there are issues writing to the duke.txt file + */ + public void writeToFile(ArrayList transactions) throws IOException { + FileWriter fileWriter = new FileWriter(FILE_PATH); + String transactionEntry = ""; + for (Transaction transaction : transactions) { + + transactionEntry = transaction.getType() + " | " + transaction.getCategory() + " | " + + transaction.getAmount() + " | " + transaction.getDate() + " | " + + transaction.getDescription(); + + fileWriter.write(transactionEntry + "\n"); + } + fileWriter.close(); + } + } diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index db8d23fc7..b1eaa7a98 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -6,9 +6,12 @@ import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; +import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.MoolahException; import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.StorageWriteErrorException; +import java.io.IOException; import java.time.LocalDate; import java.util.logging.Level; @@ -129,34 +132,41 @@ public void setDate(LocalDate date) { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { //@@author chinhan99 - addLogger.setLevel(Level.WARNING); - addLogger.log(Level.INFO, "Add Command checks the type of the transaction " - + "before adding into the transaction class."); - assert date != null; - //@@author wcwy - switch (type) { - case Expense.TRANSACTION_NAME: - String expense = transactions.addExpense(description, amount, category, date); - Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); - addLogger.log(Level.INFO, "New expense transaction has been added " - + "and the UI should display acknowledgment message respectively."); - break; - case Income.TRANSACTION_NAME: - String income = transactions.addIncome(description, amount, category, date); - Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); - addLogger.log(Level.INFO, "New income transaction has been added " - + "and the UI should display acknowledgment message respectively."); - break; - default: - addLogger.log(Level.WARNING, "InputTransactionUnknownTypeException thrown when the transaction type" - + " is unknown."); - throw new InputTransactionUnknownTypeException(); + try { + addLogger.setLevel(Level.WARNING); + addLogger.log(Level.INFO, "Add Command checks the type of the transaction " + + "before adding into the transaction class."); + assert date != null; + //@@author wcwy + switch (type) { + case Expense.TRANSACTION_NAME: + String expense = transactions.addExpense(description, amount, category, date); + Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); + addLogger.log(Level.INFO, "New expense transaction has been added " + + "and the UI should display acknowledgment message respectively."); + storage.writeToFile(transactions.getTransactions()); + break; + case Income.TRANSACTION_NAME: + String income = transactions.addIncome(description, amount, category, date); + Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); + addLogger.log(Level.INFO, "New income transaction has been added " + + "and the UI should display acknowledgment message respectively."); + storage.writeToFile(transactions.getTransactions()); + break; + default: + addLogger.log(Level.WARNING, "InputTransactionUnknownTypeException thrown " + + "when the transaction type is unknown."); + throw new InputTransactionUnknownTypeException(); + } + } catch (IOException e) { + throw new StorageWriteErrorException(); } //@@author chinhan99 addLogger.log(Level.INFO, "End of Add command."); } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index db04e6d2d..039027932 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -5,6 +5,7 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +import java.io.IOException; import java.time.LocalDate; /** diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 42e4ae215..bddf26293 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -5,7 +5,9 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.InvalidIndexException; import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageWriteErrorException; +import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -65,6 +67,7 @@ public void setEntryNumber(int entryNumber) { } //@@author brian-vb + /** * Executes the "delete" command. Checks and parses the necessary parameters before deleting transaction. * @@ -79,31 +82,37 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws Checks if userInput is in the correct input format by further parsing, before adding entry to arraylist */ - deleteLogger.setLevel(Level.WARNING); - deleteLogger.log(Level.INFO, "Delete Command checks whether the index is valid " - + "before executing the command."); - boolean isInputValid = true; - int index = entryNumber; - int numberOfTransactions; - numberOfTransactions = transactions.size(); - if ((index > numberOfTransactions) || (index <= 0)) { - isInputValid = false; - } - assert index > 0; - if (isInputValid) { - String transaction = TransactionList.deleteTransaction(index); - Ui.showTransactionAction(INFO_DELETE.toString(), transaction); - deleteLogger.log(Level.INFO, "The requested transaction has been deleted " - + "and the UI should display the confirmation message respectively."); - } else { - deleteLogger.log(Level.WARNING, "InvalidIndexException thrown when the index " - + "is invalid."); - throw new InvalidIndexException(); + try { + deleteLogger.setLevel(Level.WARNING); + deleteLogger.log(Level.INFO, "Delete Command checks whether the index is valid " + + "before executing the command."); + boolean isInputValid = true; + int index = entryNumber; + int numberOfTransactions; + numberOfTransactions = transactions.size(); + if ((index > numberOfTransactions) || (index <= 0)) { + isInputValid = false; + } + assert index > 0; + if (isInputValid) { + String transaction = TransactionList.deleteTransaction(index); + Ui.showTransactionAction(INFO_DELETE.toString(), transaction); + deleteLogger.log(Level.INFO, "The requested transaction has been deleted " + + "and the UI should display the confirmation message respectively."); + storage.writeToFile(transactions.getTransactions()); + } else { + deleteLogger.log(Level.WARNING, "InvalidIndexException thrown when the index " + + "is invalid."); + throw new InvalidIndexException(); + } + } catch (IOException e) { + throw new StorageWriteErrorException(); } deleteLogger.log(Level.INFO, "This is the end of the delete command."); } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index a160c695c..b88377c5d 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -3,7 +3,10 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageWriteErrorException; +import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; @@ -42,6 +45,7 @@ public PurgeCommand() { } //@@author brian-vb + /** * Executes the operations related to the command. * @@ -50,7 +54,7 @@ public PurgeCommand() { * @param storage An instance of the Storage class. */ @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) { + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { // Shows confirmation prompt before deleting all transactions purgeLogger.setLevel(Level.WARNING); purgeLogger.log(Level.INFO, "Purge Command checks if there are no transactions" @@ -68,12 +72,17 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { String input = ui.readCommand(); if (input.equals("Y")) { - TransactionList.purgeTransactions(); - assert PurgeCommand.isEmpty(transactions); - Ui.showInfoMessage(INFO_PURGE.toString()); - purgeLogger.log(Level.INFO, "The transactions list is now empty" - + " and the UI should display that information to the user respectively."); - purgeLogger.log(Level.INFO, "The end of the Purge Command."); + try { + TransactionList.purgeTransactions(); + assert PurgeCommand.isEmpty(transactions); + Ui.showInfoMessage(INFO_PURGE.toString()); + purgeLogger.log(Level.INFO, "The transactions list is now empty" + + " and the UI should display that information to the user respectively."); + storage.writeToFile(transactions.getTransactions()); + purgeLogger.log(Level.INFO, "The end of the Purge Command."); + } catch (IOException e) { + throw new StorageWriteErrorException(); + } return; } @@ -92,6 +101,7 @@ public static boolean isEmpty(TransactionList transactions) { } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/common/DateFormats.java b/src/main/java/seedu/duke/common/DateFormats.java index 686314ffc..26b36145e 100644 --- a/src/main/java/seedu/duke/common/DateFormats.java +++ b/src/main/java/seedu/duke/common/DateFormats.java @@ -6,7 +6,8 @@ public enum DateFormats { //@@author wcwy DATE_INPUT_PATTERN("ddMMyyyy"), - DATE_OUTPUT_PATTERN("MMM dd yyyy"); + DATE_OUTPUT_PATTERN("MMM dd yyyy"), + DATE_STORAGE_OUTPUT_PATTERN("yyyy-MM-dd"); public final String message; diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index a142b6645..0e41850ca 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -19,7 +19,10 @@ public enum ErrorMessages { ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), - ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"); + ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), + ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. To preserve data, please STOP the program and " + + "edit your data file correctly."), + ERROR_STORAGE_WRITE("Unable to write to Duke.txt. Please save your current Duke.txt file and restart Moolah"); //@@author chydarren public final String message; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 94076c384..c27efaed8 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -21,7 +21,12 @@ public class TransactionList { //@@author chinhan99 private static ArrayList transactions; + public TransactionList(TransactionList transactionList) { + transactions = transactionList.getTransactions(); + } + //@@author wcwy + /** * Initialises the variables of the TransactionList class. */ @@ -30,6 +35,7 @@ public TransactionList() { } //@@author brian-vb + /** * Gets a specific entry from the transactions list, to be used by other classes. * @@ -62,13 +68,14 @@ public static String deleteTransaction(int index) { } //@@author wcwy + /** * Adds a transaction of class type Expense into the transactions list. * - * @param description More information regarding the transaction, written without any space. - * @param amount Value of the transaction in numerical form. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param description More information regarding the transaction, written without any space. + * @param amount Value of the transaction in numerical form. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @return A string that states the details of the added expense transaction. */ public String addExpense(String description, int amount, String category, LocalDate date) { @@ -80,10 +87,10 @@ public String addExpense(String description, int amount, String category, LocalD /** * Adds a transaction of class type Income into the transactions list. * - * @param description More information regarding the transaction, written without any space. - * @param amount Value of the transaction in numerical form. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param description More information regarding the transaction, written without any space. + * @param amount Value of the transaction in numerical form. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @return A string that states the details of the added income transaction. */ public String addIncome(String description, int amount, String category, LocalDate date) { @@ -92,12 +99,25 @@ public String addIncome(String description, int amount, String category, LocalDa return income.toString(); } + public void addIncomeDuringStorage(String description, int amount, String category, LocalDate date) { + Income income = new Income(description, amount, category, date); + transactions.add(income); + } + + + public void addExpenseDuringStorage(String description, int amount, String category, LocalDate date) { + Expense expense = new Expense(description, amount, category, date); + transactions.add(expense); + } + + //@@author chydarren + /** * Checks whether the transaction belongs to the Income or Expense class type. * - * @param transaction The transaction record from the transactions list. - * @param classType The transaction class type that is either Income or Expense. + * @param transaction The transaction record from the transactions list. + * @param classType The transaction class type that is either Income or Expense. * @return A boolean value indicating whether transaction record belongs to the given class type. * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ @@ -108,15 +128,15 @@ public boolean isTransactionInstance(Object transaction, String classType) throw /** * Checks whether a transaction fulfills the given filter criteria. * - * @param transaction The transaction record from the transactions list. - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param transaction The transaction record from the transactions list. + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @return A string containing the formatted transaction list. * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ public boolean isMatchListFilters(Transaction transaction, String type, String category, - LocalDate date) throws InputTransactionUnknownTypeException { + LocalDate date) throws InputTransactionUnknownTypeException { boolean isMatch; try { isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) @@ -131,9 +151,9 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c /** * List all or some transactions based on selection. * - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @return A string containing the formatted transaction list. * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ @@ -167,7 +187,14 @@ public String findTransactions(String keywords) { return transactionsList; } + + public ArrayList getTransactions() { + return transactions; + } + + //@@author brian-vb + /** * Purges all records in the transactions list. */ diff --git a/src/main/java/seedu/duke/data/transaction/Expense.java b/src/main/java/seedu/duke/data/transaction/Expense.java index 8a2ed2115..59870dc87 100644 --- a/src/main/java/seedu/duke/data/transaction/Expense.java +++ b/src/main/java/seedu/duke/data/transaction/Expense.java @@ -15,6 +15,10 @@ public Expense(String description, int amount, String category, LocalDate date) super(description, amount, category, date); } + public String getType() { + return TRANSACTION_NAME; + } + public String getIcon() { return ICON_EXPENSE; } diff --git a/src/main/java/seedu/duke/data/transaction/Income.java b/src/main/java/seedu/duke/data/transaction/Income.java index b1b892aab..047e6b352 100644 --- a/src/main/java/seedu/duke/data/transaction/Income.java +++ b/src/main/java/seedu/duke/data/transaction/Income.java @@ -15,6 +15,10 @@ public Income(String description, int amount, String category, LocalDate date) { super(description, amount, category, date); } + public String getType() { + return TRANSACTION_NAME; + } + public String getIcon() { return ICON_INCOME; } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 9e07ac4ac..1a5aacd58 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -27,6 +27,10 @@ public Transaction(String description, int amount, String category, LocalDate da this.date = date; } + public String getType() { + return null; + } + public String getDescription() { return description; } diff --git a/src/main/java/seedu/duke/exception/StorageInputCorruptedException.java b/src/main/java/seedu/duke/exception/StorageInputCorruptedException.java new file mode 100644 index 000000000..fb8e67419 --- /dev/null +++ b/src/main/java/seedu/duke/exception/StorageInputCorruptedException.java @@ -0,0 +1,17 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class StorageInputCorruptedException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STORAGE_FILE_CORRUPTED.toString(); + } + +} diff --git a/src/main/java/seedu/duke/exception/StorageWriteErrorException.java b/src/main/java/seedu/duke/exception/StorageWriteErrorException.java new file mode 100644 index 000000000..764baf451 --- /dev/null +++ b/src/main/java/seedu/duke/exception/StorageWriteErrorException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class StorageWriteErrorException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STORAGE_WRITE.toString(); + } +} diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index 4fc2e06a8..247879591 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -90,7 +90,7 @@ public static String[] splitInput(String fullCommandInput) { * @return Command object created. * @throws InvalidCommandException If the command word is not supported by the application. */ - private static Command getCommand(String commandWordInput, String parameterInput) throws InvalidCommandException { + public static Command getCommand(String commandWordInput, String parameterInput) throws InvalidCommandException { // TODO: Remove parameter input once a solution is found for managing parameter that allows space Command command = null; switch (commandWordInput.toUpperCase()) { diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index b438817e2..e26429d6f 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -106,7 +106,7 @@ public static void parse(Command command, String parametersInput) throws MoolahE * @param splits The user input after the command word, split into a list for every space found. * @throws InputMissingTagException If there is a missing mandatory tag. */ - private static void checkMandatoryTagsExist(Command command, String[] splits) throws InputMissingTagException { + public static void checkMandatoryTagsExist(Command command, String[] splits) throws InputMissingTagException { String[] tags = command.getMandatoryTags(); for (String tag : tags) { boolean found = findMatchingTagAmongInputs(tag, splits); @@ -125,7 +125,7 @@ private static void checkMandatoryTagsExist(Command command, String[] splits) th * @param splits The user input after the command word, split into a list for every space found. * @throws InputUnsupportedTagException If there is an extra tag that is not recognised. */ - private static void checkUnsupportedTagsNotExist(Command command, String[] splits) + public static void checkUnsupportedTagsNotExist(Command command, String[] splits) throws InputUnsupportedTagException { String[] mandatoryTags = command.getMandatoryTags(); String[] optionalTags = command.getOptionalTags(); @@ -155,7 +155,7 @@ private static void checkUnsupportedTagsNotExist(Command command, String[] split * @param splits The user input after the command word, split into a list for every space found. * @throws InputDuplicateTagException If there is an extra of the same tag. */ - private static void checkDuplicateTagsNotExist(String[] splits) throws InputDuplicateTagException { + public static void checkDuplicateTagsNotExist(String[] splits) throws InputDuplicateTagException { HashMap tagOccurenceMap = new HashMap<>(); for (String split : splits) { assert split.length() >= MINIMUM_TAG_LENGTH; @@ -178,7 +178,7 @@ private static void checkDuplicateTagsNotExist(String[] splits) throws InputDupl * @param splits The user input after the command word, split into a list for every space found. * @throws EmptyParameterException If there exists a tag without parameter. */ - private static void checkParameterNotEmpty(String[] splits) throws EmptyParameterException { + public static void checkParameterNotEmpty(String[] splits) throws EmptyParameterException { for (String split : splits) { if (split.length() == 2) { parserLogger.log(Level.WARNING, "An empty parameter error is caught for the given tag input: " + split); @@ -236,7 +236,7 @@ private static boolean findIfParameterTagAmongTags(String parameter, String[] ta * @param splits The user input after the command word, split into a list for every space found. * @throws MoolahException If Moolah Manager captures any command input exceptions. */ - private static void setCommand(Command command, String[] splits) throws MoolahException { + public static void setCommand(Command command, String[] splits) throws MoolahException { assert command != null; for (String split : splits) { String tag = split.substring(0, SPLIT_POSITION); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index d1ca9609f..041ad83fb 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,3 +1,6 @@ +* Created new directory * +* Created new file for use * +* duke.txt loaded successfully! * ____________________________________________________________ Hello! I'm Moo and I will help you to manage your finances. Enter if you need the list of commands. From ed0d993dfef5accc28969fe64977208b69f3d124 Mon Sep 17 00:00:00 2001 From: chinhan99 Date: Mon, 17 Oct 2022 14:30:22 +0800 Subject: [PATCH 150/416] Change incorrect comments and add constants --- src/main/java/seedu/duke/Storage.java | 33 +++++++++++++++------------ text-ui-test/EXPECTED.TXT | 6 +++++ 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/main/java/seedu/duke/Storage.java b/src/main/java/seedu/duke/Storage.java index 4f24c0978..3af22a821 100644 --- a/src/main/java/seedu/duke/Storage.java +++ b/src/main/java/seedu/duke/Storage.java @@ -26,6 +26,10 @@ public class Storage { private static final String DIRECTORY_PATH = "data"; private static final String FILE_PATH = "data/duke.txt"; private static final String DELIMITER = " \\| "; + private static final int NUMBER_OF_STORED_PARAMETERS = 5; + private static final int TENS = 10; + private static final int HUNDREDS = 100; + private static final int THOUSANDS = 1000; private TransactionList storedTransactions; @@ -48,13 +52,13 @@ private static File checkIfFileExist() throws IOException { if (!directory.exists()) { directory.mkdir(); - System.out.println("* Created new directory *"); + Ui.printMessages("* Created new directory *"); } if (!file.exists()) { file.createNewFile(); - System.out.println("* Created new file for use *"); + Ui.printMessages("* Created new file for use *"); } else { - System.out.println("* Existing file detected *"); + Ui.printMessages("* Existing file detected *"); } return file; @@ -72,10 +76,10 @@ public TransactionList initializeFile() throws MoolahException { File file = checkIfFileExist(); Scanner input = new Scanner(file); storeFileValuesLocally(storedTransactions, input); - System.out.println("* duke.txt loaded successfully! *"); + Ui.printMessages("* duke.txt loaded successfully! *"); } catch (MoolahException e) { - //Catch any parsing errors and throw the default StorageInputCorruptedException + // Catch any parsing errors and throw the default StorageInputCorruptedException throw new StorageInputCorruptedException(); } catch (IOException e) { throw new StorageWriteErrorException(); @@ -98,14 +102,14 @@ private void storeFileValuesLocally(TransactionList storedTransactions, Scanner while (input.hasNext()) { String line = input.nextLine(); String[] splits = line.split(DELIMITER); - if (splits.length != 5) { + if (splits.length != NUMBER_OF_STORED_PARAMETERS) { throw new StorageInputCorruptedException(); } String type = splits[0]; String category = splits[1]; String amountString = splits[2]; - //Date has been formatted in duke.txt and must be synthesized into the correct string format before parsing. + // Date has been formatted in duke.txt and must be synthesized into the correct string format before parsing try { LocalDate date = LocalDate.parse(splits[3], formatter); String dateString = synthesizeDateString(date); @@ -154,18 +158,17 @@ private String synthesizeDateString(LocalDate date) { String dateOfMonth = String.valueOf(date.getDayOfMonth()); String month = String.valueOf(date.getMonthValue()); String year = String.valueOf(date.getYear()); - // 10, 100 , 1000 would be renamed to constant once an appropriate name is discussed. - if (date.getMonthValue() < 10) { + if (date.getMonthValue() < TENS) { month = "0" + month; } - if (date.getDayOfMonth() < 10) { + if (date.getDayOfMonth() < TENS) { dateOfMonth = "0" + dateOfMonth; } - if (date.getYear() < 10) { + if (date.getYear() < TENS) { year = "000" + year; - } else if (date.getYear() < 100) { + } else if (date.getYear() < HUNDREDS) { year = "00" + year; - } else if (date.getYear() < 1000) { + } else if (date.getYear() < THOUSANDS) { year = "0" + year; } @@ -177,7 +180,7 @@ private String synthesizeDateString(LocalDate date) { * The function called each time there are changes to the transactions Arraylist. * * @param transactions The updated Arraylist. - * @throws IOException If there are issues writing to the duke.txt file + * @throws IOException If there are issues writing to the duke.txt file. */ public void writeToFile(ArrayList transactions) throws IOException { FileWriter fileWriter = new FileWriter(FILE_PATH); @@ -188,7 +191,7 @@ public void writeToFile(ArrayList transactions) throws IOException + transaction.getAmount() + " | " + transaction.getDate() + " | " + transaction.getDescription(); - fileWriter.write(transactionEntry + "\n"); + fileWriter.write(transactionEntry + System.lineSeparator()); } fileWriter.close(); } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 041ad83fb..9011019ec 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,7 +1,13 @@ +____________________________________________________________ * Created new directory * +____________________________________________________________ +____________________________________________________________ * Created new file for use * +____________________________________________________________ +____________________________________________________________ * duke.txt loaded successfully! * ____________________________________________________________ +____________________________________________________________ Hello! I'm Moo and I will help you to manage your finances. Enter if you need the list of commands. ____________________________________________________________ From d14a6d51da820e927cd395c943f2712121ab379e Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 17 Oct 2022 14:40:53 +0800 Subject: [PATCH 151/416] Add heading structure for Developer Guide --- docs/DeveloperGuide.md | 168 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 160 insertions(+), 8 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 64e1f0ed2..84eff8dd5 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,15 +1,167 @@ # Developer Guide +- [Preface](#preface) +- [Acknowledgements](#acknowledgements) +- [Setting Up the Project](#setting-up-the-project) +- [Design](#design) + * [Architecture](#architecture) + * [Command Component](#command-component) + * [Data Component](#data-component) + * [Storage Component](#storage-component) + * [Parser Component](#parser-component) + * [UI Component](#ui-component) + * [Common Component](#common-component) +- [Implementation](#implementation) + * [Overview for Transaction](#overview-for-transaction) + * [Implementation for Transaction](#implementation-for-transaction) + * [Help Command](#help-command) + * [Add Command](#add-command) + * [Edit Command](#edit-command) + * [List Command](#list-command) + * [Find Command](#find-command) + * [Stats Command](#stats-command) + * [Delete Command](#delete-command) + * [Purge Command](#purge-command) + * [Storage Operations](#storage-operations) + + [Reading From a File](#reading-from-a-file) + + [Writing To a File](#writing-to-a-file) + * [Logging Operations](#logging-operations) +- [Appendix A: Product scope](#appendix-a--product-scope) + * [Target user profile](#target-user-profile) + * [Value proposition](#value-proposition) +- [Appendix B: User Stories](#appendix-b--user-stories) +- [Appendix C: Non-Functional Requirements](#appendix-c--non-functional-requirements) +- [Appendix D: Glossary](#appendix-d--glossary) +- [Appendix E: Instructions for manual testing](#appendix-e---instructions-for-manual-testing) + +## Preface + +{Provide brief details of the Moolah Manager application and the purpose of the Developer Guide} + +_Written by: Author name_ + ## Acknowledgements -{list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +{List here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} + +_Written by: Author name_ + +## Setting Up the Project + +{Detail how to set up the project on one's computer, assuming the software is Intellij IDEA} + +_Written by: Author name_ + +## Design + +{Describe the design of the product. Use UML diagrams and short code snippets where applicable.} + +### Architecture + +_Written by: Author name_ + +### Command Component + +_Written by: Author name_ + +### Data Component + +_Written by: Author name_ + +### Storage Component + +_Written by: Author name_ + +### Parser Component + +_Written by: Author name_ + +### UI Component + +_Written by: Author name_ + +### Common Component + +_Written by: Author name_ + +## Implementation + +### Overview for Transaction + +{Give a brief overview of the Transaction feature in Moolah Manager application.} + +_Written by: Author name_ + +### Implementation for Transaction + +{Provide the class diagram for Transaction} + +_Written by: Author name_ + +### Help Command + +{Describe the implementation for the Help Command} + +_Written by: Author name_ + +### Add Command + +{Describe the implementation for the Add Command} + +_Written by: Author name_ + +### Edit Command + +{Describe the implementation for the Edit Command} + +_Written by: Author name_ + +### List Command + +{Describe the implementation for the List Command} + +_Written by: Author name_ + +### Find Command + +{Describe the implementation for the Find Command} + +_Written by: Author name_ + +### Stats Command + +{Describe the implementation for the Stats Command} + +_Written by: Author name_ + +### Delete Command + +{Describe the implementation for the Delete Command} + +_Written by: Author name_ + +### Purge Command + +{Describe the implementation for the Purge Command} + +_Written by: Author name_ + +### Storage Operations + +#### Reading From a File + +#### Writing To a File + +_Written by: Author name_ + +### Logging Operations -## Design & implementation +{Describe how logging is performed in the developer code} -{Describe the design and implementation of the product. Use UML diagrams and short code snippets where applicable.} +_Written by: Author name_ +## Appendix A: Product scope -## Product scope ### Target user profile {Describe the target user profile} @@ -18,21 +170,21 @@ {Describe the value proposition: what problem does it solve?} -## User Stories +## Appendix B: User Stories |Version| As a ... | I want to ... | So that I can ...| |--------|----------|---------------|------------------| |v1.0|new user|see usage instructions|refer to them when I forget how to use the application| |v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| -## Non-Functional Requirements +## Appendix C: Non-Functional Requirements {Give non-functional requirements} -## Glossary +## Appendix D: Glossary * *glossary item* - Definition -## Instructions for manual testing +## Appendix E: Instructions for manual testing {Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} From 9c5492abad75e515b537238d21f21c2696097adb Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 17 Oct 2022 18:18:27 +0800 Subject: [PATCH 152/416] Update DeveloperGuide.md --- docs/DeveloperGuide.md | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 84eff8dd5..aeac41ae2 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -49,8 +49,20 @@ _Written by: Author name_ ## Setting Up the Project {Detail how to set up the project on one's computer, assuming the software is Intellij IDEA} +Before setting up the project on your computer, kindly check that you have installed: +* Java JDK 11 +* Intellij IDEA - highly recommended -_Written by: Author name_ +Firstly, you should fork this repo, before cloning the fork to your computer. + +Next, + +1. **Ensure that Intellij JDK 11 is defined as an SDK**, as described in this [[Set up JDK guide]](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you have used JDK 11 in a previous Intellij project. + * You _might need to set the Project language level_ section to the SDK default option. +2. **Import the project _as a Gradle project_**, as described in [[se-edu's Import Gradle Project guide]](https://se-education.org/guides/tutorials/intellijImportGradleProject.html). +3. **Running the project**: After finishing the import, locate the `src/main/java/seedu.duke/Duke.java` file in this project, right-click it, and choose `Run Duke.main()`. + +_Written by: Paul Low_ ## Design @@ -120,7 +132,20 @@ _Written by: Author name_ {Describe the implementation for the List Command} -_Written by: Author name_ +The full command for list is `list [t/TYPE] [c/CATEGORY] [d/DATE]` +For example, if `list' is called, all transactions that are present in Moolah Manager will be listed out +Adding tags such as type, category and date will list all transactions to that category + +In a command like `list c/food` +1. The `main()` method in Duke calls `run()` in Duke. The `ui` reads the command and parses it + through `CommandParser.parse()`. +2. Within `CommandParser.parse()`, `getCommand()` is called to obtain the command, before `ParameterParser.parse()` +is called +3. Various checks are done through functions within `parameter.parse()` +4. The list command is undergoing execution in `command.execute()` which will call `listTransactions()` in ListCommand +5. `ui.showTransactionsList()` is then executed since parameters are present + +_Written by: Paul Low_ ### Find Command @@ -158,8 +183,14 @@ _Written by: Author name_ {Describe how logging is performed in the developer code} -_Written by: Author name_ +Our team used `java.util.logging` package for the purposes of logging. We instantiated various objects +for different classes such as `parserLogger` and `addLogger` to set the log messages. + +**Logging Levels**: +* `WARNING`: An exception has been caught by the app +* `INFO`: Information details what the app has done +_Written by: Paul Low_ ## Appendix A: Product scope ### Target user profile From 6fae03cb1c010834073969a8007138cda7a96ce1 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 17 Oct 2022 19:50:48 +0800 Subject: [PATCH 153/416] DG: Add implementation for Transaction object --- docs/DeveloperGuide.md | 32 +++++++++++++++++++++--- docs/diagram/Transaction.puml | 28 +++++++++++++++++++++ docs/images/TransactionClassDiagram.png | Bin 0 -> 9576 bytes 3 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 docs/diagram/Transaction.puml create mode 100644 docs/images/TransactionClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index aeac41ae2..6a1ff227a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -100,15 +100,39 @@ _Written by: Author name_ ### Overview for Transaction -{Give a brief overview of the Transaction feature in Moolah Manager application.} +{Give a brief overview of the Transaction features (i.e. purpose of each command) in Moolah Manager application.} _Written by: Author name_ ### Implementation for Transaction -{Provide the class diagram for Transaction} - -_Written by: Author name_ +Each `Transaction` object in Moolah Manager represents a transaction record, which can be of `Income` +or `Expense` type. Below is a simplified class diagram (note: methods have been omitted) containing the attributes +within each transaction and how the transactions are associated with the `TransactionList`. + +

+ +
+ Figure 1: SimplifiedClass Diagram for Transaction +

+ +The `TransactionList` holds a dynamic array list that can store multiple `Transaction` objects. + +Each Transaction object contains the following mandatory member attributes: +1. **category** A category for the transaction. +2. **description** More information regarding the transaction, written without any space. +3. **amount** Value of the transaction in numerical form. Only integers within 0 and 10000000 is accepted. +4. **date** Date of the transaction. The format must be in "yyyyMMdd". + +Some important operations are performed within the `TransactionList` class, which implements the following: +- `TransactionList#addIncome(String description, int amount, String category, LocalDate date)` - Adds a transaction of class type Income into the transactions list. +- `TransactionList#addExpense(String description, int amount, String category, LocalDate date)` - Adds a transaction of class type Expense into the transactions list. +- `TransactionList#listTransactions(String type, String category, LocalDate date)` - List all or some transactions based on selection. +- `TransactionList#findTransactions(String keywords)` - Find specific transaction(s) based on any keywords inputted by the user. +- `TransactionList#deleteTransaction(int index)` - Deletes a transaction from the transactions list based on the specified index. +- `TransactionList#purgeTransactions()` - Purges all transactions in the transactions list. + +_Written by: Chua Han Yong Darren_ ### Help Command diff --git a/docs/diagram/Transaction.puml b/docs/diagram/Transaction.puml new file mode 100644 index 000000000..1b545efa8 --- /dev/null +++ b/docs/diagram/Transaction.puml @@ -0,0 +1,28 @@ +@startuml +'https://plantuml.com/sequence-diagram + +hide circle +skinparam classAttributeIconSize 0 + +class TransactionList { +} + +class "{abstract}\n Transaction"{ + - category: String + - description: String + - amount: int + - date: LocalDate +} + +class Income { +} + +class Expense { +} + + +Income -up-|> "{abstract}\n Transaction" +Expense -up-|> "{abstract}\n Transaction" + +TransactionList -> "*" "{abstract}\n Transaction" : transactions +@enduml \ No newline at end of file diff --git a/docs/images/TransactionClassDiagram.png b/docs/images/TransactionClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..d9d1586cca6980a79bc6d7a4ef406da3cfac9a6f GIT binary patch literal 9576 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 literal 0 HcmV?d00001 From cabb13859b5bde83f706f31bb5abe217830d4ff0 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 17 Oct 2022 23:15:51 +0800 Subject: [PATCH 154/416] Add missing abstract tag to Transaction.java --- src/main/java/seedu/duke/data/transaction/Transaction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 1a5aacd58..bb873fbde 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -5,7 +5,7 @@ import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; -public class Transaction { +public abstract class Transaction { //@@author chydarren private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; From 68e815b4112885bcd3ebc8dcbc53d47fdc19501f Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 17 Oct 2022 23:20:51 +0800 Subject: [PATCH 155/416] Add basic explanation on the data component in the developer guide --- docs/DeveloperGuide.md | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6a1ff227a..151df2089 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -77,8 +77,27 @@ _Written by: Author name_ _Written by: Author name_ ### Data Component +The data component is represented by a `data` package which consists of all the classes that is part of the data stored +by Moolah Manager. Within the `data` package, a transaction package and a transactionList class is stored. -_Written by: Author name_ +The `transactionList` class is a representation of a list of transactions, and the +operations related to the `transactionList` such as CRUD are implemented within this class. + +Within the transaction package, the following classes are stored: +1. Transaction +2. Income +3. Expense +4. Category + +The `Transaction` class is the abstract classes of an `Income` or an `Expense`. The `Category` represents a category of +a transaction. + +A more detailed explaination on the implementation on the transactions can be viewed under Section +[Implementation for Transaction](#implementation-for-transaction). + + + +_Written by: Chia Thin Hong_ ### Storage Component From 16270874e29dfcee9e8cb72b0be0640d4a238b94 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 17 Oct 2022 23:42:08 +0800 Subject: [PATCH 156/416] Extract the style used for the class diagrams into Style.puml --- docs/diagram/Style.puml | 2 ++ docs/diagram/Transaction.puml | 5 +---- 2 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 docs/diagram/Style.puml diff --git a/docs/diagram/Style.puml b/docs/diagram/Style.puml new file mode 100644 index 000000000..408e46cb2 --- /dev/null +++ b/docs/diagram/Style.puml @@ -0,0 +1,2 @@ +hide circle +skinparam classAttributeIconSize 0 \ No newline at end of file diff --git a/docs/diagram/Transaction.puml b/docs/diagram/Transaction.puml index 1b545efa8..1d8f7e81c 100644 --- a/docs/diagram/Transaction.puml +++ b/docs/diagram/Transaction.puml @@ -1,8 +1,5 @@ @startuml -'https://plantuml.com/sequence-diagram - -hide circle -skinparam classAttributeIconSize 0 +!include Style.puml class TransactionList { } From a8f9333d4fd298410faf1d816e18df7f1f8f11e3 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 17 Oct 2022 23:57:01 +0800 Subject: [PATCH 157/416] Create setter for date and change getType() to abstract for transaction setDate() method could be used in editCommand in Milestone 2. --- .../java/seedu/duke/data/transaction/Transaction.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index bb873fbde..cd12b57b1 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -27,9 +27,7 @@ public Transaction(String description, int amount, String category, LocalDate da this.date = date; } - public String getType() { - return null; - } + public abstract String getType(); public String getDescription() { return description; @@ -59,6 +57,10 @@ public LocalDate getDate() { return date; } + public void setDate(LocalDate date) { + this.date = date; + } + //@@author wcwy public String printFormattedDate() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_OUTPUT_PATTERN.toString()); From 1fe2c58983131b417c862a348d35878ba6c34d34 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 18 Oct 2022 00:21:41 +0800 Subject: [PATCH 158/416] Remove class level restriction for the ArrayList in transactionList --- src/main/java/seedu/duke/command/DeleteCommand.java | 2 +- src/main/java/seedu/duke/command/PurgeCommand.java | 2 +- src/main/java/seedu/duke/data/TransactionList.java | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index bddf26293..c63708227 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -95,7 +95,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws } assert index > 0; if (isInputValid) { - String transaction = TransactionList.deleteTransaction(index); + String transaction = transactions.deleteTransaction(index); Ui.showTransactionAction(INFO_DELETE.toString(), transaction); deleteLogger.log(Level.INFO, "The requested transaction has been deleted " + "and the UI should display the confirmation message respectively."); diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index b88377c5d..5296171c5 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -73,7 +73,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws if (input.equals("Y")) { try { - TransactionList.purgeTransactions(); + transactions.purgeTransactions(); assert PurgeCommand.isEmpty(transactions); Ui.showInfoMessage(INFO_PURGE.toString()); purgeLogger.log(Level.INFO, "The transactions list is now empty" diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index c27efaed8..6b651ceba 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -19,7 +19,7 @@ public class TransactionList { private static final String LINE_SEPARATOR = System.lineSeparator(); //@@author chinhan99 - private static ArrayList transactions; + private ArrayList transactions; public TransactionList(TransactionList transactionList) { transactions = transactionList.getTransactions(); @@ -61,7 +61,7 @@ public int size() { * @param index An index of the transaction that is to be retrieved. * @return A string tht states the details of the deleted transaction. */ - public static String deleteTransaction(int index) { + public String deleteTransaction(int index) { Transaction transaction = transactions.get(index - 1); transactions.remove(index - 1); return transaction.toString(); @@ -198,7 +198,7 @@ public ArrayList getTransactions() { /** * Purges all records in the transactions list. */ - public static void purgeTransactions() { + public void purgeTransactions() { transactions.clear(); } } From 881a67305b0bf1f830451ca549f27c62419dec6f Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 18 Oct 2022 00:53:45 +0800 Subject: [PATCH 159/416] Add class diagram and explain the details for data component in DG --- docs/DeveloperGuide.md | 12 +++- docs/diagram/DataComponentClassDiagram.puml | 74 ++++++++++++++++++++ docs/images/DataComponentClassDiagram.png | Bin 0 -> 54726 bytes 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 docs/diagram/DataComponentClassDiagram.puml create mode 100644 docs/images/DataComponentClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 151df2089..bbcd6099d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -81,7 +81,7 @@ The data component is represented by a `data` package which consists of all the by Moolah Manager. Within the `data` package, a transaction package and a transactionList class is stored. The `transactionList` class is a representation of a list of transactions, and the -operations related to the `transactionList` such as CRUD are implemented within this class. +operations related to the `transactionList` implemented within this class. Within the transaction package, the following classes are stored: 1. Transaction @@ -89,8 +89,16 @@ Within the transaction package, the following classes are stored: 3. Expense 4. Category +The structure of the data component in Moolah Manager is illustrated in the class diagram below: +![Data Component Class Diagram](images/DataComponentClassDiagram.png) + +From the class diagram, it can be seen that the transactionList mainly contain methods for CRUD operations to the list, +such as getting, adding, editing, deleting and purging of transaction(s) from the list. + The `Transaction` class is the abstract classes of an `Income` or an `Expense`. The `Category` represents a category of -a transaction. +a transaction. Within the transaction class and its subclasses, getters and setters are used to access the private +variables. These classes override the toString() method for a self-defined print format when the transactions are +displayed. A more detailed explaination on the implementation on the transactions can be viewed under Section [Implementation for Transaction](#implementation-for-transaction). diff --git a/docs/diagram/DataComponentClassDiagram.puml b/docs/diagram/DataComponentClassDiagram.puml new file mode 100644 index 000000000..91c8c8bef --- /dev/null +++ b/docs/diagram/DataComponentClassDiagram.puml @@ -0,0 +1,74 @@ +@startuml +!include Style.puml +class Category{ + +} + +class CategoryList{ + +} + +class TransactionList { + + TransactionList() + + TransactionList(TransactionList) + + getEntry(int) : Transaction + + size() : int + + addExpense(description:String, amount:int, + category:String, date:LocalDate) : String + + addIncome(description:String, amount:int, + category:String, date:LocalDate) : String + + listTransactions(type: String, category:String, + date:LocalDate) : String + + findTransaction(String) : String + + deleteTransaction(int) : String + + purgeTransactions() +} + +class "Transaction {abstract}"{ + - category: String + - description: String + - amount: int + - date: LocalDate + + Transaction(description:String, amount:int, + category:String, date:LocalDate) + + getType() : String {abstract} + + getDescription() : String + + setDescription(String) + + getAmount() : int + + setAmount(int) + + getCategory() : String + + setCategory(String) + + getDate() : LocalDate + + setDate(LocalDate) + + printFormattedDate() : String + + printFormattedCategory() : String + + toString() : String +} + +class Income { + + TRANSACTION_NAME: String = "income"{static} + + ICON_INCOME: String = "[+]" {static} + + Income(description:String, amount:int, + category:String, date:LocalDate) + getType() : String + getIcon() : String + toString() : String +} + +class Expense { + + TRANSACTION_NAME: String = "expense"{static} + + ICON_INCOME: String = "[-]" {static} + + Expense(description:String, amount:int, + category:String, date:LocalDate) + getType() : String + getIcon() : String + toString() : String +} + + +Income -up-|> "Transaction {abstract}" +Expense -up-|> "Transaction {abstract}" + +TransactionList -> "*" "Transaction {abstract}" : contains > + +@enduml \ No newline at end of file diff --git a/docs/images/DataComponentClassDiagram.png b/docs/images/DataComponentClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..8c2f74444e8569bc7c96877722a5e6dc26cdf1b9 GIT binary patch literal 54726 zcmbSzbzGEd*YyY@3JOR{htee>NQ<qj z`JONM=lJ6=_gt}K?X}mweP7Cm-9sTjfj}VlB*cZ~ArQnb5D0?wZ3OU}-o}jz@E=;J zhzeBK+`{3V{#z(SOy5l3>J3z1k4(pb%n%B-;AUpFc=yH(YGeA2N!Q%;{$nm8@Prm4 zMHT2DzlYoakFo!<@k-Ks^cluC^;rQau4t;yw^xLo8)KueQy^g?#Ynz-Md&n_Z(NW! zVS^pBSZjBtHI|x+>{#R(R5Qi7H4@F18+^{Z{MET$n?>V?9{{@`vV&r@@~x)DOsEfC9T$(VmJfQs;1c` zk1?6@D#mj5rIhQZgU4S`ZqH@K9(;b)q#*asBYu$}OuuUVCjBP@zpv%{Q?>o3%0ElW zG<7XISQ*uP2#n-oIAUqTUOiXBkaiBUS2cH4g;>5}cXahEkEQEQk9%4soEa<@?eP=) zW2UeR%17C-WSOL-!ACf~+Gh%aCz>85W-kg7IqdVr+6N4)<29LTnZ^8#^nn%!`Yhf~bb1W7&g{$Rx5T!7+6ceFu6+E|<6;NHjoL*eu7KRj$;-j@2{OKL zUHWt)YHO$1alEDA<{b^)`%!^&JD9q#DCbV}*KJ$Tw<}~TS((gmDQ6}K*B&Z-O-A7J zi1lPivU{|P{bJbT!!c3aQ(ls4;s@4s6FwE0U#xVAB6*_cmzqe=*H2cfKXt}KAnp(e zVL?TEt@Su36~&>-OEJIO6gND{bl9QBbUq%pqq-GHKNLQ>W0V`k-=oVLI7r1o_o;_t zI&VJ6Khm-KO}uUo2LXYOH2cp&j?C9gF&OW1q(AyZW{Su$wdtRH+51wKv2^BYuKKgh zYNPaU9sjM@&+YTGAH|1NLkpTFZ)?}8yrb=SE=YkZfu9;8?|0Y#N-V!2hkEr-M?Q?w ztA8X8A^snJqLIO8?Uj^p59$M8GHUAT2AyBHET+G+=r&dVnsmxlN8{0Z z!o$stf`WpHiAh00K_&A={3LM1#!UQWX=kU5(jb?R4gbZ_+)7`Dn21PlMQ(onTidyS z02wt^mhw4;mE_QTC?(7Wcb%?-=OlC{$Z>g1vYw zXK(t7P@P7_p+JWxC$*H67NOkHazhet-prJlj;X1sy?F6Le03*YRgj@}yACx+xxj35 z_IN2FfzOGfAQ39`Q6_kP!JFG4GpE15f9mq$*L>Gtp1OoPYn7E~NT`xh$A;dW-BAB6 zvRlcx!=&IEp9OU{i!fdW$tRbVmTK3!=1L718yg?dPRfF1NpPBRQrL!k`Qp6L79QKx z?ih?CFE7ueRo&8-;Bq+PbN`_|QW_ye>Id`61>BKQPxcf)6(89`QpXwL6*h&iH(-`*st}Gx#g3V;;&!7 z*3`r)zqG|9Az>PPBM!d)*4+o)gM&?Niiuv;?|bhB_xARVk8AWKL%mRx3$*n0cm3S3 zo^d^Fm7u3zv@L27Lg(A>OSn)hxk>q_@qXlIGH$HTQ!nR^>kJ7Ac_{X|e_+5D^JOj- ziITGN)moO)jmT2; z1vNhGVnQaOrL~pZTtxCE!r#1h;b~kaIFyJ8+z_a4^XFID+&e+3WBHm@qobp3?d`{1 zWze`>jmo0j+<^7ed3gneXcjuU)uWv`sk+^mSWa_XH<~8;wQ-Cjst?HM*WZ3n;C8+~ zQYQ{R8N=_QnfWZjDw#rR@gVScUe|vkoq@)C$|HvKR-{1e?!-vRdM9Y}4jpb#PYI{p4 z6e=av0bE2jj=OQ4n^TyZho_O6dHG9Ho!31=ue4Hk1K|-OhyViT1qiUe{R*6j`y~jY ze?IyjAAMwOw>f@H0r@_(K3aV~4`!10U5S;ZC0(*ytj~cLKA3b{X(bJPFuJcunQfYa4u>w^-Hxb&%P4}h0mgUT3 zyg`MWAst(0yD{qJb*r~1l1a0zt4q5`?+%YXr?TMoU#a9Pg|Yg-Lu-fq?tJU`_;^D@ z1Mu>m6tS(zny~>&EUw(qBK>w9U0rW9;*O0AQ+b%Rs*idlndPEKvUq`$qvA0Iv6ArE zGMMkyp)%)c)xawKmlE7u57$RJWN_YgL<#D~@w>zbFU6XFYh7;uG1}l~Oe6ZI)YMdL zJiO4*UEf$u9B&L+IS)}@%UNLqgQc8;Z!cgGBQ^?OUM8DSS5y=>e#*`cJy;#+V1m)1k;f<39jpy~vYo(&VEpBz z4@5TbDE6(BaPjdaVB3poZkM&SeJc#Kv|Lv6;+mRqURIa9CH-em+=?!)%-;-U@YP8thyNw<_EVlv=#~|J+REs z1(Jjp$R^H+peEab6s)hW&&bHgW4AefxoO88mzI{MmpXv!MhjxD&DoJB?f zR|r{peR6WL(^gI7z(}EPbFzqkiwx{B8(V1kM72Zlkq`@igdFQ4Ktc2K9noxJMTDv_ zsF9%H4>3{~VVA+7Av`=h0RU^(!8P< zbl4I}^YV>Ie+bbRZX08Orqt9%kx8Au2mF->3?*hPUx{V2u(DcBfA`4GXD((^Edu^b zuII4*1Gy`nul>|{G1}l3I*Ie?MtNjd7>GVB4+pgWo(hh|l0;jN4#&(xgZJ6XAFMqd z9UGGf3%!{kH#;}Cx3_o9Ng=FkGyBTG&L|z?ej5%9jpa=A%^{GRURT(c=4H>E$J*KP z9&fvcjj@SIXh?{j3Hr7aXAOX;F^_d4HC6%Qa`Ps|RpkH5Gc3u_`_NjqY;#)O*_s)6Nmm1*Oy1KeAGE3R7a=@96(1!Z=ESb5#M)0@|&tqNkE+GdT?T+T}qIoqR>AZ)@-!b?&cqgQ%xoWos%VglIG(8OgM;w8X4h^f7LCt%A(w z_gb_c*rEL$l>;0f9UB`P74-lF7{~qPi7LB2WA(Ck_*_)boVT~PN1qQUYp2x% zh>)AhuJIIpWOuIQ(o<3FQ=l8D3%YlF?|`beQ$tE0N}Fx^*V2qv1onb zh{B?lf!f*@-3uSddgi^cu>o9LCLv0fGK@P0d}WDX#t?DS)(5gw6*)*%y6OZF;DSzQi9@WRNL>QMQSCp7Sm64 z!PX?Jzl(T80lW!f1-Hn1n;-9y#ye4TM*7y|#6VqPY02V=bo}v+MqF3hjG_KwN3``! z9g@)j@Q^=@;qEWJto;0lfPlflLD%z>Le^I~Sy?j~_5xsQC4p8|_Y1K`g$pz)t@a!o z*mRLb&kXSl7P|OP0|dwwmb@&r?ph1j-Q^f(%ZMe5>}C=f8mC0)tJLihjP8Q16Ytr_fu$oe*B%L+5g~ z)12!({ryJy-^~}S*a%)}l=J~(nO|7wIechFJyf8*WK-b+LXV=1v@|0F!+hN|2FPk)<*ye2?78R+`25-S z_BN{EvFo6w_AFATQ+gf9EvnJ^Ss(>T;4~Cv45i#5CME{?RYXK2lu8=q-aXA4$0x;- zbF0ouXkt0d37mt&!-kFbFxbg3Zh&%ojX7XOtLWVXY+wZ%5f(`M#g#ji*@$jLMdSwecfUSiwfX5E9~KiZB)z#$ z8Oz7(L^eRsB<)q9+0H?QCpzE#Z#U%qR6DGj^i1SW^ftRgt5qtBtxy%Zr!H(`!Une(Sd3kunyt@ zy-G8-JXzV<$uUVuN$X3)9hqw%ZHuxLGQ7OKGt$yrKpyQ7Csfzdn+G{a^LaTiIDb6m z&)~<`H=5st`|^S4l9!ODir`Ad?_^N;phBFXsks>)pH=VtXwuXJ^hA3n&W} zcQwmrXV|)U+3dHc$INSMQd2*2;~9I9Azw!i$W?wFX0up<8kjG^Gcgz9GU~O>qqXj;@RlTV#-j*6}OeOZ1aGwRk;V zVF^6a($W$YmjT1g|JHkw|Lc5u<0^Ik*OYtxC%yCtLjXtVg)SYXVnKMXl4D-xM5XnT zKg2!$E$|``ig$I)F-p%`%c&})qh6! z{jI6m>gq4A0BHVq9c12u(o2q0_HKmLYQ7~X2xBr(==*TQoE$ae=B>Y<3dkA&Jem%F zP0Gs3x=u^Nbj6|1ocDXgn6Xm?IuGWmD$ampOi9~HSN(6Y>H3?fq@pZl8+-v3 zR!BZz3oY6;NO0lMX}F#B z9xCn$c#*U?LL2xIU!NVQuZM{NaskGQM#``8@?}tDB*2G{+8WBrIJ7p`*XQ+>9)QB4 z;F;(4w#C&m)`kk`H^wvT|L0P!qM-iTU>*n(sj2gz_Or3Ec@dq-0GK;VImzd207+y| zuVPlUp*0G%<_jyU8X%KF{$Zc=9`yYD7XUHmYA7hAg!#5vT{AZEijuj`vkiCjy=Rkf z__A|yixDv(@N?+#Q=13%JQ|s7o{d%snQ@$q0mw$cvO&cQpj-}k#O~tH*hW&nwDfdV zCy)rWT@D7oamtr)adCl#)MsX9&NT;GyW}?fxg+q<U7SXgW>C(y$j*7uZ@x9{A+e`4HkQc#n={*sr0iRp7d0MS#6 z#6q;@6dB0vyMH&xn;xi;OOGo!^V{JtDj?gW|G$4C8+1)Rq(AHVAoPI@4kX`GS=@Ln z1V5x5k4&4)Ao>FgUO`xU5K%KX%!`{xh$zN>Ew7wPHol|n*MuD@v7f<>$2Y*|cu_QP z?+jM;y^h1XUeD39eya`wPiZNs z|41ea2edHcIQ&18$STUp&4Aeo*weA7>X9{C2D!~?vr_C`Y+Rh?t5=k2kZuo3=TC4nMqV#eXQDH&kK$C1!oiB&6b9;cFjh}zh?uo zoq>ZRA_k=QzJ44w*uDFhuv6o?xJt>nC`o2x&4j79#YIJxjSe@+;87iH!_>y6R8Q~a z(dJMADZN4|2nDWwzk~!MpmgPg_j%y8x79-1&n0s!-$(^{`6ITz-(1Ev$_w~0MxD}@ zVb(f*f9kkT^UVXo@c!!f{0q${U^w(j-I^>}H6VWPt|{7;GibGd*cZ!TnjHHEg^amo zA2a-zmJ-&TR8^of8C_)~#pS2Hgmgj$w{?3m(7i}SnI$iN>5luqXRC!4`ui3A4#4SF zzb^^}p{Ni|2Y@ArU~x zxd{&})oz!r3j>T3R@P2RN)h%;9X1(g`b$jbE~UGj8p114R@iBBBjXFl)z7AG!t*NV z)`;OVY0_2cjlgVj{!W81(fE503=BB^+Wgtssa0V?UcAih8xXL*ygYpmUq`u`2nVN8 zgfVLvqzMo?4Zq;kkIU}7Gc?THC*|XQ^!Hm97H;@N$f;79lq6U!6K>b8g9PKTxxIb* zhFC}a{seqw;|rZE3pB|+)s^jg4j$j`z!TpMoKiq|pRt92yVSRTLIQ(bsCIpV$%KT4 z7HC#AwGkE4YstvU;t5dA)FKR4zS;MFQ{wOHXP~RA%d81&K)+)}#>6C30!f5NivW*y z^6w1EnWlivvz?eIvL{C;U>83b3gv3T_|ZL~OuyAJ!JSUjSg?oSf(6fX2z17@aOO^a z|IrS$Ypis36S_pk=4h#ZvYUeg*X6{A{LpZE<%sa`U>86Rf-Kk}9@`8k{pgPiPe8fI z;F|PF87%7RR4kV*=bCEvI(e$e1-i}>S8NWSo%|M1&kLFodOlp3P)^as5Fu+_aB72pk%ab}|>DGgjG4%$6n+KJ#YOO0Do&&5(e`hmn(>szu_4&Ql-RTeLpi3Ds(BnJgHhyC&K++9< zMnhawxWWzUCZUT;i%9Ob;Rb|JdgHw{T{< zCt@N(PxWQJ4<04#Ku&&*1UK&L`=#8iIwhfSi&G`_Vx+#>mt(j>~!x)C|o5`1P!H+&72Gb4Gf4xVv5Zn_! zjD2!_A*8ING-=(%r=#vwXn?G=zQ;W>G6IU*YE$i7z)w36@gRvjR|ak;#{>A9^z`)q zz<~&37KjpTf(A@nT->Ze`$&o<;GWvs+P=t8gJsb&FpQU(s!K{rN=V$vVxMHA_gTQx zvKMp~T>y27186xmIj~e{u4D|awo)f(RM|2L%s+5y^0NepRcMV$?S$dlJ0XyX8}Mtd z^&eebHEwM&G&SA$dwRhlB$UkdAN%AJ_vng5Us&8T_61yjUA(=F^WliWL(zcgzan?q z*`|l&0Gm>PHE7&9BYIqCXjDiy@fC=9*3Yddj?*CY79`>S(%6Kjihe*~93I+g364nE z+gGS~lfxR?5pUf!G%?ZDr+X=+CAe};l{6~#^z?lC^yw@}cYVYI=adL@oP~uYIjswx z-P*n*LALMwPEuTEl>VJXHfu^5eX8>E0Q(1nTZ_AeiJp^_^J{Ygz=|4+ndfhNY%uZh z!TtUqWDUb)1dM?sd#@f~#O0Azx}P$8RVK`ifPNl-vQMEw?gMYpR2wWt7hHh9S-s<()V2uM2ns*}oO=fgq;>etza2S5thUjs{1Dva4Of@GCT(!AEH_Ra_HlAOs(-g0f?5ul$0Y7`%z+aXFkw(H$SWUeZJ z!g(SjBs3DZ09x{QR`UL=zjw6taq#fOL`Bhvo_140$9j4S?RREDeHIlJ1*8E9+%|rI zntZD0gA?ZJC&a8)B3_wVjT|S!shSm2Nez=(uhzMl_uyVzn+p**Z87!%Nq>*6Bk$X*EZbNYyej?f{5mhW0(i}9Qr2O zNLL3MX|}{MN-Vf@7ked|+0@i@Z}BG~$PmY0T~2K+2iqeU{Xcz5Qvj3?`=vyt6|Shc zrq{wOpF$ES#s<~RiFxg}0V$Bh$abP&0!G8`Lc8}} zd{*b9Z2(979@6#HH1u1Hm6?{*%@4FQ*J@~J5Mg0GMAKunQwl;0M{{Eth{YT1^P;-0 z;RwkNH^w4ELm#Sp+)~TooN`tuJA8F*fGdaZQYP+WcRdS^KQ6k4j6sO0H4R&Xkr5Ff zJ^G#iB6U`hxkgz3t#aq{rMwCaz!ig_K`P+-MJCt~ko`Bxi{GmpLTxEP^E(xLB3+t& zM}g~RUK1d$nKUZM@}kftB&F_=y6pF~M>6;I_DI*|supLye;U*}t}4jg&y~B^z2=4a z_xcJr#)_@k?{$51dTciSG#*JGl!(=802wYrBD336m#c9iCBt4q*}B>e4i4sP;j9@q zPw=lPHvzB?28MTSVYJXfB7Wzngpj7*<1C{0N{e%W0&f=(khqo|V#P!us z%nXo1D~sPlGp$vYl7{Y4UZ>dKRXnQ&*L7^W^WGp)yvrFBn-!LGx9B<9*?k5szELF9 zSH1f4STw#lM~Ao8|3QBihXOyy%+3ZSRSdh4yrJPT5Hdw;5=bs(Slw1*ISjv}JY?Pj z74oOi)}hzr@V(r6&lFgV}usDIzj=H zIs0{Gbl3QBK-}a7O1$nDPj&M~A-Zj;;Z(HN8(CMkLq)`E-+iX=X626xl%egVE(CIVYJ70$~*!UcaE&Ga?$hNl-yh* zN$&oUt#7Ah@!!5CCqL&Z@!6ytJf7|um;LfI3DoEP4w*TL#cS|ZPzJJaUL$W8H*92_ z@nW+U`Oa(>66bY_`|BtqZY~syK;w?K@M(J4v<>$GWeIP)^SGYbYng&(4PfCJBwi7K z?&R{M)0V#N0z=s3Dp4W=tr5XzZf(&1)6ya-KGn0O=y&+t2sB*-d}YjUzS4i@&}RI1 zE72jOjQ##XHw7$G&8r9Aq~-j^k^$G>BeJ)=18#GGWR@!8AcclR}^f4Vx| z^4p1KeiQ}jap3$#uIG-0W0aB+LBXQVzq^O)ZK^ai16$(IcAc!7u2AH4Q=`BmXQ*ec%lS^a zyYjn@m#QS>n zjE{ebiW=_jCetTddZ@ON(9+qNpPfDXldJkYnl93&X+h^Q8Up%N^_|Zz-o?My_yExf z?>{l7I|^sM`hvb@ws1oR9LXxH;+mDC|4-@U`1rS6 z=4NSbIzMBc^nWlc^szKfE@$eKnt0JXEO5h#H-+am@3qC9knx>%;OX97*96=VoP33r zlj7l(19Et$-0-^gawapj!Oyngs-ZN}|CnGEOEpUVsLypFTp7rA8f1ec zbW8{xJjOlQ$%&=>vAq7QYCcNYTr4jdGkDlB} z(Uc{DOCJqO6VhdZYEqk7r|uPnS5GwwVTk{8_52v6u)71WFOx5f^o6OjrDA|aFCuK8 zI>DLMe*5*sO78Vj&u1I`0eSt`gFkua$NzQ9O!ULNk;QQn#l`0Fm{Qg|MF&vSasepcF@sBsSgF22la`cT21-@!{^Ech&Q={tg>w7M4$u5G;pm5&l~7pq z6gTIyX?Rhx_gx=}x%c%c5scjJ&wlVSf_AAf&Lv&2uhYf1=M#tKv$2@v#J0#RU;I?0 z=5_R$wDefEHh$N!Pm()c=C!XdQu=VzW$%C#($4(T9(3|s#Zdy)Q}51`9LT;LYxKu+ zliRWm5R!aAFAhJyMj?Ip`Lz+Pw59beoDWvsxUy?|f{K9Q85e%j(UMY=Ar5x-pokji zioB~UzieUuhs{djQB$NlvTIHsnZC%+ry8y(4XN$q@u+q<(Ow;lXunnJ-2smbzz@^X zN|Zp^5FJECn}Q#T>Cr=O+ypQs%%eT3cqC0?dmZETKtOaEep|;l6>Y*qdLQm9>?h{G z#X`DM?jhrI6GhcS)yUrb^(DQg#OzZJdy75et@RsY<$EU+s@#?@;}Bu`B^TVV{p7&| zhdOg5C64l^c#-`64qvZGEzk*;@LGjf1Nn)SJ3^4&FK$Gx(igI_k$I4ta8xaKi%Gxp zt>$|JIm8uK(=}G>*i>iRSQ+(4l$2=|6)GJgb@wN*s&Hhv*zwbC%+^$ZtNP*4XS!v% z+C|6+FS(oH*EkV}ryLnGX&JC=rCr?yqMS*>-GSiXVCrcxszQYYJ61J{l>MLVnE0<_9C|@ZNnRL562f`yD#G+M5@0N5s+J58u0<%?(((`z#eTU@l0_(MM9CT*I!Qascn&|uhKrQu{!eirWqmM3$ERa)|#V= z{Z72dla%(7y!H;$klhheZd2dSpV2U@0Mn4_4!|@}ZT?(OcG4-IrS+z&FX=QTR=jNb zM3~FqB3QSUhSkHBs4lvtCH9+Kg8sEtpi91P8eQ#bkkTG^AmDmejbUAF&{3+OLum<# zH~NM2S?UYe4MK5a+`@8Wpo_m|XRsm+MO%*OsTz{w@+(;ET{Okr(?YqX9IhJWtFZ=0^LM@<~S%Aiv997cn*||DYz3 zb+(wb|H)wF6qx-t)#WLB?{zYSb(^F4SnIezY1Z&O4K)Y)VYOvDaK}4sbzQF3Z&+UhFpQW{2!kXUNa-}#5nf}U4CHozl zHSH!EQ^E!0)%T*1o-rc;*F+$#s0LOdl|lUaQ8^&x=)c@lS}{@WxDW3Ui1xqe4RR6j zHA@7<@p6-LuJxXD(VPb|$!mk~%3OMS`mM?(5~a7x^6G0PwB40`}u(%c5q7@W03 ze-NKuhAWcCdj3?HarqP6OB|*VAW&rZ^0e|J zQ%6mvHBjR-Ipc$+4Eid^5xZ;MxMuSq&+Oo6vzhZ+o1EedlKb3yB-C%2sa^WSgoI8j zy_7w(M#d&{%Zs)UN2F^!@CV64>N7e&KmTX9T~eTx+wI~EG{Vyi2v@CCf7jyoO&nuJ z)u^?OlcMAa-6!GAnJDD^&ApSgg5v5_z;%HBHX0%j>_m(~1A)<#UTk|S9<3_Zo>Wbp z%kZ1N5E7~%ue8R(#H3-EvK=#BZ$(|lPVl*pGODOrh5!!05pmK-%{Mzc8(>v=AG|#^ z9vF1K#HcSdajSQ!C_Re)(`hnQ>65kQ+vSotM}J=hljiu>PGY<9o!F@}(GraciytYM zR1OWGXr%pEvDBuKh8FqJ?Nfo+^O8r4gVlp;|zF=11yicIgEdKVZ2i(wQFi`-M zwj*M7^h&uZl~$BEV9o((#Fg{Z!h?dI8Q854|2XMkB)IJeF9r*PGbw3iYvE%N7rQ+JP9EDh4E7v~@4BFh2viX#7*lS>V|RK)N2n$HjFFc;#~-NF*R2AaOg}al2S@ zOFsdXDiH4AIg!PKf(eu%WXNyn;&$LfS8>i z>>!KKpX^SR$q>_I9v(V*duQk5*Eb*D)i_W9zSjTq=Xe&~TgqKPQ$9^-UM=VeGTF;# z=_A4BZb$W`;<VrY`tVBZplQUqyZ5zUX#OiW236lwA5gZ2gYwx7eS>C$cJRJo(iO!Eb!<#ED1(%0oU+l~iv ztHeO$d(IHP9)j>0G=fjjUAGZ?EvorJ-{MndCe6yoYk|{|uHnla3ED}^W@4ZoO56o~ zL-FZdZs2pdvJ)zB=qQ`>=4>a>EwxbCU&K)B`1lyev-0xXM3OuW&{Ozo?CJtEH*ZP(eKv0{S*$+lV_VUbK{87N{lk2nxaz#yU?mBQ#yA`~9`$+#FF&w5t zgf~pfCz=!rf{HrY+H^?Mf&Qg9P+0`DLx5VgVF??wsE(N^XKZa;0wWaX0LdvHRV2#| zMCU!VZf-1GziQ9gYac6z>sG?zU-p3_O--D%E0$BXBua?S#~UuC!Zo6(tgI}kz2GhY zB8(y+pqESf`ZefWBVebV^diW`My1>^kr^JR(S=PPTdWVWjLdT)a47}O9q84VWx`zw z^tT0$exL(aBp@UN6A%Y+Th88(wt!%uRfaqK(FEvP0Uc4f`4m5Nxx2>cS3IwS70}Wu zs-C^J87(oIHLrXMPHajSR4~N}=K;j7M>F32#~<%tfk-Ow5*_Ft+FM#)J1TQUOwf#)+WOaL{etb3pNPjEAC`vpB{Q6# zYQ~~!yQYY_qSCBu4}>r2cwW^q+Zh4#i9_Dk-yvGI>LVy6MtnOBSaRnF$ z`i6&v1($w~Sa16`} zbtejbc-s~Klg*U2nW7~PkmyQOt`~a=^m`7$&nZduOz_~Y6AuENOyo8ge~Ncl#OL5K zqF~QIqGy54k5D&Pe;0r8g2HnB$|r6zVAKG%V^#pe@jmdJ6vCF;vjyheurr}8&CJad zbz>*r(b%rx;Hpj7+6Q$0pghg}#7llsM{ti&>i+KTXKIHa?*K=Xj9_?^;mjWqx8qo( z#i@w-wI;QE&Sv_gk=b?Ulex1{nPWRpq0y%c1IuxpUb$SBsN0>g2}Za8BSUZ>J6xpW z3pB^OH=N*(lnigBoUPh!y>A!<$!&r5*juaS<%C#eKggU$_5A;DHnrIzWPS_JMRl-seIAYiEzpaqVj}6@K#- z->LG=`8WuubkRw_S&dB;<#V2%fzs^rkJB) zfQp8}6Jck&9(~W8b)UgijlEm_whCF1EBRO`m||#G*hQ~#NrDAhX1S0V|DGiw@Il~i z)RD*R`1YH1j!SI?F#?Rs8n zq+IKKfByW5$E@WHKtyK8`se(ulDMaq@6Al2D)ej*4W|wd4?({qqt9rvHB^?r(sn~l z$##W0*(9GR2SlcQRY^(ItXI9I9pailfnwP)y|P%yOu17{gIg=zsRF1!Iqi1N_PT&n zHdgs1lP5546k3t^KuzJ_1nwt5IsmLBsK!DJKUuhd%*Y@uEhYA&YVlh+Bulctv&uCO zUpq;dH}hKfp226?T3QmFNE|O;dY~;3XbBbtzQ<|bYfGx)-)YrSI@&OIYv1kf)hrAf z%R%1hOZJ$2{v9Fs0Cgn(brOQt)gg+2iOti4wdOYLabPUVob^Iuc4;22O*V_Ef4YK$7X zqEic@u5{js;WNIUfO#Ymsd*m_CVOSb#Oa9A?QK0#i{_yh_vZGtA#t4O<_Q>LdU+$Ev&7ciXG-54qMZUZ^g)6?^;{3T4Lg~`=EE7m8! z(u#q*y`3n#sV~0xk1frHx@Jz>(mvwm<_2IHG`j#dhfP4xmcaPvQ`#Q9b@AXb`F38a znc@8fb$^suJjtU-zub;Epe>H8=SHtFTPAK=8#DuhR_*DrNl3xcuFvqnp7{T-l|r*32TEb|lN zpOwrqH@PYG#i1(aeNF2~8pd$OKz z$#8_a*-Y{w=sTEs57v}I`R&>5vJ?O0*zK_9Y)m{aFSaftq+K6^#w5qV%|*~_?rfWaAG_e=(>%lDm1Rv^ z)A-2lcQjVDxcK%T4THnd8_Gj{8{tC!%adx{q`WbwzF5?Mb}kOhbgIqrw$qqhe6^ZE z<#>cwd(=pJc_emRB5Qeb{)0+6D7egK$HwcwHFkbuS>GbJ@VE15K|OOMM}}N))RCT5 zo7Sk__G-ci3>>8nq)thK24^Sw4_rqbGNj1CMADuGEmw}q57Jy2W%1vnag)&7;&m0tIC z)oS8+?0VOu? zThT$eQfSyiVHKasC@l-_kD41iWR>;y4f8psNl(*sguF-Wmm5-7EPB9apGS8 z-S7fuPux9Fmf{pYRzZS2uRSa+5D^val^&umsIF6AiNP}ecKr4-zk%%6xHWpsV8=4j zqDM}w_(-D2$<&wG`lYAinzY~A6d4z1zJVst^7i7M*)!L*>87e@^GFl5K~9HMD5wk6 z0h0ugql9ww;3R@WLY`MqnR=(HHQW|)gv&kv>H}(5I}<$4JD+pt)9^MNay!4doZ7!& zCNMF?18V4LQrq6iYQ0v>)#yXGsyj%aC)lQO#v8~r=4}lPX+nA6I`W&*C9G|}%Q5uL z6aymiAIkPhO4u*x+2Q6Gx|~`l9_$m|`U%jtVq#)IvL=A;#;vKy7&6sIvlRCp~p2Er_R8&<5f5uVL{8NA0FFlyzl{^8$ zVIB_AttgAsI)J_6oi@4TMmrM(E)RyZFZU8IrPic!eg^D&O#vkfyn4JiTN2O)TDGl- zNp!tb|6uNUQ6;iv%CRatW*eEjqajRtBzSi;&FRCpXz6@Ah-tlbOK=F6}ps9 zU{+d+H>Y8qiVpHRX*?PAHaAcGa^6)2a=N(}ed;UZji&$hZQCHE_hIoN7SMw&N!;-P z+He%=51-6BJMAabk|diag#n5X{Y-S6LVCUo!i4++n0zu;(MaLjQ{sf*}QB>5sjHnA*x~W0pTTGg;eNgB`%DMrZV)cND zRC7>IH{R%*S8hxpJuOdsA+lC;S#)+9@3^q7Er%0#{zuD`fZI;<<+ohx;6^s<3C7M# zffxm3lD@I5%F1Jbi#Y)rm7aUW(~=Lk*&Ub$}i(7g4S4O8Ngx%YT%Tk9^2XF!nY(V%GxeTB6F* zOm&>+k+98#;oxCU-iNdcig=an*{wV5@RqBR5`SVdkg3v|vueh~QifIu+OkCi2mfGx zbxTuc9KOTI@O zHZLY{(gSXI4XF0;>1krwT5iCWk}zlq@Rjp5T01+xL02%(pB(qZg1YtUay8?P3M+SC zK*v7(WP3%JaKjbIkOIGXyskeR{O)~ax79%_re}b8LD4!Rj&JIjzrj~sv`M94SFC5n z*<#*A)3#$S0)t^5bi_RIC)G(BXA0Y-TeMvegTOC&GcOs}{o98fSZQ6t#Xj{&szFb( z2&n!K6E4q|v_TWO8x7LTf4(Sd^S?}0a9ltuH8`Gk53L5hO{9RJf2)`|X zKW;0>4YW*tpfdI%I#y`fg=ST$vQ`q#bLtjU@1oSytBwp%;x{s{0{i^O2E4!ckPowfi@{X_+0i-NHVFc|}fZQ#8~nToU%k?2HEgTevL!{fMbco9gKm0olV zMqk4K$?7U30gF)H#@_o*i6jhM>gv>4%KSt%(V3hK;1*T|OMQ$l;xD$Z=xQ=ejez0v zVAfO0L!^3uCE4j2dO>1UYySj8^!UBqwU~8yn-bP@`h|M<&DC(iz*^JeOQxbRU78sPj7}gDxn+X;pNg}l0!N&E&dC=zBtJ#=5 zadh9kFx)choYMHyO2a$P-yOxH%A2kYkk{2_r(_^*U9QNwyf%I|L=a9o}Y)kbvL%ot6}zm*i%sm5fAGE}`NZ z=2a+VLFXg}japCggYS2oT}?2=5paDPe$9YcBC$^drEh-{!1B^sWVhU4MMvfsHt5Uu z&6A;=(Xd!bf4dJiCtfZ79*NdXQt{B$pwT71b#-I>11%N(7nr5ofr!3*Y#vpA_94w` z3!_XY{mff-HgV@{#tg_cC3~Vb4QS@`z?*s8E`PZ-7Hc6h(V7&Q?yQZCMS1f)d^iVY zPmXYEV|^pCYM{rnlySS2{SQ9ecW zuvNJAv*+wR{3cJU%-6O`g2(sW_Z1&T8fSrb3bj3&v6XSfeG@1({a@OZW?J`@rhT+2 z6yfbkJge%edi#k(eeoeUGYG_s`;+N-mf-y-yqEc`7AuciW8SHsoyTMEStyY6@trU3 z@d=qbr@aqxN1_O1(o2}0_$d$i@$&K^?;4-ucQY#fVqq#pPF9M3G;Y?~CHyYrj`IY} z1`y1N7isXW>3w+9bbr%E@$+mB{A#(H*2)5fU>r}L91u<&i9W}Jay|iiXD}qBKi%6y zSWI<#%1666lm7mJu2azD(F-KX8>qr0nk`y=KV#DX=$6j`R6{HE*O#2SMK33sEBZlC zUq8g+KF=cotU?JY3zX(F)KO3vDi8jGt_X(_!}j5QxlAGvBSj9o=uRZA$)9g}?=4$! z#{o{7^og;etLwLFTL^^o4K{}B^Q>piGN2a$#w@`sNcZ?SL&Epx69KKyZAj*V3lH_ZBmW8kMb$gg)CT0PoQD!iIC39N?8JU~C$eHUgMI5VY}Z zU;NLi3{;w|5)~663BJRRuEtvZSk$-vRWM9LXkH&NYP?9gg?~Wz=79fNUF(ZO2>B(eD0HPMa3fNmSemP1v!T zX7eEA)BA5NO4_mN{L1jkilDg(P}Y{A_nuSx*&y#oQ`ERIEWSG=g|#;*0FI7{1Owr3 zU)SSV4Z1gYPSil6TK633)&krn&i%m2stQ{p1!jmDwITo4XCz4`b6BE)5Xy zCV!+Db|d_o%({ry-!#n?4xSb)+ERPaTwKB1Am+iaSsyKCF1&iMRl;ch{jGrLw*qJ} zcYW1Yu{)kB=avWcYmb(*=Vby{r@b1FT?zuv+vl7Uw=k~*A!-YZ{Cn!;)F59vYO~H5PpJ@gw@lAit_9q zhXVxP(FW$cN!bm9fGTpWSCQv`5%=ElT=s7q=$A;89g6Io2$AfWY}s3pEtHkL8dj1W zvS(S@e60$VJcP@s%g(;;OoY*y`ZpckW*I ze|8zGxn?ksyi)Vg$NA~2#7u0A2`gzaj^3L(VQy*x>jh(!6BZNQ+C#l_5Q?R@WIcz= zrXW})s5$d3l))#*?(567j}$IS3qR(!t=M?=F<6^H<3dF^?orB>NRPPML^s=~ZYeSE zj_aL;mtPrVDLA|dNu~ML$;W;=iDGZn>xYx6p8nu~57!Ocki8m3=h{Cfi+7<3?eN#< zZ!_N*!E0)KPXtNp#6(V`pP{h?qxagL212l+;nei76!RUB@aC+70t&y7kPshVlw%#} zt$^Wlv~v0*8ZihtawH#nhJ*YT7kOMVaOZ)W0HSTi#^ZlL$2nMfvkD2(4;Sm*Y?H>s zTG8|VQ|kp>eGUGnzL-iMEI-bzrW>{;dJUYf2lUZkS!7=3NG?-G4Z zucE4gc{jxOrMRN)Zj7WI5iPcPGZzs46=b+((JKcm|$CD>GG1AH*X zL?FX?{~ILj8^%LX;Tx@Uh#YpZ*N?mRF{flRFrOlW;cdwkN!{Ejx7p5vBjnH(`3d^VW^yy(H1ClJviU))E+#d_bW9E70&$=9LWYF^o?a?l*1xuY6_fZA`xM z6@c4du32B#lofVF@vt%uc^24zDZ3TsMvndPib?2~pIPfKvTC((5h8H6tO;()gg@pO zU^ou4u(BnM5BE3nB+W9gZ#yG(06^31NX~ialu1~3(mg-YQfOP`8U>R z%}+bq5tDULbE-yaPNDx$Ei_qJY~*mIG@1X(ZV6FlH(TjsI&%rXinBC%;L3>)B+{bd z+Agia@J-mz2}!)JU+HHG*pO5oK7ovkjC=(3w^92NPSNAUZZY5TGi^3FG6VY;ZmUV+9e;0PT#GGOTp6y)iu6b)Op0;sWAv8LlnG3OjEx zaHM1BcZEa&$gyfa#c%q^RY(+szCeAjjqU`Ce@g|ocDn59^({U%`VGN@!m|!Z0Lv$~ zjm<9Egexgi>SvBB9sE6b<5yhDTd^b`U35-i7pnRC`U34A-~objJG1&-&m{%qyey7I z{o(?j=;t^UdGF8xw3fk+0*3*=7QexFtanBa&lC~Xfcqo5%vBw~Ph9V!o!ZL;mXd)($#&^3TM1dBngw^GQL+SBIiP$~kAwHRu5H}Sc| zb}`b*EOt6-CUO2(Z{vJhvz!<_1Wp~07yrWEIp^P4NF&SiE+#0ETAFM?D!(F<*=xY? z(;xZ$`iDG`p`oPQv;Y0~aLaDvpU{$V?a?7keh)Cj2axUv#Jm4C*3w@LnT7iW1$g{n zlrFIoNyF0g82X)ms9`d@U`4sN`(~FGwq(Nd$P=go+Qt$Wk=M# zZuxzS5UZo;647)#8`ECW7!H@qDpOvr_!WcwUqo%ML-VPaY4Cq15BPb&d~IF4WO6y? zj5;YXYN8EQWQVRw4MJ%bYT+?KqmTHm zc~B30wn^XAQzq@vY<%${>g zh)P#-QquMZ$axO$e`MM6s3vDqxf)#9C>vK2mEmfH*L4euBLcV9Za~mikHA+?X(Ra6 zqNWEtKXvE6`w&}MTEYe3re~_w09(oR=LcxSvY?IFho-@+QGCZ)B2Ni38KUNYv$BsK zJqpSg@A~^eA?2l@<$=PIf|yBtEYpLw*d%pa{3`8@AD$kd$XD zCkMTx6hV7RNs9xuJ!MG(tB^0D6JdF$l6)dWs@)0?1eFGxw3|eg*)v_Fs_#!WJZRVO z5u{Rgu^asqM|k1@F(jgrO4d(9N`F%J?#9MOa?hb&lV9@+s(j%KG!g%3@=2$6{K)Sh zw~c;Xibf~JUGbcWQ;cCNEh|vgJ}LRx`(L_7dLQ8w?r-qiRUC=lQJqI49|pwOCqvQ0$n}n-8`98*DkDSi(06ws8~eLfC$j;Yf6Cj(6YFP zhlMF%1>d_@Y}$}Ca&fEW&zSP}5LURs_nUdM+e-;HwjH>(q`&r*FLiGFGZu5N2;!)3z6MN7y zQf{rt?z`5;4&q zc7gh$ys4?Fni>L8VG+Hy(4T{giwnFl(r;QDJz-Yj|KTeTOy7 zI!`6ZC^5Wa{c%#DzoqvtWPI9HpZwS0Mdnl1U(q-zY(8h$`Gp!vjC0c?ymWoBHA`J8 z4fj9;q3FsgkGjRLJQN{A^cdXBDb%nFhI#XHZ#gMQBFnLN)L1@8LC6Y_Zf#v%afth$ zE3W%8EtKy_I_9-HPu^>dXn^FgUvv;!moshfiZcWm3l)hE)+*z(J(bw+O4Zd=WAv)5 z5757?_|ls>pi&1ib%kMJ1dc)yOm}|X3VQJw3Zv|7Y+zVHvgUDnJ-5zql>?1nWO%su z&P)QRkX|!4Z^84Krwe7;c|}b76z{IWp7sH!da}}Of!-H8;DBw!X;+uX&JUuA0ObUT z#~QxmfNFK%9bad=(apG440EH^^3-kLySWVwfO_j(aOKbXLdvLVMApdSX<~Bat7)T; zaJ5z?lGt_(Qy7glP8GD*y>=~YJ4R`OStEPzqS;fQ%OU#vzd6(!`9&S;m(l_xFuFzJ zoSY|ZhP3mJ}gU4haVfEX7(dr1kzc}`nDebrd6c1uosmVSC| z7JVu6QHbHpZJCzu0ICqPTU^`iV^8lvTrHw4j*)LdZt*X_?VN1@1!Az}wWr^TGu;t1 z`x8O%yS27!N3aLyIs-=XYifMyUsdm&IN3#vAW#ZQXY{w8MVy<;QmVvvoDBnvISR~w+p2`mC!8sJ_i{CR)b8=_ zZ4_ThHs5+q+W(Md+?F&_^;y{&7k}AO8eo>@)_kxo4Fcfh{~}0d_QNzv4gxZ2s&+2* zM`i*~K1hdWEY32vlh|@7kgDr*Q@hJ=}EEa?L8YK;zNk(R;B`hqU<}}?RZnxME zBSB$!pP+Ss)^9ud3g;ma*5u{uOosW2KtKmPqp^u3PxpT+^9WuekWa9)iTNewuK25~ zi(FFd)|XR1oU-N*i5}NW-J0#~i~>xVu){v&i{FE1a&&!gv(h9fLEgDDC$U8NV?bMY zE8C-}D6_KJahn)77qhda_dTZwbbn_exUV%|N<(>L0?#XuiN(rxUO!LW#((YAF*5Qc zQM-ft{%;K*&C~Ts{J2@pHYV(V>H%n_y{#?da(?GTa|hc9Yf)W>GP|O9NT}g28lNsB zV6H*T`;+jK3P`9tBS32>i9m=H79QUE{*p^}TNu~b#=>^t{$8+xwKqOs;|~B%p8ARs z{*2wCDRrsPgpV81qk4UxAhaM=X&bs0R`r$G4P3qr`DvjrIecxP7>0Y*;{tK46Y&Rj-1(sh?&rQ(sWpUwMc#L+ar#f+bCYbtT*8OeG1)|v=iNjb!JmNG5fvFqaX zGDn^z)_AZ0(#LSa9zQ-cVu$rZ>^sgd{V#R}5k+S=*RvpgEEY*FbAjlKN>60XGXX;g zT$$tdF2rM|IU)Cte2;3Gvf(Y+1hr@p6^M#n&LdG#HM8~yprIG4WF4ax2#IrhB7o5@ z?$d?Tj1rH)fp&4XF94Khb~uBr9)L34ep-V|WY<%rE&sejIZj;t$upEZ10UNeVvRT% z*qb>#KAaM4#!3vi&8BkXgguQTIe|Why9L4C<%cpsIU2E-BQAZ`ILxgV$>5xb6I5|I z1KZY?6W42meU4?0KeWjj{h*_D)PDI^Q}x@4#`it+7R%Npi+)?>sb}lH{0~-H=kG@YJ+Megb-9;rq*yow1e6dL?PjMa#*LX~` zjL#7FV$3o24D2|73v+OBA(c`Gha%@Q3%wE*#W5`{a+=)rjI#Y)g;Q*G*{(Ke_jppW z-+p850F~=h0@z(6^`7TSZmiF$>av}bGsjGoi464v$qgMIU4;4EwC~5$4G`WOJNH;U zqpiM>hJ|+p$pIb2%AQ@c^Tn94z4H8sjk<#w8xyPDNpoz49|k!ZW!ZUo{(x~V^nsY( z#A(y7CBrSn!ph3|?Hkg%X-lCI)@6Ws4fN*3uz-NCO8+v55vr=G&8oXRc?nP-gB@U8 zLX2l+X>wW>DR@msb^`kHB04NCE{`- zzulf^I_kfM#$*?V&ye#ekQ^}w=TO!pkGdufkaPT1|u;_(#uDT;b9ISfwYy|={ zfgRwQ00I>f(q&rht^B*DpCNMs;a1XNMS-NepINb&O&umKF{`Qk8g2aNq;QWe z;+vB-GAtWHj&~++<1NI6V=Fx)#rjHqKR@o-rJI)63Y@}B&2)XN%20;{ZGoP-EFwk1 z;n&ebak7gdjHUdroDkT7rC3Y55xV|K&~1eEP}03vHBB_b6`xgHL3W4NT)m84?pLXR zhq}v`#}k+P)0`inj9rfQxgQ>`tge2RgJa6SOOGf1!mpTkO^kI$){tzSBrXtwkSuFKg%j&QY$)pS16HE1OCbW&_E+e9 zH#9`*x9aKZ*O|zABd#)IPqH7&q^xNXPO(r`?Sg9SMP*_FeBhmkwV_sjyx%vyV2f>w z%~N{CrMafwF@%*Tbp93VcjlkO=b-fitOW$?8T&;*_pt^MBTq<#-}y{{?#pT&Av|@8$J-ggp~nu5TJyUc452iaMG7xks4Y;`Zz_>4RGw`C zse(5ghL2af!xf~2f+CK1APqw5iOfTl9}Dp)o?Dm<%J3cu9)7&14ox>YiD_3}a$U?G zYScF|Fu?wPIOvwC95$Nty4>?0&?jKZjYQcvbG*h}TmL)er7yTh0N6jBEV$(HIgY8{ z{>qX50RTQ_ww|T)B*mYXM>ZsqR~nK&b2D`P6{{`Vgb}an*&igyktffAije$;o$9m) zkR%C@Yd9FMZo)2WmJNQ0qtE~K-&0Xi_6IXPu26WncC6jhJ11mglqv{q-n8Ky`e(>; zmj^dCr13}Ecj^*0rh6t^gkO>{`KV74DziEcwGJ|8^RvVsYy=B+p-dBGYX z-y-su*DT29=Bqy&BZ#WfPlzEndt7PP{RF}E=4qyxguV|}+zPftOgJ#5}S;q-lYb<&ZZC?4-#a4VX)te7)Wh? z2J||VAf>S3k9tGsL>kSc+eZTRr^$q95~8O}CrNFClH-2HF>MYtLf3;206xvk8N+uh zea>vVy`1LA&Gr zS%J>{@uq`@Cg}J%RVbd&Iawk9Co_a}h*JZC7#%rrBXF7ro(&sQYyEN(-p3ZR?PH0EFnde-J|@waQdtfb5q zz>7k`wr!74zC_~mf>$b}yuU$H&S&{3MawM#S7t^Y$`gl?gMbxcIO@Bt?zOc%mKUoO zXhlt>;cl}~{=nMGD$I9OLrq>dtUv#{qi``UjF#nKwBS^5k$J>nuv^$4fHI#YBIc}d z8aKt(ST3)@)!_A$=l;5^(_$wP-f?a1OwC{8If^2o#C%aU(!=J1CTFb5M<$WwoDPxH zM~@W3R`R?|wo~pq$KELV-*0qrae>;1!46!Zj8ET<{{6;t=gv7z*6(OF@a(F+l0Xs) zOciNWo(hy)=SFvHejqAO2Y_}1)7>NFAH*Wt)Q2~-%o_h zjr-S0)sKkk9gSN|Db6>rFXl)S+-fKGV`*DPGuZWnug;vh|+CF62oC5;`0LlT~pqA=dWJ$d(@5ALj(e3e_ zDNr+n>IP^2V_+kkbc-Q9$^`78nXq>mwaJzG$SF7kr@)m)Z$6~^;=W1t{>1@0kC}Fg z(Yq4Rr2-fmAP+4nwWBf!UC?uDJVR6`WA4}bVA!G1K}3qq4r|~XyXY!NvmhM zDVLNuW$lt@rKZ2Zf*`b(>g1k?qK-*IuT#ipLe7ll_mKrFs9{UGR3ZSZB;cEM1CL7H zgwn3m0TMeYo1&8Flw%*@Oyimad|Bz;6t zR$h)E3;oaJbjWli@q+Mw%WkiGQ1@StW%~hCnMY*eH>Obg%V#23#S;V{(};$^QmK<_gN7ooCQz2F=HyaTXv-kFf7=4wM2FxYU`Ww%85S3u@6h6h)>D z-=R}g-b+Yqop+k-yw76P`gKX^#G43O^G(Mo+W&9>mQ zsiS9Ojfc>f73>c~0zBuP-vOSrmX=n@m8R~+M?Dd=Htw$!rkR~R zbl?nG-azdeKr9WAsR8xIz1!@tlzdLEAg={ftQvr}0yd%l!9g2~-Hs<2pV03tAHAFT zT-C7wsX}$g*zfJ`jDwox`b@%=HQDvr(nDR7gkndLsNf}o-H6jw+r^6OMyXd?8v45% z-0BGV*gZMQKO1oN684Z>fNdKcaK0G2Pd%Jp8pw-$RA`uR*RYdD6^Q5EiRak4xg*Yi zPbrz>8}I9U8nJe7{(Vp ztTNfTb95=WY@&bpj=hpN7cTn4No+jWU1YtlW^M*`@S138or6{>eZY`-0rjgsGTZD# zZ(eVqBSaXZ$ZU5}m7JL&HLUN`yraE6h+Dk?g+N4n2U3@HVw#360#ohdvxP~{a+YCk zChWjP^aJu8Y$&Ai27=M0j&Vm_6@rT&-?P?A4|kL6>>-&)4Db4@8DE)B-ih&Xl_K3D zL)H-}w|BXhzsX3hy;XMWb}w=o)blfSMUD$b@L$>j-ql_^SII-evblLH4sLVm8^PnV z?WA#>@7$0UR?cunDg>d|4X2j&TA8ef>x_VxF@fv)IUJFLljNC3a!v(OanS>P4_HQk ztO!9i+sWo|$JxoYNkSPs#Eg(0uWUaxQL(z3cKGX9bx*NvEF~XdO-SsRv2Z4TD>`IY z(aI|K(eacFz#$%8Lo8OoexWp5A1XH+N%)4J7m#WA1O zDl|$`0|}#6kz+I1wEY=ed41$s=`^W~y@NuPgE)U0767 zVm$*XiZQ{6E8)rGvzxMulgAg)YOv|S&9t?jn!z^7m7NO~=fOqqoW!OD7jtk&*yzmI z>K%ANG2i#=VcbC1qS_Qm?(oU!Edl%p!UPeoEn6hkCW6EZbdbZ7+0&P{#z=e@`+ z0So>ZC&5&L;2`j{)YC@Cwt=dilBz1Os;7XcGott58lu+JNwWTqbKt77xc*a(Ge$D@ zTr0;0MCy1dtHgz?a}0uVs48{0i%|#eF)&{cPcz-<`@zXWf=LmT1b#r$`T)NcvNdS; zW$L#X{N>?Y%h75ApZl+d7rACB;1?A=Kc~VqXSJYZ&lThH!z9Q#_L(u(Z#!mBlr2@g zaSn%w0dsqZc74QS$v@I+@wWS{8L0+!j`8StG0jCK8wcD}GecoQ}D3)T;;@iz=ygmzKcp> zxq9>FDs!aR%O#?PL=X(|m7woAk*|E4do%s_oQ?7bokS0gwtpH&vko7<&ujW?=E?mu z3dS^r{kUordug*|iU(B{6i4r#3uK(Vx-}Mg!G;8X4CA)!^PyiOfhb!eS z+ZT4b3GU#+2jZY_a%!>Na&XP8+mz{hm$ok`JIxPtvA(Pn;DDgudD}Y%6XZZWK zMW`7acN%_jgBuTr5UfMcWKDex|43@@=CLB(Zt92_+lo z=*F2qB5U}-!{KMQQo?&W9OP045gvX|t_mf&p*MT8019J~Z|CPcf9^kzhHRyuZi3O6 zFRt>H;A=yQ7Q&uu*%lTGw$IhmB)ZgB{h*YgnCDzaTS{&@>8yxb7R0k&>qreH5V@WF z!m}p9Qsh-&x2^*X8uY$kpQooRLRX^Q*osrDqu*2Fc$&nI962}&sDR6u89-&8m%sb= zTf!pZQ$zqA&}p;aVqaG>bvc+>cb1a#X9f@9Hn0FZXdU+vrJ%>4^XfdK#K&G+hK%8+ zQhi~EPjT|S*xM6dD=?q#Ua)wS?9zi%oIE_K624w(623~x%84fbF%7XaR49fsi2Y|r zX>>$ohjQ2%85(XIm^eCYQJnFmePF__NGW9dJf|4|1CZFd1)QX_O}qS{Fs?DnouJLY zHUDc1Y>7Iw(b>Fo4+R+u3row=GG}3cofUUD5o2Rx3k=z+b zswRKdwD|H7aU+*`agQ+5s#8;kks#-e^k6)um&C(kPoF$V6tE?eEa)g&u9lI)M=y&g zmd*DEEV`ap7zDYK63FF2!cCtR;BxL%gv5T44VG=RjtVH#3da5_=G=kLWOjaq zAG6iu2S*v>Ks`4#I=akufUQyQv$Oq=B9hBNj+4pVnaYQFbUp^d;$3*2RjN#$WSrn`&*qxP3}jY^&fZ4s#7c-EiTjf9@S&Ht@y8ls-Z#ERLYBFR zj^fFslTWyH%$H7Jxxe8$*COEF{`j3HalGAe<+Kz-nS_ne)bnHgov-`D4Y_?vb!mM^p^wh(z42a$NGwLY*Z)RW^kqgaUXq_dGWoh4Z68q zo1*kH9A*X&C_G+XpR)WF*qgMRTEhB}a%VZU%jtdjL6FJ%M95O!&cyusR;I3;(*3Sw zw1(@*6INHnk~rJ)i?Qn(4>AQ#CmVgA(GO(rJZy@|BK-D4974q>DB{_eD|z_N+nb-| zihxft$DQCnFAb8RvxnL%M&g@xI4+S!ba!$8bS_MXS?c-EOj(^*x%IE__(2+hh6ix{ z{Q$qYtW&IKI!a~K9ubP+9^??QRzwb5R_y04v=H+4s`7jae}kM3Tkfj}LP&liB3kDHl zso0ZG+(Jupu6Hq+yC|n&p5^UsCt6s`zfj5VP~|9LF3ocP@8uO&A9UW{{7lOv!n&H! z_r{J+2ljvVrR&B-cOIpT-uB=%JAZ`w*G+?UK|UB-pAn0#NI5&Ng`aJFGjM*SU={o4 zSIGmP(^&>GINz`Afu%%b`K$U_H>`#!vIoog9scc$?r5`J8*(%6ap_KV;RPAVOO$0vm&gxh*9%A5I&YIF%2(pA@spqlMYQI3Na z1aiU9(jraLe-Bg;6eYsmk2ovLcRl=WO<8IkT=5~>3z$t~BO{!9EHa7rO~5wv)LvjU zeP^jeY(uT7>weUrD+~W%V~Yo+muq>Gd}Zl8Y=*fnd?2l?AT4(W0U%vn%IbaX1yc?0 zwNHwU;7F&D23bf)wE84j8K--igJI&~3bxI^Tuf}+@WmFjSkLabOx*-d@pM}SgAr~E z!AQ0P7y9q}$mH zHCG#q9akF%2%qubc}Q546X7?U)Rc4&PpczM&|->D$Na+V1>(WBAXB#!WOdPnVl{??yVhYl?5zS+4gov#sW`) z0|Uh+PMph@K( z-_v);PsL--MoY`;hi}D_z&a{Q(dgBX(2g{6o@8#Zov*qy8f3Gz zUSMzX;DmYEGAMR9jMtQAWCT_$0a^!`_8juhw0PKm-Ev{qmT~HX_>iYFiov`Gu@QYp z4iAE+AO6g4_*Jd0Lz$!H?C}psNw~&tzR}|*tOm9)B!r+R8>r7E?4_yfpQNNuIDO7K ze1ODEH5R|=2_zS=`8D;Bo~Ci0fy`{DHbg}Va!>X7mO1=5b;5N4x^QlP0L`Oey=U3@ zEyFyV1@u|paZaL*7o;%0Wa6wHNM{*Cfek|0;o(P6h4EN5uRT03le0zp6NS?BP?VF_ z{$tQ!u(o+&eeA=O1190o3W@rLOYh9ixgvwz0->^gD~0~^fIbGeA|Sq}Z=gFz(6;kr zeocFzPzUtWZ_KPm^mqbkChiy0@NHO(K_`XmwHz_tW41e3zCp;Su#toSjB4xa`?Q!C z*?ru+eHSg`4?W5OcOJ#jf>9tiFbe%@6j|!exO3|$S)=$%D_HVawmE}+-uB;EqEV_K z*Gy!4wOmhhNvWx8+1wyuCE49%k9-&ssH<%4fwA%v^Jr38^(tSGJOFLeSFNpxcFW`G zgK%;;KtkH2*p=aTN(dz-hg@x)KZa?%SVH5Bne$8QCQ`(P=6FhdBYNZ4s8YwPGZfD9xT3j-o?aLHXQ z{e{7xQi~1Q^~LPS?&3~02lif{x9Bn+m#PbdrArT2k}KT2yhKtmMOBS+LmqLDISZj3 z<+j?YJgdpz1_Vk(EQKGZr)_4D(9fHQC=FY3aD-bG_5^h0u8-6O0XeB2m_n{(DV7D& zwyG$u2`Hl=#Y`+z15Sux|8a01+ae*ca_h+&f= z+4fNNPVk7&$~N_VDrJ-RbbNYfIS{CD7qh zT^;&FJRJ@rfLjc$gpUVMjGzJsiHgK1X5Fd*=*pt6f5E1gA+2W%G^U^#C4Zf74Xvnq zHWWv5auA&{NGm`^)#t)AIDK{TZ^!YwYHDf_>OQEzFxuzb5i|f%XE#K)0O(#&`{QG# zfXly>{AW9$H`K2|Ar2`(x8Dad1L|yvaX%k)Kjpl{qFZ8`5*^JCa&ukDLhJ2*C7po2 zf>dv-D5HYIz$t}LIticcj9@x4b=5@v8HC$>_eF3_x-@cSWK{G-uf;oP`0zV^A-LyU zDo*`peW8^IkWUeny@7}MfK~mt39(A_DkR*@xlTV!7xvz~4t>u71FG=i1_&rwhJsIm zehibJrz=67{li_VM-vj`TF`C^78%3JPUWhun~jsqfpenIsj!+IKFRGwM;ruy)JU< z={lm~7uQN)?g@Nh=sPq#v*3JN>8uI=H`u#pwpF%=`gmmg-vMhN!yMmHFgx8k&xFw4>V$tR^&s z0Xa`te?aQL`zoMN7;bl;IcyXZM<*=+ISBf!l5ZEkFARe!u>MFFcwGdyRJ;zjB~S7M zJu6^a^hoZDQ9!Tca_Apx#X@-6&wA){bd$w#ZY=3A10SoNWpt#X_0Yd2l_Wo3++&4- z3+ZrSgF=prN4(j6F9Y9W>IyjoJCzdFbH#3Xf6Pv^u6svg`BsmvT6{w`ud7< zLBr`sLeSlVWqD0Yyn_k<#~bFzXJyHKZp>1(AnvzYvwf81CbhXIq`EN0GP|Kqm@o(e4LO^AGO@+|Ge3`+QnbGHoe)bH07O zil^k5bXp%s_|EpE^>BmA{T08RoXf45&G_VOA(YnhS?sj_p9eXq8=wJraU00QjBp04 zzx!&EVRSw6a3&=N#3FSTai|Zw8D{vNg@c;Ko(@imO5cd$VOHR}u&@j-hPXmz7P`_u z55R&!_K2+?^S_@5UsYUO+}+;3w&Rn{X}i~M8)8X)3(Pu;O4}OrLI#0RjHqOx)p9ki%kgL>z>mZSWw6F2`3+7S@Ae1hJ+ZLt1t6ugp*6fP*y zi_mI1W!^RW8qo(@)PN>5Pnng~10ZjJ|E-rFV!nCn=_j>r+Whgg*c6pz_>VFh8&j=? zZ>ax*Id#DG3Gf9MJHSFepc6lBh)$WJT$KLvR}6Lyoq%J6D-LMqAr=4ibBp9$cBV}WBYT$NGeUO;3UC9NTi(wbuhRz?|R15 zfU+hC-gzy)<8Ctr%CEzyfNpNk_Jex>3TgEs|6}$4AH@t1VJr__@#jv)W^9?AUARep z*!0jpOUlTg(ed7RM(XcNRYFu$sa01J@DbeGGdwvYrGG#xK6>vZK3zXhCR{)~HLs)@ ziTEFvjKg9#!BP9?>XZH9;OZ04ZS+d5)|L(jiqMibr*XKn0*Stgf5lrTKiqfdpC~2> z(lk0I_XFO3(=31#AT+(%@o7Y~nyRYeb+VcfJMtO)Dgb*nXE&SxUgzcY9iV(8j0Ng< zA*=w3fY|x-RZj040h&=?0EvdY?CfK-BCa5%Q@;mkc6zUQM$_O&P-KTVU+(B_p(tK4 zGZLLY!>^&L?Ee4>e`RH5C%09gJvKn6-ph`EG5{3-H^i9u1|*Gs=QyCdy%3;0P4}T?{AkThX0CI;+}r&R__y=1|IE9MhO*%b zfGRM4Yds-}?q6B$5I<1wos{bjfzGKFX_<&L_VOmcGEyb|Q{&)P)U%8Tp+nbg;(3}Q z_W-Cz@XzMJ$;pp^J4(G8NM*rwPCte+j3ET5tq|5S$#Rr6J+>zEs!si3WNK%ncE9Cfi z``e+^pQoW%A1*u%R|}S}`ohN}E_kkeI!eX!%-PLNusSa{*R;tmCGMmE#_FC(smt#5^$k#pqRWNOQfiX*tO*@H^a-;CHeVpdU~2X z*V#B*p?L4u)7G{Ft)czxt{S*rFnfv9H~k;Wg|U27$^aEqPL0xQ6Io7|B<=ySq^ITh{_M)ZXWU z5?FUY$$ZR1lLq78p2zxU<7MQ9jKXZqefqTR(+D{QFyyOw&@Wd9OfPiK1F3%YU2UOh z-hZ8Mc-K)Wb)0`50*?zgvI+|BU;mgiB+kG2@!qKjEEJ=bj?T9d&}A#@z?$O2T!`*; zcX#h^?eDz@SEVGb;^()Ac3ZUI3elanN;i_J@`?lt(Xn?pU<+;&D3t7DG0!#R@rX)M zJG)ecjL@M8GS_Qw*iooJdSSClM^pQUd2$7Ap+?ekJ@G!Jo zs_`L`6LMAVjgYR!bLB=cVk3VTb(hod`xe0X5Lv=c0}SX4qaep)elc(Iuu#Rw4J_aY zQ&=20)vLf7b0$O{FD>+ncx&=BDu4m=4h6=xSm^#xP*Jfld75Dq^Y^;oeRTjAoxyO(hT=;!0pX>QYxI?XHyZbWhJy9q>%)5EB z$XWMyd-f4jcH5E6Q)t%tR}b%_jgFwEP!LO^?vUdNuMzLB5JSt4xT0T!ANlOTjD?Ct z?u$~xHz=9>w{H1w(eJO)mr)#iKci9Ijm8ud3hwDB6bap9){xuoSQ-zS_uU^m!Dk)8 zJQ=HLT6Rm%3iY@tVS4KwHi{QBTc)6|+-*l*bl1F9mXVqS9%>%! zAPOZj4cQS8=Iw;ey3paF`n?}l+8*huLq00xLX%{o<7sr|{Nb#z`;O;#1W;3#FvB^v z3wL|CXo<3`J>Et%nZXaQEztYvqg@w}Dt+u@uy@+l0C&q5*4pzZDR6JF zToG^&h17gyRaKen>e$09`H0N&EnU1RCuCCw|4bfnW$s(PGj-McP8%i8&3$Fy8BMFSrtQ0fn5$7q^`Tuw$l@fB(O9P&VFkUWMDJ zfJ2zavA{bzZ^N(8V-DX{7;Z23lo>Zl1oMF>RtWfdO{Q?K{hE#dcy(_C@^dB3&#$gL zqI5lrjG7j+Lyw8&;!Y6Jy~DnWq%2U_*8mLN+S*!1fhzA+4T0B$U}gaYR~bk|^);>G zqdJ3(NKyEhzYdt@fgVT`SYPB(a>(PKb9Qhg|R5_K5a9NT!2wt!x2HbkAk{5Cxd&Gay_J=QjuR}^YD=yk>O zkI0uuB0~zeV(R-%2Y!Vv*ii$mVabK+gUlBSbs_L|Mf#-x7&+#*$YHv?2;h4&Paz}j zg@>f$1qN3#v?Hs4;#TlNc~Qdb9vs-qx8cXHksl{vek_RmHL_bWDUrXv8~{HK4uT)s zVSbDYGll#HhwEZL;Ta>W`DM&IRX}xtZXhW2DC~slQXlL9Q$l29Z;sz$gx?XKqC2dC zF)`!K{y&HOBj_dR)N7?8?>SyYM=V&ytD}0o;prM(O+Uw)5WP2b)OZJ?wL60IB?oLQ z$V@ytqwM}P1j~u=`1q^mC)Z6+*)rHkZ)k#(IlI4hmJ-Fl0TSP(aCaOjVLDV#5uk9j zKb#PDS7ri`tK0VoQAco`nSTB3h*{^sMwqGVe1FWjldCSknqS&D@`Gl&;oD&)k` z;1yjI(;dFE=fUWPn3Awz9U;B;fn<_E>JIO0>tWc4eMeuipcu{1oO)@56+-f=ei%hk z$>1gZ2!}!L{#{uQE)?m@M^rSZOiH=4M`BQSLP~EOal&PgIk9ejNNO->9@nfN1zl*% z@Hd!{U$@T=)nL@aZXms8dL8U5NdtolHR@*Rt!v2hnF);r?fCwAZ8nxicJ@mMlc2&+ zM?(V!_dOslt(qX;1bPVx4}-}&A#-RFplt>Sc8G2r?Cn3gli(i=t1omkFU`*j8b%cEzQCgw+uQ!A zt2YDMTX=LH48W3WrMkj$cJ^BwQNGK*xc6qS8L$eWAu%+}O%cWVdkPMdTwI2b{h->{ z#&ohAT7*QqA}wiK6+kz=1~MT1{ryHJ^_!#iXX{t6zkY(ICl1%IXR{qYGS`(1G;@|s zP(5z6?4Y!BgCh(5!?Y4dr0?)j5`8F&l4rMrQe$jPj0?h86>^F`Ee<#Sw>rYWH3tMW z7^;o^9td=F4dwzww(_uPtE#Kvn?Oadxt9Fz#kWOi43^#C7~vF{thsr6>?8!V$9wd5 zAeN;+fBgiPRslq$`X)WMo*3)ty9jg{MPwxE1E4%N!`PeZ`# zqUTyX#Vh}D4?!)*l^>v%x3Emu|Gfvryr9(_^w%Bk>tjr1`VR4YD_84Fi05@FMK$6% z;R06WEE*#4>Lp{nV7~(0X&LABQ1(<%l&$glcDnrX49s=e8mtAtz~FtC=k{8F_k~D; zTDHeRYk))p*OY+258R<`ic0AAE+*m(X@=V5H@M(4SnXY|3LMXr{1HPG4w0Ggm*47zY@K!^M@n3nu!XiYCfMWIj) zvYzS-IvN_kQZBD4Qx26CU)WuN3^4@yUk37sm2W@4k5>C%mP!0Bi>7qstN$`Y-;R@1 zHsJrUH4q>mv+!0+YH*Y7|6@ydSIiSaF@J0nPAkC3tq%+;4qL;4L48q9|No=zWG1wc|8jzU&(989=Aa=Cyb_Z*!VPST(}+62S8i z1UdoI4YA6OjQmNQR$z(nfeCCxO0=A`HA%6D7~E~(pJVC zYOWqy{ct~G@xA~T#&5Mx<#`Bt6x@05*o;f_X7~o~N5(C>pBOy5g_a;vV)lh97GR|3q_x1gXPfQ7Ew#plX~ z-5C4;!0=)KltZo(y5wF5U3E~Vf1v-`*;vNjei4$DHZ@4!mRn!OGl-fXX$KjN_582A z*0g@f978g0uOjKy^#Y7I-e^eH7rno^e8KoFgV#|}qQE?wn~&lU_o-tzRWuQD`-%ri z@CfxUKicZ_Nc($JPT3fD1upRk-T?Y{Z{KRvpurbHVe3if^56;-BX*%{5QCxqi@EjC zn|EKD8X8hd4!Qx2z8$2r)@040J_PjyO73{Om*Z;?=u%Qqz4z`P1${cuMdT9gGzfMf zpD@f<)6gIl{6v}b3ff0{7zN*S%X8HelnTjaOw7~_ZH|1xb zHlis7B8|}=VSrSH%11w|CF$J}q)QW=FOaG2zXOSoqR0C832X0J_NDJ$>1eIV4pV?W zrB#~uSxh?aLt-A1sSR&iA%nO1^61$WwN&7r0)Ny_Ch!JSGhAs~CZR@FeSJix2#OK% zT$unIE+^wdf(WIf^>J|xQ$mE7JuMN6K0Ejl3$H{+H9exJ0$C0wVswZuP( zW3q)Ou7Q&2RZ8m8Khez4ZAV6ElCnXs?PxtOa`X9bAA{y?6EY@!66Epb3=<18bi}(k z*G=ob*-R|C`?kcdl}l?UD^g;b(BK2e@e?|fWVU|(F1q%2UXp(FRN_R2EP<7eFqFR-TzAD_6waH-hr_E6O6^=42P2>U+Gnd!g3XI!s&=6*y3K!?dK z6N3skh{k_^bAdLJt3Bdb(Cs^qjVQMP^Bz)5mXx0(_M;=MUeZ@DdOehl&)TESJlqma2b z=lUGKY2I-q7&5X>^{a;^FG9&s9AdD{R%hdvkb7?}Y3FkXxnB_F)OCMUPGmwpSGl6Q zv^8Pw$na!}ZbGN>%d+Zc10g0!$PM!EW1B-AfP`FY`qp@7rwS)k+}m&LS63JD#CD<_ z1M5`?LM2EYZ{7aAn>7u@21l4b6^U$da&!AG7FEylhh33`6e|Ol7CHJ}At5Fm&LppLYP3LJ2Vd=(x>RSg4&$p6A6QtjQ0M?W)Cih;R7iQcXtKp%m-yStm^ zYb2C8&Ap=A@ebKLCm5Y{(8Q|A+J4?tuB>rDJSAmn*+mE>UmGNiV zCUi#2>S;hA++HewP0=b1o7=FEblnWZ3p;SaFEX-oadqq&J%qeoub;)f))deRySQEB zS(N|IjoVW3i`=qt`-ywwfUpg<>|Z>+oObZTnRgFoN_`8zvKy4=LggQ@1e4Ov<~Q`x z2=1)VA#bN=hu*GMT>h^1B^ghh5l(_cXc4f~5u)j0Qa$|v{4?+gFWo<(p{pyOaXL~=eP)PlaHI4n7&IO#@;HGHv(*Fm7}Z-; z1($VnTD82Qp3x0ogkDqo*TT#2bkiuZn-N3+oj4l6re_GT&wc*OI7$aAqD}$xeGVVb zOB{)~Ddd72S9McDzViGPb-!%=U$w>g986Y%ga9GdWuKSNN;B>*xC)7@{4T z&=W|ZyUe2PnOU>PtO8#X^EWM1Qy%TD{cHNBFKQh)%Bg{~0b7Dwd!I%ie%p#c6p%o+ zm>9VaTGxQ8B#3oE3)g067qVSJJ@?)Jqqi@QrgDGVM@W)nDpD#kBpb=BB!mb>=6Rk) zn@Fi-3QcAS5g|h;WFvE##|WKmNTQ7lnaAJ#Y_^XnaYBufjMDt>=0>T2F<2rsUeXqZX5p5(}#h--$wy z*I32h7B;VW0_O1kv9QZ5_ySkzjn-Ts9lc(GvIR1N$ExdihZj&%)G}Juys}+-< z&}<9-t*|A>T5yyu-5dep3TtgkyW^A3EbKE1q(hLtV)uj;R z1VegvdUa03@oe12Yp{qE-`||2ol|j%zBLw_=cckL3$YOqmaZMc*_EJ>0D)hcdP=U0 zX8ga%P99o-mQ)lKnZ(hlF*mwL;lSR%uKVn2*TcV#2|ZT9SE!1^*X@34mG@f>Bsmh& zm{N<$DIT8D7^e^WRQDk=Y<|mzDDX@1RMiCz1JocgSlVZsYJoGr)o1itHkbu@U4FV_{zIdITrPko~FVrhisJvX+3?Jrz+jj^V=LJ@1cS-p#VQJK^{{I+yQ ztc;aKs0H@J?U<5Re=X&})T6~_&^OZaQcqG+J5-)Zhw-hkKpv!|&q3%`;~B=o>*7EV z-A4KZ@YO4d9-)yso}WAo1&ncM@H+SWjY#g5GC*q~<@ORw+lb$X%NhNd;?J8&wocI_ z72j2*&)x0TA*1AlGfrX3Ln1Qv=#DU&G$Tvu zE*Nb(7im)j!2B188$f7=uu%g4LG7af1(+CSeN8>tUnO>3BGZ>dl1D^Hh)hI4qAhH! zE1ekG3%2nL#aI1*>m1ptY8pg+SCC+nViB}dzI5rKI}XYx&{Ozk)y?`t7G+q>wf6G~ z14{orKcIMM@R#akpV938>Sv(|qzhnRz)pdh7c^J~-EwG7@1$mV;i#iCcIig!Mnl)X z78<%kRi^eeLiSvvT@T%qPU9ob_*?ISTkX zhjGA2x(Kj6GBXJHZ<#<%0cBD`!T>aLyuLl)T+kY+a@4V8h39h0MFF`V+`T%;>mk?) z_Hnf%Dg7Yf%Uto@(X~#J=1(7Q=cswc{pfJC;pYWa4|Gx>0_AU*6 zaS%bZbn|%jgma=NZ&a1&?hALQZa9;{SiRvm?rzRXB$21=HHe zX|Q{#e-&WjlHtfXQcaF+!2^em1LTeeAJyhyVqfe(h?a>YJhS61{7iIoY%!PzFf(>0 z=hNZPOaocR5HHVPU*53tEoYAT0nb$~h!M&RTqqQZjm@m_nGlejk@3K?+J1BNnCc~i zv@DYY@Zj!GYD%~y{~~`hb}h)y!df*!FQYFU$LzP8@GwdDaagE}Y*Kh@I#C$s5EIs$ zki2J@gifDUqbT-T1s+5>44vyr%YHm$;caN&vIh(CVou-5mFN-6Tu!laOVA!nf)WA! zd=pW=sJBb%f4IG2_7NogQZp?w#nj#Vfx7GJx8cg*+*J1isfb(#ivegaYa19e2G>Qa zYiYp99z?l2_T+2*-^wUGr`Y5(#3DFO$ULuGrMs#z;+q5wrC;X56jr1GDc0vp? zgSzS&xkwQd>K68ps{LDlddXDG89OHX8ra`m;;qnSypfXgcz(}9glZTHF z#s%)q1Bv}6)fWmHA)>#5NVXcTWtb2ptv3fGibZzU#9H>fw$&=;}@I%ia zJiOpt(*yK={h#_6mHwIthm%;zaGA2FmA-@%t2cG1hH;KsuDTD{c9IteTEop%)gi{| zfLgVqbwPMqj=TB({19li9j$z!I2KqkMRR<$Hssh0fiV30tE7-n_?)Ao6_=HYL9C?5 z6(CW)etkmol}$TIGNoy^$zq)ym&<=f84r_VQd}r7r+^u4H3VE79{)~h*3M?HIYG@w zQ}esTGmrtXSRYqGD$LeS9HZYw-=@tz2wd|r9v&x~Sh{L!)cdwVbZpsE-mm!Oc9+e6 zYj2KJm+_44qhNs68Xv5XUIb0{6g? zzEY_^8k@>MLZ!nr_XSI%eEh2d-qq9;-yQ#rV*EF_~S^j?9e0z6ye_uuJuWHKgMXk z127s6hV&Had{}+*G$Mjy&3qWT#PlDvb;Kgjsc*q)IC?ejDX^L~sd+xi>`rh`jY-JzZaNIj-^v#y9V!qc^qflapsx;vl{!7$VOg(Fw6RKGcCmD~ z1s^|z8@u-X8Uc4Ac)STHx9D#Yss%;&1RO@{qnC^mDy`Ooe1fhmfC1LT1eTNF3U62#Y$p zWCMAb6iK}qvs|2$A$K*@)^-PQW~&Dxp?a964dQW@pn-WazR8M+(R&I+dD}IW*O7-?Scx*FR*`@H$4&bExZshgis|{d)$IiiV zcJX^Cj2*Oe>-mHXHkn!?9e)cDPUAWpXT4%r#aw)Z5usT2p=hRX{EDSlA$NmH^rv{L z%NdC|hznJnV+7yfK(~-X^hwAIg0iQTFNDElZkzU3wVLWPGurQA)qtsOd^1R3z`B5Q&4-LM zFz4EV%TD82dd!blD7UZZ06#^dMEoi1zs377*%b=8GS7aR)-WzEa+r~;#D;gH9#gcR zubEfvX1i{MHz6lg&~hw(wZJ_65AbYdb%MG*Xr@_qWp09B(#Kksh|47+FMvhY;s>@ByIi;u;_+QIJM zAlsNYGu9F)7b=EQVM?6TuVe21I)zzh;k_O8&g+&F4G&LDk^XK0pfSL)e6rFI-xsl% zHS5ou?7U`1U=SHZLBn$hC^eu10BGnQ#w_7B2GtYA)>J6^0(23!u-t#6V|scTOz_p% z;T*n+r8I!YjVR)6LK*tRJ^)Uh3jc=CUf!zvLKmz}I}&A(_~c_BPSGB`gQ0P?^5iL= zjDDUjbS#3m0hwu`@#1pRhIZbM6#LG#n4qoY>)1Qg_F#nLMNx41ZJ4)Fkr%P&pm6a_ zo6<`5e*7`3@K7(M(;hy;CT;btg{etUudUI%2W+DCF)JxE3+FrFIGEV=#HrC7?o^Uz zqOUYwBw_M`1UC=hr1=%0#~K6BZpv7J>B&&8hz-xoa_Cu`?BA}4f{_Eo&Ol@Lx=0yX ztT#o^pb~%9pMLtoD;c( zthKt`lbY^!?u1Go^jeVF6Y-7#Y73y>fkL%#HTzJOxN+It1H?H0Zs4Na`FaUV$DP_> z!q(as)QQO@jRXL{U%`E?qT=hl9Xr9rFWMmS<;(jOR-mb={=6l^J(GjnCx9k10LuEpSE|5^DhH%rQRfGUKC>OtIV%NG!EVfaVHpzh810vw^H2Dc0H zm@A*32LH*rsj64ACZE;_*eKK zv6F>pM)JHzIyK=02PhH5gV5(ZZU#!&bvrCH<-?Z;VT=t_ifZNa=XM#`!k~ zu*vDxX+P05DoD(N=HF%*CRZSusBoid&`k_ZHQ<&!DCI;o8H++NS5eZEpD*djZ_`?R z*}g32E>}6BmK?xP)b~fN7sF>ed{v>z4Hj~l9uu|Q&)dDPD+z@p48IN@S2!+BEJN3J zftBXxofTksn@b`hsiMHO)#Z4x>wXUHdinM`Ap3M^L~Fx{52WBE4$gznC^1h-mC5lb zKpH<)eae$LV-mA~AyU8`4F*HjHh~qrhl$AmR8v#qx4A=qn?VnNzWI{gP6JlvXFt?7 ztlPFd=ozt|-N8V@4n>_~)8vbClRJ zgpNbZJ%Ka#HaFM$Q&Qv8Z#(-~x%mMXfT~K{z$hH~T~IO9i34&zsL_6()ymL*`uw@1 zX;lDF@xUs}GHFRE;v^Hl@j1||2sTSrvcBsYEQe0N^o5>hg>o6~;AC_3p4&B?*qJjR zs=d1|ed&1|iUl_oVcB!#(7wPcr-?Bnz~BGodcF|-?VP{~ZM`82s*&sT8+M}tZHV0{ z&7Gz8c zTE+`~P|=$+srKWw_)_Y8nO^1jiQk%s2mwiek>fwP5%{rzBj0CRpejy3pcv$LSfgg| zk3)*-&=1ee^T*dAW1XXfi-jm|CU4t+IsAqH%=*ezd!SiTYj-X;M?%!ezwgV?R{`?FlePGwX%YCKc!ecac<+bEA)Vg zxXBb%dS8)r%7^Qri3x;KNinf4g*78@F_=>%A`EkYi0&WbYZ{qmG&gzz41Y>xqCT~M zQ&O2uYUkNrwY9}T_ZreuPiLnkb~$@EWp3W@IUKwnGYee=bLlU`R7|IQOc}0%RsSRUwPmVBgcLnI|g|S!Qo+_E1uDcpp~U1E-u!vorUw}p z`O?~%gNG*_W^bgWDV}wnc%BAhEcBmsfbhEt@^2o}BYpe!I*?lFxuXr>wkGhmb+JrO z`aR(_K=@bLM#lIs7`h77$71F?&tFpQ3Yh}UGEm6GrGO1`($iR`1Hfuj4rnk<5AckB zq+NejE9$l4e(p}YBv65B*86%_ePyFp?+_VPU6B7>`W}fSnCP*~H;@G=^MCR>|8TI>a~mqsN+li!}>gztumu%-=4`#3US(nVp>t=_oy2NrkNXds$f-j+>ck z&z{VzQAg%mJzd{uuz`+bLD{YXJM3-C^)XTJzK%~nl#6fXyfgTzy88C3fZ$;ISs}Kg zsqevoA3ARV{Xa)WYMPoNQZA{g22Fu%skg+lT6+qcwI-3R&zfX`f;tS0VeYCAzfVB6Wa;aMjvPT5&lDlXBK`Tap#>96XQNB zk*kQ#T_TPwEEKyaD$sjT8Ao`Um|#aTyFZU}=4Odwfv1~Wgx5roK5Fq`X<^|ah#@sn z%wK0_A~NCBPgPY^d-tkOt;%Es(hVTZRW2^B6DL9##2?0egN#0t4Z29Qcj{PbvpZX>6S6*J6xvMMm<7q8c`ytoD>|#GveU?}XVQhX0eKb@E|dPM zHek@kni@4841YTu<r#Rp5$4>O^w*VNn`9}}Y<#JVpO`b^N1F;(6%`=-i? zEy$61?3%rEfa9K{$e&mKEMj8gP-Ih48t=Cr0YVJ@jXyCWOP~n8{N(xas zVQD4=Dhg?wKg{n>eF!pJX;lOYR67~p^KfFhPV5TR&E0OKMe@k^5 zQ9|XPSJ=_f0ht)Ml2CNMGWZ=HJ7DVgJ8O%Gd=CQ83d)|{nX739%=|w9N={8hqzxC> z66($!>7R0k!{fNyGE|Gf-+}{&?f|vN+(aKMBV)c*Gji?>Sc2**GW76cMe9rXqN1XX zz!&wBHxm)fMCH_!^Hj(0^hj)PFIWYJ#K-edS1)3Gc}Jc09q>g~J}$R4lji)N7Xy~R z07Pfthzhz}&`;J-EkJVy3>SxC+9N0DPZs7N3^T3E3e@aZK}_mZS_+9R)YMeXPVi#Bs--pjd(oRylXn?< z>-4F7hQ(?r=-utuWBhxNb=)CAzk|d%Uee>(N2raQ+L5~Uqw%1}GpbS{ zvuQ}xxLOilM!AdwrMDKZcu5>LfX)oNYUtj?4>8;`v$MBbJfH1lJ=;)Ilih6(%q*}I z0Hv(U3G411J9ec17uQU}Iz}4h4E|m;C$Ay|c~>F!?@^XR{*p-n0TffhTnYD$1ZLR(u~!&Gxo5C&?Zk`{LD-c2|7BI>Ds znC&EnrET*CJ(b%4!kKGdm&?Ba3iSfG_sMxU9lu+S1d0HO&2DLSUArZWuOe% z|6y=&fU~D(QNvv*HX)qTRgd+zmc78WHFk5;?DPet@a&#Ti&i($Ho)9JEhLnz(mM3* zEnAE%XS&hYSrHNWU7R27P!c0Z_Sk$^76t`G$c2eWH+Qqr{!qg=U&h_kYcA8i*GEMY z1FA(vrHETxb#Oo&l;qBEa9Fy#Yk!ZIEPVCq2T(QVmsd+y=k(m%^0`0-ZLm7>AY-(x z1|Q2`tD{HI;vIEMAV@fKxVgXo$eF>B5o>sfKrxBZz205nCy-VoXUvQQi!8(ZyNP`cJ(#%y&vRsC1OasqA3hMb*n2fGF)vrgFvkG`F|?ZMB302gvX?cCjNYiU9zEl&yALg$_x`=Yz1ZkzMJYL; zJlNQz-BNXHwAg*{?e4=V;^j70CQd4XJ1BVmsg=IUo5b%ifW*g;(tD?5>NlxD}ld6Dv$gl3g7kFNFd&FiZtrl|!J^ zYI1sf$Ti6LAQHZ2Uyi5hkxI~Pr4U7;F8062;q%HQ_V^1CZ>6fE9K`tnNJJrGH2IS+ z`ubS)muU2gpZ8_y@sF_}>yp6@>uoY@w)GkAQgQ1;;35WkVW=kw$MAxl37FW^MT1`o z_2=igMFs&n0T-Bd5KiiC19gjgCixC1mofT}XbM=gdY7C330@+hdyvRn4=tceCW1mj zSD{^b`SQ_M!q*q&f&21g?0)U$2FZeGB3M0L@Ei)+!@p|gi_kmlVx z^zxho<>AYq^|u6nJK#kfmhyTY8p_s;SlqSPzJa5G96%w$1y5+_81u7izTdp)s3dgs z^XK$~P@S)HibCwrE?n?ywB$K?5}c*RaJZ02(&VOXm>67T7J#K<`d4Qs$0)8S`n-`* z-_DYh+G1mVQe^d!@i0>P5niw`T_}t;gUOAi%J*w(6vP-PNHIl4c6N3^-`;olaBoY? z5u9||;QVrjj2L+G85tA+v|jeQbv8Z&kTqF}Jv3`$78cy|a*sA`KCcrv&65YHK5a%# z2M!JxlxePfMa9S0x0d_rW;EQH{D>vp9|K@5Lm74_lTM08Xg$rylR(>{``RU&cUs*Q zNOpgK@cJ98%)yLc$?w&Qr#Sc~;1m(5L0SLPsI>f8`zDJdzL;L|pIC6m&grrG(4@NBEY7IV+cd(QMrHrQ4$b0eO4CStgKS%hpt$~s#rnaLbuxu z=Uq;NM|!+y9tFZurVjoW`iy>FS6k~b*?;c<61B8c9A3IIQgI-cf-~@2V}2&Ws@!3g zo0-`-aII^64)7^J>e!@DN$a#G}?Ab5i>A*?tzV`Y*t6!P!?C6)w+ zb0@5;Yj<{({W!_Xi$p$#GRo&0=d3TK>uaM^?oYyqSW_U4mzGMsf0J Date: Tue, 18 Oct 2022 01:50:19 +0800 Subject: [PATCH 160/416] Add AddCommand description to Developer Guide --- docs/DeveloperGuide.md | 101 +++++++++++++----- docs/diagram/AddCommand.puml | 43 ++++++++ docs/diagram/AddCommandExternalClasses.puml | 54 ++++++++++ docs/images/AddCommandClassDiagram.png | Bin 0 -> 80246 bytes .../images/AddCommandDetailedClassDiagram.png | Bin 0 -> 91483 bytes 5 files changed, 173 insertions(+), 25 deletions(-) create mode 100644 docs/diagram/AddCommand.puml create mode 100644 docs/diagram/AddCommandExternalClasses.puml create mode 100644 docs/images/AddCommandClassDiagram.png create mode 100644 docs/images/AddCommandDetailedClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 6a1ff227a..2a8a4313a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -42,7 +42,8 @@ _Written by: Author name_ ## Acknowledgements -{List here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +{List here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the +original source as well} _Written by: Author name_ @@ -50,6 +51,7 @@ _Written by: Author name_ {Detail how to set up the project on one's computer, assuming the software is Intellij IDEA} Before setting up the project on your computer, kindly check that you have installed: + * Java JDK 11 * Intellij IDEA - highly recommended @@ -57,10 +59,15 @@ Firstly, you should fork this repo, before cloning the fork to your computer. Next, -1. **Ensure that Intellij JDK 11 is defined as an SDK**, as described in this [[Set up JDK guide]](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you have used JDK 11 in a previous Intellij project. - * You _might need to set the Project language level_ section to the SDK default option. -2. **Import the project _as a Gradle project_**, as described in [[se-edu's Import Gradle Project guide]](https://se-education.org/guides/tutorials/intellijImportGradleProject.html). -3. **Running the project**: After finishing the import, locate the `src/main/java/seedu.duke/Duke.java` file in this project, right-click it, and choose `Run Duke.main()`. +1. **Ensure that Intellij JDK 11 is defined as an SDK**, as described in + this [[Set up JDK guide]](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you + have used JDK 11 in a previous Intellij project. + * You _might need to set the Project language level_ section to the SDK default option. +2. **Import the project _as a Gradle project_**, as described + in [[se-edu's Import Gradle Project guide]](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) + . +3. **Running the project**: After finishing the import, locate the `src/main/java/seedu.duke/Duke.java` file in this + project, right-click it, and choose `Run Duke.main()`. _Written by: Paul Low_ @@ -68,31 +75,31 @@ _Written by: Paul Low_ {Describe the design of the product. Use UML diagrams and short code snippets where applicable.} -### Architecture +### Architecture _Written by: Author name_ -### Command Component +### Command Component _Written by: Author name_ -### Data Component +### Data Component _Written by: Author name_ -### Storage Component +### Storage Component _Written by: Author name_ -### Parser Component +### Parser Component _Written by: Author name_ -### UI Component +### UI Component _Written by: Author name_ -### Common Component +### Common Component _Written by: Author name_ @@ -119,17 +126,24 @@ within each transaction and how the transactions are associated with the `Transa The `TransactionList` holds a dynamic array list that can store multiple `Transaction` objects. Each Transaction object contains the following mandatory member attributes: + 1. **category** A category for the transaction. 2. **description** More information regarding the transaction, written without any space. 3. **amount** Value of the transaction in numerical form. Only integers within 0 and 10000000 is accepted. 4. **date** Date of the transaction. The format must be in "yyyyMMdd". Some important operations are performed within the `TransactionList` class, which implements the following: -- `TransactionList#addIncome(String description, int amount, String category, LocalDate date)` - Adds a transaction of class type Income into the transactions list. -- `TransactionList#addExpense(String description, int amount, String category, LocalDate date)` - Adds a transaction of class type Expense into the transactions list. -- `TransactionList#listTransactions(String type, String category, LocalDate date)` - List all or some transactions based on selection. -- `TransactionList#findTransactions(String keywords)` - Find specific transaction(s) based on any keywords inputted by the user. -- `TransactionList#deleteTransaction(int index)` - Deletes a transaction from the transactions list based on the specified index. + +- `TransactionList#addIncome(String description, int amount, String category, LocalDate date)` - Adds a transaction of + class type Income into the transactions list. +- `TransactionList#addExpense(String description, int amount, String category, LocalDate date)` - Adds a transaction of + class type Expense into the transactions list. +- `TransactionList#listTransactions(String type, String category, LocalDate date)` - List all or some transactions based + on selection. +- `TransactionList#findTransactions(String keywords)` - Find specific transaction(s) based on any keywords inputted by + the user. +- `TransactionList#deleteTransaction(int index)` - Deletes a transaction from the transactions list based on the + specified index. - `TransactionList#purgeTransactions()` - Purges all transactions in the transactions list. _Written by: Chua Han Yong Darren_ @@ -142,17 +156,51 @@ _Written by: Author name_ ### Add Command -{Describe the implementation for the Add Command} -_Written by: Author name_ +**This feature allows the local and external (handled by Storage class) storage of transaction entries by the user.** + +The `AddCommand` inherits properties from the abstract `Command` class.he inheritance of `Command` from `AddCommand` is +shown below. + +

+ +
+ Figure 3.1: Class Diagram for AddCommand Showing Inheritance of Command +

+ +`AddCommand` is dependent on `CommandParser` which accesses its `COMMANDWORD` and creates a new `AddCommand` object. +It is also associated with `TransactionList`, `Ui`, `Storage`, which are used in `AddCommand#execute()`; and the `Duke` +which calls for `AddCommand#execute()`. Lastly it is also associated with `ParameterParser` which calls for +`AddCommand#getMandatoryTags()`. The relationship between the classes are shown below. + +

+ +
+ Figure 3.2: Class Diagram for AddCommand and Related Classes +

+ + +These are the important operations performed within the `AddCommand` class, with task description: + +- `AddCommand#execute(TransactionList transactions, Ui ui, Storage storage)` - Adds a `Transaction` object to the + `TransactionList transactions` ArrayList via transactions#addIncome() or transactions#addExpense() which would be + called based on the type of transaction. For successful additions of the Transaction object to the Arraylist, The UI + would be called to display the acknowledgement message to the interface. Also, the storage#writeToFile() method would + be called to store the newly updated transactions values in the duke.txt file. -### Edit Command +- `AddCommand#getMandatoryTags()` - This method returns the mandatory tags which should be used in the user input. + It is used externally by ParameterParser to verify if the user input contains the mandatory command tags, to correctly + store the Transaction object in the program. + +Written by: Yong Chin Han + +### Edit Command {Describe the implementation for the Edit Command} _Written by: Author name_ -### List Command +### List Command {Describe the implementation for the List Command} @@ -161,10 +209,11 @@ For example, if `list' is called, all transactions that are present in Moolah Ma Adding tags such as type, category and date will list all transactions to that category In a command like `list c/food` + 1. The `main()` method in Duke calls `run()` in Duke. The `ui` reads the command and parses it - through `CommandParser.parse()`. + through `CommandParser.parse()`. 2. Within `CommandParser.parse()`, `getCommand()` is called to obtain the command, before `ParameterParser.parse()` -is called + is called 3. Various checks are done through functions within `parameter.parse()` 4. The list command is undergoing execution in `command.execute()` which will call `listTransactions()` in ListCommand 5. `ui.showTransactionsList()` is then executed since parameters are present @@ -195,11 +244,11 @@ _Written by: Author name_ _Written by: Author name_ -### Storage Operations +### Storage Operations #### Reading From a File -#### Writing To a File +#### Writing To a File _Written by: Author name_ @@ -211,10 +260,12 @@ Our team used `java.util.logging` package for the purposes of logging. We instan for different classes such as `parserLogger` and `addLogger` to set the log messages. **Logging Levels**: + * `WARNING`: An exception has been caught by the app * `INFO`: Information details what the app has done _Written by: Paul Low_ + ## Appendix A: Product scope ### Target user profile diff --git a/docs/diagram/AddCommand.puml b/docs/diagram/AddCommand.puml new file mode 100644 index 000000000..75d193492 --- /dev/null +++ b/docs/diagram/AddCommand.puml @@ -0,0 +1,43 @@ +@startuml +'https://plantuml.com/sequence-diagram + +hide circle +skinparam classAttributeIconSize 0 + +class AddCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - type: String + - category: String + - description: String + - amount: int + - date: LocalDate + + +getMandatoryTags() + +{abstract}execute() +} + +class "{abstract}\n Command"{ + + + COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - type: String + - category: String + - description: String + - amount: int + - date: LocalDate + + +getMandatoryTags() + +{abstract}execute() +} + + + +AddCommand --|> "{abstract}\n Command" +@enduml \ No newline at end of file diff --git a/docs/diagram/AddCommandExternalClasses.puml b/docs/diagram/AddCommandExternalClasses.puml new file mode 100644 index 000000000..f1121e619 --- /dev/null +++ b/docs/diagram/AddCommandExternalClasses.puml @@ -0,0 +1,54 @@ +@startuml +'https://plantuml.com/sequence-diagram + +hide circle +skinparam classAttributeIconSize 0 + +class AddCommand { + + +getMandatoryTags() + +{abstract}execute() +} + +class "{abstract}\n Command"{ + + + +getMandatoryTags() + +{abstract}execute() +} + +class Ui{ + ++showTransactionAction() +} + +class ParameterParser{ ++checkUnsupportedTagsNotExist() ++checkMandatoryTagsExist() +} + +class CommandParser{ ++getCommand() +} +class Storage{ ++writeToFile() +} + +class TransactionList{ ++addIncome() ++addExpense() +} + +class Duke{ ++run() +} +AddCommand <-- Ui +AddCommand .right.> CommandParser +AddCommand <-- ParameterParser +AddCommand <-- Storage +AddCommand <-- TransactionList +AddCommand -left-> Duke + + +AddCommand -up-|> "{abstract}\n Command" +@enduml \ No newline at end of file diff --git a/docs/images/AddCommandClassDiagram.png b/docs/images/AddCommandClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..7e45b09da7c8688d7b9775aaa90b469d838f9626 GIT binary patch literal 80246 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℘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 literal 0 HcmV?d00001 diff --git a/docs/images/AddCommandDetailedClassDiagram.png b/docs/images/AddCommandDetailedClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..5f87bc2c9cff16cb890fb2fc4c6910f191d623ac GIT binary patch literal 91483 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 Date: Tue, 18 Oct 2022 03:50:42 +0800 Subject: [PATCH 161/416] Add sequence diagram and explain how data component interacts in detail --- docs/DeveloperGuide.md | 15 +++++- docs/diagram/Style.puml | 3 +- .../TransactionListSequenceDiagram.puml | 50 ++++++++++++++++++ .../images/TransactionListSequenceDiagram.png | Bin 0 -> 41423 bytes 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 docs/diagram/TransactionListSequenceDiagram.puml create mode 100644 docs/images/TransactionListSequenceDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index bbcd6099d..770b694a4 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -98,12 +98,25 @@ such as getting, adding, editing, deleting and purging of transaction(s) from th The `Transaction` class is the abstract classes of an `Income` or an `Expense`. The `Category` represents a category of a transaction. Within the transaction class and its subclasses, getters and setters are used to access the private variables. These classes override the toString() method for a self-defined print format when the transactions are -displayed. +displayed. + + A more detailed explaination on the implementation on the transactions can be viewed under Section [Implementation for Transaction](#implementation-for-transaction). +#### How the data component works +- When MoolahManager starts running, the Duke class will call the storage methods to initialize the transactionList. +A temporary transactionList will be returned by the storage if the exists stored transaction records. +Based on the whether the initialization is successful, corresponding constructor will be called to initialize a +transactionList object to be used throughout the application running time. + ![Sequence Diagram on Creation of TransactionList](images/TransactionListSequenceDiagram.png) + +- A transaction (either an income or expense) is created by an `add` command, can be modified by an `edit` command and +can be deleted by a `delete` or `purge` command. These interactions is described in further detail under each command +section below. + _Written by: Chia Thin Hong_ diff --git a/docs/diagram/Style.puml b/docs/diagram/Style.puml index 408e46cb2..31118cd29 100644 --- a/docs/diagram/Style.puml +++ b/docs/diagram/Style.puml @@ -1,2 +1,3 @@ hide circle -skinparam classAttributeIconSize 0 \ No newline at end of file +skinparam classAttributeIconSize 0 +hide footbox \ No newline at end of file diff --git a/docs/diagram/TransactionListSequenceDiagram.puml b/docs/diagram/TransactionListSequenceDiagram.puml new file mode 100644 index 000000000..a0a413867 --- /dev/null +++ b/docs/diagram/TransactionListSequenceDiagram.puml @@ -0,0 +1,50 @@ +@startuml +!include Style.puml + +actor Main +Participant ":Duke" as duke +Participant ":Storage" as storage +Participant "storedTransactions:\nTransactionList" as storedList +Participant "transactionList:\nTransactionList" as transactionList +Participant "transactionList:\nTransactionList" as emptyTransactionList + +create duke +Main -> duke:Duke() +activate duke + create storage + duke -> storage:Storage() + activate storage + create storedList + storage -> storedList:TransactionList() + activate storedList + return :TransactionList + return :Storage + + duke -> storage:initializeFile() + activate storage + storage -> storedList:Add transactions + note right + Minimal notation. + Details hidden for adding transactions + from file read into storedTransactions. + end note + return stored:TransactionList + + destroy storedList + note over storedList + Due to UML tool limitation, the lifeline is not shown ended after the red cross + end note + + alt Successful initialization + create transactionList + duke -> transactionList:TransactionList(stored) + activate transactionList + return transactionList:TransactionList + else File initialization failure + create emptyTransactionList + duke -> emptyTransactionList:TransactionList() + activate emptyTransactionList + return transactionList:TransactionList + + end +@enduml \ No newline at end of file diff --git a/docs/images/TransactionListSequenceDiagram.png b/docs/images/TransactionListSequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..7c87b4ec7cc4cb30f9525fe90a0b6d90fd3786ad GIT binary patch literal 41423 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

FRi$A>~xl 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 Date: Tue, 18 Oct 2022 03:58:43 +0800 Subject: [PATCH 162/416] Minor fix class diagram and explanations in data component --- docs/DeveloperGuide.md | 18 ++++++++++-------- docs/diagram/DataComponentClassDiagram.puml | 8 ++++---- docs/images/DataComponentClassDiagram.png | Bin 54726 -> 54711 bytes 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 770b694a4..b039556ad 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -105,16 +105,18 @@ displayed. A more detailed explaination on the implementation on the transactions can be viewed under Section [Implementation for Transaction](#implementation-for-transaction). -#### How the data component works -- When MoolahManager starts running, the Duke class will call the storage methods to initialize the transactionList. -A temporary transactionList will be returned by the storage if the exists stored transaction records. -Based on the whether the initialization is successful, corresponding constructor will be called to initialize a -transactionList object to be used throughout the application running time. +#### How the data component interacts +- When MoolahManager starts running, the `Duke` class will initialize a `Storage` object which will attempt to +read from the file and initialize a `transactionList`. The temporary `transactionList` containing all the stored +transaction records will be returned by the `Storage`. +Based on the whether the initialization is successful, the corresponding constructor will be called to initialize a +`transactionList` object which will be used throughout the application running time to hold the `transactions` added. + ![Sequence Diagram on Creation of TransactionList](images/TransactionListSequenceDiagram.png) -- A transaction (either an income or expense) is created by an `add` command, can be modified by an `edit` command and -can be deleted by a `delete` or `purge` command. These interactions is described in further detail under each command -section below. +- A transaction (either an income or expense) is created by an `addCommand` class, can be modified by an `editCommand` +class and can be deleted by a `deleteCommand` or `purgeCommand` class. These interactions is described in further detail +under each command section below. diff --git a/docs/diagram/DataComponentClassDiagram.puml b/docs/diagram/DataComponentClassDiagram.puml index 91c8c8bef..0375dbfa0 100644 --- a/docs/diagram/DataComponentClassDiagram.puml +++ b/docs/diagram/DataComponentClassDiagram.puml @@ -24,7 +24,7 @@ class TransactionList { + purgeTransactions() } -class "Transaction {abstract}"{ +abstract class "Transaction"{ - category: String - description: String - amount: int @@ -66,9 +66,9 @@ class Expense { } -Income -up-|> "Transaction {abstract}" -Expense -up-|> "Transaction {abstract}" +Income -up-|> "Transaction" +Expense -up-|> "Transaction" -TransactionList -> "*" "Transaction {abstract}" : contains > +TransactionList -> "*" "Transaction" : contains > @enduml \ No newline at end of file diff --git a/docs/images/DataComponentClassDiagram.png b/docs/images/DataComponentClassDiagram.png index 8c2f74444e8569bc7c96877722a5e6dc26cdf1b9..633b74db25792e222afdf9061b190cc312f3596d 100644 GIT binary patch literal 54711 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 literal 54726 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 Date: Tue, 18 Oct 2022 03:59:28 +0800 Subject: [PATCH 163/416] Add class diagram and explanations on help command in DG --- docs/DeveloperGuide.md | 18 +++++++++-- docs/diagram/HelpClassDiagram.puml | 50 +++++++++++++++++++++++++++++ docs/images/HelpClassDiagram.png | Bin 0 -> 53195 bytes 3 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 docs/diagram/HelpClassDiagram.puml create mode 100644 docs/images/HelpClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index b039556ad..f8df39409 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -177,10 +177,24 @@ Some important operations are performed within the `TransactionList` class, whic _Written by: Chua Han Yong Darren_ ### Help Command +The help command displays the help message to the users to guide them on the usage and provide descriptions for each +available command. -{Describe the implementation for the Help Command} +The help command can be run as `help` or `help o/detailed`, where the latter will display a more detailed version of +help messages to the users. -_Written by: Author name_ +The structure of the application focusing on the help command is illustrated in the class diagram below: +![Data Component Class Diagram](images/HelpClassDiagram.png) + +For each command subclass, they will implement the getHelpMessage() and getDetailedHelpMessage() methods. These methods +will contain their corresponding HelpMessage Enum that stores the help messages as strings inside the enum. + +In the help classes, during the execute() call, it will call either generateBasicHelp() or generateDetailedHelp() method +based on the help option given by the user. + + + +_Written by: Chia Thin Hong_ ### Add Command diff --git a/docs/diagram/HelpClassDiagram.puml b/docs/diagram/HelpClassDiagram.puml new file mode 100644 index 000000000..628293f89 --- /dev/null +++ b/docs/diagram/HelpClassDiagram.puml @@ -0,0 +1,50 @@ +@startuml +!include Style.puml +abstract class "Command"{ + + getHelpMessage() : HelpMessage {abstract} + + getDetailedHelpMessage() : HelpMessage {abstract} + + execute() : void {abstract} +} + +class HelpCommand{ + + getHelpMessage() : HelpMessage + + getDetailedHelpMessage() : HelpMessage + + execute() : void + - generateBasicHelp() : String + - generateDetailedHelp() : String +} + +class AddCommand{ + + getHelpMessage() : HelpMessage + + getDetailedHelpMessage() : HelpMessage + + execute() : void +} + +note left: Similar for all other \ncommand subclasses + +enum "<>\nHelpMessage" { + ADD_CMD_BASIC_HELP + ADD_CMD_DETAILED_HELP + EDIT_CMD_BASIC_HELP + EDIT_CMD_DETAILED_HELP + DELETE_CMD_BASIC_HELP + DELETE_CMD_DETAILED_HELP + PURGE_CMD_BASIC_HELP + PURGE_CMD_DETAILED_HELP + LIST_CMD_BASIC_HELP + LIST_CMD_DETAILED_HELP + STATS_CMD_BASIC_HELP + STATS_CMD_DETAILED_HELP + FIND_CMD_BASIC_HELP + FIND_CMD_DETAILED_HELP + HELP_CMD_BASIC_HELP + HELP_CMD_DETAILED_HELP + BYE_CMD_BASIC_HELP + BYE_CMD_DETAILED_HELP +} + + +HelpCommand -up-|> "Command" +AddCommand -up-|> "Command" + +@enduml \ No newline at end of file diff --git a/docs/images/HelpClassDiagram.png b/docs/images/HelpClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..144e3875624654132f8719b31414cea8547d1a11 GIT binary patch literal 53195 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 GQT_{SWslbY literal 0 HcmV?d00001 From c1fdaf4490c9e9b8e90b677e164e63f411ec876e Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 18 Oct 2022 14:33:25 +0800 Subject: [PATCH 164/416] DG: Add implementation (draft) for Find Command and UI Component, and fix issues with line breaks --- docs/DeveloperGuide.md | 76 ++++++++++++++----- docs/diagram/FindCommandSequenceDiagram.puml | 55 ++++++++++++++ docs/diagram/Transaction.puml | 12 ++- docs/diagram/UiComponentClassDiagram.puml | 38 ++++++++++ docs/images/FindCommandSequenceDiagram.png | Bin 0 -> 40327 bytes docs/images/TransactionClassDiagram.png | Bin 9576 -> 13418 bytes docs/images/UiComponentClassDiagram.png | Bin 0 -> 25826 bytes 7 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 docs/diagram/FindCommandSequenceDiagram.puml create mode 100644 docs/diagram/UiComponentClassDiagram.puml create mode 100644 docs/images/FindCommandSequenceDiagram.png create mode 100644 docs/images/UiComponentClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 2dcac055b..a4eae0330 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -42,8 +42,7 @@ _Written by: Author name_ ## Acknowledgements -{List here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the -original source as well} +{List here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} _Written by: Author name_ @@ -59,15 +58,12 @@ Firstly, you should fork this repo, before cloning the fork to your computer. Next, -1. **Ensure that Intellij JDK 11 is defined as an SDK**, as described in - this [[Set up JDK guide]](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you - have used JDK 11 in a previous Intellij project. +1. **Ensure that Intellij JDK 11 is defined as an SDK**, as described in this [[Set up JDK guide]](https://www.jetbrains.com/help/idea/sdk.html#set-up-jdk) -- this step is not needed if you have used JDK 11 in a previous Intellij project. * You _might need to set the Project language level_ section to the SDK default option. -2. **Import the project _as a Gradle project_**, as described - in [[se-edu's Import Gradle Project guide]](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) - . -3. **Running the project**: After finishing the import, locate the `src/main/java/seedu.duke/Duke.java` file in this - project, right-click it, and choose `Run Duke.main()`. +2. **Import the project _as a Gradle project_**, as described in +[[se-edu's Import Gradle Project guide]](https://se-education.org/guides/tutorials/intellijImportGradleProject.html). +3. **Running the project**: After finishing the import, locate the `src/main/java/seedu.duke/Duke.java` +file in this project, right-click it, and choose `Run Duke.main()`. _Written by: Paul Low_ @@ -84,6 +80,7 @@ _Written by: Author name_ _Written by: Author name_ ### Data Component + The data component is represented by a `data` package which consists of all the classes that is part of the data stored by Moolah Manager. Within the `data` package, a transaction package and a transactionList class is stored. @@ -113,6 +110,7 @@ A more detailed explaination on the implementation on the transactions can be vi [Implementation for Transaction](#implementation-for-transaction). #### How the data component interacts + - When MoolahManager starts running, the `Duke` class will initialize a `Storage` object which will attempt to read from the file and initialize a `transactionList`. The temporary `transactionList` containing all the stored transaction records will be returned by the `Storage`. @@ -139,7 +137,22 @@ _Written by: Author name_ ### UI Component -_Written by: Author name_ +The UI component consists of a `Ui` class that displays information and error messages based on the user +input and the behavior of the application. Static messages are pre-defined in the `ErrorMessages` and `InfoMessages` +classes from the Common component, while dynamic messages such as a transactions list may be generated during +execution of the application. + +

+ +
+ Figure 2.6: Class Diagram for UI Component +

+ +_Written by: Chua Han Yong Darren_ + +As seen from the class diagram, every command that requires the ability to print to the system output will have +to call the functions from the `Ui` class. To add on, the `Duke` class will also use the `Ui` class to read user +input. ### Common Component @@ -156,13 +169,13 @@ _Written by: Author name_ ### Implementation for Transaction Each `Transaction` object in Moolah Manager represents a transaction record, which can be of `Income` -or `Expense` type. Below is a simplified class diagram (note: methods have been omitted) containing the attributes -within each transaction and how the transactions are associated with the `TransactionList`. +or `Expense` type. Below is a simplified class diagram (with methods omitted) containing the attributes +within each transaction and how each transaction is associated with the `TransactionList`.


- Figure 1: SimplifiedClass Diagram for Transaction + Figure 1: Simplified Class Diagram for Transaction

The `TransactionList` holds a dynamic array list that can store multiple `Transaction` objects. @@ -235,7 +248,6 @@ which calls for `AddCommand#execute()`. Lastly it is also associated with `Param Figure 3.2: Class Diagram for AddCommand and Related Classes

- These are the important operations performed within the `AddCommand` class, with task description: - `AddCommand#execute(TransactionList transactions, Ui ui, Storage storage)` - Adds a `Transaction` object to the @@ -278,9 +290,39 @@ _Written by: Paul Low_ ### Find Command -{Describe the implementation for the Find Command} +The `FindCommand` class provides the functionality of finding a specific or few transaction(s) +from the list of transactions recorded in Moolah Manager, based on multiple searching keywords that +match the details of the transaction(s). -_Written by: Author name_ +The sequence diagram below shows the interactions of a successful execution of the `FindCommand`. + +

+ +
+ Figure 3.3: Sequence Diagram for Find Command +

+ +**Step 1.** The user executes `find KEYWORDS` command with an intent to view a filtered list of transactions +that match the searching keywords. + +**Step 2.** The `CommandParser#parse()` method is called to initialize the `Command` object with `FindCommand`, +accompanied by a string of keywords to search for. + +**Step 3.** Moolah Manager (`Duke`) calls `FindCommand#execute()` method which first checks whether the string of +keywords is empty via the `FindCommand#checkFindFormat()` method. If `keywords` is empty, a +`FindTransactionMissingKeywordsException` object will be thrown with an error message. + +**Step 4.** Since there exists a string of keywords in a successful execution, the `TransactionList#findTransactions()` +method will be called to loop through all `Transaction` objects from `ArrayList`, checking if they match +(i.e. contain) any searching keywords given. + +**Step 5.** `Transaction` objects that contain the searching keywords will be appended into a formatted string and +returned by the `TransactionList#findTransactions()` method. + +**Step 6.** If `FindCommand` checks that `transactionsList` string is not empty, it will call `Ui#showTransactionsList()` +method to display the transactions. Otherwise, `Ui#showInfoMessage()` will be called. + +_Written by: Chua Han Yong Darren_ ### Stats Command diff --git a/docs/diagram/FindCommandSequenceDiagram.puml b/docs/diagram/FindCommandSequenceDiagram.puml new file mode 100644 index 000000000..5ffd3a04e --- /dev/null +++ b/docs/diagram/FindCommandSequenceDiagram.puml @@ -0,0 +1,55 @@ +@startuml +'https://plantuml.com/sequence-diagram +hide footbox + +Participant ":Duke" as Duke +Participant ":CommandParser" as CommandParser +Participant "command:FindCommand" as FindCommand +Participant "transactions:TransactionList" as Transactions +Participant "ui:Ui" as Ui + +-> Duke +activate Duke + +Duke -> CommandParser:parse("find bus_fare") +activate CommandParser + +CommandParser -> CommandParser:splitInput("find bus_fare") +activate CommandParser +return inputTokens: String[] + +CommandParser -> CommandParser:getCommand(commandWordInput: String, parameterInput: String) +activate CommandParser + +create FindCommand +CommandParser -> FindCommand +activate FindCommand +return + +return command: FindCommand + +return command: FindCommand +deactivate CommandParser + +Duke -> FindCommand:execute(transactions: TransactionList, ui: Ui, storage: Storage) +activate FindCommand + +ref over FindCommand + checkFindFormat(keywords: String) +end ref + +FindCommand -> Transactions:findTransactions(keywords: String) +activate Transactions +return transactionsList: String + +alt transactionsList is empty + FindCommand -> Ui:showInfoMessage(message: String) + activate Ui +else else + FindCommand -> Ui:showTransactionsList(transactionsList: String, listMessage: String) +end + +return + +return +@enduml \ No newline at end of file diff --git a/docs/diagram/Transaction.puml b/docs/diagram/Transaction.puml index 1d8f7e81c..8ddd77c5d 100644 --- a/docs/diagram/Transaction.puml +++ b/docs/diagram/Transaction.puml @@ -4,7 +4,7 @@ class TransactionList { } -class "{abstract}\n Transaction"{ +class Transaction{ - category: String - description: String - amount: int @@ -12,14 +12,18 @@ class "{abstract}\n Transaction"{ } class Income { + + TRANSACTION_NAME: String = "income"{static} + + ICON_INCOME: String = "[+]" {static} } class Expense { + + TRANSACTION_NAME: String = "income"{static} + + ICON_INCOME: String = "[+]" {static} } -Income -up-|> "{abstract}\n Transaction" -Expense -up-|> "{abstract}\n Transaction" +Income -up-|> Transaction +Expense -up-|> Transaction -TransactionList -> "*" "{abstract}\n Transaction" : transactions +TransactionList -> "*" Transaction : contains @enduml \ No newline at end of file diff --git a/docs/diagram/UiComponentClassDiagram.puml b/docs/diagram/UiComponentClassDiagram.puml new file mode 100644 index 000000000..3d656d13e --- /dev/null +++ b/docs/diagram/UiComponentClassDiagram.puml @@ -0,0 +1,38 @@ +@startuml +'https://plantuml.com/sequence-diagram + +hide circle +skinparam classAttributeIconSize 0 + +package "UI Component" <> { + class Ui +} + +class Duke { + - parser:Parser + - transactions: TransactionList + - storage: Storage + + main() + + run() +} + +class Ui { + - in: Scanner + - input: String + + printMessages(String... messages) + + readCommand(): String + + showErrorMessage(String errorMessage) + + showInfoMessage(String infoMessage) + + showGreeting() + + showHelp(String helpMessage) + + showExit() + + showTransactionAction(String infoMessage, String transactionDetails) + + showTransactionsList(String transactionsList, String listMessage) +} + +package command <> { +} + +Duke -> Ui +command -[dashed]left-> Ui : uses > +@enduml \ No newline at end of file diff --git a/docs/images/FindCommandSequenceDiagram.png b/docs/images/FindCommandSequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..e9aff42c161d360b8497df381781d8dec2721d8b GIT binary patch literal 40327 zcmc$GWn7hQ^X;}pMN&{uX^=({kwyU(Q97hWL6mN!5v5TC35hL&NJ(zGMY=_rO-m@7 z?&iz}RD9m&J^$Z1=fm*>68FAiuDNE`tXXq=N=x3vJw|p6fk5Dj-x8HYAP!zaAoklG z-4DNccZdEb{Kse^cF*FWv5Ad=&La!NO&ud0Gc5}pZOR8Wl#eYeOnBMZO$@Y*EG!KT z*d7`i5^(UGg)=xkk-um0>-UI#aGIy#6N>0pT^#rh*=0gLPoKQbVMTifxO-U{cd^EO z+K+ZmNVs#!`B0jiW=Bu))YXHxu6JfN9-=ac$Xz~b#~)?P(%we*iOeoD^$M3(?3?7A zd1*db@ydQF_e)Me8=y5BW_~63yH`cV1-Ods6FG-oi zb9`_v$8n|*%9{FK$(mNFy1G>R;27rii&izV^U9IYIF)PV!l%PrNjT&@Evdsj>Yh*+ zB(|-oxmatOd#|7>){knlnc~R_9b7o$_dK3c=#kpPLYbo}x02EbP2PTuX4W#+85&(-Fq?hFPZ^lPv)Qg z961>mkeIJVXF=1bTjy$|A3eT!#N4Mc#E3l`AAgYOM#BSXCib`%C89W&IIGb6iQnvB zZQe(Te}79RWqw%i?ySVsuM8^pGvbod~oNmF{{<769`E+WF zLDKEOdwtFT(uS@=qm*_>QlE2Qj&Qs8@>n!pUwvn-Wx;va*eJDs#1V0W=Nwg8MlOlm zs0~-gIZgBB!RS*srYqr51^Me4hx%_PyDDC~_N23?)MRRU(Gv3^mE-!v@gG`MY^D|G z)GGsh}YV!sc2^>!8X#94~5>#LUc>%Ot59 z0q!l{!6dJPiMNDHJ}ExgPg)>NobWBmIq(*t+q7fc$`mRk-`%`nJf!ZS?nwBQF0#BU zeMwMvk!MAxVXCrD`1U#Qe8lBAV!I>QZ{CV2V>j0OXEH(9A0OWjpu_%8_O_6P$Kt|t z$3v-&+2H2$HbZHm9gZ{JmFFESx*|5`lMRun%Eg-#H_tPgYY1n(HTD%;U6zv)P*qjc z-#UXp_|qb9ifp zLL$8VC97oE@;RHu&Yz1E2N0iGjGPPzj5l)wx#f*0t}Pm|ONcsr=Qnz8ytYK)IOxe> zAmMt+uCv2h;iTf8&tA%N1A~Plt;Q zSaz4TrQe-OgpJ(1!JZvuMSHFKcK$L-y?9x(Q5JpG&UDhNqqJQy)Hhwb50R>UjS{P!a$M@mGTzEJFADi{Mf$s; zynN((>1^~h7Pr#l^P>aJRh}DM%-vsNb)t=(`!4PBkm7ksBjX;ml-P8+g{$r@ihbh( zUr=SMCJo_R2E7F~RW+jNKTNV#-@Q$FtPz}q*4$e49UL?&-dt#&D&Cr5h>+jhTuRZ^ z(J7w(Qri%MlbKtwk*vNJVQ)6_qv;`XuHInBsxi46Vj-4gR-6T=OQlE~NZ!ziS>e}f z!0NfbLA!T{2RC(o?;Z;)bXN_!7lF<_DHC$pD13}}EL3iD(wccH-#Ed*yTX-J!(yg< zVlofYD`1z9w5F1cy{{K5dG=FR5M7avbX|;K+)a_-?kbh!J3%#qW`beW@ zv=#A3F{8 zLIU&cX5!?)?57E4_D5Td_c?v#n=&}1pW3k1n({83 ze}(FzX2Fo56Ycr_m_nM4Ym?#>GTp4Aa9>J+-V~P~pmPdA8 z3z*v0ui(A38q9`s&kyZ!iBNRJLQtfQ0N&`XLr5aM5|@Z`J4tBBL(xXswI-7fCjR}= z=G!|v<0II!A=KQc^;h`ROI*T)66fklP`KeG)*MrB;I6Q};+Iou+gMpyC#IZ)evAjJ zzM;R{IvOl0vcXc;SiYJ|zcA^w!0;)8GAodI>*|wl&p+JsEu8;mk9pO$5a%c(IMFFb zhlWKCu`FtdllcICF0@=?=(K?mx-M8g*%PSOz?{NDA|NKFJ_YNOaOEV)i!2Aj-H7
~mYW?R<@usa@*2gbcgPqnsNW**o$UoVM5s#JovD>l*V~m&)wL=JE`B z@+>hL8bzgDG~ZU0(;b=7tSTAuDJKVqg`+o7h8xfFvefyKCSoHTudRQLu%ES&309Q1 z@6Y?N5Shuc^5X-$OMSMHmY{1#TMD~o>is8uMM^!UmFg&ld-%LdnMS_@S#GcI5XlH@ z>FQS->=uR?&?v~KE%0hWI^kS{Vf*6O+!O8fHe0jXTQ|Kgx8c6s#9Vz6|2-q)!b@2` zJv=X8LC&D)8F?UMR@oasrc(rsbJ zMrJ)ttJAZ!YwL|$_S9%*rDVJ-eR-}c#CodfBT{-7?%^LsEB)uA*^s98^0}OgmW!pv z@U1k6#9Lj>Rv#^O7-cWDG=F9L{n*uZqQt&D$ux95k|G5pn1LPgkS8(0L_}aArcBhpWw^pquy0+Hm%+VRSCc`yCH8L`6 zD#bR6YTb7i^NRxRp_7qqR*sG(bs_22Q^~~aM4>6EpWGkJLcnJVlF=yoU_IqnGNPKz z5plghv1oaa*=|wZ`@MpzMJiZmp5k!Wyz{mT18l#e*8<8rDQfUJxzX~Z7^zOo$BzpWcCG$&#&J|L z>C4qplTTWiXlOnxyuC;_t2&-Vle4`UbZXnICe>*5lx9RoGhbs~Ww{RyGlKIR^?^+P z^Zs0t>RYLqRSbFAi;AuJl=rwKFG6F{qhZ_NEEL^`HK;~wdA(7@_XYksRX5uo*sO-{ zr{#OrMx;4rYhB2@J=sCZDvymm9G$!niMga^tk587toDqUyA3B9^Ou2?aC0}tl%%{= zGQ{7+oa42ypRFEMC^ihW#BL5FoXc2F<+}fSqGzN6*t#M zh0yuVa|E;qL>UgZvS_u*(egBw_^Irv0g;0^2!!dEyCgD~&R!3odwlEvItyNO(Qr2}U~AYMF29fbdc4}4(3vIt*4Uy#85`f}>kAUd~Vy64)W zuMr4*hHh;Fx>73QJeK&xM710fElT`UycZ9a`-nv@QQtx!Jcx`?9{vOP$JB$44i68D ziHUu<@zQp#o_pe9$r(W+S{j#w2Lm6#7aX#+OWkmD zb8}G@Kl+NDQZx#ai&n>S%|~mjCFWh-9f{!}XXDlnEPh<#*RDOgAa?Bdae$%R#=Qo} z_w4-hAFyY!XYNLcw$R$x*uY)3W#2UT^GR2Qpn%|DQc_aBzVm5Kq~}*%O64 z8|SG}wq$3A8~Zu-K3(~xkT$SRQw|e5I|avmXM^B?fH)xxH4}rHwdsV9Y(QlO^5^Gg z2bh_d8p8Y#C*1iEh|Q9nts+BLbY=C62q)9*RIsLFu1;-`lBUA~e9OqkZP?XP?6d{A zBr!4Z&~cKu5V)V>_1RiXgF^))7gwfo`XeO`o8kPaB4&n`wl*(Oej1wZF=0l$W+HH0&w6&d^d4O@9&$GC?WYQ>M}r|c%W|sM0dS%F z$#b&EHXPJIo&f`<>$=9V_Vlcfix1s$PA>MkJ=(j(X4R=QF^v3U86T)iix%2c@hAjY zWJgWES5!9)4i3U&Ao*>~HrH1|LPGRLyxEJMPWPAOnMarsPNWpA>H$!2b#*oS5-t7) zLZRv6c&E+W$lZ`jVpa)KCOpcY2wLA>H62WQSP2n}?cH^&Uen5RIyyQGlHvHc`e~6K z4HO$cZazqXD*Db_j%$`G3{`FzlzP1S&K=uqZ7dN@o2$7M-#&zJb~1S*7I4D-wS?Qb zi+2ZPV$YpAvpCt4FZLlOrh;dpE`+l!^jabEl0ir6#fKH}bzphsyPg?PCt_A?WYn7M z7cRU;CaGp8CnYJEC#uyFE)XYhGBac58ihz-3fL_kWR@s<{n+Ch9Xq?!uN&csxX|&@ zL_nWhW+C%Qb4BlQ@N8Oy-5@!xu9()xGpEjEtv|@FN@^u1Zf$La=c1#dgFKl!GT3K7 z_tLbj7LH?z1B4NVWq5PG=Xi?wv2 z<{f^KQoNZ5{*chjd+mx^@Wky2!dcmzcbPA9_7Ad*7sB<0_nZ7#vXG!DF^_fC!d8yQ zU*9vz#UyGJ+E%_g*QTa6L5E#9U<}6kd3bnG{HZl{b;LKbEN?f^s774REZ16^>|rJY z>zF4;PIlL^7cF1E{E9+|&Fe@jBe{ZtLRO(ct<^^>92-Wp#;zNQ1B2e|SxZ>X3+FRk z>8rPb)ongrC-O5g=BxZ@8U@$Jd#w9d`8Y3K$};L9J9@C;1|*-(sKZhZ!>bgl472l{nJhf^%hL<(n(QPrOkVeOs>fPC&CSh$jX!O&vaEc> z=Gub!ZxVX!?K=6g`7a8HlXVSgl0P57%j^^M&7~h_9Ru^ zsh3yfJ_qb%~LUjYP+D+0=V=s7pVk zwb76?Ixg;qzwwh^{)_7hOn+jvPY~U7ESq|shY+9T*!*smG(7;mMZuvG z)REsWrr(}iUtLWgG+9tkKqcZf4>28flKE&u;Fe^t;EC;AIL%Z;SmvK74$C@=oU)IV ziNM0sV1y9a6P+wr4RSP0VB z7v1Hxn(6bURHDi*r;lRur28{$PJYoiT|9Yg>LL~laopZHHNIde4_!f}$x)f0&lem{ z|G2piA-sHIG+^iB`#;R@TxNhXT<(kA33q%Q9G`E$&$bjeAmAqAfjW5OCtR5bi_2rmii!7{Ahb?7OxX^$=2p+GYx zIbTw3-x}aU=l%yF-9OB4Z(Ojy4)@nob(M3uuI^5&)vFs7ws#_>A_O5H61@y!RZS3b zZE0xa70mFKAamCkuoQakbvAlGM>U!3Ts=vOvvVN z*+=!E>NdlHQ$=gZ+?$hI3b+2J3|N9uldqzst7Qq0cSWL zd0_5Ofo~lBLigSCtzc}6?-vH_I%X2aFGAW|ZdKA`RnN;vO_jFtYFPoN6|i4Ut@5mV zNgm7loXY@7#30EJDR6eUw!B0kcEq_YD^t$FrM8-43y#sww4;J*^?Sm~1o@@Gf;OP)swJoy%Vj?XGwz|IN+x?Id1 znVYG$kQ-Odk|Hj%l=dmPEuB6|73{zc#%`?Kky!h#q+9W8>y?RembwhGny}Gtf<&F8sWr71~EGA zyfOju!L7@pKmCJ)V|kN^v`ZJR=b8yNg>dSswRaXd*vY){aEp^YcI;S-J+n;hZj$7v z`n6{kMQO?Q+|zB^vgb$ejJ`#S+uE)IajpY*HO!qQ_RYYdlI9*wxMcl4Lrw)SXI zlSAQCwwHI3<5MflK{1OO98ISUOR(A=lZ}myfo1*u{Q-2}YhS77?yItDj=3vWME8n? z&#>$izOELm>S^t(u`m9`Mn8wD-^`8Frx*EB^=adKX3hy5)ZM-d@G>>)M#z|Rts=~$ z_U)q(jym(LQy!0_vm1G@5g;|~XIvkj6>?M~#!Ha9l6d@$0clNOm(JJs*I;>+3vCww zqp7ts1~GV3bAe~e}gB9e)@_myg^ljPQW)dnY zDycH6v2)I|`9BT2GHiZ+3|8~C>2p}}EYwC^hvs(SCgR_5{kH6NOC|x)Fj7c^F=OJ-xC+1i>G{o%yUMiz82ki2gm9*$uFi958V!Jo z=>;~qP>CsuJr=e9{7%5ms1sG*iy{=Fc9$mU$Y`J^Yg-)*W=7`VQbnu{@Jm<;bBe z>1aQK5T4~&Ps!h#JX%s;s6en^(Ao9m8p6{bAwI`x;bfjhAjWEUJ?mGJDkaprh(NRi z4}=r;KD_$kg2ep*?4!BSwLCx|;@E%1jK9xu;05%yr=vR_wG^d?~?{gOwIZC<3F-!iV{0;_(aZ7ICugvDQRUUeS9<9VT5y;?q3EO zjkC2AC853UdtTr~3$3LQ+!&?j=^ZD==G@LN!l%eRtMlX$2!HQA#$i1%FGF?Xz_ZWY zx#mak&fAxtQt~-3koW$*u*JyGP@+m^jUYJ!aqskI(WTwQAGaDrmrw}h4(wG)B=5`9 z7qN&^o*uit*I|9B*(7KeM#QbGtTZ%wfrm99g1@}>0UOB&=y+21@n4+lfkGNlMh@`6 zOF@~se8I^En#;}?VGWtGu%yY+iai#?cAD?I4LJ3j+Fej$>>-c#JlpOOzgPUf9WcdQ zx{FGpuvCmQ5!}H4{N3jhdz=Q_{&qg&wq>dG%XP60@V8oj)?)Vv^V|Q!0lOO(p4n@g zF_8Q(-!Uobh~K_#a6KD^lD#6^9WJz_d0FJ55_Ha0R6+v-TLC70`SJyF0RW2IC*#Y} zqN)6tm9a+X+f$x@5Muw>zba4q!?fqUK)N*lkdP-B90nRZUCG35`?-24!4+*veEim1 zmX-^U5r8RTau4_ReLLZE=N%;wStpz{&tQ!Xr}^>b7T_%ErW!)s&53c-V{nGc#=Yx5 znm&vTK!|25T7J;^YTy!ZTc7PtY+;hIkdU}`NF+jZ?B)Pb8k0m~#MMwvT}db)<5tccHb; zM(zBptgPs0+CMZw=ufp?uT#urrF&pjC?ok5=m6L)54g7;$tFe*ialJN|A}QM3_eMY zrYk8aY1&O>PIP64ap?zH#u(%X9z@X6>_Xe}ODkV#{jgx4Xy$?hy=3^)@s70N+F%7)4Kj zD?{#a{d&CxoAF=JNyoRhUDHWjM&_bn*X6Viacm^4Dzs~cx}t+iR4vVSOG1On15?-&Nk{vpnvhbaH+Spw)Tx7xr@`QFtlErgVKBR9(oi$e}#`U1-!C{=uS2j1?&w z|H%fRmiR|r4F`^#tjf}^t*Z2;NGVb^LmOr*kJ@i6v=`bf^=27{jT5`~6glKypnBB! zuBX_^33Ep^A4)=?w0Z0n6W1s6L;J@ng*InseEDZfj!<|6daGdKW8cYYnx?V!E_kZ( zP34Zhd(s@^$s`dTin%i?0zvMnx6W*&I5p<_%P$N*fpm=mOKOm%j0$FM_&L=|u4GRA zD&$94v%CF+g6PrytTZVJ31mEG+5lFk2TH}zS4MrE*1rRw*3OTKxxtToDl7A8ygRq~ zN^@88A3*eRDEtZCJBu<80$`hrfQ-wxnxL0@XFoe6&ZDhffFa7YQiQE%Ue5(&utYH1 zDSeX$3)p%HkL*2Otf=F8UfJ@hXbI7HvGBltU|vdphaVSIbV(-k!s|o1fJu8gU2;U? z_HAtj$OjN?ccQyA%NslgLTn8t9dtS86tf~RG4W;nHiN3yRBKC9!AQo@t7wGtkj}EE zwJQK)?EYD$#nuiSa#%&vpZXS?=3LeT@tw$wKH**62N`VWF0RMh9lVmoaaGCs$2|t} zw$#Rx2;qQ#)WBSI(<#&lM4Ha;()u^-!It1VwKMMb9xP-0@E3U4fhdTAm%A0R-svgq z9i0~WubAnp@8LgSWbnB)^cBQklApoy@jEAVc?l#_Zbn8%PEJliLAM|YcL^3`$a4Hu zpBrArKU#<6vj(OCLJ*J!nD&(HR~2CcnqmmV1Mj^jY>BhCgI=s81Z7qxrkd(%VC?Ew z%=aOjU+uvZf+xzb#~Jq(^{q5m&(6+@N{O`6x;dTNhwxzClT1J2JkN%Z>TfV>~ z)zIKL5~3^WO4)^y_ich}qxV%ggJ8Q!`eUHnL;NxF&^9 zit6B2%F4=7vCyN~#v;8pw(WMGT;S>Sd;2-j7KW9Tl^v9_gF^8QELsy3fO24GVv4#1 zaOuu35Dl4loA4;zss*B3{H0U=p`E7C_OF|&I%?j6e(f=MIIC(_U1MVeHjP{`jgDT& zkect%7}+Q2*>cB$Yy-wBt^pfH=TlTNZ!dVrT)Zi8;!PfXn>W!*lQDExR~G~fpvQss zCng~Y3Jip7sAP7bV=_%egZik|tK^`yj-O$l$_XKR+9!`_g-y_SCN|K7OizP5uuTqoA(tFp=2~f#0Yt zQJIM#J}HTnk+Hg}D(7cR*sHTVves-j{e}+uodu^=KPS385@xIo{Q9H$A5BRrnPi-g z>Y&Kcj_sHURMM#C*9FIcoLyT}16V~p*G%Uv5k%3QUWY|f@%@thneE{`=Db$pkCDm> z3S8P%_-W37Ka^RQ-`P!qY%9j#$o^;09f0Pzx>cu+YLa`*I zjS9oPkd@rJw--g$_t#u`9sLUcc5BYld}w4`_%MxwrTvmBd?~iV`qoDvI{+xjzfA+~ zn*lp7UAM6zjN7;-fDr|HDgcOTI`UV5oop1^T8GCS0feOW%G>RiQP zGp$J}Ti+mGUjEc8&ueQzfyy712P z-y(nzUvl{66bXuilB_T^dxRd$*JtZIC+GPF|3FElt<}4|fHeZZN zx}#09DY*|O_uyJn+@XVE@3%bN*L!fTP;J`|&1GSA_~FRt?B?3k91k0UgK-|0(*95B z+rq@az<__W1-Z7Fahs+q+nD`ZOrH7HfM=D8b}ZLjaR~_`o@Cp{fe{hxTVFJyjUYXu zYnXcN;Q^Z|&Z_O`Z~tXp4(RPhx(bH}$< zQ?~Nv8u-M%2dNs_e1FL;`W=8r?#f7*n8>`hg|uWddl6ez$pVJpWlz3!JJOE%hU=S! zg7C_wwN*?CboXGNUNPw4@GECi$+)$Me)ThtP&Zb;`|x*Lw%g!0gS=E7Qv5?xlBqZi z<|o>I@{?j_k~kG&-JV5w$H_vIhX##69QcyA??tuE_l$49nM6;I^`n(Pn`0Honlhd= z?*Kh3mF9qC_58`MObG#Ex|+dqZynWfX1oiV8Jnk4Rs(`rdvMIJTUuH|P!|^$hrV8- z9gpr6qwae{ewL#Vem??o@Au@&DK>tjg=K%h2K=VqRj&skL9$UdiKzx%tjAi!9hfPH z2G#KUl`&+dU!Ue5shu=r;*$ixj^Rg?QKF|!h^N|30 zPM5qhQIDvWdhJpcn5O{eoV#lB-oX2y%4WTTp`jswBJEiH!w=yb74-ANxS^MHYM{4b zJ>C)7C&8lOxb~p0$RUA~lT%JR1jS02@`gdGCd>bAEs0U_#v;Xf|B*JQu4M?413>xl zKPWreN@O;Ze8l>S0df|X#Iz?oUQawuN0m>buuNF`*Q+2GYNyOU$J zc*$pcpy+yH=xy)>g6A{g``tOFtA#+_aafMYr!D7C=1EvWo6H)BNtC*ZO5US-vWr?X zgXQ3=!AQ?sgKKK{W~3AEE)*Zvk&(hX>PnE%Y^I* zDSiz1)BSu5Hea;iDDHKZSD1tCB8M;*?<8Ed)XH3Fmhr5dL87vQq^XujH>&=0V`P+i zH{ZKnh)TxNj_fYSgR1DsRJ)I&#$afOy3&tVt_S*mIgs zfLD^0(?`lYiSPuM?~99jI&_HGE{?}<1{1yr=>KV)nxn8Ko~z&zAyG)XoO{)YZ7?_) z7}7->VbsMPm@CacH)+_fq1=Tjx5agG%tDqY@QFA2w>0Fu#`8Y@&{!W?^U;eSu@ z{o}8kzfczxal2IJX1n^Y<|&Y!y{X#O{;PXLV(IRV|STSX18mBC4AvH zoKGl}*}xaqXTqNa`==MtPPPlJ?J!YOHxfCp z-uq{8AP0G9R+lwG9|TfYik`m&dtnA(Y{2#XJ%m{fK;jO9Ob=@`PpMW~@DjjaeOsGL zD@HhBmLFKeN&a5UTP8r8eY^#Jw=h9KUPBmR96(p?5<$FMdc&%{u7a*Hpm}Vkefx33 z<0&7kN8^LWPZSE%3lh#RPHk?@!aUGiy-AFV$IP4Spz*;*5MZSFKa*ogUoZXGH;;-Q zr8jI%mll4n*r4U(iv<(+)p0-V%~}iKh0eY7&aG7jj#*$Y-hpF<0rBZtYqH`0EM;vSbU!clma01 z^xYUF1=eb2Uz)(WN#|1WmXl6#6=;MuHr**EPvu`W>>9W0TN45}7Qs0xBo=jc zE&pOiI)%f$>kp^tlAEHBQhtq-^yG8jM{a%66yUL$6?srp)t+xvSjRPCHIaeay0nbs zBZUgO$?9@iiUDo=f*-_n=cn%>_65*69|3(8uO(Agu6aa~9PKbVdrLhl+YpU>^M(OU zjoFxs*g{Qp+p$DJM}@7GV&ou(enicr1iJR1nU@vObM!r5m}RhXD+^H7ux&W;kx^6G zF`M;~`mm|4h1^hMv_EFG3MeCCere#9Y6w3ca0#RX6u&m#Os1*}GZstZ@j-@7_~zQG z+Jy<48Ljz8+A>syt{E^ZZJNSr0!v``^W#d}IdJypdauSvhfCfGD4;e;U>xrK|SY<+#*1tbC9vDhJowv@+5B=XCL+|S#}vwSG9zd6;rq4w*_TLbQP z$B!LzTB&6a{Z_2szBw8ZfsUzng=LU=MGuw@8@O|b;5&mW=yyR%ijAjFg;PTI1wIdnNQk6zH-nq9?UR8r%J_wNF!!w>K%p?JN8dRR;`Vkwc0KY6zDr zV7a|A_e_I4{>A?=Nrye^U;gkrxcwa=rx80*@y|9DhEVsHqwWpS*vCcacs6o_igreL zJ3u1v-m}cC{F@&XFCWtJbjsdX!xNgT<6L|zd_BrpbWil`Z@L*f(L-)RpJ?FNA-LRc z3FsGLXj~!| zaA#Dsr%+#gZR>#HX$a?e=zEwpLAQS7w+ISaV6^8p^!*%(9}E9&6X9=7#UwvLG^MrU zXbAsTo0+#jgmrYc6ZzU=K1B>PT*7a6QonQb2Pf8VzWWhhc5=r5aN=i&wSR%}9S@@X z`9srt{)6y=OMeJoNH4pWdB5*(xNwViwVit7!dK@#2=tOkGFkqXV$S zH@8%xL1Fg76Jm0C0*i9S<0fcpdwY377Mys9z?TbckAgNN!kGgwaNLDjf8Z&xg(PsY zP~d}h^gaE)`_6NjRkwec!z*X|EYAB5u<^t~69oa28weM(8~qFzg!i_89|kT-AA^;((M41k*6)AI%V zA({z-bu|?gEFDEab7OwI^G(e`VUdgL@rNlN?7@Zv!F2w%bCAjT*x8MtvdF?g;2XP5 zH4B${C}77CcFaR4R%G+Vx^LfG-DN>sQ6KBYU$_B_IM>;y5Opw2u#;l2VDmFSY@2N) z1pA1x^US9F0mSmb9W1e@&;9vCZ>PwgeS-LWqUgw;c!OigfSO>Y%bdv(6krF_g4Q>OC2UOi{%7F+n1f{J# z-Z3mgpH@dWFCGo*qHD@gFT(NtdA~qw+RWS>1nI3XPB__}n-2L7NJ>E`qw5}<0s9cY zq>9ok=?8zcjV@3zyoi3q0opUb*zudtZvatHyt^mOs@!41XM0WZ=Tl_~EI0@E0|Rofp28D7Pzl*!0t^4H3Pmq5*0ZX$o zH!1}RbnKliqK$>1O$5!^EZpkcBse8 z&6Rcc=-OYtW_((+lDLsTFd~O#eiJpb4E-^o2=S6>2QrV-Q{zjq-7Ev0~6JV}4YCl7FQ3j$tfqx`$SuZZ;XOS8nZ z2~%Umv?kdS2u_>dX=hIWae!q4D4poaBHXZA-afX2h)uB~ zX@sZmp8#v1Ws*GkHBItLqE^H?;V%&IcET){HzB(*vhVXL_LB&Fyk9Ls#I1un>vL_p z+xVV>%cmG0)U+IKkO;SllzZ1%L9Mkqh!ofj@7u!Z?dGqr?DrdPyLb<)Y1`qFe#@A) znH``>p@I(#3|xG+%(4FID5dggi0=3ojkJHo$yJV{SMvU5Yq}4ro zUx5dL)r4`Ca3N9z*ecCs8RaEvi6}fi0B(Nf@gfsV~p-Kb0=Bp&om)zHZM1 zF8rl3)(<;Zhs96xT01RY7;*zq3jy}yT6=)eHW4?yHG2|~jJ+p71Angs;`PygWIn7< z>=&lyz}mTD?|p3l`u$$lfy4g!GY2>&zM7KcKz1a(IY3%R@K*ITgMQz^8mL% z=&pMwu(qCyL)F2E*;V>i?%bnUS1+>9y@f0dmsV3N0RA1iE?%Wx zm(?f!rTIt$R*MdA>`Eci5)^}GMfs`@nw~M)b9+7moMf-_bemUhlj3nVtgIFx4;x|; z?=0>l}QW|QC@7)qvIy8G`w60obL$lE(_7=1X zZHf)hwB=koRscNmC&K+U1PvFLf`g>^ldg=PFpNh>HYkPBT>li(meCP<2g4w*q!bJR zB_IIybxB^Z`6G~>i-oe0L1+17kPGv_raW0SqV0JPN`lixQU?gW??kiQ(N})k`r(%l zOTc7ZyyoWS!+ZxaGqLKrH^}5%)ON=QkL>be=m`VA+ne^#-7y712`HYtDnP~U^0EJR zDh>gEIY_+pH)_@ZeSkAxK#QB+-JT|K7TwP1{qOj0+=!4HyUBH1LZUrU*&JwJ$i+bP zt6M=+LBqj!Fbi$;=Zd=8+R)79!awRm&MjHo4&w$fAZ}Cy4Tbd~iITwkMAT2cq=Ysy zU~&-DP=upJrr&SKq(iI!_DQQ4Us|njf z6sguqosM0Tr%_V3Q~=Sqmev4sNi1A^8tJIUpl^5O)c9mu*pJa*$E zOf!Kxj?jsOn7GPC6x2H~T$6WIw<$2`IT4rX6k@qm#!NSBe7&cj?12a^Vwgo0s%J|!L1soc;h#ii_B@aR(S$SddEdGwY zcd^AmkQrjj57X_DNa@l5I{$@wm|vC-f$jtacD+PIg6L}Cqc>ljt+t~Fo%D9)@fu&p zlXx7fGp01fCQ!t(=>9-DNG^zVC|Pkm;mp4Ww;}uCdip*-@Ovm5CZpW`bvzhf09(jV zSNs_ocPl-Ih^jnJ>VGKBYbUc`rrrETApGs(c9QsYASl!!eQxJa$YI+aj{PDmuyb!O zifjBqTVfYninReAg$8NwP!5WXl8!D&h%t?_)TS*tKgWuRVC(&Ydy;wBd)si4 z0gtEg7v=wpGWd)tcDRtiQECE)DJet5{K*e}z)n{`(th-)EB`DAWYfWw^QP%516K}- z1~K0(T?u|A^Um^MEdDU3-c{1?<}zz4b{V1 zqTf4s$e0N;yA9JM`>&3T>P6&eG;8NZ%*{pNf9`u;kh4|6nKGRagf zJv1G3cN%E;?8fM7dLBjrl7VonfCn9A6Hsi_W>pX4p1qnH6$SDhM5;-z_A^@;BuTqN z17q8dGX5zC9zT2W!Hf0q3V7F2h0mD}A#j@&ug--91%b*Y*sv2NW~IVO+X*`3aSMSK zfu(Lv^N~BF+D&f-9ae{fHQBntt{9HNkh-m{EldNs9D=v%fJ_v+*%!IEEQf0XQRdJ* z9Ml5nom?#5C|N(%i)5GFS> z+cjYHFyIFcL<`)Z^miL2qc#5l`$4V|lvPM+j*;=!xCgGsuh* z?_?x&8ZyoBX9MNnT~6Im1W$gF?2SHYW9@hOq`>i#o|=o7BtJ78;Q?+gUs7{ zmQnlsPusX1(F`wx_aX?g;?LiPv2vL9x+{%@fk|?(F?6p53m_U^8XRytgUzipirX)Opyo~>-JV^A zc0CMFhhBaXwJNkSMAS6*Gd5yO*Q@MDm~oMeIc;F`J#?F7y9gIZW9Wsms-u$LMdjt? zfut}xIvRAE$u|`Z{lsvgA$OJ^@)Ia8P|cR`aMxH=9UX71|6C<`w%NImSm}rN-JuOC zzyQPYg|`C{nG?hY$Yg!aam*Z05S0m4R?HAGcw*7$j+eNo%h&YEe05)W4 z^adC!JMAl-0cj2F^J>_%o_%sNXeP9?);77dTh&pSeXwnUQQEF1unhM79d?a{s+XWJ zhQe2AA*X>@xd~j>5Xlr9pO7GHsNXeY8wVqueyr75mZ(hDpvyWn0j-~uraIC}7pmH_ zDBG?W_wrk=Cg)?MO1c`L>~_L|fj)GyI$!;r>cw%}EQdEUiH5Z%6B$YMmZaod4EL_) zl0k&Xq{NEmV3+Ff4Hfx6il<8P#BdeEIP9L#icw*Fkr$FCD3Rks*xVO)@IQ8{zw z49d{VX7EU4ksjV@kd^|;plC!)?=bo4Od%+)vW@%L%#bh{wlvkJX_YV)1O(vR2VcQ& zbu8PZ`pX|`1y4<3UQsNhBa@^iv%#{IL*-@{M__2JscMuKg|E=Xpp_<6oYi??KRr-Ur z^}lrEve(9wdDS8Itjs{~tcu3h9yHd2dRQsFl)ys3>X-+1VVi*utG;so^me-S=#JC4 z?0OSvl@Sv&0%LFnFt9Y$TWCGm&B)hUTt;^(7xs_dBd@xc=x9-k#j#ci2Q76++&t(} zu@}z!*BBe+4hLmqT&|A<&0P@;uQ%{?hDw64HW5f)P*cGxBdqJC74oB^uD8fz=hnrO z#w~h!7KqhIr4>-;x7PYV zW0fZUhJH|LVd`${A>{&vs*i33^vUf75PU=RlVLmnh?n?P`~b|93npQ#K_F$h2m3BB z=xKFj>T}!7YA*x@ZyVsq`KQ8J&#HE&)}=%JK4|^BnW?y$fvcMKK; zHtJF}-zbQc#yZkyjNg6c+<9XO78q5V?7TGvP#a8o z5ks&nj{uRAap6LcLtTku=>g#6cWPecAe10ZfVtu2W`!)AsPuH`6*z8S zG;86dFiY&%F2tT|PAkPDK8v@}&WfmkM_^xkv(#gqMD))C5&fD`^l(Cl-M;+BhQw|+ z0r5F@XiL?s6qmcB7hV$6R}7Q%*j5CQYL1|$zqCe7X$0?lIA>Q!rML4A znqLr7^iB{Ri2hcepa}Fym`O2C46@LRk1VXcF68wZrk$=S6YjnVXzwSeYlQ*~%0kO) zpm8d*#_0OLTKn#JtoJ_rTb=eiB|=%Dl+jRDMx|_$h7nFh+*#RsG)|?Gkrkl`4H_s} zX%pEUvWw82-1c6->vN+z=RD8z`~C4duh;WB&nfQvJ3ixjU+?R>KI-Ipd2ivJMx#l|q;lQC%P@b>|* zk|FirLNZ?fo+|k)%FQ>iLL`{*^``dhM{7<5HZ~sZVh)Ae`Nf$z?Fr1=$lICzRx>3M z7{PtCZ@uciBDSq3K7bnJ=cG5}Ow2*bAJ*ZZ;9xX+`LuX$O9pyO=v+9UzK``A-qUVe za@VVASdEV%;W)ovx*;^wZtXP!+;i!?Q=LPo^B{b>+1SKlx*%}ZNr+4{z5M#2z?}sS z+9rv)%(6yZ7_^_HBuN*TZMd&AtnWwGvvHZsc zOyp#{`Q5*pv0UN`^U~#fOF5}Z&gXgOo(t6r+M(dGeuehxc!T?KxBuWd9sK9w3aNr^ zJ8h~@AC23dx>`T};0v?Ab_=FHiP?2y&+b)+68_R+Rx~`d)bKMc(kP{^>txZhoRNH) z*0&>Lqc5tRCW?+lUybakk+ZT2jPXu@WaK4tr~MrNRNMxt>hMrhv72S~pscNp@OY+w z%&a$@Z`lSfn$~Lt-9TAB-COHE31V<YtDIzkch_YqC`Yvif|{{pPI>eA6#qK+HF$?dv$4GaFkP(yFdqFWQoJF+cm#gv7kk zo>1xP2S#!U2D-W~bq*~qC!_E+rDu?oX%t%1 zNV{x$f+=sdkIY{CF1e>n6m`|@=D7;2cdW)e#$@K%IBIrD;FT1tb|^YvWCWI@8{&

8E`$Ti-1exL96`fN9d&Kia*($u_Savue9lqG0#rAyL_oa0%%ElTEUvMG)( zv7*SAHyTNkxg(A8fJB}kODCMO>eHc|;R6tK>p|D@J*7Cj2yhjgE;d47KQ0isRh42a zLOL5wi&i=LMPz=|Nj1|#yQi)ddtC>t0%$#qd_3kyGkFL82w7q$s%+sgff89fGT8*^ z8mxx;NUPk0OYUsg`(Z~nICA9X$YJ;rIAz_Pt*orT*+n}y&P%z(W6Aa$^1;Yg;pgnA z<8Ea@aT8+=6TxqRL~8JO1*z`~hkQr(MUS$;fY0TQ#VvzLH9+dPg!LiO__@J6Ph z~M`Rag6 z(ERMq66`~2Ek@G$FNdM&zL{8wUbdyH@fpy*t0ir}Tr-UdXudNzRF%qI87v2eV46-i?91?^r!;MSlLNyD!Uc7SyDqL=9p}f zL9tsds1GAK>gI}6>kOK}9kzoH425%ZCdNlhjt!*Wa@I*X?t3_NKkL2>3B`@R@N6Jf zImgKL&cc?)wKgB}kemfsE6gZ@nW~~ul3m@7%p)(ATEGIu4%MA zBZel(1rR{d(koFLAxS(AZc@<{Qcemv?~rD{%%2&ji(lTiW82I z9E<<#|Gh;vlnT6m2eDeT-oxP&QF*UU!U|J{0ysvf7V1ND*-(`M~#NKy->Cs`7h!$jv^LQ0^+y`VgklG%K3Fma;PUqN@6#29K(;tccIn5$7_63kEJ; zoUi-Ui>7MS9j$tWI)6zyEnRFvCg6pAd-Va9F0_+FzBOZR0g(lqV_r%63Gmx`K`d$Q znYF&_vlq3x>e;gbh)AlPhr~L8aG}yuLP;zr@37@T`#b3x=M%atQv2CN; z3w;0NyTm+e`=3Hf+HTSQJkPn?alUiLOr>zI_H_%)S{E_B%Gd@EVfTbpmD$Mn7f71A z-g4B*a1Kl96qzjEFw5IP^*r;u<36G_tjx?|8f}|m9|MZmApPZmLFN;5+Q;qQZ9>pi zGu_@Oyf3O2)@Bt(+n+!N$cbh{Rk8^v4e`j*ei?^vA5d_axDEVX+w0JYv^KKB z7_gLsYItZxBg9j&*LV-HA?l^(>CJs(0pj++p+hkCMA;QdYM2&;2&-?!u=}kzVW=+j zc=lS+tj^Ql$IGL~KV-SX;U4!XAd{~hA_Z)%bGY0~Chv22pko3q2SlNjx%byqecZL! zvm<-J6V)7X!RqA=t-4@Gt;jsmjxKxu=unTzO567-MN$R!`6fhPTlDt#nh!gV@x0^R z&7{dw@Jfp6`uV0}K;(=lIwcUx6fFk@23+3(XMj~q3m%| zOGxejeThY}jc!w3`2u5VC5-I1ICLNDr`dnUYoH#-6!0uN@fojp(y<=rnFU)k9s59YsB%mW3@i z#pd<4$0a&S$x2$!wev={tNdx`{8h_1!CMaKlvZ-H*}>G?kB%u6-wox2+4jz{B}S%+Q%_S$@Bood*vOT6?(fq- zjIKX`4(BY&YjE@wiZA&ku=D_f6tiC30uu!bGa|@h0nX;bE5Ja)09M@d2-1_k@XMtQ zvKBMRFC)KnYU!`w&wh?K-&X*?89cndJPF!jigF40=A8!!ss`jZ;Ea#RL?-s@0zkeUd2Juz?Nt#AcggWzZ0)(VfSnS2BJ zH5^m8T%{#qBYSh{DxVdzDBN3UL#jRkkID+4<82=M2yT);^zhMYnuO=FK#sSPt=H|4 zFV@-&*#ID%arL{^CEvCp73jLk-@Sb`WAq$S>kxRNGVdalzMka z4V1XHCe7@3=UIhVuyIgmZKIz;UU@ZKznUREmUa*BU^u|xB!YqUc1kl0jLlcBZR?zM zd}5obMyg`UO^nYxHP`WdG4xrt%b!jHw9)rlit~X)CFmMl-Ysz_m$-=s*V$!&(T&%Vo z!lUzCo;n(QnZ?&7%YELuIJT~R`rfd+1I!_EQA}K4uXd_^gp!?@36-D#F_+5eOR|;$ zW;@=528R+L8XJNtE{Ki(mbG7d$@WffpZreV(mAx+U2TzRizUJlHwKjDKr7olTQu&C zzIAr<+3f>`l6Pd;zNm6H$({W49GP-oEQT-w*N)j8mS2t}Ad2^%Vk4Cg*%SwM0QY`- z78Vvy+*O$~_V7A$-GOaIs-{l4u=Dmd`GH#jh6f6iK=dok#;!x8zGB8jYcApb7sYpnx%|7ME(HfWH z_y8#&w{NdQJHIL=!P8y0iii&oS%6^=@mYL%MrPvOy2q!E$h1_MMq7H?DvvviCp2Wl z^?kQ}7yoUezQ(o9%^Qq{E)LlU?Yh$NeM|BKT969aXg7Dm9!{F5WqYmd>9s?^Z&1?u z&*wvuYejGJY4PZR3)_@h*^`{=r9Mey!yW@A+g8isx|15;diAYCFPC4glHp4~w0Xa2 z)8OQ)Si)AE$t>vCqLjEUS^IeoqLLX)-Zvv}y+bhwo#yf!yeS65_L_m&bjwjuAhJzh zdO=|w!5%u?%5+gWpAHB*b@uE z$1g;2NDznB(-&2SbRWT$+l@aqjQ6MI^gn(eDYI|q_Q6kQNK$Aj2{}Y?N!WlIug$`J z={-?Qiffwv1^q8WeWpY;t}dmY8-D}Kd&H2gs9@RkBG4dG(YG|`PA;pFUFu=HW0{Bi zc%=#FG5NN}XWe$-m%<8(e&f4uju`(@Zyt5;3(3JNfqVg7&J@p4m;>edK4`Dij1uE& z*%nz*hps#7fDLPgfc<1nev&U2%p^G|sgc6yg3#S9nm~Z}D%4rFRiShJ#H4^_AOwHG zx!L$a }>=2@csvN;d}) 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 literal 0 HcmV?d00001 diff --git a/docs/images/TransactionClassDiagram.png b/docs/images/TransactionClassDiagram.png index d9d1586cca6980a79bc6d7a4ef406da3cfac9a6f..00fded3ed6d539b37673d89696c7e2cd8644928e 100644 GIT binary patch literal 13418 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 literal 9576 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 diff --git a/docs/images/UiComponentClassDiagram.png b/docs/images/UiComponentClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..087d1193c86283f74e0d49bf737f37e5db29a90c GIT binary patch literal 25826 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 Xsj}lRdIx?96iVluzQz+Zn~?tkmkE(- literal 0 HcmV?d00001 From 8542a2d9f57a5e76da6a4a282b03964896edacc1 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 18 Oct 2022 14:42:17 +0800 Subject: [PATCH 165/416] Move my name down --- docs/DeveloperGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index a4eae0330..33a4c551d 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -148,12 +148,12 @@ execution of the application. Figure 2.6: Class Diagram for UI Component

-_Written by: Chua Han Yong Darren_ - As seen from the class diagram, every command that requires the ability to print to the system output will have to call the functions from the `Ui` class. To add on, the `Duke` class will also use the `Ui` class to read user input. +_Written by: Chua Han Yong Darren_ + ### Common Component _Written by: Author name_ From 02e24f930e0f6f8897c2c42dac5dbb1522b17c40 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 18 Oct 2022 22:28:30 +0800 Subject: [PATCH 166/416] Add DeveloperGuideUML Parser --- docs/DeveloperGuide.md | 18 ++++- docs/diagram/Parser.puml | 76 ++++++++++++++++++ docs/images/ParserClassDiagram.png | Bin 0 -> 164671 bytes .../seedu/duke/parser/ParameterParser.java | 2 +- 4 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 docs/diagram/Parser.puml create mode 100644 docs/images/ParserClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 2dcac055b..e77e888d5 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -134,8 +134,24 @@ _Written by: Chia Thin Hong_ _Written by: Author name_ ### Parser Component +The Parser component comprises of two main parsers: `CommandParser` and `ParameterParser`. Together, both these +parsers are used to generate a command object with its accurate parameters according to the input from the UI. -_Written by: Author name_ +The structure of the data component in Moolah Manager is illustrated in the class diagram below: +![Data Component Class Diagram](images/ParserClassDiagram.png) + +After `run()` is called by `main()` in Duke, the `CommandParser` is first called to parse the command. The initial + input is split into the commandWord and parameters using `splitInput()`. Next, the command word is parsed using + `getCommand()`. With the new command created, the parameters are then parsed by calling `ParameterParser.parse()` + +In `ParameterParser`, multiple checks are done to ensure that the userInput is accurate. For example, checks are done +to confirm that all mandatory tags are filled, that no unsupported tags are used, no duplicate tags, no tags are +without parameters and finally that the formats of parameters are accurate. After all the checks are done`setCommand` +is called to customize the command accordingly. + +With the checked and accurate commands and parameters, the command is then executed by `Duke` + +_Written by: Paul Low_ ### UI Component diff --git a/docs/diagram/Parser.puml b/docs/diagram/Parser.puml new file mode 100644 index 000000000..b336b40a0 --- /dev/null +++ b/docs/diagram/Parser.puml @@ -0,0 +1,76 @@ +@startuml +'https://plantuml.com/sequence-diagram + +hide circle +skinparam classAttributeIconSize 0 + +class Duke { + - storage: Storage + - transactions: TransactionList + - ui: Ui + + +run() + +main() +} + +class "CommandParser"{ + - EMPTY_STRING: String + - DELIMITER: String + - SPLIT_POSITION: int + - parserLogger: Logger + + +parse(): Command + +splitInput(): String[] + +getCommand(): Command +} + +class "ParameterParser"{ + - EMPTY_STRING: String + - DELIMITER: String + - SPLIT_POSITION: int + - MINIMUM_TAG_LENGTH: int + - parserLogger: Logger + + +parse() + +checkMandatoryTagsExist() + +checkUnsupportedTagsNotExist() + +checkDuplicateTagsNotExist() + +checkParameterNotEmpty() + +findMatchingTagAmongInputs(): boolean + +findIfParameterTagAmongTags(): boolean + +setCommand() + +setParameter() + +parseTypeTagForListing(): String + +parseTypeTagForAdding(): String + +parseCategoryTag(): String + +parseAmountTag(): int + +parseDateTag(): LocalDate + +parseEntryTag(): int + +parseHelpOptionTag(): boolean + +parseStatsTypeTag(): String + +containNumeric(): boolean + +containAlphabet(): boolean +} + +class "{abstract}\n Command"{ + + + COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - type: String + - category: String + - description: String + - amount: int + - date: LocalDate + + +getMandatoryTags() + +{abstract}execute() +} + +Duke --> "CommandParser" +"CommandParser" --> "ParameterParser" +"CommandParser" --> "{abstract}\n Command" + +@enduml \ No newline at end of file diff --git a/docs/images/ParserClassDiagram.png b/docs/images/ParserClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..fb939f63e4e005213000457c39b9fd2b01b60618 GIT binary patch literal 164671 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

@@ -270,10 +270,9 @@ _Written by: Author name_ ### List Command -{Describe the implementation for the List Command} The full command for list is `list [t/TYPE] [c/CATEGORY] [d/DATE]` -For example, if `list' is called, all transactions that are present in Moolah Manager will be listed out +For example, if 'list' is called, all transactions that are present in Moolah Manager will be listed out Adding tags such as type, category and date will list all transactions to that category In a command like `list c/food` @@ -332,15 +331,107 @@ _Written by: Author name_ ### Delete Command -{Describe the implementation for the Delete Command} +The `DeleteCommand` inherits properties from the abstract `Command` class. The inheritance of `Command` from `DeleteCommand` is +shown below. -_Written by: Author name_ +

+ +
+ Figure 3.4: Class Diagram for DeleteCommand Showing Inheritance of Command +

+ +The full command for `delete` is `delete [e/ENTRY]`. +For example, if 'delete' is called, the specific entry inputted in the command is deleted from the list of transactions in +Moolah Manager. + +In a command like `delete 2`: + +1. The `main()` method in Duke calls `run()` in Duke. The `ui` reads the command via `ui.readCommand()` and parses it + through `CommandParser.parse()`. + +2. Within `CommandParser.parse()`, a few functions are called internally. + 1. `spiltInput()` is called which splits the command from the parameter. + 2. `getCommand()` is called which searches for the command. + 3. `ParameterParser.parse()` is called. + +3. Within `ParameterParser.parse()`, a few functions are called internally as well. + 1. `checkMandatoryTagsExist()` is called where the parameters are checked for all required tags exist based on the command. + 2. `checkUnsupportedTagsNotExist()` is called to check if the parameter do not contain any unsupported tags based on the command. + 3. `checkDuplicateTagsNotExist()` is called to check if the parameter do not contain any duplicate tags. + 4. `checkParameterNotEmpty()` is called to check that the parameter inputted is not empty. + 5. Once all these checks are successful, `setCommand()` is called. + +4. Within `setCommand()`, more functions are called internally. + 1. `setParameter()` is called to set the index of the transaction to be deleted. + 2. The setting is done via `command.setEntryNumber()` which takes in the parameter and executes it in the DeleteCommand Class. + 3. The parameter, however, needs to be further parsed through the execution of the `parseEntryTag()` function. + 4. It converts the parameter, which is currently a `String`, to a `Int`. + +5. The delete command is undergoing execution in `command.execute()` which will call functions within the DeleteCommand Class. + 1. The index, which is the local `entryNumber` variable, goes under further checks by ascertaining whether it is greater than the total + number of transactions in the list or lesser than or equal to zero. + 2. It tells the total size via the local `numberOfTransactions` variable which takes the value called by `transactions.size()` + which is located in the TransactionList class. + 3. Should the above condition be true, it is no longer a valid input and the local `isInputValid` variable is set as false. + 4. An exception is thrown if `isInputValid` is false. Otherwise, `transactions.deleteTransaction()` is called to remove it. + +6. The above function is called in the TransactionList class which does the following: + 1. Retrieves the transaction to be deleted via `transactions.get()`. + 2. Removes it via `transactions.remove()`. + +7. The display shows the successful deletion via `ui.showTransactionAction()` and writes it to file by `storage.writeToFile()`. + +_Written by: Brian Wong Yun Long_ ### Purge Command -{Describe the implementation for the Purge Command} +The `PurgeCommand` inherits properties from the abstract `Command` class. The inheritance of `Command` from `PurgeCommand` is +shown below. -_Written by: Author name_ +

+ +
+ Figure 3.5: Class Diagram for PurgeCommand Showing Inheritance of Command +

+ +The full command for `purge` is `purge`. +For example, if 'purge' is called, all transactions in Moolah Manager are removed. + +This is how the command works: + +1. The `main()` method in Duke calls `run()` in Duke. The `ui` reads the command via `ui.readCommand()` and parses it + through `CommandParser.parse()`. + +2. Within `CommandParser.parse()`, a few functions are called internally. + 1. `spiltInput()` is called which splits the command from the parameter. + 2. `getCommand()` is called which searches for the command. + 3. `ParameterParser.parse()` is called. + +3. Within `ParameterParser.parse()`, a few functions are called internally as well. + 1. `checkMandatoryTagsExist()` is called where the parameters are checked for all required tags exist based on the command. + 2. `checkUnsupportedTagsNotExist()` is called to check if the parameter do not contain any unsupported tags based on the command. + 3. `checkDuplicateTagsNotExist()` is called to check if the parameter do not contain any duplicate tags. + 4. `checkParameterNotEmpty()` is called to check that the parameter inputted is not empty. + 5. Once all these checks are successful, `setCommand()` is called. + +4. Within `setCommand()`, there is no parameters required to be set for `purge`. + +5. The purge command is undergoing execution in `command.execute()` which will call functions within the PurgeCommand Class. + 1. The function calls `isEmpty()` which returns `true` if the list of transactions is zero, `false` otherwise. It is stored + in the local `check` variable. + 2. The above function compares the size of the transactions list through the `transactions.size()` which is executed in + the TransactionList class and see if both are equal to zero. + 3. The display will show an empty message if `isEmpty()` returns `true` via `ui.showInfoMessage()`, which exits the command. + 4. Otherwise, a warning is displayed through `ui.showInfoMessage()` and reads in an input for the user to respond through `ui.readCommand()`. + 5. If the input is `Y`, the command goes ahead and executes the `transactions.purgeTransactions()`. Any other input will + abort the command and the display will show an aborted message through `ui.showInfoMessage()`. + +6. The `transactions.purgeTransactions()` function is executed in the TransactionList class. + 1. The `transactions.clear()` function is called which deletes every single entry in Moolah Manager + +7. The display shows the successful purging via `ui.showInfoMessage()` and writes it to file by `storage.writeToFile()`. + +_Written by: Brian Wong Yun Long_ ### Storage Operations @@ -376,10 +467,10 @@ _Written by: Paul Low_ ## Appendix B: User Stories -|Version| As a ... | I want to ... | So that I can ...| -|--------|----------|---------------|------------------| -|v1.0|new user|see usage instructions|refer to them when I forget how to use the application| -|v2.0|user|find a to-do item by name|locate a to-do without having to go through the entire list| +| Version | As a ... | I want to ... | So that I can ... | +|---------|----------|---------------------------|-------------------------------------------------------------| +| v1.0 | new user | see usage instructions | refer to them when I forget how to use the application | +| v2.0 | user | find a to-do item by name | locate a to-do without having to go through the entire list | ## Appendix C: Non-Functional Requirements diff --git a/docs/diagram/DeleteCommand.puml b/docs/diagram/DeleteCommand.puml new file mode 100644 index 000000000..e194b1cc2 --- /dev/null +++ b/docs/diagram/DeleteCommand.puml @@ -0,0 +1,36 @@ +@startuml +!include Style.puml + +class DeleteCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - entryNumber: int + + +getMandatoryTags() + +setEntryNumber(): int + +{abstract}execute() +} + +class "{abstract}\n Command"{ + + + COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - type: String + - category: String + - description: String + - amount: int + - date: LocalDate + + +getMandatoryTags() + +{abstract}execute() +} + + +"{abstract}\n Command" <|-- DeleteCommand +@enduml \ No newline at end of file diff --git a/docs/diagram/PurgeCommand.puml b/docs/diagram/PurgeCommand.puml new file mode 100644 index 000000000..f834e8893 --- /dev/null +++ b/docs/diagram/PurgeCommand.puml @@ -0,0 +1,35 @@ +@startuml +!include Style.puml + +class PurgeCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + + +isEmpty(): TransactionList + +{abstract}execute() +} + +class "{abstract}\n Command"{ + + + COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - type: String + - category: String + - description: String + - amount: int + - date: LocalDate + + + +{abstract}execute() +} + + + +"{abstract}\n Command" <|-- PurgeCommand +@enduml \ No newline at end of file diff --git a/docs/images/DeleteCommandClassDiagram.png b/docs/images/DeleteCommandClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..3bdb0ce4cf76f677c61ea3b3599e5199447eff4d GIT binary patch literal 18893 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 TP!hP6e@jkES+Y#rB;bDmf#k^h literal 0 HcmV?d00001 From 211bba8141e06d14259839eac594ae524fd3ab29 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 18 Oct 2022 23:52:07 +0800 Subject: [PATCH 168/416] Change log level as severe To prevent logging messages to flood the terminal when the program is being run in a command prompt. --- src/main/java/seedu/duke/command/AddCommand.java | 2 +- src/main/java/seedu/duke/command/DeleteCommand.java | 2 +- src/main/java/seedu/duke/command/ListCommand.java | 2 +- src/main/java/seedu/duke/command/PurgeCommand.java | 2 +- src/main/java/seedu/duke/command/StatsCommand.java | 2 +- src/main/java/seedu/duke/parser/ParameterParser.java | 1 + 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index b1eaa7a98..2e9a7b472 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -133,7 +133,7 @@ public void setDate(LocalDate date) { public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { //@@author chinhan99 try { - addLogger.setLevel(Level.WARNING); + addLogger.setLevel(Level.SEVERE); addLogger.log(Level.INFO, "Add Command checks the type of the transaction " + "before adding into the transaction class."); assert date != null; diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index c63708227..b9988ac46 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -83,7 +83,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws before adding entry to arraylist */ try { - deleteLogger.setLevel(Level.WARNING); + deleteLogger.setLevel(Level.SEVERE); deleteLogger.log(Level.INFO, "Delete Command checks whether the index is valid " + "before executing the command."); boolean isInputValid = true; diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index b7d8cee76..9707d40b5 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -98,7 +98,7 @@ public void setDate(LocalDate date) { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - listLogger.setLevel(Level.WARNING); + listLogger.setLevel(Level.SEVERE); listLogger.log(Level.INFO, "List command starts passing the tags for filter, if any," + " into the listTransactions method."); diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index 5296171c5..d5c9a57ac 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -56,7 +56,7 @@ public PurgeCommand() { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { // Shows confirmation prompt before deleting all transactions - purgeLogger.setLevel(Level.WARNING); + purgeLogger.setLevel(Level.SEVERE); purgeLogger.log(Level.INFO, "Purge Command checks if there are no transactions" + " to be purged. If so, the command is aborted."); boolean check = isEmpty(transactions); diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index ab96582d7..04fa1b0ae 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -68,7 +68,7 @@ public String[] getMandatoryTags() { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - statsLogger.setLevel(Level.WARNING); + statsLogger.setLevel(Level.SEVERE); statsLogger.log(Level.INFO, "Stats command starts passing the type of statistics" + " and transactions list into the listStatisticsByStatsType method."); diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index ec37e7071..4239b5311 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -99,6 +99,7 @@ public static void parse(Command command, String parametersInput) throws MoolahE } //@@author wcwy + /** * Checks if all the mandatory tags exists in the split user inputs. * From f26e259f1f6530374c66a2b02a9b261a8682f9c4 Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 19 Oct 2022 00:03:42 +0800 Subject: [PATCH 169/416] Reformat files to abide with Java coding conventions --- src/main/java/seedu/duke/Ui.java | 12 ++++--- src/main/java/seedu/duke/command/Command.java | 5 +-- .../java/seedu/duke/command/FindCommand.java | 1 + .../java/seedu/duke/command/HelpCommand.java | 1 + .../java/seedu/duke/command/ListCommand.java | 10 +++--- .../java/seedu/duke/command/StatsCommand.java | 6 ++-- .../java/seedu/duke/data/TransactionList.java | 2 +- .../java/seedu/duke/parser/CommandParser.java | 3 ++ .../seedu/duke/parser/ParameterParser.java | 32 +++++++++++++++++-- 9 files changed, 57 insertions(+), 15 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index f91e6aa0f..631e8a7ab 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -28,6 +28,7 @@ public static void printMessages(String... messages) { } //@@author paullowse + /** * Initialises the variables of the Ui class. */ @@ -46,6 +47,7 @@ public String readCommand() { } //@@author chydarren + /** * Prepares the error message to be displayed to the user. * @@ -72,16 +74,18 @@ public static void showGreeting() { } //@@author chinhan99 + /** * Prepares the help messages to be displayed to the user. * - * @param helpMessage A help message that specifies the details of how to use the program. + * @param helpMessage A help message that specifies the details of how to use the program. */ public static void showHelp(String helpMessage) { printMessages(INFO_HELP_GREET.toString(), helpMessage); } //@@author chydarren + /** * Prepares the exit message to be displayed to the user. */ @@ -93,9 +97,9 @@ public static void showExit() { * Prepares the messages to be displayed to the user when add or delete has been performed on * the transaction list. * - * @param infoMessage An information message that describes the functionality of - * the program. - * @param transactionDetails Details of the action that has been performed on the transaction. + * @param infoMessage An information message that describes the functionality of + * the program. + * @param transactionDetails Details of the action that has been performed on the transaction. */ public static void showTransactionAction(String infoMessage, String transactionDetails) { printMessages(infoMessage, transactionDetails); diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 039027932..a8db11d42 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -22,6 +22,7 @@ public abstract class Command { public static String COMMAND_PARAMETERS_INFO; //@@author paullowse + /** * Get the default mandatory tags of the command (no mandatory tag). * To be overridden by subclasses which the command requires mandatory tag. @@ -47,9 +48,9 @@ public String[] getOptionalTags() { /** * Executes the operations related to the command. * - * @param ui An instance of the Ui class. + * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. - * @param storage An instance of the Storage class. + * @param storage An instance of the Storage class. */ public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 42da90947..370ad0655 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -81,6 +81,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 8db91b298..f2747d320 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -107,6 +107,7 @@ private String generateDetailedHelp() { } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 9707d40b5..bbecfe123 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -89,6 +89,7 @@ public void setDate(LocalDate date) { } //@@author chydarren + /** * Executes the operations related to the command. * @@ -109,10 +110,10 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws /** * List all or some transactions based on selection. * - * @param transactions An instance of the TransactionList class. - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". + * @param transactions An instance of the TransactionList class. + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date) @@ -135,6 +136,7 @@ private static void listTransactions(TransactionList transactions, String type, } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 04fa1b0ae..ba270b1fa 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -59,6 +59,7 @@ public String[] getMandatoryTags() { } //@@author chydarren + /** * Executes the operations related to the command. * @@ -83,8 +84,8 @@ public void setStatsType(String statsType) { /** * Lists the statistics depending on the type of statistics requested. * - * @param statsType The type of statistics that is needed, e.g. categories. - * @param transactions An instance of the TransactionList class. + * @param statsType The type of statistics that is needed, e.g. categories. + * @param transactions An instance of the TransactionList class. * @throws ListStatisticsInvalidStatsTypeException If the type of statistics is not recognised. */ private static void listStatisticsByStatsType(String statsType, TransactionList transactions) @@ -120,6 +121,7 @@ private static void listStatisticsByStatsType(String statsType, TransactionList } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 6b651ceba..4fcd71cde 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -136,7 +136,7 @@ public boolean isTransactionInstance(Object transaction, String classType) throw * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ public boolean isMatchListFilters(Transaction transaction, String type, String category, - LocalDate date) throws InputTransactionUnknownTypeException { + LocalDate date) throws InputTransactionUnknownTypeException { boolean isMatch; try { isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index 247879591..9dbeea07a 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -34,6 +34,7 @@ public class CommandParser { private static final Logger parserLogger = Logger.getLogger(ParameterParser.class.getName()); //@@author wcwy + /** * Parses the user input into Command class based on the command word. * @@ -65,6 +66,7 @@ public static Command parse(String fullCommandInput) throws MoolahException { } //@@author chydarren + /** * Splits the user input into two parts, i.e. the command word and the parameter(s). * @@ -83,6 +85,7 @@ public static String[] splitInput(String fullCommandInput) { } //@@author paullowse + /** * Creates a Command object based on the command word entered by user. * diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 4239b5311..aed6936ab 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -57,6 +57,7 @@ public class ParameterParser { private static final Logger parserLogger = Logger.getLogger(ParameterParser.class.getName()); //@@author chinhan99 + /** * Parses the parameters input into proper parameters of the command object. * @@ -71,7 +72,7 @@ public class ParameterParser { * @throws MoolahException If Moolah Manager captures any command input exceptions. */ public static void parse(Command command, String parametersInput) throws MoolahException { - parserLogger.setLevel(Level.WARNING); + parserLogger.setLevel(Level.SEVERE); assert command != null; String[] splits = parametersInput.split(DELIMITER); @@ -119,6 +120,7 @@ public static void checkMandatoryTagsExist(Command command, String[] splits) thr } //@@author chinhan99 + /** * Checks if the split user inputs contains any unsupported tag. * @@ -150,6 +152,7 @@ public static void checkUnsupportedTagsNotExist(Command command, String[] splits } //@@author wcwy + /** * Checks if the split user inputs contains a tag multiple times. * @@ -172,6 +175,7 @@ public static void checkDuplicateTagsNotExist(String[] splits) throws InputDupli } //@@author chinhan99 + /** * Checks if there are missing parameter within the user input. * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. @@ -189,6 +193,7 @@ public static void checkParameterNotEmpty(String[] splits) throws EmptyParameter } //@@author wcwy + /** * Returns a boolean value on whether a tag can be found among the split user inputs. * @@ -208,6 +213,7 @@ private static boolean findMatchingTagAmongInputs(String tag, String[] splits) { } //@@author chinhan99 + /** * Returns a boolean value on whether a tag can be found among the split user inputs. * @@ -230,6 +236,7 @@ private static boolean findIfParameterTagAmongTags(String parameter, String[] ta } //@@author paullowse + /** * For each split parameters, split it into tag and parameter, then check and set the parameters into the Command. * @@ -247,6 +254,19 @@ public static void setCommand(Command command, String[] splits) throws MoolahExc } //@@author wcwy + + /** + * Sets the parsed parameter for each valid tag of the command. + * + *

The parameters set will be parsed into a correct format/data type before storing into a parameter + * within the Command object given. This ensures that the execution of the command will not encounter + * any unformatted input or unsupported data type issue, as far as possible. + * + * @param command A command object created based on the command word given by user. + * @param tag The tag used before the parameter to indicate the type of the parameter provided. + * @param parameter The value of a parameter given by the user for the chosen command. + * @throws MoolahException If an error is found when the parameter is parsed. + */ private static void setParameter(Command command, String tag, String parameter) throws MoolahException { switch (tag) { case COMMAND_TAG_TRANSACTION_TYPE: @@ -285,6 +305,7 @@ private static void setParameter(Command command, String tag, String parameter) } //@@author chydarren + /** * Parses the user parameter input for the description and returns it. * @@ -305,8 +326,9 @@ public static String parseTypeTagForListing(String parameter) throws InputTransa } //@@author wcwy + /** - * Check if the type parameter is a valid transaction type and returns the parameter if it is valid. + * Checks if the type parameter is a valid transaction type and returns the parameter if it is valid. * * @param parameter The user input after the user tag. * @return The user input after the user tag. @@ -325,6 +347,7 @@ public static String parseTypeTagForAdding(String parameter) throws InputTransac } //@@author chinhan99 + /** * Parses the user parameter input for the description and returns it. * @@ -374,6 +397,7 @@ private static int parseAmountTag(String parameter) throws AddTransactionInvalid } //@@author wcwy + /** * Parses the user parameter input for date into a LocalDate object and returns it. * @@ -393,6 +417,7 @@ public static LocalDate parseDateTag(String parameter) throws InputTransactionIn } //@@author brian-vb + /** * Parses the user parameter input for entry number into an integer and returns it. * @@ -414,6 +439,7 @@ public static int parseEntryTag(String parameter) throws MoolahException { } //@@author wcwy + /** * Return a boolean value indicating if the option selected by user is "detailed". * @@ -433,6 +459,7 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt } //@@author chydarren + /** * Check if the type parameter is a valid statistic type and returns the parameter if it is valid. * @@ -455,6 +482,7 @@ public static String parseStatsTypeTag(String parameter) throws ListStatisticsIn } //@@author chinhan99 + /** * Checks if the parameter contains numeric characters. * From 1fb58ab20c3c80407cf6d12c47f8b5f3cf4d8781 Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 19 Oct 2022 00:04:15 +0800 Subject: [PATCH 170/416] Add Javadocs for methods in HelpCommand.java --- .../java/seedu/duke/command/HelpCommand.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index f2747d320..18b7774df 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -41,6 +41,14 @@ public HelpCommand() { this.isDetailed = false; } + /** + * Sets a boolean value to indicate if the user chooses to display the help message in detail. + * + *

When the boolean isDetailed is set to true, the help command will display the detailed help messages + * to the user. + * + * @param isDetailed A boolean indicating if the user chooses to display the help message in detail. + */ @Override public void setIsDetailedOption(boolean isDetailed) { this.isDetailed = isDetailed; @@ -78,6 +86,11 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { Ui.showHelp(helpMessage); } + /** + * Consolidates all the basic help messages from the available commands and returns it. + * + * @return A string containing all basic help messages of valid commands. + */ private String generateBasicHelp() { String helpMessage = HelpCommand.COMMAND_HELP + LINE_SEPARATOR + AddCommand.COMMAND_HELP + LINE_SEPARATOR @@ -92,6 +105,11 @@ private String generateBasicHelp() { return helpMessage; } + /** + * Consolidates all the detailed help messages from the available commands and returns it. + * + * @return A string containing all detailed help messages of valid commands. + */ private String generateDetailedHelp() { String helpMessage = HelpCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + AddCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR From 99b4c8ccb6f76f77b5065071f40f55fd79f0c814 Mon Sep 17 00:00:00 2001 From: wcwy Date: Wed, 19 Oct 2022 00:09:24 +0800 Subject: [PATCH 171/416] Add missing author tag --- src/main/java/seedu/duke/data/TransactionList.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 4fcd71cde..a858b2129 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -99,6 +99,8 @@ public String addIncome(String description, int amount, String category, LocalDa return income.toString(); } + //@@author chinhan99 + public void addIncomeDuringStorage(String description, int amount, String category, LocalDate date) { Income income = new Income(description, amount, category, date); transactions.add(income); @@ -110,7 +112,6 @@ public void addExpenseDuringStorage(String description, int amount, String categ transactions.add(expense); } - //@@author chydarren /** From 989db7f0f7166c711c3b17262b10e3ef44e0fd2d Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 19 Oct 2022 12:06:48 +0800 Subject: [PATCH 172/416] Update master with most recent changes from team repo --- data/duke.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 data/duke.txt diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 000000000..e69de29bb From 45eaa5cc35cf22d6c771a548b8c58a5fecfb5f21 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 21 Oct 2022 07:59:49 +0800 Subject: [PATCH 173/416] Remove category list class from Moolah Manager to replace with a hashmap --- .../java/seedu/duke/data/CategoryList.java | 65 ------------------- ...> ListStatsInvalidStatsTypeException.java} | 32 ++++----- ...java => ListStatsMissingTagException.java} | 30 ++++----- 3 files changed, 31 insertions(+), 96 deletions(-) delete mode 100644 src/main/java/seedu/duke/data/CategoryList.java rename src/main/java/seedu/duke/exception/{ListStatisticsInvalidStatsTypeException.java => ListStatsInvalidStatsTypeException.java} (96%) rename src/main/java/seedu/duke/exception/{ListStatisticsMissingTagException.java => ListStatsMissingTagException.java} (96%) diff --git a/src/main/java/seedu/duke/data/CategoryList.java b/src/main/java/seedu/duke/data/CategoryList.java deleted file mode 100644 index 0ff21fe35..000000000 --- a/src/main/java/seedu/duke/data/CategoryList.java +++ /dev/null @@ -1,65 +0,0 @@ -package seedu.duke.data; - -import seedu.duke.data.transaction.Category; - -import java.util.ArrayList; - -public class CategoryList { - //@@author chydarren - private static final String EMPTY_STRING = ""; - private static final String LINE_SEPARATOR = System.lineSeparator(); - - private static ArrayList categories = new ArrayList<>(); - - /** - * Browses the list of transactions for unique categories and adds them into a category list. - * - * @param transactions An instance of the TransactionList class. - */ - public static void addCategories(TransactionList transactions) { - for (int i = 0; i < transactions.size(); i++) { - Category category = new Category(transactions.getEntry(i).getCategory()); - - // Adds a category in the list only if the category is not in list - if (!categories.contains(category)) { - categories.add(category); - } - } - } - - /** - * Calculates the total amount of transactions belonging to each category in the category list. - * - * @param transactions An instance of the TransactionList class. - */ - public static void calculateTotalAmount(TransactionList transactions) { - addCategories(transactions); - for (Category category : categories) { - int amount = category.getAmount(); - - // Gets each transaction and if belongs to category, compute to total amount - for (int i = 0; i < transactions.size(); i++) { - if (transactions.getEntry(i).getCategory().equals(category.getCategory())) { - amount += transactions.getEntry(i).getAmount(); - } - } - - category.setAmount(amount); - } - } - - /** - * List the statistics for the total amount of savings in each category. - * - * @return A string containing the formatted categories list. - */ - public String listCategories() { - String categoriesList = EMPTY_STRING; - // Loops each category from the categories list - for (Category category : categories) { - categoriesList += category.toString() + LINE_SEPARATOR; - } - categories.clear(); - return categoriesList; - } -} diff --git a/src/main/java/seedu/duke/exception/ListStatisticsInvalidStatsTypeException.java b/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java similarity index 96% rename from src/main/java/seedu/duke/exception/ListStatisticsInvalidStatsTypeException.java rename to src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java index d4dc39427..c1c5b8f13 100644 --- a/src/main/java/seedu/duke/exception/ListStatisticsInvalidStatsTypeException.java +++ b/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java @@ -1,16 +1,16 @@ -package seedu.duke.exception; - -import seedu.duke.common.ErrorMessages; - -public class ListStatisticsInvalidStatsTypeException extends MoolahException { - - /** - * Returns the error message of the exception to alert user of the exception. - * - * @return A string containing the error message - */ - @Override - public String getMessage() { - return ErrorMessages.ERROR_STATS_COMMAND_INVALID_STATSTYPE.toString(); - } -} +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class ListStatisticsInvalidStatsTypeException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STATS_COMMAND_INVALID_STATSTYPE.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java b/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java similarity index 96% rename from src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java rename to src/main/java/seedu/duke/exception/ListStatsMissingTagException.java index caa69c7e5..f7a92ea31 100644 --- a/src/main/java/seedu/duke/exception/ListStatisticsMissingTagException.java +++ b/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java @@ -1,15 +1,15 @@ -package seedu.duke.exception; - -import seedu.duke.common.ErrorMessages; - -public class ListStatisticsMissingTagException extends MoolahException { - /** - * Returns the error message of the exception to alert user of the exception. - * - * @return A string containing the error message - */ - @Override - public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_MISSING_TAG.toString(); - } -} +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class ListStatisticsMissingTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_ADD_COMMAND_MISSING_TAG.toString(); + } +} From 19271c4dff84ce135dea1a0d85150c95fefd8938 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 21 Oct 2022 08:00:44 +0800 Subject: [PATCH 174/416] Implement a hashmap for categorical savings to fix duplication bug --- .../java/seedu/duke/command/StatsCommand.java | 76 +++++++++---------- .../java/seedu/duke/data/TransactionList.java | 59 +++++++++++--- .../seedu/duke/parser/ParameterParser.java | 8 +- 3 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index ba270b1fa..143539725 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -2,9 +2,8 @@ import seedu.duke.Storage; import seedu.duke.Ui; -import seedu.duke.data.CategoryList; import seedu.duke.data.TransactionList; -import seedu.duke.exception.ListStatisticsInvalidStatsTypeException; +import seedu.duke.exception.ListStatsInvalidStatsTypeException; import seedu.duke.exception.MoolahException; import java.util.logging.Level; @@ -40,7 +39,6 @@ public class StatsCommand extends Command { //@@author chydarren private static final Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); - private String statsType; //@@author paullowse @@ -58,8 +56,12 @@ public String[] getMandatoryTags() { return mandatoryTags; } - //@@author chydarren + @Override + public void setStatsType(String statsType) { + this.statsType = statsType; + } + //@@author chydarren /** * Executes the operations related to the command. * @@ -70,58 +72,52 @@ public String[] getMandatoryTags() { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { statsLogger.setLevel(Level.SEVERE); - statsLogger.log(Level.INFO, "Stats command starts passing the type of statistics" - + " and transactions list into the listStatisticsByStatsType method."); - - listStatisticsByStatsType(statsType, transactions); - } + statsLogger.log(Level.INFO, "Entering execution of the Stats command."); - @Override - public void setStatsType(String statsType) { - this.statsType = statsType; + listStatsByStatsType(statsType, transactions); } /** * Lists the statistics depending on the type of statistics requested. * - * @param statsType The type of statistics that is needed, e.g. categories. - * @param transactions An instance of the TransactionList class. - * @throws ListStatisticsInvalidStatsTypeException If the type of statistics is not recognised. + * @param statsType The type of statistics that is needed. + * @param transactions An instance of the TransactionList class. + * @throws ListStatsInvalidStatsTypeException If the type of statistics is not recognised. */ - private static void listStatisticsByStatsType(String statsType, TransactionList transactions) - throws ListStatisticsInvalidStatsTypeException { - statsLogger.log(Level.INFO, "A new instance of CategoryList is created."); - CategoryList categories = new CategoryList(); - + private static void listStatsByStatsType(String statsType, TransactionList transactions) + throws ListStatsInvalidStatsTypeException { switch (statsType) { case "categories": - statsLogger.log(Level.INFO, "The categories and amount for each category are " - + " being tallied and computed."); - categories.calculateTotalAmount(transactions); - String categoriesList = categories.listCategories(); - - if (categoriesList.isEmpty()) { - statsLogger.log(Level.INFO, "Categories list is empty, so UI should display that" - + " there are no statistics available."); - Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); - statsLogger.log(Level.INFO, "End of Stats command."); - return; - } - assert !categoriesList.isEmpty(); - statsLogger.log(Level.INFO, "Categories list is available, so UI should display the" - + " categories and amount of savings per category."); - Ui.showTransactionsList(categoriesList, INFO_STATS_CATEGORIES.toString()); + statsLogger.log(Level.INFO, "Stats type has been detected for categorical savings."); + statsTypeCategoricalSavings(transactions); + statsLogger.log(Level.INFO, "End of Stats command."); break; default: - statsLogger.log(Level.WARNING, "An exception has been caught due to an invalid statistics type."); - throw new ListStatisticsInvalidStatsTypeException(); + statsLogger.log(Level.WARNING, "An exception has been caught due to an invalid stats type."); + throw new ListStatsInvalidStatsTypeException(); } + } - statsLogger.log(Level.INFO, "End of Stats command."); + /** + * Display the statistics requested for current amount of savings in each category. + * + * @param transactions An instance of the TransactionList class. + */ + public static void statsTypeCategoricalSavings(TransactionList transactions) { + String categoricalSavingsList = transactions.listCategoricalSavings(); + + if (categoricalSavingsList.isEmpty()) { + statsLogger.log(Level.INFO, "Categorical savings list is empty as there are no transactions available."); + Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); + return; + } + + assert !categoricalSavingsList.isEmpty(); + statsLogger.log(Level.INFO, "Categorical savings list is found to contain categories-amount pairs."); + Ui.showList(categoricalSavingsList, INFO_STATS_CATEGORIES.toString()); } //@@author paullowse - /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index a858b2129..68ac3c42c 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -7,6 +7,8 @@ import java.time.LocalDate; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; /** * Represents a list of transactions added by the user into the application. @@ -15,7 +17,9 @@ */ public class TransactionList { //@@author chydarren - private static final String EMPTY_STRING = ""; + private static final String PREFIX_CATEGORY = "["; + private static final String POSTFIX_CATEGORY = "]"; + private static final String SYMBOL_DOLLAR = "$"; private static final String LINE_SEPARATOR = System.lineSeparator(); //@@author chinhan99 @@ -25,6 +29,10 @@ public TransactionList(TransactionList transactionList) { transactions = transactionList.getTransactions(); } + public ArrayList getTransactions() { + return transactions; + } + //@@author wcwy /** @@ -113,7 +121,6 @@ public void addExpenseDuringStorage(String description, int amount, String categ } //@@author chydarren - /** * Checks whether the transaction belongs to the Income or Expense class type. * @@ -137,7 +144,7 @@ public boolean isTransactionInstance(Object transaction, String classType) throw * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ public boolean isMatchListFilters(Transaction transaction, String type, String category, - LocalDate date) throws InputTransactionUnknownTypeException { + LocalDate date) throws InputTransactionUnknownTypeException { boolean isMatch; try { isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) @@ -150,7 +157,7 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c } /** - * List all or some transactions based on selection. + * Lists all or some transactions based on selection. * * @param type The type of transaction. * @param category A category for the transaction. @@ -160,7 +167,7 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c */ public String listTransactions(String type, String category, LocalDate date) throws InputTransactionUnknownTypeException { - String transactionsList = EMPTY_STRING; + String transactionsList = ""; // Loops each transaction from the transactions list for (Transaction transaction : transactions) { if (isMatchListFilters(transaction, type, category, date)) { @@ -171,13 +178,13 @@ public String listTransactions(String type, String category, LocalDate date) } /** - * Find specific transaction(s) based on any keywords inputted by the user. + * Finds specific transaction(s) based on any keywords inputted by the user. * * @param keywords Any partial or full keyword(s) that matches the details of the transaction. * @return A string containing the formatted transaction list. */ public String findTransactions(String keywords) { - String transactionsList = EMPTY_STRING; + String transactionsList = ""; // Loops each transaction from the transactions list for (Transaction transaction : transactions) { // Includes only transactions that contain the keywords used in the search expression @@ -188,14 +195,46 @@ public String findTransactions(String keywords) { return transactionsList; } + /** + * Reads the transactions list and adds each amount to the categories in categorical savings hashmap. + * + * @param categoricalSavings A hashmap containing all category-amount pair for total savings. + * @return A hashmap containing all category-amount pair for total savings. + */ + public HashMap processCategoricalSavings(HashMap categoricalSavings) { + for (Transaction transaction : transactions) { + String category = transaction.getCategory(); + int amount = transaction.getAmount(); + // Creates a new category with starter amount if category not exists in hashmap + if (!categoricalSavings.containsKey(category)) { + categoricalSavings.put(category, amount); + continue; + } + categoricalSavings.put(category, categoricalSavings.get(category) + amount); + } - public ArrayList getTransactions() { - return transactions; + return categoricalSavings; } + /** + * Calculates and stores total savings for each transaction category into a hashmap. + * + * @return A hashmap containing all category-amount pair for total savings. + */ + public String listCategoricalSavings() { + String categoricalSavingsList = ""; + HashMap categoricalSavings = new HashMap<>(); + categoricalSavings = processCategoricalSavings(categoricalSavings); + + for (Map.Entry entry : categoricalSavings.entrySet()) { + categoricalSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getKey(), + POSTFIX_CATEGORY, SYMBOL_DOLLAR, entry.getValue(), LINE_SEPARATOR); + } + + return categoricalSavingsList; + } //@@author brian-vb - /** * Purges all records in the transactions list. */ diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index aed6936ab..e067a2a00 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -16,7 +16,7 @@ import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.EntryNumberNotNumericException; import seedu.duke.exception.InputTransactionUnknownTypeException; -import seedu.duke.exception.ListStatisticsInvalidStatsTypeException; +import seedu.duke.exception.ListStatsInvalidStatsTypeException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -465,9 +465,9 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt * * @param parameter The user input after the user tag. * @return The statistic type. - * @throws ListStatisticsInvalidStatsTypeException If the statistic type given is not supported. + * @throws ListStatsInvalidStatsTypeException If the statistic type given is not supported. */ - public static String parseStatsTypeTag(String parameter) throws ListStatisticsInvalidStatsTypeException { + public static String parseStatsTypeTag(String parameter) throws ListStatsInvalidStatsTypeException { String statsType; switch (parameter) { case "categories": @@ -476,7 +476,7 @@ public static String parseStatsTypeTag(String parameter) throws ListStatisticsIn default: parserLogger.log(Level.WARNING, "An invalid statistic type error is caught for the given parameter: " + parameter); - throw new ListStatisticsInvalidStatsTypeException(); + throw new ListStatsInvalidStatsTypeException(); } return statsType; } From 5a19fc4a0c439ce25933065115963b9a9d72d8dd Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 21 Oct 2022 08:01:33 +0800 Subject: [PATCH 175/416] Combine the use of some methods in UI class and shorten ListStats exception names --- src/main/java/seedu/duke/Ui.java | 56 +++++++------------ .../java/seedu/duke/command/ByeCommand.java | 4 +- .../java/seedu/duke/command/FindCommand.java | 4 +- .../java/seedu/duke/command/ListCommand.java | 24 +++----- .../ListStatsInvalidStatsTypeException.java | 2 +- .../ListStatsMissingTagException.java | 2 +- 6 files changed, 38 insertions(+), 54 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 631e8a7ab..1a2fcd4f6 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,13 +1,12 @@ package seedu.duke; +import java.util.Scanner; + import static seedu.duke.common.InfoMessages.INFO_DIVIDER; -import static seedu.duke.common.InfoMessages.INFO_EXIT; import static seedu.duke.common.InfoMessages.INFO_GREET; import static seedu.duke.common.InfoMessages.INFO_HELP_GREET; import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; -import java.util.Scanner; - public class Ui { //@@author chydarren private String input; @@ -28,7 +27,6 @@ public static void printMessages(String... messages) { } //@@author paullowse - /** * Initialises the variables of the Ui class. */ @@ -47,23 +45,22 @@ public String readCommand() { } //@@author chydarren - /** * Prepares the error message to be displayed to the user. * - * @param errorMessage An error message when an exception is handled by the program. + * @param message An error message when an exception is handled by the program. */ - public static void showErrorMessage(String errorMessage) { - printMessages(errorMessage); + public static void showErrorMessage(String message) { + printMessages(message); } /** * Prepares the information message to be displayed to the user. * - * @param infoMessage An information message that describes the functionality of the program. + * @param message An information message that describes the functionality of the program. */ - public static void showInfoMessage(String infoMessage) { - printMessages(infoMessage); + public static void showInfoMessage(String message) { + printMessages(message); } /** @@ -74,44 +71,33 @@ public static void showGreeting() { } //@@author chinhan99 - /** * Prepares the help messages to be displayed to the user. * - * @param helpMessage A help message that specifies the details of how to use the program. + * @param message A message that specifies the details of how to use the program. */ - public static void showHelp(String helpMessage) { - printMessages(INFO_HELP_GREET.toString(), helpMessage); + public static void showHelp(String message) { + printMessages(INFO_HELP_GREET.toString(), message); } - //@@author chydarren - /** - * Prepares the exit message to be displayed to the user. + * Prepares the transaction list messages to be displayed to the user. + * + * @param list A string containing the formatted transaction list. + * @param message A message that complements with the transactions list. */ - public static void showExit() { - printMessages(INFO_EXIT.toString()); + public static void showList(String list, String message) { + printMessages(message, list); } /** * Prepares the messages to be displayed to the user when add or delete has been performed on * the transaction list. * - * @param infoMessage An information message that describes the functionality of - * the program. - * @param transactionDetails Details of the action that has been performed on the transaction. - */ - public static void showTransactionAction(String infoMessage, String transactionDetails) { - printMessages(infoMessage, transactionDetails); - } - - /** - * Prepares the transaction list messages to be displayed to the user. - * - * @param transactionsList A string containing the formatted transaction list. - * @param listMessage A list message that complements with the transactions list. + * @param message A message that describes the functionality of the program. + * @param transactionDetails Details of the action that has been performed on the transaction. */ - public static void showTransactionsList(String transactionsList, String listMessage) { - printMessages(listMessage, transactionsList); + public static void showTransactionAction(String message, String transactionDetails) { + printMessages(message, transactionDetails); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index 44d7b396b..cc7b3fa21 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -4,6 +4,8 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import static seedu.duke.common.InfoMessages.INFO_EXIT; + /** * Represents a bye command object that will execute the operations for Bye command. */ @@ -38,7 +40,7 @@ public ByeCommand() { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) { - Ui.showExit(); + Ui.showInfoMessage(INFO_EXIT.toString()); } /** diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 370ad0655..31da3c378 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -71,13 +71,15 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws // Checks the format of find to ensure that it contains keywords used in the search expression checkFindFormat(keywords); assert !keywords.isBlank(); + String transactionsList = transactions.findTransactions(keywords); if (transactionsList.isEmpty()) { ui.showInfoMessage(INFO_LIST_UNFILTERED.toString()); return; } + assert !transactionsList.isEmpty(); - ui.showTransactionsList(transactionsList, INFO_LIST_FILTERED.toString()); + ui.showList(transactionsList, INFO_LIST_FILTERED.toString()); } //@@author paullowse diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index bbecfe123..48f574211 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -66,9 +66,9 @@ public ListCommand() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_DATE + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_DATE }; return optionalTags; } @@ -100,10 +100,8 @@ public void setDate(LocalDate date) { @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { listLogger.setLevel(Level.SEVERE); - listLogger.log(Level.INFO, "List command starts passing the tags for filter, if any," - + " into the listTransactions method."); + listLogger.log(Level.INFO, "Entering execution of the List command."); - // Passes the tags to the filter for transaction list listTransactions(transactions, type, category, date); } @@ -118,21 +116,17 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws */ private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date) throws InputTransactionUnknownTypeException { - listLogger.log(Level.INFO, "Listing of transactions is being processed into a" - + " transaction list variable."); String transactionsList = transactions.listTransactions(type, category, date); + if (transactionsList.isEmpty()) { - listLogger.log(Level.INFO, "Transactions list is empty, so UI should display that" - + " there are no transaction records available."); + listLogger.log(Level.INFO, "Transactions list is empty as there are no transactions available."); Ui.showInfoMessage(INFO_LIST_EMPTY.toString()); - listLogger.log(Level.INFO, "End of List command."); return; } + assert !transactionsList.isEmpty(); - listLogger.log(Level.INFO, "Transactions list is available, so UI should display the" - + " transaction records."); - Ui.showTransactionsList(transactionsList, INFO_LIST.toString()); - listLogger.log(Level.INFO, "End of List command."); + listLogger.log(Level.INFO, "Transactions list is found to contain transaction records."); + Ui.showList(transactionsList, INFO_LIST.toString()); } //@@author paullowse diff --git a/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java b/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java index c1c5b8f13..1ffccfdf3 100644 --- a/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java +++ b/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class ListStatisticsInvalidStatsTypeException extends MoolahException { +public class ListStatsInvalidStatsTypeException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java b/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java index f7a92ea31..fbe1f3dab 100644 --- a/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java +++ b/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class ListStatisticsMissingTagException extends MoolahException { +public class ListStatsMissingTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * From f35129ed0dd50a92109fdf5c28a263ab5991b819 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 21 Oct 2022 08:02:44 +0800 Subject: [PATCH 176/416] Update EXPECTED.TXT to rectify a minor error --- text-ui-test/EXPECTED.TXT | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 9011019ec..0bc2770bf 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -231,9 +231,9 @@ Here are your transaction records: ____________________________________________________________ ____________________________________________________________ Here are the total savings for each category: -[food] $20 -[salary] $2000 [transport] $1 +[salary] $2000 +[food] $20 ____________________________________________________________ ____________________________________________________________ From 6f464181d3ede0bc8eb02e4e0fc7653ff166708f Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 21 Oct 2022 08:11:15 +0800 Subject: [PATCH 177/416] Fix minor indentation error for optional tags at ListCommand class --- src/main/java/seedu/duke/command/ListCommand.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 48f574211..a4ec90464 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -66,9 +66,9 @@ public ListCommand() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_DATE + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_DATE }; return optionalTags; } @@ -88,8 +88,6 @@ public void setDate(LocalDate date) { this.date = date; } - //@@author chydarren - /** * Executes the operations related to the command. * @@ -105,6 +103,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws listTransactions(transactions, type, category, date); } + //@@chydarren /** * List all or some transactions based on selection. * @@ -130,7 +129,6 @@ private static void listTransactions(TransactionList transactions, String type, } //@@author paullowse - /** * Enables the program to exit when the Bye command is issued. * @@ -140,4 +138,4 @@ private static void listTransactions(TransactionList transactions, String type, public boolean isExit() { return false; } -} +} \ No newline at end of file From 8989ab1613141965923cea445cb7a50679e6c8fa Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 21 Oct 2022 13:18:27 +0800 Subject: [PATCH 178/416] Implement helper functions to facilitate the retrieval of transactions by date intervals --- .../java/seedu/duke/data/TransactionList.java | 113 +++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 68ac3c42c..ccd4c465b 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -6,9 +6,11 @@ import seedu.duke.exception.InputTransactionUnknownTypeException; import java.time.LocalDate; +import java.time.Year; +import java.time.YearMonth; import java.util.ArrayList; import java.util.HashMap; -import java.util.Map; +import java.util.stream.Collectors; /** * Represents a list of transactions added by the user into the application. @@ -20,6 +22,10 @@ public class TransactionList { private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; private static final String SYMBOL_DOLLAR = "$"; + private static final String WEEKS = "weeks"; + private static final String MONTHS = "months"; + private static final int START = 0; + private static final int END = 1; private static final String LINE_SEPARATOR = System.lineSeparator(); //@@author chinhan99 @@ -121,6 +127,7 @@ public void addExpenseDuringStorage(String description, int amount, String categ } //@@author chydarren + /** * Checks whether the transaction belongs to the Income or Expense class type. * @@ -224,9 +231,11 @@ public HashMap processCategoricalSavings(HashMap categoricalSavings = new HashMap<>(); + // Adds each amount from transactions list to the categories in categorical savings hashmap categoricalSavings = processCategoricalSavings(categoricalSavings); - for (Map.Entry entry : categoricalSavings.entrySet()) { + // Formats every entry in the hashmap into a categorical savings list + for (HashMap.Entry entry : categoricalSavings.entrySet()) { categoricalSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getKey(), POSTFIX_CATEGORY, SYMBOL_DOLLAR, entry.getValue(), LINE_SEPARATOR); } @@ -234,7 +243,107 @@ public String listCategoricalSavings() { return categoricalSavingsList; } + /** + * Gets the range of dates for the last N number of weeks from occurring week. + * E.g. If the date is 21 October to backdate 2 weeks, the range will be 3 October to 16 October. + * + * @param numberOfWeeks N number of weeks to backdate, must be minimum 1 week. + * @param date A specified date to backdate N weeks from occurring week. + * @return An array containing the start and end date. + */ + public static LocalDate[] getWeekRange(LocalDate date, int numberOfWeeks) { + // Solution below adapted from https://stackoverflow.com/a/51356522 + int dayOfWeek = date.getDayOfWeek().getValue(); + LocalDate from = date.minusDays((dayOfWeek - 1) + (numberOfWeeks * 7)); + LocalDate to = date.minusDays(dayOfWeek); + + return new LocalDate[]{from, to}; + } + + /** + * Gets the range of dates for the last N number of months from occurring month. + * E.g. If the date is 21 October to backdate 2 weeks, the range will be 3 October to 16 October. + * + * @param numberOfMonths N number of months to backdate, must be minimum 1 month. + * @param date A specified date to backdate N months from occurring month. + * @return An array containing the start and end date. + */ + public static LocalDate[] getMonthRange(LocalDate date, int numberOfMonths) { + // Solution below adapted from https://stackoverflow.com/a/51356522 + LocalDate lastMonth = date.minusMonths(1); + LocalDate from = date.minusMonths(numberOfMonths).withDayOfMonth(1); + LocalDate to = lastMonth.withDayOfMonth(lastMonth.getMonth().maxLength()); + + return new LocalDate[]{from, to}; + } + + /** + * Gets all transactions recorded on a specific year. + * + * @param year A specified year. + * @return An array list containing all transactions recorded on a specific year. + */ + public ArrayList getTransactionsByYear(int year) { + //@@author chydarren-reused + //Reused from https://stackoverflow.com/a/69440139 + // with minor modifications + ArrayList transactionsByYear = (ArrayList) transactions.stream() + .filter(transaction -> Year.from(transaction.getDate()).equals(Year.of(year))) + .collect(Collectors.toList()); + //@@author + + return transactionsByYear; + } + + /** + * Gets all transactions recorded on a specific month. + * + * @param year A specified year. + * @param month A specified month within the year. + * @return An array list containing all transactions recorded on a specific month. + */ + public ArrayList getTransactionsByMonth(int year, int month) { + //@@author chydarren-reused + //Reused from https://stackoverflow.com/a/69440139 + ArrayList transactionsByMonth = (ArrayList) transactions.stream() + .filter(transaction -> YearMonth.from(transaction.getDate()).equals(YearMonth.of(year, month))) + .collect(Collectors.toList()); + //@@author + + return transactionsByMonth; + } + + /** + * Gets all transactions recorded on the last N weeks or months. + * + * @param numberOfType An integer that represents the last N number of weeks or months. + * @param type A string that represents the getting of N "weeks", or "months". + * @return An array list containing all transactions recorded on the last N number of weeks or months. + */ + public ArrayList getTransactionsByLastN(int numberOfType, String type) { + ArrayList transactionsByLastN = new ArrayList<>(); + LocalDate[] dateRange = {}; + + if (type.equals(WEEKS)) { + dateRange = getWeekRange(LocalDate.now(), numberOfType); + } else { + assert type.equals(MONTHS); + dateRange = getMonthRange(LocalDate.now(), numberOfType); + } + + for (Transaction transaction : transactions) { + LocalDate transactionDate = transaction.getDate(); + // Transaction is added into the filtered array list if it falls within the expected date range + if (!(transactionDate.isBefore(dateRange[START]) || transactionDate.isAfter(dateRange[END]))) { + transactionsByLastN.add(transaction); + } + } + + return transactionsByLastN; + } + //@@author brian-vb + /** * Purges all records in the transactions list. */ From eb96f0b97846bceeeafeff44a3c7753cfd396584 Mon Sep 17 00:00:00 2001 From: chydarren Date: Fri, 21 Oct 2022 18:04:11 +0800 Subject: [PATCH 179/416] Amend two comments in TransactionList get date range --- src/main/java/seedu/duke/data/TransactionList.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index ccd4c465b..b6dc75190 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -245,7 +245,7 @@ public String listCategoricalSavings() { /** * Gets the range of dates for the last N number of weeks from occurring week. - * E.g. If the date is 21 October to backdate 2 weeks, the range will be 3 October to 16 October. + * E.g. If the date is 21 October 2022 to backdate 2 weeks, the range will be 3 October to 16 October 2022. * * @param numberOfWeeks N number of weeks to backdate, must be minimum 1 week. * @param date A specified date to backdate N weeks from occurring week. @@ -262,7 +262,7 @@ public static LocalDate[] getWeekRange(LocalDate date, int numberOfWeeks) { /** * Gets the range of dates for the last N number of months from occurring month. - * E.g. If the date is 21 October to backdate 2 weeks, the range will be 3 October to 16 October. + * E.g. If the date is 21 October 2022 to backdate 2 months, the range will be 1 August to 30 September 2022. * * @param numberOfMonths N number of months to backdate, must be minimum 1 month. * @param date A specified date to backdate N months from occurring month. From 48d4f92146a9ffbaa5b84bfb972d5697c117e520 Mon Sep 17 00:00:00 2001 From: wcwy Date: Fri, 21 Oct 2022 21:40:15 +0800 Subject: [PATCH 180/416] Add basic budget feature Today's date and the default budget set is displayed on welcome message. The default value of the budget is set as $1000. --- src/main/java/seedu/duke/Ui.java | 36 ++++++++++++++----- .../java/seedu/duke/common/InfoMessages.java | 4 ++- src/main/java/seedu/duke/data/Budget.java | 31 ++++++++++++++++ .../duke/data/transaction/Transaction.java | 6 ++++ 4 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 src/main/java/seedu/duke/data/Budget.java diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 1a2fcd4f6..9450e1d6b 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -1,11 +1,13 @@ package seedu.duke; +import seedu.duke.data.Budget; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.Scanner; -import static seedu.duke.common.InfoMessages.INFO_DIVIDER; -import static seedu.duke.common.InfoMessages.INFO_GREET; -import static seedu.duke.common.InfoMessages.INFO_HELP_GREET; -import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; +import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; +import static seedu.duke.common.InfoMessages.*; public class Ui { //@@author chydarren @@ -27,6 +29,7 @@ public static void printMessages(String... messages) { } //@@author paullowse + /** * Initialises the variables of the Ui class. */ @@ -45,6 +48,7 @@ public String readCommand() { } //@@author chydarren + /** * Prepares the error message to be displayed to the user. * @@ -67,10 +71,13 @@ public static void showInfoMessage(String message) { * Prepares the greeting messages to be displayed to the user. */ public static void showGreeting() { - printMessages(INFO_GREET.toString(), INFO_HELP_PROMPT.toString()); + printMessages(INFO_GREET.toString(), INFO_CURRENT_BUDGET.toString() + Budget.getBudget(), + INFO_TODAY_DATE + showDateOfTheDay(), INFO_HELP_PROMPT.toString() + ); } //@@author chinhan99 + /** * Prepares the help messages to be displayed to the user. * @@ -83,8 +90,8 @@ public static void showHelp(String message) { /** * Prepares the transaction list messages to be displayed to the user. * - * @param list A string containing the formatted transaction list. - * @param message A message that complements with the transactions list. + * @param list A string containing the formatted transaction list. + * @param message A message that complements with the transactions list. */ public static void showList(String list, String message) { printMessages(message, list); @@ -94,10 +101,21 @@ public static void showList(String list, String message) { * Prepares the messages to be displayed to the user when add or delete has been performed on * the transaction list. * - * @param message A message that describes the functionality of the program. - * @param transactionDetails Details of the action that has been performed on the transaction. + * @param message A message that describes the functionality of the program. + * @param transactionDetails Details of the action that has been performed on the transaction. */ public static void showTransactionAction(String message, String transactionDetails) { printMessages(message, transactionDetails); } + + /** + * Returns the today's date in MMM dd yyyy format. + * + * @return Formatted today's date + */ + public static String showDateOfTheDay() { + LocalDate todayDate = LocalDate.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_OUTPUT_PATTERN.toString()); + return todayDate.format(formatter); + } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 8b7d6bd0b..0b73c70fa 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -21,7 +21,9 @@ public enum InfoMessages { INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), - INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."); + INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."), + INFO_CURRENT_BUDGET("Monthly budget set as: $"), + INFO_TODAY_DATE("Today is "); //@@author chydarren public final String message; diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java new file mode 100644 index 000000000..f3c63a97e --- /dev/null +++ b/src/main/java/seedu/duke/data/Budget.java @@ -0,0 +1,31 @@ +package seedu.duke.data; + +/** + * Represents the user's budget for the current month. + * + *

The default budget value is $1000, or the value read from the file storage. + * Operations related to the budgets are also defined under this class. + */ +public class Budget { + // Default value of the budget is $1000 + private static int budget = 1000; + + /** + * Retrieves the budget value set + * + * @return The budget value set by the user + */ + public static int getBudget() { + return budget; + } + + /** + * Update the budget value set + * + * @param budget The new value for budget + */ + public static void setBudget(int budget) { + Budget.budget = budget; + } + +} diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index cd12b57b1..6ad62f0f8 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -62,6 +62,12 @@ public void setDate(LocalDate date) { } //@@author wcwy + + /** + * Returns the date of transaction in MMM dd yyyy format. + * + * @return Formatted date of the transaction + */ public String printFormattedDate() { DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_OUTPUT_PATTERN.toString()); return date.format(formatter); From a30acb66c513b62477a466fa2bfe1ee5a56437ec Mon Sep 17 00:00:00 2001 From: wcwy Date: Fri, 21 Oct 2022 22:22:47 +0800 Subject: [PATCH 181/416] Define the maximum number of transactions allowed in the list This will allow us to calculate the maximum amount of exceeded budget possible in the execution. --- .../java/seedu/duke/command/AddCommand.java | 19 +++++++++++++------ .../java/seedu/duke/common/Constants.java | 13 +++++++++++++ .../java/seedu/duke/common/ErrorMessages.java | 10 ++++++---- src/main/java/seedu/duke/data/Budget.java | 17 ++++++++++++++--- .../duke/data/transaction/Transaction.java | 3 +++ ...putTransactionInvalidAmountException.java} | 4 ++-- .../MaximumTransactionCountException.java | 15 +++++++++++++++ .../seedu/duke/parser/ParameterParser.java | 16 +++++++++------- .../duke/parser/ParameterParserTest.java | 6 +++--- 9 files changed, 78 insertions(+), 25 deletions(-) create mode 100644 src/main/java/seedu/duke/common/Constants.java rename src/main/java/seedu/duke/exception/{AddTransactionInvalidAmountException.java => InputTransactionInvalidAmountException.java} (67%) create mode 100644 src/main/java/seedu/duke/exception/MaximumTransactionCountException.java diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 2e9a7b472..49fb1c39e 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -6,10 +6,10 @@ import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.MoolahException; import seedu.duke.exception.InputTransactionUnknownTypeException; import seedu.duke.exception.StorageWriteErrorException; +import seedu.duke.exception.MaximumTransactionCountException; import java.io.IOException; import java.time.LocalDate; @@ -22,6 +22,7 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; +import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; @@ -86,11 +87,11 @@ public AddCommand(String type, String description, int amount, String category, public String[] getMandatoryTags() { String[] mandatoryTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_AMOUNT, - COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_TRANSACTION_DESCRIPTION + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_AMOUNT, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_TRANSACTION_DESCRIPTION }; return mandatoryTags; } @@ -138,6 +139,12 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws + "before adding into the transaction class."); assert date != null; //@@author wcwy + // The expected maximum number of transactions allowed to store is only one million. + if (transactions.size() == MAX_TRANSACTIONS_COUNT) { + addLogger.log(Level.WARNING, "A transaction is attempted to be stored beyond its capacity"); + throw new MaximumTransactionCountException(); + } + switch (type) { case Expense.TRANSACTION_NAME: String expense = transactions.addExpense(description, amount, category, date); diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java new file mode 100644 index 000000000..81bae1ea2 --- /dev/null +++ b/src/main/java/seedu/duke/common/Constants.java @@ -0,0 +1,13 @@ +package seedu.duke.common; + +/** + * Represents all the constant settings of the application. + * Developers should update the constant in this file to allow for different limits for the application. + */ +public class Constants { + // The amount of transaction is allowed to be in the range or 0 <= x <= 10000000 + public static int MIN_AMOUNT_VALUE = 0; + public static int MAX_AMOUNT_VALUE = 10000000; + // One million transactions is the capacity allowed + public static int MAX_TRANSACTIONS_COUNT = 1000000; +} diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 0e41850ca..89691e522 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -14,15 +14,17 @@ public enum ErrorMessages { ERROR_INPUT_MISSING_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), ERROR_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_ENTRY_NUMBER_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), - ERROR_ADD_COMMAND_INVALID_AMOUNT("Invalid amount, " + ERROR_INPUT_INVALID_AMOUNT("Invalid amount, " + "please ensure your amount is in positive numerals ($10000000 or less) only!"), ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), - ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. To preserve data, please STOP the program and " - + "edit your data file correctly."), - ERROR_STORAGE_WRITE("Unable to write to Duke.txt. Please save your current Duke.txt file and restart Moolah"); + ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. " + + "To preserve data, please STOP the program and edit your data file correctly."), + ERROR_STORAGE_WRITE("Unable to write to Duke.txt. Please save your current Duke.txt file and restart Moolah"), + ERROR_MAXIMUM_TRANSACTION_COUNT_REACHED("Unable to add transaction. " + + "The maximum allowed transaction size (1000000) has been reached."); //@@author chydarren public final String message; diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index f3c63a97e..b8517f3f0 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -7,11 +7,11 @@ * Operations related to the budgets are also defined under this class. */ public class Budget { - // Default value of the budget is $1000 + // Default value of the monthly budget is $1000 private static int budget = 1000; /** - * Retrieves the budget value set + * Retrieves the budget value set for the current month * * @return The budget value set by the user */ @@ -20,7 +20,7 @@ public static int getBudget() { } /** - * Update the budget value set + * Updates the budget value set for the current month * * @param budget The new value for budget */ @@ -28,4 +28,15 @@ public static void setBudget(int budget) { Budget.budget = budget; } + /** + * Return the amount of budget left in + * + * @param transactionList + * @return + */ + public static String getBudgetLeft(TransactionList transactionList) { + + return ""; + } + } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 6ad62f0f8..a30f934d2 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -5,6 +5,9 @@ import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; +/** + * Represents a transaction made by the user, which could be either an income or an expense. + */ public abstract class Transaction { //@@author chydarren private static final String PREFIX_CATEGORY = "["; diff --git a/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java similarity index 67% rename from src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java rename to src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java index 80d97c3e6..0bd57edf0 100644 --- a/src/main/java/seedu/duke/exception/AddTransactionInvalidAmountException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java @@ -3,7 +3,7 @@ import seedu.duke.common.ErrorMessages; //@@author chinhan99 -public class AddTransactionInvalidAmountException extends MoolahException { +public class InputTransactionInvalidAmountException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -11,7 +11,7 @@ public class AddTransactionInvalidAmountException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_INVALID_AMOUNT.toString(); + return ErrorMessages.ERROR_INPUT_INVALID_AMOUNT.toString(); } } diff --git a/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java b/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java new file mode 100644 index 000000000..606a2ffa4 --- /dev/null +++ b/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class MaximumTransactionCountException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_MAXIMUM_TRANSACTION_COUNT_REACHED.toString(); + } +} diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index e067a2a00..50e66a9c1 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -12,7 +12,7 @@ import seedu.duke.exception.EmptyParameterException; import seedu.duke.exception.UnknownHelpOptionException; import seedu.duke.exception.InputTransactionInvalidDateException; -import seedu.duke.exception.AddTransactionInvalidAmountException; +import seedu.duke.exception.InputTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.EntryNumberNotNumericException; import seedu.duke.exception.InputTransactionUnknownTypeException; @@ -36,6 +36,8 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; +import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; +import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; /** @@ -371,28 +373,28 @@ public static String parseCategoryTag(String parameter) throws InputTransactionI * * @param parameter The user input after the user tag. * @return The amount integer if no exceptions are thrown. - * @throws AddTransactionInvalidAmountException If the transaction amount provided is not a valid accepted integer. + * @throws InputTransactionInvalidAmountException If the transaction amount provided is not a valid accepted integer. */ - private static int parseAmountTag(String parameter) throws AddTransactionInvalidAmountException { + private static int parseAmountTag(String parameter) throws InputTransactionInvalidAmountException { Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); try { if (containAlphabet(parameter) || hasSpecialSymbols.find()) { parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + parameter); - throw new AddTransactionInvalidAmountException(); + throw new InputTransactionInvalidAmountException(); } int amount = Integer.parseInt(parameter); - if (amount < 0 || amount > 10000000) { + if (amount < MIN_AMOUNT_VALUE || amount > MAX_AMOUNT_VALUE) { parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + parameter); - throw new AddTransactionInvalidAmountException(); + throw new InputTransactionInvalidAmountException(); } return amount; } catch (NumberFormatException e) { parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + parameter); - throw new AddTransactionInvalidAmountException(); + throw new InputTransactionInvalidAmountException(); } } diff --git a/src/test/java/seedu/duke/parser/ParameterParserTest.java b/src/test/java/seedu/duke/parser/ParameterParserTest.java index f77425e42..985120ee6 100644 --- a/src/test/java/seedu/duke/parser/ParameterParserTest.java +++ b/src/test/java/seedu/duke/parser/ParameterParserTest.java @@ -2,7 +2,7 @@ import org.junit.jupiter.api.Test; import seedu.duke.command.AddCommand; -import seedu.duke.exception.AddTransactionInvalidAmountException; +import seedu.duke.exception.InputTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; import seedu.duke.exception.EmptyParameterException; @@ -30,7 +30,7 @@ public void execute_InvalidNegativeAmount_ExpectedException() { String parametersInput = "t/expense c/food a/-20 d/13092022 i/isThisAnIncome"; assertThrows( - AddTransactionInvalidAmountException.class, + InputTransactionInvalidAmountException.class, () -> ParameterParser.parse(addCommand, parametersInput) ); } @@ -41,7 +41,7 @@ public void execute_InvalidAmountInput_ExpectedException() { String parametersInput = "t/income c/bonus a/one_dollar d/13092022 i/thank_you_boss"; assertThrows( - AddTransactionInvalidAmountException.class, + InputTransactionInvalidAmountException.class, () -> ParameterParser.parse(addCommand, parametersInput) ); } From 46b261cd51b8527f32419b2fbc77def8748e0788 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 21 Oct 2022 22:28:17 +0800 Subject: [PATCH 182/416] Add Stats command parameter time --- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/java/seedu/duke/command/Command.java | 8 +- .../java/seedu/duke/command/CommandTag.java | 2 + .../java/seedu/duke/command/StatsCommand.java | 78 +++++++++++++++++-- .../java/seedu/duke/common/ErrorMessages.java | 2 + .../java/seedu/duke/common/InfoMessages.java | 1 + .../java/seedu/duke/data/TransactionList.java | 19 +++++ .../exception/StatsInvalidMonthException.java | 15 ++++ .../exception/StatsInvalidYearException.java | 15 ++++ .../seedu/duke/parser/ParameterParser.java | 69 +++++++++++----- text-ui-test/EXPECTED.TXT | 51 +++++++++--- text-ui-test/data/duke.txt | 1 + text-ui-test/input.txt | 3 + 13 files changed, 226 insertions(+), 40 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/StatsInvalidMonthException.java create mode 100644 src/main/java/seedu/duke/exception/StatsInvalidYearException.java create mode 100644 text-ui-test/data/duke.txt diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b7c8c5dbf..e750102e0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index a8db11d42..ba559a73c 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -5,7 +5,6 @@ import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; -import java.io.IOException; import java.time.LocalDate; /** @@ -87,4 +86,11 @@ public void setStatsType(String statsType) { } + public void setStatsMonth(int month) { + } + + public void setStatsYear(int year) { + } + + } diff --git a/src/main/java/seedu/duke/command/CommandTag.java b/src/main/java/seedu/duke/command/CommandTag.java index 490df5eaa..136f6e778 100644 --- a/src/main/java/seedu/duke/command/CommandTag.java +++ b/src/main/java/seedu/duke/command/CommandTag.java @@ -10,4 +10,6 @@ public class CommandTag { public static final String COMMAND_TAG_LIST_ENTRY_NUMBER = "e/"; public static final String COMMAND_TAG_HELP_OPTION = "o/"; public static final String COMMAND_TAG_STATISTICS_TYPE = "s/"; + public static final String COMMAND_TAG_STATS_MONTH = "m/"; + public static final String COMMAND_TAG_STATS_YEAR = "y/"; } \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 143539725..b458dafb4 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -3,15 +3,17 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.ListStatsInvalidStatsTypeException; import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StatsInvalidYearException; +import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; -import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; -import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; +import static seedu.duke.command.CommandTag.*; +import static seedu.duke.common.InfoMessages.*; /** * Represents a get command object that will execute the operations for Get command. @@ -40,6 +42,8 @@ public class StatsCommand extends Command { //@@author chydarren private static final Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); private String statsType; + private int month = -1; + private int year = -1; //@@author paullowse public StatsCommand() { @@ -56,11 +60,36 @@ public String[] getMandatoryTags() { return mandatoryTags; } + @Override + public String[] getOptionalTags() { + String[] optionalTags = new String[]{ + COMMAND_TAG_STATS_MONTH, + COMMAND_TAG_STATS_YEAR + }; + return optionalTags; + } + + @Override public void setStatsType(String statsType) { this.statsType = statsType; } + @Override + public void setStatsMonth(int month) { + this.month = month; + } + + @Override + public void setStatsYear(int year) { + this.year = year; + } + + public int getStatsYear() { + return year; + } + + //@@author chydarren /** * Executes the operations related to the command. @@ -74,7 +103,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws statsLogger.setLevel(Level.SEVERE); statsLogger.log(Level.INFO, "Entering execution of the Stats command."); - listStatsByStatsType(statsType, transactions); + listStatsByStatsType(statsType, transactions, month, year); } /** @@ -84,14 +113,24 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param transactions An instance of the TransactionList class. * @throws ListStatsInvalidStatsTypeException If the type of statistics is not recognised. */ - private static void listStatsByStatsType(String statsType, TransactionList transactions) - throws ListStatsInvalidStatsTypeException { + private static void listStatsByStatsType(String statsType, TransactionList transactions, int month, int year) + throws ListStatsInvalidStatsTypeException, StatsInvalidYearException { switch (statsType) { case "categories": statsLogger.log(Level.INFO, "Stats type has been detected for categorical savings."); statsTypeCategoricalSavings(transactions); statsLogger.log(Level.INFO, "End of Stats command."); break; + case "time": + statsLogger.log(Level.INFO, "Stats type has been detected for monthly savings."); + if (year == -1) { + statsLogger.log(Level.WARNING, "An exception has been caught due to a missing year tag"); + throw new StatsInvalidYearException(); + } + statsTypeTimeSavings(transactions, year, month); + break; + + default: statsLogger.log(Level.WARNING, "An exception has been caught due to an invalid stats type."); throw new ListStatsInvalidStatsTypeException(); @@ -117,6 +156,33 @@ public static void statsTypeCategoricalSavings(TransactionList transactions) { Ui.showList(categoricalSavingsList, INFO_STATS_CATEGORIES.toString()); } + //@@author paullowse + public static void statsTypeTimeSavings(TransactionList transactions, int year, int month) { + //System.out.println(year); + //System.out.println(month); + + ArrayList timeTransactions; + // only year + if (month == -1) { + timeTransactions = transactions.getTransactionsByYear(year); + } + else { + timeTransactions = transactions.getTransactionsByMonth(year, month); + } + String timeSavingsList = transactions.listTimeStats(timeTransactions, year, month); + + if (timeSavingsList.isEmpty()) { + statsLogger.log(Level.INFO, "Categorical savings list is empty as there are no transactions available."); + Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); + return; + } + + assert !timeSavingsList.isEmpty(); + statsLogger.log(Level.INFO, "Monthly savings list is found to contain categories-amount pairs."); + Ui.showList(timeSavingsList, INFO_STATS_TIME.toString()); + + } + //@@author paullowse /** * Enables the program to exit when the Bye command is issued. diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 0e41850ca..e714e25e7 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -19,6 +19,8 @@ public enum ErrorMessages { ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), + ERROR_STATS_COMMAND_INVALID_MONTH("Month of statistics given is invalid, please check your input!"), + ERROR_STATS_COMMAND_INVALID_YEAR("Year of statistics given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. To preserve data, please STOP the program and " + "edit your data file correctly."), diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 8b7d6bd0b..077ccf953 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -18,6 +18,7 @@ public enum InfoMessages { INFO_LIST_UNFILTERED("There are no transaction records that match your search expression."), INFO_STATS_EMPTY("There are no statistics available yet for the given statistics type."), INFO_STATS_CATEGORIES("Here are the total savings for each category:"), + INFO_STATS_TIME("Here are the total savings for "), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b6dc75190..4976d556f 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -243,6 +243,25 @@ public String listCategoricalSavings() { return categoricalSavingsList; } + + public String listTimeStats(ArrayList timeTransactions, int year, int month) { + String timeSavingsList = ""; + + if (month == -1) { + timeSavingsList += "Year: " + year + LINE_SEPARATOR + LINE_SEPARATOR; + } else { + timeSavingsList += "Year: " + year + ", Month: " + month + LINE_SEPARATOR + LINE_SEPARATOR; + } + + // Formats every entry in the hashmap into a categorical savings list + for (Transaction entry : timeTransactions) { + timeSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getCategory(), + POSTFIX_CATEGORY, SYMBOL_DOLLAR, entry.getAmount(), LINE_SEPARATOR); + } + + return timeSavingsList; + } + /** * Gets the range of dates for the last N number of weeks from occurring week. * E.g. If the date is 21 October 2022 to backdate 2 weeks, the range will be 3 October to 16 October 2022. diff --git a/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java b/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java new file mode 100644 index 000000000..034cb6d88 --- /dev/null +++ b/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class StatsInvalidMonthException extends MoolahException{ + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STATS_COMMAND_INVALID_MONTH.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/StatsInvalidYearException.java b/src/main/java/seedu/duke/exception/StatsInvalidYearException.java new file mode 100644 index 000000000..2f66eaba0 --- /dev/null +++ b/src/main/java/seedu/duke/exception/StatsInvalidYearException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class StatsInvalidYearException extends MoolahException{ + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STATS_COMMAND_INVALID_YEAR.toString(); + } +} diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index e067a2a00..1b8c811c6 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -5,18 +5,7 @@ import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -import seedu.duke.exception.InputDuplicateTagException; -import seedu.duke.exception.InputMissingTagException; -import seedu.duke.exception.InputUnsupportedTagException; -import seedu.duke.exception.MoolahException; -import seedu.duke.exception.EmptyParameterException; -import seedu.duke.exception.UnknownHelpOptionException; -import seedu.duke.exception.InputTransactionInvalidDateException; -import seedu.duke.exception.AddTransactionInvalidAmountException; -import seedu.duke.exception.InputTransactionInvalidCategoryException; -import seedu.duke.exception.EntryNumberNotNumericException; -import seedu.duke.exception.InputTransactionUnknownTypeException; -import seedu.duke.exception.ListStatsInvalidStatsTypeException; +import seedu.duke.exception.*; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -27,15 +16,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; -import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; - +import static seedu.duke.command.CommandTag.*; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; /** @@ -298,6 +279,12 @@ private static void setParameter(Command command, String tag, String parameter) case COMMAND_TAG_STATISTICS_TYPE: command.setStatsType(parseStatsTypeTag(parameter)); break; + case COMMAND_TAG_STATS_MONTH: + command.setStatsMonth(parseStatsMonthTag(parameter)); + break; + case COMMAND_TAG_STATS_YEAR: + command.setStatsYear(parseStatsYearTag(parameter)); + break; default: parserLogger.log(Level.WARNING, "An unsupported tag exception is caught: " + tag); throw new InputUnsupportedTagException(); @@ -473,6 +460,9 @@ public static String parseStatsTypeTag(String parameter) throws ListStatsInvalid case "categories": statsType = "categories"; break; + case "time": + statsType = "time"; + break; default: parserLogger.log(Level.WARNING, "An invalid statistic type error is caught for the given parameter: " + parameter); @@ -481,6 +471,43 @@ public static String parseStatsTypeTag(String parameter) throws ListStatsInvalid return statsType; } + //@@author paullowse + public static int parseStatsMonthTag(String parameter) throws StatsInvalidMonthException, EntryNumberNotNumericException{ + int month; + try { + month = Integer.parseInt(parameter); + } catch (NumberFormatException e) { + parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + + parameter); + throw new EntryNumberNotNumericException(); + } + + if (month > 12 || month < 0) { + parserLogger.log(Level.WARNING, "An invalid month number error is caught for the given parameter: " + + parameter); + throw new StatsInvalidMonthException(); + } + return month; + } + + //@@author paullowse + public static int parseStatsYearTag(String parameter) throws StatsInvalidYearException, EntryNumberNotNumericException{ + int year; + try { + year = Integer.parseInt(parameter); + } catch (NumberFormatException e) { + parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + + parameter); + throw new EntryNumberNotNumericException(); + } + if (year < 0) { + parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + + parameter); + throw new StatsInvalidYearException(); + } + return year; + } + //@@author chinhan99 /** diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 0bc2770bf..6a6a29efd 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,8 +1,5 @@ ____________________________________________________________ -* Created new directory * -____________________________________________________________ -____________________________________________________________ -* Created new file for use * +* Existing file detected * ____________________________________________________________ ____________________________________________________________ * duke.txt loaded successfully! * @@ -155,10 +152,14 @@ ____________________________________________________________ Duplicate tag(s) detected, please check your input! ____________________________________________________________ ____________________________________________________________ -There are no statistics available yet for the given statistics type. +Here are the total savings for each category: +[transport] $1 + ____________________________________________________________ ____________________________________________________________ -There are no transaction records found. +Here are your transaction records: +[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ + ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: @@ -204,6 +205,7 @@ Type of transaction given is invalid, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: +[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ [-][food] $20 at Sep 13 2022 | Description: NIL [-][transport] $1 at Oct 02 2022 | Description: bus_fare @@ -231,7 +233,7 @@ Here are your transaction records: ____________________________________________________________ ____________________________________________________________ Here are the total savings for each category: -[transport] $1 +[transport] $2 [salary] $2000 [food] $20 @@ -249,6 +251,7 @@ There are no transaction records that match your search expression. ____________________________________________________________ ____________________________________________________________ Here are your transaction records: +[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ [-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare @@ -286,7 +289,31 @@ ____________________________________________________________ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ +Here are the total savings for +Year: 2022, Month: 10 + +[transport] $1 +[transport] $1 +[bonus] $10000000 + +____________________________________________________________ +____________________________________________________________ +Here are the total savings for +Year: 2022 + +[transport] $1 +[food] $20 +[salary] $2000 +[transport] $1 +[bonus] $10000000 + +____________________________________________________________ +____________________________________________________________ +Year of statistics given is invalid, please check your input! +____________________________________________________________ +____________________________________________________________ Here are your transaction records: +[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ [-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare @@ -298,10 +325,11 @@ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: -[-][food] $20 at Sep 13 2022 | Description: NIL +[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ ____________________________________________________________ ____________________________________________________________ Here are your transaction records: +[-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss @@ -314,16 +342,17 @@ ____________________________________________________________ Invalid index, please ensure your index is correct! ____________________________________________________________ ____________________________________________________________ -Invalid index, please ensure your index is correct! +I have deleted the following transaction: +[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: -[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +[-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ Here are your transaction records: +[-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary -[-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ diff --git a/text-ui-test/data/duke.txt b/text-ui-test/data/duke.txt new file mode 100644 index 000000000..a28ec27b5 --- /dev/null +++ b/text-ui-test/data/duke.txt @@ -0,0 +1 @@ +expense | transport | 1 | 2022-10-02 | i/i/i/i/ diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 63bce7157..f4fde7c1b 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -48,6 +48,9 @@ add t/income c/bonus a/9999999999999 d/03102022 i/thank_you_boss add t/income c/bonus a/10000000 d/20220101 i/thank_you_boss add t/income c/bonus a/10000000 d/obviouslyFalseDate i/thank_you_boss add t/income c/bonus a/10000000 +stats s/time y/2022 m/10 +stats s/time y/2022 +stats s/time m/9 list delete 1 delete e/1 From 8960182c68d2cb84baded0e06f49870040d65ecc Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 21 Oct 2022 22:39:21 +0800 Subject: [PATCH 183/416] fix checkstyle --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../java/seedu/duke/command/StatsCommand.java | 15 +++--- .../exception/StatsInvalidMonthException.java | 2 +- .../exception/StatsInvalidYearException.java | 2 +- .../seedu/duke/parser/ParameterParser.java | 47 +++++++++++++++---- 5 files changed, 50 insertions(+), 18 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e0..b7c8c5dbf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index b458dafb4..cb6fc906b 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -12,8 +12,12 @@ import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.duke.command.CommandTag.*; -import static seedu.duke.common.InfoMessages.*; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_YEAR; +import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; +import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; +import static seedu.duke.common.InfoMessages.INFO_STATS_TIME; /** * Represents a get command object that will execute the operations for Get command. @@ -63,8 +67,8 @@ public String[] getMandatoryTags() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_STATS_MONTH, - COMMAND_TAG_STATS_YEAR + COMMAND_TAG_STATS_MONTH, + COMMAND_TAG_STATS_YEAR }; return optionalTags; } @@ -165,8 +169,7 @@ public static void statsTypeTimeSavings(TransactionList transactions, int year, // only year if (month == -1) { timeTransactions = transactions.getTransactionsByYear(year); - } - else { + } else { timeTransactions = transactions.getTransactionsByMonth(year, month); } String timeSavingsList = transactions.listTimeStats(timeTransactions, year, month); diff --git a/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java b/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java index 034cb6d88..b651bf55a 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java +++ b/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class StatsInvalidMonthException extends MoolahException{ +public class StatsInvalidMonthException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * diff --git a/src/main/java/seedu/duke/exception/StatsInvalidYearException.java b/src/main/java/seedu/duke/exception/StatsInvalidYearException.java index 2f66eaba0..c569f05d6 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidYearException.java +++ b/src/main/java/seedu/duke/exception/StatsInvalidYearException.java @@ -2,7 +2,7 @@ import seedu.duke.common.ErrorMessages; -public class StatsInvalidYearException extends MoolahException{ +public class StatsInvalidYearException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 1b8c811c6..b1c99fbe0 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -5,7 +5,20 @@ import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -import seedu.duke.exception.*; +import seedu.duke.exception.AddTransactionInvalidAmountException; +import seedu.duke.exception.EmptyParameterException; +import seedu.duke.exception.EntryNumberNotNumericException; +import seedu.duke.exception.InputDuplicateTagException; +import seedu.duke.exception.InputMissingTagException; +import seedu.duke.exception.InputTransactionInvalidCategoryException; +import seedu.duke.exception.InputTransactionInvalidDateException; +import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.InputUnsupportedTagException; +import seedu.duke.exception.ListStatsInvalidStatsTypeException; +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StatsInvalidMonthException; +import seedu.duke.exception.StatsInvalidYearException; +import seedu.duke.exception.UnknownHelpOptionException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -16,7 +29,16 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static seedu.duke.command.CommandTag.*; +import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; +import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_YEAR; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; /** @@ -167,7 +189,8 @@ public static void checkDuplicateTagsNotExist(String[] splits) throws InputDupli public static void checkParameterNotEmpty(String[] splits) throws EmptyParameterException { for (String split : splits) { if (split.length() == 2) { - parserLogger.log(Level.WARNING, "An empty parameter error is caught for the given tag input: " + split); + parserLogger.log(Level.WARNING, "An empty parameter error is caught for the " + + "given tag input: " + split); throw new EmptyParameterException(); } } @@ -307,7 +330,8 @@ public static String parseTypeTagForListing(String parameter) throws InputTransa case "income": return CLASS_TYPE_INCOME; default: - parserLogger.log(Level.WARNING, "An invalid type error is caught for the given parameter: " + parameter); + parserLogger.log(Level.WARNING, "An invalid type error " + + "is caught for the given parameter: " + parameter); throw new InputTransactionUnknownTypeException(); } } @@ -326,7 +350,8 @@ public static String parseTypeTagForAdding(String parameter) throws InputTransac boolean isIncome = parameter.equals(Income.TRANSACTION_NAME); if (!isExpense && !isIncome) { - parserLogger.log(Level.WARNING, "An invalid type error is caught for the given parameter: " + parameter); + parserLogger.log(Level.WARNING, "An invalid type error " + + "is caught for the given parameter: " + parameter); throw new InputTransactionUnknownTypeException(); } @@ -378,7 +403,8 @@ private static int parseAmountTag(String parameter) throws AddTransactionInvalid return amount; } catch (NumberFormatException e) { - parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + parameter); + parserLogger.log(Level.WARNING, "An invalid amount error " + + "is caught for the given parameter: " + parameter); throw new AddTransactionInvalidAmountException(); } } @@ -398,7 +424,8 @@ public static LocalDate parseDateTag(String parameter) throws InputTransactionIn LocalDate date = LocalDate.parse(parameter, formatter); return date; } catch (DateTimeParseException exception) { - parserLogger.log(Level.WARNING, "An invalid date error is caught for the given parameter: " + parameter); + parserLogger.log(Level.WARNING, "An invalid date error " + + "is caught for the given parameter: " + parameter); throw new InputTransactionInvalidDateException(); } } @@ -472,7 +499,8 @@ public static String parseStatsTypeTag(String parameter) throws ListStatsInvalid } //@@author paullowse - public static int parseStatsMonthTag(String parameter) throws StatsInvalidMonthException, EntryNumberNotNumericException{ + public static int parseStatsMonthTag(String parameter) throws StatsInvalidMonthException, + EntryNumberNotNumericException { int month; try { month = Integer.parseInt(parameter); @@ -491,7 +519,8 @@ public static int parseStatsMonthTag(String parameter) throws StatsInvalidMonthE } //@@author paullowse - public static int parseStatsYearTag(String parameter) throws StatsInvalidYearException, EntryNumberNotNumericException{ + public static int parseStatsYearTag(String parameter) throws StatsInvalidYearException, + EntryNumberNotNumericException { int year; try { year = Integer.parseInt(parameter); From f2babde0442dec39df3c7e086ba72cf7a451f125 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 21 Oct 2022 22:52:09 +0800 Subject: [PATCH 184/416] undo testing --- text-ui-test/EXPECTED.TXT | 23 ----------------------- text-ui-test/input.txt | 3 --- 2 files changed, 26 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 6a6a29efd..861683a0b 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -289,29 +289,6 @@ ____________________________________________________________ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ -Here are the total savings for -Year: 2022, Month: 10 - -[transport] $1 -[transport] $1 -[bonus] $10000000 - -____________________________________________________________ -____________________________________________________________ -Here are the total savings for -Year: 2022 - -[transport] $1 -[food] $20 -[salary] $2000 -[transport] $1 -[bonus] $10000000 - -____________________________________________________________ -____________________________________________________________ -Year of statistics given is invalid, please check your input! -____________________________________________________________ -____________________________________________________________ Here are your transaction records: [-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ [-][food] $20 at Sep 13 2022 | Description: NIL diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index f4fde7c1b..63bce7157 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -48,9 +48,6 @@ add t/income c/bonus a/9999999999999 d/03102022 i/thank_you_boss add t/income c/bonus a/10000000 d/20220101 i/thank_you_boss add t/income c/bonus a/10000000 d/obviouslyFalseDate i/thank_you_boss add t/income c/bonus a/10000000 -stats s/time y/2022 m/10 -stats s/time y/2022 -stats s/time m/9 list delete 1 delete e/1 From 7c3629596a47111792c686c479afab3b688dfff7 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 21 Oct 2022 23:24:43 +0800 Subject: [PATCH 185/416] why --- gradle/wrapper/gradle-wrapper.properties | 2 +- src/main/java/seedu/duke/common/InfoMessages.java | 2 +- src/main/java/seedu/duke/data/TransactionList.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b7c8c5dbf..e750102e0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 077ccf953..0518164e9 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -18,7 +18,7 @@ public enum InfoMessages { INFO_LIST_UNFILTERED("There are no transaction records that match your search expression."), INFO_STATS_EMPTY("There are no statistics available yet for the given statistics type."), INFO_STATS_CATEGORIES("Here are the total savings for each category:"), - INFO_STATS_TIME("Here are the total savings for "), + INFO_STATS_TIME("Here are the total savings for"), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 4976d556f..a20d85e99 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -248,9 +248,9 @@ public String listTimeStats(ArrayList timeTransactions, int year, i String timeSavingsList = ""; if (month == -1) { - timeSavingsList += "Year: " + year + LINE_SEPARATOR + LINE_SEPARATOR; + timeSavingsList += "Year: " + year + LINE_SEPARATOR; } else { - timeSavingsList += "Year: " + year + ", Month: " + month + LINE_SEPARATOR + LINE_SEPARATOR; + timeSavingsList += "Year: " + year + ", Month: " + month + LINE_SEPARATOR; } // Formats every entry in the hashmap into a categorical savings list From 6ee3f6d6833c383101261834d25d1655c659ff02 Mon Sep 17 00:00:00 2001 From: Paul Date: Fri, 21 Oct 2022 23:37:43 +0800 Subject: [PATCH 186/416] Update gradle-wrapper.properties --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e0..b7c8c5dbf 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From a9e42725af032e357561bb4013d525fa4415b112 Mon Sep 17 00:00:00 2001 From: wcwy Date: Fri, 21 Oct 2022 23:53:13 +0800 Subject: [PATCH 187/416] Change the usage of "\n" to System.lineSeparator() --- src/main/java/seedu/duke/command/AddCommand.java | 6 +++--- src/main/java/seedu/duke/command/HelpCommand.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 49fb1c39e..24680d9cb 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -49,10 +49,10 @@ public class AddCommand extends Command { + LINE_SEPARATOR + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + LINE_SEPARATOR + "DESCRIPTION: More information regarding the transaction, written without any space."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + COMMAND_DESCRIPTION + + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; //@@author chinhan99 private static final Logger addLogger = Logger.getLogger(AddCommand.class.getName()); diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 18b7774df..6039b099c 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -29,7 +29,7 @@ public class HelpCommand extends Command { public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; //@@author wcwy private boolean isDetailed; From d563269f0caa2865a75b0145c981a3fb979309ba Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 22 Oct 2022 00:04:59 +0800 Subject: [PATCH 188/416] Update StatTime fixes --- .../java/seedu/duke/command/AddCommand.java | 17 +++++------------ .../java/seedu/duke/command/HelpCommand.java | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 2e9a7b472..b97c0ff2c 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -2,26 +2,19 @@ import seedu.duke.Storage; import seedu.duke.Ui; - import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -import seedu.duke.data.transaction.Transaction; -import seedu.duke.exception.MoolahException; import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.MoolahException; import seedu.duke.exception.StorageWriteErrorException; import java.io.IOException; import java.time.LocalDate; - import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; +import static seedu.duke.command.CommandTag.*; import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; @@ -48,10 +41,10 @@ public class AddCommand extends Command { + LINE_SEPARATOR + "DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + LINE_SEPARATOR + "DESCRIPTION: More information regarding the transaction, written without any space."; // Basic help description - public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + "\n" + COMMAND_DESCRIPTION + "\n" - + COMMAND_USAGE + "\n"; + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; //@@author chinhan99 private static final Logger addLogger = Logger.getLogger(AddCommand.class.getName()); diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 18b7774df..6039b099c 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -29,7 +29,7 @@ public class HelpCommand extends Command { public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; // Detailed help description - public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + "\n"; + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; //@@author wcwy private boolean isDetailed; From 5a1736721bc959af08dc75ce8b87fea20639dc32 Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 22 Oct 2022 00:12:17 +0800 Subject: [PATCH 189/416] Implement basic budget checking feature on every add command --- src/main/java/seedu/duke/Ui.java | 6 + .../java/seedu/duke/command/AddCommand.java | 97 +++-- .../java/seedu/duke/common/Constants.java | 19 +- .../java/seedu/duke/common/InfoMessages.java | 1 + src/main/java/seedu/duke/data/Budget.java | 31 +- .../java/seedu/duke/data/TransactionList.java | 58 ++- .../seedu/duke/data/TransactionList.java~ | 387 ++++++++++++++++++ 7 files changed, 548 insertions(+), 51 deletions(-) create mode 100644 src/main/java/seedu/duke/data/TransactionList.java~ diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 9450e1d6b..2dde12836 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -103,7 +103,13 @@ public static void showList(String list, String message) { * * @param message A message that describes the functionality of the program. * @param transactionDetails Details of the action that has been performed on the transaction. + * @param budgetInfo A message that contains the monthly budget information. */ + public static void showTransactionAction(String message, String transactionDetails, String budgetInfo) { + printMessages(message, transactionDetails, INFO_REMAINING_BUDGET.toString(), budgetInfo); + } + + // A temporary overload method for backward-compatibility for delete command public static void showTransactionAction(String message, String transactionDetails) { printMessages(message, transactionDetails); } diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 24680d9cb..42e435db1 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -3,9 +3,11 @@ import seedu.duke.Storage; import seedu.duke.Ui; +import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; +import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.MoolahException; import seedu.duke.exception.InputTransactionUnknownTypeException; import seedu.duke.exception.StorageWriteErrorException; @@ -63,6 +65,7 @@ public class AddCommand extends Command { private int amount; private String category; private LocalDate date; + private Transaction transactionCreated; public AddCommand() { } @@ -121,6 +124,9 @@ public void setCategory(String category) { public void setDate(LocalDate date) { this.date = date; } + public void setTransactionCreated(Transaction transaction) { + this.transactionCreated = transaction; + } /** * Executes the "add" command. Checks and parses the necessary parameters before adding transaction. @@ -138,40 +144,77 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws addLogger.log(Level.INFO, "Add Command checks the type of the transaction " + "before adding into the transaction class."); assert date != null; + //@@author wcwy - // The expected maximum number of transactions allowed to store is only one million. - if (transactions.size() == MAX_TRANSACTIONS_COUNT) { - addLogger.log(Level.WARNING, "A transaction is attempted to be stored beyond its capacity"); - throw new MaximumTransactionCountException(); - } - - switch (type) { - case Expense.TRANSACTION_NAME: - String expense = transactions.addExpense(description, amount, category, date); - Ui.showTransactionAction(INFO_ADD_EXPENSE.toString(), expense); - addLogger.log(Level.INFO, "New expense transaction has been added " - + "and the UI should display acknowledgment message respectively."); - storage.writeToFile(transactions.getTransactions()); - break; - case Income.TRANSACTION_NAME: - String income = transactions.addIncome(description, amount, category, date); - Ui.showTransactionAction(INFO_ADD_INCOME.toString(), income); - addLogger.log(Level.INFO, "New income transaction has been added " - + "and the UI should display acknowledgment message respectively."); - storage.writeToFile(transactions.getTransactions()); - break; - default: - addLogger.log(Level.WARNING, "InputTransactionUnknownTypeException thrown " - + "when the transaction type is unknown."); - throw new InputTransactionUnknownTypeException(); - } + checkTransactionCapacity(transactions); + String messageBanner = addTransaction(transactions); + long addedMonthExpenseSum = transactions.calculateMonthlyTotalExpense(date); + String budgetLeft = Budget.getBudgetLeft(addedMonthExpenseSum); + + Ui.showTransactionAction(messageBanner, transactionCreated.toString(), budgetLeft); + + //@@author chinhan99 + storage.writeToFile(transactions.getTransactions()); } catch (IOException e) { throw new StorageWriteErrorException(); } - //@@author chinhan99 addLogger.log(Level.INFO, "End of Add command."); } + //@@author wcwy + + /** + * Checks if the number of transactions in the transaction list has reached the capacity. + * This prevents the number of transactions added to the list to be more than the capacity. + * + *

With this, it is possible for the application to compute the maximum and minimum value budget difference + * to prevent integer overflow. + * + * @param transactions The list of transactions in the application. + * @throws MaximumTransactionCountException If the transaction list capacity has been reached. + */ + private static void checkTransactionCapacity(TransactionList transactions) throws MaximumTransactionCountException { + // The expected maximum number of transactions allowed to store is only one million. + if (transactions.size() == MAX_TRANSACTIONS_COUNT) { + addLogger.log(Level.WARNING, "A transaction is attempted to be stored beyond its capacity"); + throw new MaximumTransactionCountException(); + } + } + + /** + * Adds the transaction into the transaction list based on the parameters stored by the AddCommand object. + * + *

Store the transaction object created in transactionCreated and return the message banner based on the + * transaction type created. + * + * @param transactions The list of transactions in the application. + * @returns A string containing the message banner based on the type of transaction created. + * @throws InputTransactionUnknownTypeException If the type of the transactions + */ + private String addTransaction(TransactionList transactions) throws InputTransactionUnknownTypeException { + String messageBanner = ""; + Transaction transaction; + switch (type) { + case Expense.TRANSACTION_NAME: + transaction = transactions.addExpense(description, amount, category, date); + messageBanner = INFO_ADD_EXPENSE.toString(); + addLogger.log(Level.INFO, "New expense transaction has been added " + + "and the UI should display acknowledgment message respectively."); + break; + case Income.TRANSACTION_NAME: + transaction = transactions.addIncome(description, amount, category, date); + messageBanner = INFO_ADD_INCOME.toString(); + addLogger.log(Level.INFO, "New income transaction has been added " + + "and the UI should display acknowledgment message respectively."); + break; + default: + addLogger.log(Level.SEVERE, "The parsed type of transaction stored in addCommand is unknown!"); + throw new InputTransactionUnknownTypeException(); + } + setTransactionCreated(transaction); + return messageBanner; + } + //@@author paullowse /** diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index 81bae1ea2..34a15ae1b 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -3,11 +3,24 @@ /** * Represents all the constant settings of the application. * Developers should update the constant in this file to allow for different limits for the application. + * + *

Note that altering the values in this folder may result in integer overflow in the program in extreme cases. */ public class Constants { - // The amount of transaction is allowed to be in the range or 0 <= x <= 10000000 - public static int MIN_AMOUNT_VALUE = 0; - public static int MAX_AMOUNT_VALUE = 10000000; + /* + WARNING: Editing the values below may result in integer overflow in + 1. TransactionList.calculateMonthlyTotalExpense() + 2. Budget.getBudgetLeft() + */ + // One million transactions is the capacity allowed public static int MAX_TRANSACTIONS_COUNT = 1000000; + + // The amount of one transaction is allowed to be in the range or 0 <= x <= 10000000 + public static int MIN_AMOUNT_VALUE = 0; + public static int MAX_AMOUNT_VALUE = 10000000; + + // The amount of transaction is allowed to be in the range or 0 <= x <= 10000000 + public static int MIN_BUDGET_VALUE = 1; + public static long MAX_BUDGET_VALUE = Long.valueOf(MAX_TRANSACTIONS_COUNT) * Long.valueOf(MAX_AMOUNT_VALUE); } diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 0b73c70fa..563e0318d 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -23,6 +23,7 @@ public enum InfoMessages { INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."), INFO_CURRENT_BUDGET("Monthly budget set as: $"), + INFO_REMAINING_BUDGET("Budget remained for the month of transaction: $"), INFO_TODAY_DATE("Today is "); //@@author chydarren diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index b8517f3f0..b8179d0d1 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -1,5 +1,7 @@ package seedu.duke.data; +import static seedu.duke.common.Constants.*; + /** * Represents the user's budget for the current month. * @@ -11,9 +13,9 @@ public class Budget { private static int budget = 1000; /** - * Retrieves the budget value set for the current month + * Retrieves the budget value set for the current month. * - * @return The budget value set by the user + * @return The budget value set by the user. */ public static int getBudget() { return budget; @@ -22,21 +24,32 @@ public static int getBudget() { /** * Updates the budget value set for the current month * - * @param budget The new value for budget + * @param budget The new value for budget. */ public static void setBudget(int budget) { Budget.budget = budget; } /** - * Return the amount of budget left in + * Return the amount of budget left in the month, as a string. + * + *

If the total amount of expenses is higher than the budget, a negative value in string will be returned. * - * @param transactionList - * @return + * @param totalMonthlyExpense The long value representing the total sum of a monthly expense. + * @return A string value representing the amount of budget left. */ - public static String getBudgetLeft(TransactionList transactionList) { + public static String getBudgetLeft(long totalMonthlyExpense) { + /* + Since the maximum number of transaction is 1000000, maximum amount of expense is 10000000, + and minimum is 1, the lowest possible budget left value is + 1 - (10^6 * 10^7) = -10^15 + 1 > Long.MIN_VALUE (approx -9.22 * 10^18) + Thus, this function is safe from integer overflow UNLESS the values in common.Constants.java is altered. + */ - return ""; - } + assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > 0); + assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > Long.valueOf(MAX_AMOUNT_VALUE)); + assert (MIN_BUDGET_VALUE > 0); + return Long.toString(budget - totalMonthlyExpense); + } } diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b6dc75190..c22963752 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -1,5 +1,6 @@ package seedu.duke.data; +import seedu.duke.common.Constants; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; import seedu.duke.data.transaction.Transaction; @@ -12,6 +13,8 @@ import java.util.HashMap; import java.util.stream.Collectors; +import static seedu.duke.common.Constants.*; + /** * Represents a list of transactions added by the user into the application. * Operations related to modifying the list of transactions are defined under this class. @@ -90,12 +93,12 @@ public String deleteTransaction(int index) { * @param amount Value of the transaction in numerical form. * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". - * @return A string that states the details of the added expense transaction. + * @return The expense object created and added to the list. */ - public String addExpense(String description, int amount, String category, LocalDate date) { + public Expense addExpense(String description, int amount, String category, LocalDate date) { Expense expense = new Expense(description, amount, category, date); transactions.add(expense); - return expense.toString(); + return expense; } /** @@ -105,12 +108,12 @@ public String addExpense(String description, int amount, String category, LocalD * @param amount Value of the transaction in numerical form. * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". - * @return A string that states the details of the added income transaction. + * @return The income object created and added to the transaction list. */ - public String addIncome(String description, int amount, String category, LocalDate date) { + public Income addIncome(String description, int amount, String category, LocalDate date) { Income income = new Income(description, amount, category, date); transactions.add(income); - return income.toString(); + return income; } //@@author chinhan99 @@ -151,7 +154,7 @@ public boolean isTransactionInstance(Object transaction, String classType) throw * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ public boolean isMatchListFilters(Transaction transaction, String type, String category, - LocalDate date) throws InputTransactionUnknownTypeException { + LocalDate date) throws InputTransactionUnknownTypeException { boolean isMatch; try { isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) @@ -248,7 +251,7 @@ public String listCategoricalSavings() { * E.g. If the date is 21 October 2022 to backdate 2 weeks, the range will be 3 October to 16 October 2022. * * @param numberOfWeeks N number of weeks to backdate, must be minimum 1 week. - * @param date A specified date to backdate N weeks from occurring week. + * @param date A specified date to backdate N weeks from occurring week. * @return An array containing the start and end date. */ public static LocalDate[] getWeekRange(LocalDate date, int numberOfWeeks) { @@ -265,7 +268,7 @@ public static LocalDate[] getWeekRange(LocalDate date, int numberOfWeeks) { * E.g. If the date is 21 October 2022 to backdate 2 months, the range will be 1 August to 30 September 2022. * * @param numberOfMonths N number of months to backdate, must be minimum 1 month. - * @param date A specified date to backdate N months from occurring month. + * @param date A specified date to backdate N months from occurring month. * @return An array containing the start and end date. */ public static LocalDate[] getMonthRange(LocalDate date, int numberOfMonths) { @@ -280,7 +283,7 @@ public static LocalDate[] getMonthRange(LocalDate date, int numberOfMonths) { /** * Gets all transactions recorded on a specific year. * - * @param year A specified year. + * @param year A specified year. * @return An array list containing all transactions recorded on a specific year. */ public ArrayList getTransactionsByYear(int year) { @@ -316,8 +319,8 @@ public ArrayList getTransactionsByMonth(int year, int month) { /** * Gets all transactions recorded on the last N weeks or months. * - * @param numberOfType An integer that represents the last N number of weeks or months. - * @param type A string that represents the getting of N "weeks", or "months". + * @param numberOfType An integer that represents the last N number of weeks or months. + * @param type A string that represents the getting of N "weeks", or "months". * @return An array list containing all transactions recorded on the last N number of weeks or months. */ public ArrayList getTransactionsByLastN(int numberOfType, String type) { @@ -350,4 +353,35 @@ public ArrayList getTransactionsByLastN(int numberOfType, String ty public void purgeTransactions() { transactions.clear(); } + + //@@author wcwy + + /** + * Calculates the total expenses spent in the month and year of the provided date, and returns the sum as a long. + * + * @param date A date object in which the monthly total expenses calculated is based on. + * @return A long value indicating the amount of expenses spent in the month. + */ + public long calculateMonthlyTotalExpense(LocalDate date) { + long totalExpense = 0; + int month = date.getMonthValue(); + int year = date.getYear(); + for (Transaction transaction : transactions) { + if (transaction.getDate().getMonthValue() == month && transaction.getDate().getYear() == year) { + /* + Since the maximum number of transaction is 1000000 and maximum amount of expense is 10000000, + the highest possible expense value is 10^6 * 10^7 = 10^15 < Long.MAX_VALUE (approx 9.22 * 10^18) + Therefore, this function is safe from integer overflow UNLESS the max values in + common.Constants.java is altered. + */ + + assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > 0); + assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > + Long.valueOf(MAX_AMOUNT_VALUE)); + + totalExpense += transaction.getAmount(); + } + } + return totalExpense; + } } diff --git a/src/main/java/seedu/duke/data/TransactionList.java~ b/src/main/java/seedu/duke/data/TransactionList.java~ new file mode 100644 index 000000000..a7d7c2999 --- /dev/null +++ b/src/main/java/seedu/duke/data/TransactionList.java~ @@ -0,0 +1,387 @@ +package seedu.duke.data; + +import seedu.duke.common.Constants; +import seedu.duke.data.transaction.Expense; +import seedu.duke.data.transaction.Income; +import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.InputTransactionUnknownTypeException; + +import java.time.LocalDate; +import java.time.Year; +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.stream.Collectors; + +import static seedu.duke.common.Constants.*; + +/** + * Represents a list of transactions added by the user into the application. + * Operations related to modifying the list of transactions are defined under this class. + * These operations include adding, listing, modifying, deleting and purging. + */ +public class TransactionList { + //@@author chydarren + private static final String PREFIX_CATEGORY = "["; + private static final String POSTFIX_CATEGORY = "]"; + private static final String SYMBOL_DOLLAR = "$"; + private static final String WEEKS = "weeks"; + private static final String MONTHS = "months"; + private static final int START = 0; + private static final int END = 1; + private static final String LINE_SEPARATOR = System.lineSeparator(); + + //@@author chinhan99 + private ArrayList transactions; + + public TransactionList(TransactionList transactionList) { + transactions = transactionList.getTransactions(); + } + + public ArrayList getTransactions() { + return transactions; + } + + //@@author wcwy + + /** + * Initialises the variables of the TransactionList class. + */ + public TransactionList() { + this.transactions = new ArrayList<>(); + } + + //@@author brian-vb + + /** + * Gets a specific entry from the transactions list, to be used by other classes. + * + * @param index An index of the transaction that is to be retrieved. + * @return The transaction entry from the transactions list. + */ + public Transaction getEntry(int index) { + return transactions.get(index); + } + + /** + * Gets the number of transactions in the transactions list. + * + * @return An integer value that indicates the number of transactions. + */ + public int size() { + return transactions.size(); + } + + /** + * Deletes a transaction from the transactions list based on the specified index. + * + * @param index An index of the transaction that is to be retrieved. + * @return A string tht states the details of the deleted transaction. + */ + public String deleteTransaction(int index) { + Transaction transaction = transactions.get(index - 1); + transactions.remove(index - 1); + return transaction.toString(); + } + + //@@author wcwy + + /** + * Adds a transaction of class type Expense into the transactions list. + * + * @param description More information regarding the transaction, written without any space. + * @param amount Value of the transaction in numerical form. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @return The expense object created and added to the list. + */ + public Expense addExpense(String description, int amount, String category, LocalDate date) { + Expense expense = new Expense(description, amount, category, date); + transactions.add(expense); + return expense; + } + + /** + * Adds a transaction of class type Income into the transactions list. + * + * @param description More information regarding the transaction, written without any space. + * @param amount Value of the transaction in numerical form. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @return The income object created and added to the transaction list. + */ + public Income addIncome(String description, int amount, String category, LocalDate date) { + Income income = new Income(description, amount, category, date); + transactions.add(income); + return income; + } + + //@@author chinhan99 + + public void addIncomeDuringStorage(String description, int amount, String category, LocalDate date) { + Income income = new Income(description, amount, category, date); + transactions.add(income); + } + + + public void addExpenseDuringStorage(String description, int amount, String category, LocalDate date) { + Expense expense = new Expense(description, amount, category, date); + transactions.add(expense); + } + + //@@author chydarren + + /** + * Checks whether the transaction belongs to the Income or Expense class type. + * + * @param transaction The transaction record from the transactions list. + * @param classType The transaction class type that is either Income or Expense. + * @return A boolean value indicating whether transaction record belongs to the given class type. + * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + */ + public boolean isTransactionInstance(Object transaction, String classType) throws ClassNotFoundException { + return Class.forName(classType).isInstance(transaction); + } + + /** + * Checks whether a transaction fulfills the given filter criteria. + * + * @param transaction The transaction record from the transactions list. + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @return A string containing the formatted transaction list. + * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + */ + public boolean isMatchListFilters(Transaction transaction, String type, String category, + LocalDate date) throws InputTransactionUnknownTypeException { + boolean isMatch; + try { + isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) + && (category.isEmpty() || transaction.getCategory().equals(category)) + && (date == null || transaction.getDate().equals(date))); + } catch (ClassNotFoundException e) { + throw new InputTransactionUnknownTypeException(); + } + return isMatch; + } + + /** + * Lists all or some transactions based on selection. + * + * @param type The type of transaction. + * @param category A category for the transaction. + * @param date Date of the transaction with format in "yyyyMMdd". + * @return A string containing the formatted transaction list. + * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + */ + public String listTransactions(String type, String category, LocalDate date) + throws InputTransactionUnknownTypeException { + String transactionsList = ""; + // Loops each transaction from the transactions list + for (Transaction transaction : transactions) { + if (isMatchListFilters(transaction, type, category, date)) { + transactionsList += transaction.toString() + LINE_SEPARATOR; + } + } + return transactionsList; + } + + /** + * Finds specific transaction(s) based on any keywords inputted by the user. + * + * @param keywords Any partial or full keyword(s) that matches the details of the transaction. + * @return A string containing the formatted transaction list. + */ + public String findTransactions(String keywords) { + String transactionsList = ""; + // Loops each transaction from the transactions list + for (Transaction transaction : transactions) { + // Includes only transactions that contain the keywords used in the search expression + if (transaction.toString().contains(keywords)) { + transactionsList += transaction.toString() + LINE_SEPARATOR; + } + } + return transactionsList; + } + + /** + * Reads the transactions list and adds each amount to the categories in categorical savings hashmap. + * + * @param categoricalSavings A hashmap containing all category-amount pair for total savings. + * @return A hashmap containing all category-amount pair for total savings. + */ + public HashMap processCategoricalSavings(HashMap categoricalSavings) { + for (Transaction transaction : transactions) { + String category = transaction.getCategory(); + int amount = transaction.getAmount(); + // Creates a new category with starter amount if category not exists in hashmap + if (!categoricalSavings.containsKey(category)) { + categoricalSavings.put(category, amount); + continue; + } + categoricalSavings.put(category, categoricalSavings.get(category) + amount); + } + + return categoricalSavings; + } + + /** + * Calculates and stores total savings for each transaction category into a hashmap. + * + * @return A hashmap containing all category-amount pair for total savings. + */ + public String listCategoricalSavings() { + String categoricalSavingsList = ""; + HashMap categoricalSavings = new HashMap<>(); + // Adds each amount from transactions list to the categories in categorical savings hashmap + categoricalSavings = processCategoricalSavings(categoricalSavings); + + // Formats every entry in the hashmap into a categorical savings list + for (HashMap.Entry entry : categoricalSavings.entrySet()) { + categoricalSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getKey(), + POSTFIX_CATEGORY, SYMBOL_DOLLAR, entry.getValue(), LINE_SEPARATOR); + } + + return categoricalSavingsList; + } + + /** + * Gets the range of dates for the last N number of weeks from occurring week. + * E.g. If the date is 21 October 2022 to backdate 2 weeks, the range will be 3 October to 16 October 2022. + * + * @param numberOfWeeks N number of weeks to backdate, must be minimum 1 week. + * @param date A specified date to backdate N weeks from occurring week. + * @return An array containing the start and end date. + */ + public static LocalDate[] getWeekRange(LocalDate date, int numberOfWeeks) { + // Solution below adapted from https://stackoverflow.com/a/51356522 + int dayOfWeek = date.getDayOfWeek().getValue(); + LocalDate from = date.minusDays((dayOfWeek - 1) + (numberOfWeeks * 7)); + LocalDate to = date.minusDays(dayOfWeek); + + return new LocalDate[]{from, to}; + } + + /** + * Gets the range of dates for the last N number of months from occurring month. + * E.g. If the date is 21 October 2022 to backdate 2 months, the range will be 1 August to 30 September 2022. + * + * @param numberOfMonths N number of months to backdate, must be minimum 1 month. + * @param date A specified date to backdate N months from occurring month. + * @return An array containing the start and end date. + */ + public static LocalDate[] getMonthRange(LocalDate date, int numberOfMonths) { + // Solution below adapted from https://stackoverflow.com/a/51356522 + LocalDate lastMonth = date.minusMonths(1); + LocalDate from = date.minusMonths(numberOfMonths).withDayOfMonth(1); + LocalDate to = lastMonth.withDayOfMonth(lastMonth.getMonth().maxLength()); + + return new LocalDate[]{from, to}; + } + + /** + * Gets all transactions recorded on a specific year. + * + * @param year A specified year. + * @return An array list containing all transactions recorded on a specific year. + */ + public ArrayList getTransactionsByYear(int year) { + //@@author chydarren-reused + //Reused from https://stackoverflow.com/a/69440139 + // with minor modifications + ArrayList transactionsByYear = (ArrayList) transactions.stream() + .filter(transaction -> Year.from(transaction.getDate()).equals(Year.of(year))) + .collect(Collectors.toList()); + //@@author + + return transactionsByYear; + } + + /** + * Gets all transactions recorded on a specific month. + * + * @param year A specified year. + * @param month A specified month within the year. + * @return An array list containing all transactions recorded on a specific month. + */ + public ArrayList getTransactionsByMonth(int year, int month) { + //@@author chydarren-reused + //Reused from https://stackoverflow.com/a/69440139 + ArrayList transactionsByMonth = (ArrayList) transactions.stream() + .filter(transaction -> YearMonth.from(transaction.getDate()).equals(YearMonth.of(year, month))) + .collect(Collectors.toList()); + //@@author + + return transactionsByMonth; + } + + /** + * Gets all transactions recorded on the last N weeks or months. + * + * @param numberOfType An integer that represents the last N number of weeks or months. + * @param type A string that represents the getting of N "weeks", or "months". + * @return An array list containing all transactions recorded on the last N number of weeks or months. + */ + public ArrayList getTransactionsByLastN(int numberOfType, String type) { + ArrayList transactionsByLastN = new ArrayList<>(); + LocalDate[] dateRange = {}; + + if (type.equals(WEEKS)) { + dateRange = getWeekRange(LocalDate.now(), numberOfType); + } else { + assert type.equals(MONTHS); + dateRange = getMonthRange(LocalDate.now(), numberOfType); + } + + for (Transaction transaction : transactions) { + LocalDate transactionDate = transaction.getDate(); + // Transaction is added into the filtered array list if it falls within the expected date range + if (!(transactionDate.isBefore(dateRange[START]) || transactionDate.isAfter(dateRange[END]))) { + transactionsByLastN.add(transaction); + } + } + + return transactionsByLastN; + } + + //@@author brian-vb + + /** + * Purges all records in the transactions list. + */ + public void purgeTransactions() { + transactions.clear(); + } + + //@@author wcwy + + /** + * Calculates the total expenses spent in the month and year of the provided date, and returns the sum as a long. + * + * @param date A date object in which the monthly total expenses calculated is based on. + * @return A long value indicating the amount of expenses spent in the month. + */ + public long calculateMonthlyTotalExpense(LocalDate date) { + long totalExpense = 0; + int month = date.getMonthValue(); + int year = date.getYear(); + for (Transaction transaction : transactions) { + if (transaction.getDate().getMonthValue() == month && transaction.getDate().getYear() == year) { + /* + Since the maximum number of transaction is 1000000 and maximum amount of expense is 10000000, + the highest possible expense value is 10^6 * 10^7 = 10^15 < Long.MAX_VALUE (approx 9.22 * 10^18) + Therefore, this function is safe from integer overflow UNLESS the max values in + common.Constants.java is altered. + */ + + assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > 0); + assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > + Long.valueOf(MAX_AMOUNT_VALUE)); + + totalExpense += transaction.getAmount(); + } + } + return totalExpense; + } +} From 3c100536e8e8bdd9526a9356ebc58ee05fd88104 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 22 Oct 2022 00:13:43 +0800 Subject: [PATCH 190/416] Update AddCommand.java --- src/main/java/seedu/duke/command/AddCommand.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index b97c0ff2c..0915835c2 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -14,7 +14,11 @@ import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.duke.command.CommandTag.*; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; From 1206240cbbe593773341c28dba61a458ed94a1e0 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 22 Oct 2022 00:14:07 +0800 Subject: [PATCH 191/416] Update duke.txt --- text-ui-test/data/duke.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text-ui-test/data/duke.txt b/text-ui-test/data/duke.txt index a28ec27b5..8b1378917 100644 --- a/text-ui-test/data/duke.txt +++ b/text-ui-test/data/duke.txt @@ -1 +1 @@ -expense | transport | 1 | 2022-10-02 | i/i/i/i/ + From f026adac343de2f28fe5e803d62af5eb94c61172 Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 22 Oct 2022 00:20:37 +0800 Subject: [PATCH 192/416] Fix coding style violation to abide with Java coding convention --- src/main/java/seedu/duke/Ui.java | 8 +- .../java/seedu/duke/command/AddCommand.java | 11 +- .../java/seedu/duke/common/ErrorMessages.java | 8 +- src/main/java/seedu/duke/data/Budget.java | 8 +- .../java/seedu/duke/data/TransactionList.java | 7 +- .../seedu/duke/data/TransactionList.java~ | 387 ------------------ .../seedu/duke/parser/ParameterParser.java | 2 +- 7 files changed, 27 insertions(+), 404 deletions(-) delete mode 100644 src/main/java/seedu/duke/data/TransactionList.java~ diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 2dde12836..787426dc5 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -7,7 +7,13 @@ import java.util.Scanner; import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; -import static seedu.duke.common.InfoMessages.*; +import static seedu.duke.common.InfoMessages.INFO_DIVIDER; +import static seedu.duke.common.InfoMessages.INFO_GREET; +import static seedu.duke.common.InfoMessages.INFO_HELP_GREET; +import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; +import static seedu.duke.common.InfoMessages.INFO_CURRENT_BUDGET; +import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; +import static seedu.duke.common.InfoMessages.INFO_TODAY_DATE; public class Ui { //@@author chydarren diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 42e435db1..9bbc7ae07 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -90,11 +90,11 @@ public AddCommand(String type, String description, int amount, String category, public String[] getMandatoryTags() { String[] mandatoryTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_AMOUNT, - COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_TRANSACTION_DESCRIPTION + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_AMOUNT, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_TRANSACTION_DESCRIPTION }; return mandatoryTags; } @@ -124,6 +124,7 @@ public void setCategory(String category) { public void setDate(LocalDate date) { this.date = date; } + public void setTransactionCreated(Transaction transaction) { this.transactionCreated = transaction; } diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 89691e522..39dc8293a 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -20,11 +20,11 @@ public enum ErrorMessages { ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), - ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. " + - "To preserve data, please STOP the program and edit your data file correctly."), + ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. " + + "To preserve data, please STOP the program and edit your data file correctly."), ERROR_STORAGE_WRITE("Unable to write to Duke.txt. Please save your current Duke.txt file and restart Moolah"), - ERROR_MAXIMUM_TRANSACTION_COUNT_REACHED("Unable to add transaction. " + - "The maximum allowed transaction size (1000000) has been reached."); + ERROR_MAXIMUM_TRANSACTION_COUNT_REACHED("Unable to add transaction. " + + "The maximum allowed transaction size (1000000) has been reached."); //@@author chydarren public final String message; diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index b8179d0d1..1b9637a04 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -1,6 +1,8 @@ package seedu.duke.data; -import static seedu.duke.common.Constants.*; +import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; +import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; +import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; /** * Represents the user's budget for the current month. @@ -22,7 +24,7 @@ public static int getBudget() { } /** - * Updates the budget value set for the current month + * Updates the budget value set for the current month. * * @param budget The new value for budget. */ @@ -31,7 +33,7 @@ public static void setBudget(int budget) { } /** - * Return the amount of budget left in the month, as a string. + * Returns the amount of budget left in the month, as a string. * *

If the total amount of expenses is higher than the budget, a negative value in string will be returned. * diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index c22963752..b721f7a3f 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -13,7 +13,8 @@ import java.util.HashMap; import java.util.stream.Collectors; -import static seedu.duke.common.Constants.*; +import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; +import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; /** * Represents a list of transactions added by the user into the application. @@ -376,8 +377,8 @@ public long calculateMonthlyTotalExpense(LocalDate date) { */ assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > 0); - assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > - Long.valueOf(MAX_AMOUNT_VALUE)); + assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) + > Long.valueOf(MAX_AMOUNT_VALUE)); totalExpense += transaction.getAmount(); } diff --git a/src/main/java/seedu/duke/data/TransactionList.java~ b/src/main/java/seedu/duke/data/TransactionList.java~ deleted file mode 100644 index a7d7c2999..000000000 --- a/src/main/java/seedu/duke/data/TransactionList.java~ +++ /dev/null @@ -1,387 +0,0 @@ -package seedu.duke.data; - -import seedu.duke.common.Constants; -import seedu.duke.data.transaction.Expense; -import seedu.duke.data.transaction.Income; -import seedu.duke.data.transaction.Transaction; -import seedu.duke.exception.InputTransactionUnknownTypeException; - -import java.time.LocalDate; -import java.time.Year; -import java.time.YearMonth; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.stream.Collectors; - -import static seedu.duke.common.Constants.*; - -/** - * Represents a list of transactions added by the user into the application. - * Operations related to modifying the list of transactions are defined under this class. - * These operations include adding, listing, modifying, deleting and purging. - */ -public class TransactionList { - //@@author chydarren - private static final String PREFIX_CATEGORY = "["; - private static final String POSTFIX_CATEGORY = "]"; - private static final String SYMBOL_DOLLAR = "$"; - private static final String WEEKS = "weeks"; - private static final String MONTHS = "months"; - private static final int START = 0; - private static final int END = 1; - private static final String LINE_SEPARATOR = System.lineSeparator(); - - //@@author chinhan99 - private ArrayList transactions; - - public TransactionList(TransactionList transactionList) { - transactions = transactionList.getTransactions(); - } - - public ArrayList getTransactions() { - return transactions; - } - - //@@author wcwy - - /** - * Initialises the variables of the TransactionList class. - */ - public TransactionList() { - this.transactions = new ArrayList<>(); - } - - //@@author brian-vb - - /** - * Gets a specific entry from the transactions list, to be used by other classes. - * - * @param index An index of the transaction that is to be retrieved. - * @return The transaction entry from the transactions list. - */ - public Transaction getEntry(int index) { - return transactions.get(index); - } - - /** - * Gets the number of transactions in the transactions list. - * - * @return An integer value that indicates the number of transactions. - */ - public int size() { - return transactions.size(); - } - - /** - * Deletes a transaction from the transactions list based on the specified index. - * - * @param index An index of the transaction that is to be retrieved. - * @return A string tht states the details of the deleted transaction. - */ - public String deleteTransaction(int index) { - Transaction transaction = transactions.get(index - 1); - transactions.remove(index - 1); - return transaction.toString(); - } - - //@@author wcwy - - /** - * Adds a transaction of class type Expense into the transactions list. - * - * @param description More information regarding the transaction, written without any space. - * @param amount Value of the transaction in numerical form. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". - * @return The expense object created and added to the list. - */ - public Expense addExpense(String description, int amount, String category, LocalDate date) { - Expense expense = new Expense(description, amount, category, date); - transactions.add(expense); - return expense; - } - - /** - * Adds a transaction of class type Income into the transactions list. - * - * @param description More information regarding the transaction, written without any space. - * @param amount Value of the transaction in numerical form. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". - * @return The income object created and added to the transaction list. - */ - public Income addIncome(String description, int amount, String category, LocalDate date) { - Income income = new Income(description, amount, category, date); - transactions.add(income); - return income; - } - - //@@author chinhan99 - - public void addIncomeDuringStorage(String description, int amount, String category, LocalDate date) { - Income income = new Income(description, amount, category, date); - transactions.add(income); - } - - - public void addExpenseDuringStorage(String description, int amount, String category, LocalDate date) { - Expense expense = new Expense(description, amount, category, date); - transactions.add(expense); - } - - //@@author chydarren - - /** - * Checks whether the transaction belongs to the Income or Expense class type. - * - * @param transaction The transaction record from the transactions list. - * @param classType The transaction class type that is either Income or Expense. - * @return A boolean value indicating whether transaction record belongs to the given class type. - * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. - */ - public boolean isTransactionInstance(Object transaction, String classType) throws ClassNotFoundException { - return Class.forName(classType).isInstance(transaction); - } - - /** - * Checks whether a transaction fulfills the given filter criteria. - * - * @param transaction The transaction record from the transactions list. - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". - * @return A string containing the formatted transaction list. - * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. - */ - public boolean isMatchListFilters(Transaction transaction, String type, String category, - LocalDate date) throws InputTransactionUnknownTypeException { - boolean isMatch; - try { - isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) - && (category.isEmpty() || transaction.getCategory().equals(category)) - && (date == null || transaction.getDate().equals(date))); - } catch (ClassNotFoundException e) { - throw new InputTransactionUnknownTypeException(); - } - return isMatch; - } - - /** - * Lists all or some transactions based on selection. - * - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". - * @return A string containing the formatted transaction list. - * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. - */ - public String listTransactions(String type, String category, LocalDate date) - throws InputTransactionUnknownTypeException { - String transactionsList = ""; - // Loops each transaction from the transactions list - for (Transaction transaction : transactions) { - if (isMatchListFilters(transaction, type, category, date)) { - transactionsList += transaction.toString() + LINE_SEPARATOR; - } - } - return transactionsList; - } - - /** - * Finds specific transaction(s) based on any keywords inputted by the user. - * - * @param keywords Any partial or full keyword(s) that matches the details of the transaction. - * @return A string containing the formatted transaction list. - */ - public String findTransactions(String keywords) { - String transactionsList = ""; - // Loops each transaction from the transactions list - for (Transaction transaction : transactions) { - // Includes only transactions that contain the keywords used in the search expression - if (transaction.toString().contains(keywords)) { - transactionsList += transaction.toString() + LINE_SEPARATOR; - } - } - return transactionsList; - } - - /** - * Reads the transactions list and adds each amount to the categories in categorical savings hashmap. - * - * @param categoricalSavings A hashmap containing all category-amount pair for total savings. - * @return A hashmap containing all category-amount pair for total savings. - */ - public HashMap processCategoricalSavings(HashMap categoricalSavings) { - for (Transaction transaction : transactions) { - String category = transaction.getCategory(); - int amount = transaction.getAmount(); - // Creates a new category with starter amount if category not exists in hashmap - if (!categoricalSavings.containsKey(category)) { - categoricalSavings.put(category, amount); - continue; - } - categoricalSavings.put(category, categoricalSavings.get(category) + amount); - } - - return categoricalSavings; - } - - /** - * Calculates and stores total savings for each transaction category into a hashmap. - * - * @return A hashmap containing all category-amount pair for total savings. - */ - public String listCategoricalSavings() { - String categoricalSavingsList = ""; - HashMap categoricalSavings = new HashMap<>(); - // Adds each amount from transactions list to the categories in categorical savings hashmap - categoricalSavings = processCategoricalSavings(categoricalSavings); - - // Formats every entry in the hashmap into a categorical savings list - for (HashMap.Entry entry : categoricalSavings.entrySet()) { - categoricalSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getKey(), - POSTFIX_CATEGORY, SYMBOL_DOLLAR, entry.getValue(), LINE_SEPARATOR); - } - - return categoricalSavingsList; - } - - /** - * Gets the range of dates for the last N number of weeks from occurring week. - * E.g. If the date is 21 October 2022 to backdate 2 weeks, the range will be 3 October to 16 October 2022. - * - * @param numberOfWeeks N number of weeks to backdate, must be minimum 1 week. - * @param date A specified date to backdate N weeks from occurring week. - * @return An array containing the start and end date. - */ - public static LocalDate[] getWeekRange(LocalDate date, int numberOfWeeks) { - // Solution below adapted from https://stackoverflow.com/a/51356522 - int dayOfWeek = date.getDayOfWeek().getValue(); - LocalDate from = date.minusDays((dayOfWeek - 1) + (numberOfWeeks * 7)); - LocalDate to = date.minusDays(dayOfWeek); - - return new LocalDate[]{from, to}; - } - - /** - * Gets the range of dates for the last N number of months from occurring month. - * E.g. If the date is 21 October 2022 to backdate 2 months, the range will be 1 August to 30 September 2022. - * - * @param numberOfMonths N number of months to backdate, must be minimum 1 month. - * @param date A specified date to backdate N months from occurring month. - * @return An array containing the start and end date. - */ - public static LocalDate[] getMonthRange(LocalDate date, int numberOfMonths) { - // Solution below adapted from https://stackoverflow.com/a/51356522 - LocalDate lastMonth = date.minusMonths(1); - LocalDate from = date.minusMonths(numberOfMonths).withDayOfMonth(1); - LocalDate to = lastMonth.withDayOfMonth(lastMonth.getMonth().maxLength()); - - return new LocalDate[]{from, to}; - } - - /** - * Gets all transactions recorded on a specific year. - * - * @param year A specified year. - * @return An array list containing all transactions recorded on a specific year. - */ - public ArrayList getTransactionsByYear(int year) { - //@@author chydarren-reused - //Reused from https://stackoverflow.com/a/69440139 - // with minor modifications - ArrayList transactionsByYear = (ArrayList) transactions.stream() - .filter(transaction -> Year.from(transaction.getDate()).equals(Year.of(year))) - .collect(Collectors.toList()); - //@@author - - return transactionsByYear; - } - - /** - * Gets all transactions recorded on a specific month. - * - * @param year A specified year. - * @param month A specified month within the year. - * @return An array list containing all transactions recorded on a specific month. - */ - public ArrayList getTransactionsByMonth(int year, int month) { - //@@author chydarren-reused - //Reused from https://stackoverflow.com/a/69440139 - ArrayList transactionsByMonth = (ArrayList) transactions.stream() - .filter(transaction -> YearMonth.from(transaction.getDate()).equals(YearMonth.of(year, month))) - .collect(Collectors.toList()); - //@@author - - return transactionsByMonth; - } - - /** - * Gets all transactions recorded on the last N weeks or months. - * - * @param numberOfType An integer that represents the last N number of weeks or months. - * @param type A string that represents the getting of N "weeks", or "months". - * @return An array list containing all transactions recorded on the last N number of weeks or months. - */ - public ArrayList getTransactionsByLastN(int numberOfType, String type) { - ArrayList transactionsByLastN = new ArrayList<>(); - LocalDate[] dateRange = {}; - - if (type.equals(WEEKS)) { - dateRange = getWeekRange(LocalDate.now(), numberOfType); - } else { - assert type.equals(MONTHS); - dateRange = getMonthRange(LocalDate.now(), numberOfType); - } - - for (Transaction transaction : transactions) { - LocalDate transactionDate = transaction.getDate(); - // Transaction is added into the filtered array list if it falls within the expected date range - if (!(transactionDate.isBefore(dateRange[START]) || transactionDate.isAfter(dateRange[END]))) { - transactionsByLastN.add(transaction); - } - } - - return transactionsByLastN; - } - - //@@author brian-vb - - /** - * Purges all records in the transactions list. - */ - public void purgeTransactions() { - transactions.clear(); - } - - //@@author wcwy - - /** - * Calculates the total expenses spent in the month and year of the provided date, and returns the sum as a long. - * - * @param date A date object in which the monthly total expenses calculated is based on. - * @return A long value indicating the amount of expenses spent in the month. - */ - public long calculateMonthlyTotalExpense(LocalDate date) { - long totalExpense = 0; - int month = date.getMonthValue(); - int year = date.getYear(); - for (Transaction transaction : transactions) { - if (transaction.getDate().getMonthValue() == month && transaction.getDate().getYear() == year) { - /* - Since the maximum number of transaction is 1000000 and maximum amount of expense is 10000000, - the highest possible expense value is 10^6 * 10^7 = 10^15 < Long.MAX_VALUE (approx 9.22 * 10^18) - Therefore, this function is safe from integer overflow UNLESS the max values in - common.Constants.java is altered. - */ - - assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > 0); - assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > - Long.valueOf(MAX_AMOUNT_VALUE)); - - totalExpense += transaction.getAmount(); - } - } - return totalExpense; - } -} diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 50e66a9c1..4c84a00fc 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -373,7 +373,7 @@ public static String parseCategoryTag(String parameter) throws InputTransactionI * * @param parameter The user input after the user tag. * @return The amount integer if no exceptions are thrown. - * @throws InputTransactionInvalidAmountException If the transaction amount provided is not a valid accepted integer. + * @throws InputTransactionInvalidAmountException If the transaction amount given is not a valid accepted integer. */ private static int parseAmountTag(String parameter) throws InputTransactionInvalidAmountException { Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); From e55749add6b367e2aba03786cad2f42dc0406ef8 Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 22 Oct 2022 00:20:59 +0800 Subject: [PATCH 193/416] test --- text-ui-test/EXPECTED.TXT | 28 +++++++++++----------------- text-ui-test/data/duke.txt | 1 - 2 files changed, 11 insertions(+), 18 deletions(-) delete mode 100644 text-ui-test/data/duke.txt diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 861683a0b..0bc2770bf 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -1,5 +1,8 @@ ____________________________________________________________ -* Existing file detected * +* Created new directory * +____________________________________________________________ +____________________________________________________________ +* Created new file for use * ____________________________________________________________ ____________________________________________________________ * duke.txt loaded successfully! * @@ -152,14 +155,10 @@ ____________________________________________________________ Duplicate tag(s) detected, please check your input! ____________________________________________________________ ____________________________________________________________ -Here are the total savings for each category: -[transport] $1 - +There are no statistics available yet for the given statistics type. ____________________________________________________________ ____________________________________________________________ -Here are your transaction records: -[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ - +There are no transaction records found. ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: @@ -205,7 +204,6 @@ Type of transaction given is invalid, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ [-][food] $20 at Sep 13 2022 | Description: NIL [-][transport] $1 at Oct 02 2022 | Description: bus_fare @@ -233,7 +231,7 @@ Here are your transaction records: ____________________________________________________________ ____________________________________________________________ Here are the total savings for each category: -[transport] $2 +[transport] $1 [salary] $2000 [food] $20 @@ -251,7 +249,6 @@ There are no transaction records that match your search expression. ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ [-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare @@ -290,7 +287,6 @@ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ [-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare @@ -302,11 +298,10 @@ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: -[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ +[-][food] $20 at Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary [-][transport] $1 at Oct 02 2022 | Description: bus_fare [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss @@ -319,17 +314,16 @@ ____________________________________________________________ Invalid index, please ensure your index is correct! ____________________________________________________________ ____________________________________________________________ -I have deleted the following transaction: -[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +Invalid index, please ensure your index is correct! ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: -[-][transport] $1 at Oct 02 2022 | Description: bus_fare +[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][food] $20 at Sep 13 2022 | Description: NIL [+][salary] $2000 at Sep 30 2022 | Description: jan_salary +[-][transport] $1 at Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ diff --git a/text-ui-test/data/duke.txt b/text-ui-test/data/duke.txt deleted file mode 100644 index 8b1378917..000000000 --- a/text-ui-test/data/duke.txt +++ /dev/null @@ -1 +0,0 @@ - From 94cb70bafd19dcca06002d441b47771885a540af Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 22 Oct 2022 00:34:09 +0800 Subject: [PATCH 194/416] Update test cases with budget message --- src/main/java/seedu/duke/Ui.java | 2 +- src/main/java/seedu/duke/data/TransactionList.java | 1 - text-ui-test/EXPECTED.TXT | 7 +++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 787426dc5..cfb00b14b 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -112,7 +112,7 @@ public static void showList(String list, String message) { * @param budgetInfo A message that contains the monthly budget information. */ public static void showTransactionAction(String message, String transactionDetails, String budgetInfo) { - printMessages(message, transactionDetails, INFO_REMAINING_BUDGET.toString(), budgetInfo); + printMessages(message, transactionDetails, INFO_REMAINING_BUDGET + budgetInfo); } // A temporary overload method for backward-compatibility for delete command diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b721f7a3f..1de11f9ba 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -1,6 +1,5 @@ package seedu.duke.data; -import seedu.duke.common.Constants; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; import seedu.duke.data.transaction.Transaction; diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 0bc2770bf..93f5093a1 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -9,6 +9,8 @@ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ Hello! I'm Moo and I will help you to manage your finances. +Monthly budget set as: $1000 +Today is Oct 22 2022 Enter if you need the list of commands. ____________________________________________________________ ____________________________________________________________ @@ -163,14 +165,17 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][food] $20 at Sep 13 2022 | Description: NIL +Budget remained for the month of transaction: $980 ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary +Budget remained for the month of transaction: $-1020 ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare +Budget remained for the month of transaction: $999 ____________________________________________________________ ____________________________________________________________ Parameter behind tag(s) is found to be empty, please check your input! @@ -260,6 +265,7 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +Budget remained for the month of transaction: $-9999001 ____________________________________________________________ ____________________________________________________________ Type of transaction given is invalid, please check your input! @@ -374,6 +380,7 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ +Budget remained for the month of transaction: $999 ____________________________________________________________ ____________________________________________________________ Not supported tag(s) detected, please check your input! From cebd5cb38c5f8e33d7366e854f22ee5f9bb99fdd Mon Sep 17 00:00:00 2001 From: wcwy Date: Sat, 22 Oct 2022 00:56:41 +0800 Subject: [PATCH 195/416] Remove showing today's date feature due to its issue in CLI testing --- src/main/java/seedu/duke/Ui.java | 2 +- text-ui-test/EXPECTED.TXT | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index cfb00b14b..6c354caee 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -78,7 +78,7 @@ public static void showInfoMessage(String message) { */ public static void showGreeting() { printMessages(INFO_GREET.toString(), INFO_CURRENT_BUDGET.toString() + Budget.getBudget(), - INFO_TODAY_DATE + showDateOfTheDay(), INFO_HELP_PROMPT.toString() + INFO_HELP_PROMPT.toString() ); } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 93f5093a1..89e2b921d 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -10,7 +10,6 @@ ____________________________________________________________ ____________________________________________________________ Hello! I'm Moo and I will help you to manage your finances. Monthly budget set as: $1000 -Today is Oct 22 2022 Enter if you need the list of commands. ____________________________________________________________ ____________________________________________________________ From c984e6aaf648dd51c7aef3d6a7008d3fdfebf51e Mon Sep 17 00:00:00 2001 From: Paul Date: Sat, 22 Oct 2022 17:19:58 +0800 Subject: [PATCH 196/416] Add Stats Insights, and lastNPeriod --- src/main/java/seedu/duke/Ui.java | 14 ++- src/main/java/seedu/duke/command/Command.java | 7 +- .../java/seedu/duke/command/CommandTag.java | 2 + .../java/seedu/duke/command/StatsCommand.java | 86 +++++++++++++++---- .../java/seedu/duke/common/ErrorMessages.java | 2 + .../java/seedu/duke/common/InfoMessages.java | 7 +- .../java/seedu/duke/data/TransactionList.java | 53 +++++++++++- .../StatsInvalidNumberException.java | 15 ++++ .../StatsInvalidPeriodException.java | 15 ++++ .../seedu/duke/parser/ParameterParser.java | 66 ++++++++++++-- 10 files changed, 233 insertions(+), 34 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/StatsInvalidNumberException.java create mode 100644 src/main/java/seedu/duke/exception/StatsInvalidPeriodException.java diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 6c354caee..0bf0f0df4 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -7,13 +7,12 @@ import java.util.Scanner; import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; +import static seedu.duke.common.InfoMessages.INFO_CURRENT_BUDGET; import static seedu.duke.common.InfoMessages.INFO_DIVIDER; import static seedu.duke.common.InfoMessages.INFO_GREET; import static seedu.duke.common.InfoMessages.INFO_HELP_GREET; import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; -import static seedu.duke.common.InfoMessages.INFO_CURRENT_BUDGET; import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; -import static seedu.duke.common.InfoMessages.INFO_TODAY_DATE; public class Ui { //@@author chydarren @@ -103,6 +102,17 @@ public static void showList(String list, String message) { printMessages(message, list); } + /** + * Prepares the stats list messages to be displayed to the user. + * + * @param list A string containing the formatted transaction list. + * @param message A message that complements with the transactions list. + */ + public static void showStatsList(String list, String message, String incomeMessage, + String expenseMessage, String savingsMessage) { + printMessages(message, list, incomeMessage, expenseMessage, savingsMessage); + } + /** * Prepares the messages to be displayed to the user when add or delete has been performed on * the transaction list. diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index ba559a73c..2d6eafd98 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -83,7 +83,6 @@ public void setIsDetailedOption(boolean isDetailed) { } public void setStatsType(String statsType) { - } public void setStatsMonth(int month) { @@ -92,5 +91,11 @@ public void setStatsMonth(int month) { public void setStatsYear(int year) { } + public void setStatsNumber(int number) { + } + + public void setStatsPeriod(String period) { + } + } diff --git a/src/main/java/seedu/duke/command/CommandTag.java b/src/main/java/seedu/duke/command/CommandTag.java index 136f6e778..08109f494 100644 --- a/src/main/java/seedu/duke/command/CommandTag.java +++ b/src/main/java/seedu/duke/command/CommandTag.java @@ -12,4 +12,6 @@ public class CommandTag { public static final String COMMAND_TAG_STATISTICS_TYPE = "s/"; public static final String COMMAND_TAG_STATS_MONTH = "m/"; public static final String COMMAND_TAG_STATS_YEAR = "y/"; + public static final String COMMAND_TAG_STATS_NUMBER = "n/"; + public static final String COMMAND_TAG_STATS_PERIOD = "p/"; } \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index cb6fc906b..e35247fe3 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -6,7 +6,6 @@ import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.ListStatsInvalidStatsTypeException; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.StatsInvalidYearException; import java.util.ArrayList; import java.util.logging.Level; @@ -14,11 +13,18 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_PERIOD; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_YEAR; import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; +import static seedu.duke.common.InfoMessages.INFO_STATS_EXPENSES; +import static seedu.duke.common.InfoMessages.INFO_STATS_INCOME; +import static seedu.duke.common.InfoMessages.INFO_STATS_SAVINGS; +import static seedu.duke.common.InfoMessages.INFO_STATS_SUMMARY_HEADER; import static seedu.duke.common.InfoMessages.INFO_STATS_TIME; + /** * Represents a get command object that will execute the operations for Get command. */ @@ -48,6 +54,8 @@ public class StatsCommand extends Command { private String statsType; private int month = -1; private int year = -1; + private String period = null; + private int number = -1; //@@author paullowse public StatsCommand() { @@ -68,7 +76,9 @@ public String[] getMandatoryTags() { public String[] getOptionalTags() { String[] optionalTags = new String[]{ COMMAND_TAG_STATS_MONTH, - COMMAND_TAG_STATS_YEAR + COMMAND_TAG_STATS_YEAR, + COMMAND_TAG_STATS_NUMBER, + COMMAND_TAG_STATS_PERIOD, }; return optionalTags; } @@ -89,6 +99,16 @@ public void setStatsYear(int year) { this.year = year; } + @Override + public void setStatsNumber(int number) { + this.number = number; + } + + @Override + public void setStatsPeriod(String period) { + this.period = period; + } + public int getStatsYear() { return year; } @@ -107,7 +127,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws statsLogger.setLevel(Level.SEVERE); statsLogger.log(Level.INFO, "Entering execution of the Stats command."); - listStatsByStatsType(statsType, transactions, month, year); + listStatsByStatsType(statsType, transactions, month, year, period, number); } /** @@ -115,10 +135,11 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * * @param statsType The type of statistics that is needed. * @param transactions An instance of the TransactionList class. - * @throws ListStatsInvalidStatsTypeException If the type of statistics is not recognised. + * @throws MoolahException If the type of statistics is not recognised. */ - private static void listStatsByStatsType(String statsType, TransactionList transactions, int month, int year) - throws ListStatsInvalidStatsTypeException, StatsInvalidYearException { + private static void listStatsByStatsType(String statsType, TransactionList transactions, int month, + int year, String period, int number) + throws MoolahException { switch (statsType) { case "categories": statsLogger.log(Level.INFO, "Stats type has been detected for categorical savings."); @@ -127,14 +148,18 @@ private static void listStatsByStatsType(String statsType, TransactionList trans break; case "time": statsLogger.log(Level.INFO, "Stats type has been detected for monthly savings."); - if (year == -1) { - statsLogger.log(Level.WARNING, "An exception has been caught due to a missing year tag"); - throw new StatsInvalidYearException(); + if (year == -1 && month == -1 && period != null && number != -1) { + statsLogger.log(Level.INFO, "Stats command uses lastNperiod."); + statsTypeTimeSavings(transactions, year, month, period, number); + } else if (year != -1 && period == null && number == -1) { + statsLogger.log(Level.INFO, "Stats command uses either monthly or yearly."); + statsTypeTimeSavings(transactions, year, month, period, number); + } else { + statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); + throw new ListStatsInvalidStatsTypeException(); } - statsTypeTimeSavings(transactions, year, month); break; - default: statsLogger.log(Level.WARNING, "An exception has been caught due to an invalid stats type."); throw new ListStatsInvalidStatsTypeException(); @@ -161,18 +186,33 @@ public static void statsTypeCategoricalSavings(TransactionList transactions) { } //@@author paullowse - public static void statsTypeTimeSavings(TransactionList transactions, int year, int month) { - //System.out.println(year); - //System.out.println(month); + /** + * Calls transactions to get the necessary transaction list, convert the parameters into a String for output. + * Produces info strings, list of categories and summary statistics. + * + * @param transactions An instance of the TransactionList class. + * @param year A specified year + * @param month A specified month + * @param period A specified period of time + * @param number A specified number of periods + * @throws MoolahException If the type of statistics is not recognised. + */ + public static void statsTypeTimeSavings(TransactionList transactions, int year, int month, + String period, int number) throws MoolahException { ArrayList timeTransactions; // only year - if (month == -1) { + if (period != null && number != -1) { + timeTransactions = transactions.getTransactionsByLastN(number, period); + } else if (month == -1) { timeTransactions = transactions.getTransactionsByYear(year); - } else { + } else if (year != -1) { timeTransactions = transactions.getTransactionsByMonth(year, month); + } else { + statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); + throw new ListStatsInvalidStatsTypeException(); } - String timeSavingsList = transactions.listTimeStats(timeTransactions, year, month); + String timeSavingsList = transactions.listTimeStats(timeTransactions, year, month, period, number); if (timeSavingsList.isEmpty()) { statsLogger.log(Level.INFO, "Categorical savings list is empty as there are no transactions available."); @@ -180,9 +220,19 @@ public static void statsTypeTimeSavings(TransactionList transactions, int year, return; } + // summary values + ArrayList amounts; + amounts = transactions.processTimeSummaryStats(timeTransactions); + + String incomeMessage = INFO_STATS_SUMMARY_HEADER + LINE_SEPARATOR + + INFO_STATS_INCOME.toString() + amounts.get(0); + String expensesMessage = INFO_STATS_EXPENSES.toString() + amounts.get(1); + String savingsMessage = INFO_STATS_SAVINGS.toString() + amounts.get(2); + + assert !timeSavingsList.isEmpty(); statsLogger.log(Level.INFO, "Monthly savings list is found to contain categories-amount pairs."); - Ui.showList(timeSavingsList, INFO_STATS_TIME.toString()); + Ui.showStatsList(timeSavingsList, INFO_STATS_TIME.toString(), incomeMessage, expensesMessage, savingsMessage); } diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index acbff2fdd..2d6d8610e 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -21,6 +21,8 @@ public enum ErrorMessages { ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), ERROR_STATS_COMMAND_INVALID_MONTH("Month of statistics given is invalid, please check your input!"), ERROR_STATS_COMMAND_INVALID_YEAR("Year of statistics given is invalid, please check your input!"), + ERROR_STATS_COMMAND_INVALID_PERIOD("Type of period given is invalid, please check your input!"), + ERROR_STATS_COMMAND_INVALID_NUMBER("Number for period stats given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. " + "To preserve data, please STOP the program and edit your data file correctly."), diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 2b89f2fb9..af60e447b 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -18,7 +18,12 @@ public enum InfoMessages { INFO_LIST_UNFILTERED("There are no transaction records that match your search expression."), INFO_STATS_EMPTY("There are no statistics available yet for the given statistics type."), INFO_STATS_CATEGORIES("Here are the total savings for each category:"), - INFO_STATS_TIME("Here are the total savings for"), + INFO_STATS_TIME("Here are the total savings and expenses for"), + INFO_STATS_INCOME("Total Income = "), + INFO_STATS_EXPENSES("Total Expenses = "), + INFO_STATS_SAVINGS("Total Savings = "), + INFO_STATS_CATEGORIES_HEADER("-----CATGORIES-----"), + INFO_STATS_SUMMARY_HEADER("-----SUMMARY-----"), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 7d0710f58..6fc74ac94 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -14,6 +14,7 @@ import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; +import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES_HEADER; /** * Represents a list of transactions added by the user into the application. @@ -246,14 +247,31 @@ public String listCategoricalSavings() { return categoricalSavingsList; } + //@@author paullow_se - public String listTimeStats(ArrayList timeTransactions, int year, int month) { + /** + * Produce Categorical saving list for timeTransactions. + * + * @param timeTransactions An instance of the TransactionList class. + * @param year A specified year + * @param month A specified month + * @param period A specified period of time + * @param number A specified number of periods + * @return String output of transactions for the time period + */ + public String listTimeStats(ArrayList timeTransactions, int year, int month, String period, + int number) { String timeSavingsList = ""; - if (month == -1) { - timeSavingsList += "Year: " + year + LINE_SEPARATOR; + if (period != null && number != -1) { + timeSavingsList += "The past " + number + " " + period + ": " + LINE_SEPARATOR + LINE_SEPARATOR + + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; + } else if (month == -1) { + timeSavingsList += "Year: " + year + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + + LINE_SEPARATOR; } else { - timeSavingsList += "Year: " + year + ", Month: " + month + LINE_SEPARATOR; + timeSavingsList += "Year: " + year + ", Month: " + month + + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; } // Formats every entry in the hashmap into a categorical savings list @@ -265,6 +283,33 @@ public String listTimeStats(ArrayList timeTransactions, int year, i return timeSavingsList; } + //@@author paullow_se + /** + * Produce Expense, Income and Savings statistics. + * + * @param timeTransactions An instance of the TransactionList class. + * @return An amount arraylist of Expense and Income + */ + public ArrayList processTimeSummaryStats(ArrayList timeTransactions) { + int timeExpense = 0; + int timeIncome = 0; + for (Transaction entry : timeTransactions) { + String category = entry.getType(); + if (category == "expense") { + timeExpense += entry.getAmount(); + } else if (category == "income") { + timeIncome += entry.getAmount(); + } + } + int timeSavings = timeIncome - timeExpense; + + ArrayList amounts = new ArrayList(); + amounts.add(Integer.toString(timeIncome)); + amounts.add(Integer.toString(timeExpense)); + amounts.add(Integer.toString(timeSavings)); + return amounts; + } + /** * Gets the range of dates for the last N number of weeks from occurring week. * E.g. If the date is 21 October 2022 to backdate 2 weeks, the range will be 3 October to 16 October 2022. diff --git a/src/main/java/seedu/duke/exception/StatsInvalidNumberException.java b/src/main/java/seedu/duke/exception/StatsInvalidNumberException.java new file mode 100644 index 000000000..809f307d0 --- /dev/null +++ b/src/main/java/seedu/duke/exception/StatsInvalidNumberException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class StatsInvalidNumberException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STATS_COMMAND_INVALID_NUMBER.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/StatsInvalidPeriodException.java b/src/main/java/seedu/duke/exception/StatsInvalidPeriodException.java new file mode 100644 index 000000000..7a1b21f57 --- /dev/null +++ b/src/main/java/seedu/duke/exception/StatsInvalidPeriodException.java @@ -0,0 +1,15 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class StatsInvalidPeriodException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STATS_COMMAND_INVALID_PERIOD.toString(); + } +} diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 9e44d1b81..c886a0084 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -4,21 +4,22 @@ import seedu.duke.command.ListCommand; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; - import seedu.duke.exception.EmptyParameterException; import seedu.duke.exception.EntryNumberNotNumericException; import seedu.duke.exception.InputDuplicateTagException; import seedu.duke.exception.InputMissingTagException; -import seedu.duke.exception.InputUnsupportedTagException; -import seedu.duke.exception.MoolahException; -import seedu.duke.exception.UnknownHelpOptionException; -import seedu.duke.exception.InputTransactionInvalidDateException; import seedu.duke.exception.InputTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; +import seedu.duke.exception.InputTransactionInvalidDateException; import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.InputUnsupportedTagException; import seedu.duke.exception.ListStatsInvalidStatsTypeException; +import seedu.duke.exception.MoolahException; import seedu.duke.exception.StatsInvalidMonthException; +import seedu.duke.exception.StatsInvalidNumberException; +import seedu.duke.exception.StatsInvalidPeriodException; import seedu.duke.exception.StatsInvalidYearException; +import seedu.duke.exception.UnknownHelpOptionException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -32,18 +33,20 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; - -import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; -import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_PERIOD; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_YEAR; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; +import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; +import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; + /** * Parses the parameter portion of the user input and set the parameters into the Command object. * @@ -59,6 +62,9 @@ public class ParameterParser { private static final int MINIMUM_TAG_LENGTH = 2; private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; + private static final String WEEKS = "weeks"; + private static final String MONTHS = "months"; + //@@author wcwy private static final Logger parserLogger = Logger.getLogger(ParameterParser.class.getName()); @@ -311,6 +317,12 @@ private static void setParameter(Command command, String tag, String parameter) case COMMAND_TAG_STATS_YEAR: command.setStatsYear(parseStatsYearTag(parameter)); break; + case COMMAND_TAG_STATS_NUMBER: + command.setStatsNumber(parseStatsNumberTag(parameter)); + break; + case COMMAND_TAG_STATS_PERIOD: + command.setStatsPeriod(parseStatsPeriodTag(parameter)); + break; default: parserLogger.log(Level.WARNING, "An unsupported tag exception is caught: " + tag); throw new InputUnsupportedTagException(); @@ -539,6 +551,44 @@ public static int parseStatsYearTag(String parameter) throws StatsInvalidYearExc return year; } + //@@author paullowse + public static String parseStatsPeriodTag(String parameter) throws StatsInvalidPeriodException { + String period; + switch (parameter) { + case WEEKS: + period = WEEKS; + break; + case MONTHS: + period = MONTHS; + break; + default: + parserLogger.log(Level.WARNING, "An invalid statistic period error is caught for the given parameter: " + + parameter); + throw new StatsInvalidPeriodException(); + } + return period; + } + + //@@author paullowse + public static int parseStatsNumberTag(String parameter) throws EntryNumberNotNumericException, + StatsInvalidNumberException { + int statsNumber; + try { + statsNumber = Integer.parseInt(parameter); + } catch (NumberFormatException e) { + parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + + parameter); + throw new EntryNumberNotNumericException(); + } + if (statsNumber < 0) { + parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + + parameter); + throw new StatsInvalidNumberException(); + } + return statsNumber; + } + + //@@author chinhan99 /** From 5ead78a40356ac5de1487e696c08428b0e5d6655 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 08:24:48 +0800 Subject: [PATCH 197/416] Update placement of authorship for certain code segments --- .../java/seedu/duke/command/ListCommand.java | 4 +++- .../java/seedu/duke/command/StatsCommand.java | 20 ++++++++++--------- .../java/seedu/duke/common/Constants.java | 1 + src/main/java/seedu/duke/data/Budget.java | 1 + .../java/seedu/duke/data/TransactionList.java | 7 +++++-- .../seedu/duke/parser/ParameterParser.java | 5 +---- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index a4ec90464..fda09bf6e 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -88,6 +88,8 @@ public void setDate(LocalDate date) { this.date = date; } + //@@author chydarren + /** * Executes the operations related to the command. * @@ -103,7 +105,6 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws listTransactions(transactions, type, category, date); } - //@@chydarren /** * List all or some transactions based on selection. * @@ -129,6 +130,7 @@ private static void listTransactions(TransactionList transactions, String type, } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index e35247fe3..cc959fa80 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -24,7 +24,6 @@ import static seedu.duke.common.InfoMessages.INFO_STATS_SUMMARY_HEADER; import static seedu.duke.common.InfoMessages.INFO_STATS_TIME; - /** * Represents a get command object that will execute the operations for Get command. */ @@ -82,8 +81,6 @@ public String[] getOptionalTags() { }; return optionalTags; } - - @Override public void setStatsType(String statsType) { this.statsType = statsType; @@ -113,8 +110,8 @@ public int getStatsYear() { return year; } - //@@author chydarren + /** * Executes the operations related to the command. * @@ -146,6 +143,7 @@ private static void listStatsByStatsType(String statsType, TransactionList trans statsTypeCategoricalSavings(transactions); statsLogger.log(Level.INFO, "End of Stats command."); break; + //@@author paullowse case "time": statsLogger.log(Level.INFO, "Stats type has been detected for monthly savings."); if (year == -1 && month == -1 && period != null && number != -1) { @@ -159,13 +157,15 @@ private static void listStatsByStatsType(String statsType, TransactionList trans throw new ListStatsInvalidStatsTypeException(); } break; - + //@@author chydarren default: statsLogger.log(Level.WARNING, "An exception has been caught due to an invalid stats type."); throw new ListStatsInvalidStatsTypeException(); } } + //@@author chydarren + /** * Display the statistics requested for current amount of savings in each category. * @@ -186,15 +186,16 @@ public static void statsTypeCategoricalSavings(TransactionList transactions) { } //@@author paullowse + /** * Calls transactions to get the necessary transaction list, convert the parameters into a String for output. * Produces info strings, list of categories and summary statistics. * * @param transactions An instance of the TransactionList class. - * @param year A specified year - * @param month A specified month - * @param period A specified period of time - * @param number A specified number of periods + * @param year A specified year. + * @param month A specified month. + * @param period A specified period of time. + * @param number A specified number of periods. * @throws MoolahException If the type of statistics is not recognised. */ public static void statsTypeTimeSavings(TransactionList transactions, int year, int month, @@ -237,6 +238,7 @@ public static void statsTypeTimeSavings(TransactionList transactions, int year, } //@@author paullowse + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index 34a15ae1b..c6f89dbdd 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -7,6 +7,7 @@ *

Note that altering the values in this folder may result in integer overflow in the program in extreme cases. */ public class Constants { + //@@author wcwy /* WARNING: Editing the values below may result in integer overflow in 1. TransactionList.calculateMonthlyTotalExpense() diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index 1b9637a04..0fa27ae5b 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -11,6 +11,7 @@ * Operations related to the budgets are also defined under this class. */ public class Budget { + //@@author wcwy // Default value of the monthly budget is $1000 private static int budget = 1000; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 6fc74ac94..34b0fdaf1 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -247,7 +247,7 @@ public String listCategoricalSavings() { return categoricalSavingsList; } - //@@author paullow_se + //@@author paullowse /** * Produce Categorical saving list for timeTransactions. @@ -283,7 +283,6 @@ public String listTimeStats(ArrayList timeTransactions, int year, i return timeSavingsList; } - //@@author paullow_se /** * Produce Expense, Income and Savings statistics. * @@ -310,6 +309,8 @@ public ArrayList processTimeSummaryStats(ArrayList timeTran return amounts; } + //@@author chydarren + /** * Gets the range of dates for the last N number of weeks from occurring week. * E.g. If the date is 21 October 2022 to backdate 2 weeks, the range will be 3 October to 16 October 2022. @@ -380,6 +381,8 @@ public ArrayList getTransactionsByMonth(int year, int month) { return transactionsByMonth; } + //@@author chydarren + /** * Gets all transactions recorded on the last N weeks or months. * diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index c886a0084..cb42095d3 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -513,6 +513,7 @@ public static String parseStatsTypeTag(String parameter) throws ListStatsInvalid } //@@author paullowse + public static int parseStatsMonthTag(String parameter) throws StatsInvalidMonthException, EntryNumberNotNumericException { int month; @@ -532,7 +533,6 @@ public static int parseStatsMonthTag(String parameter) throws StatsInvalidMonthE return month; } - //@@author paullowse public static int parseStatsYearTag(String parameter) throws StatsInvalidYearException, EntryNumberNotNumericException { int year; @@ -551,7 +551,6 @@ public static int parseStatsYearTag(String parameter) throws StatsInvalidYearExc return year; } - //@@author paullowse public static String parseStatsPeriodTag(String parameter) throws StatsInvalidPeriodException { String period; switch (parameter) { @@ -569,7 +568,6 @@ public static String parseStatsPeriodTag(String parameter) throws StatsInvalidPe return period; } - //@@author paullowse public static int parseStatsNumberTag(String parameter) throws EntryNumberNotNumericException, StatsInvalidNumberException { int statsNumber; @@ -588,7 +586,6 @@ public static int parseStatsNumberTag(String parameter) throws EntryNumberNotNum return statsNumber; } - //@@author chinhan99 /** From 85264b10444b66ae7ef0e9111d6dfc367822f534 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 10:04:23 +0800 Subject: [PATCH 198/416] Improve date helper functions based on previous PR feedback --- .../java/seedu/duke/command/StatsCommand.java | 7 +- .../java/seedu/duke/data/TransactionList.java | 67 +++++++++---------- .../java/seedu/duke/parser/CommandParser.java | 4 +- .../seedu/duke/parser/ParameterParser.java | 9 +-- 4 files changed, 40 insertions(+), 47 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index cc959fa80..355413fa1 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -7,6 +7,7 @@ import seedu.duke.exception.ListStatsInvalidStatsTypeException; import seedu.duke.exception.MoolahException; +import java.time.LocalDate; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; @@ -203,8 +204,10 @@ public static void statsTypeTimeSavings(TransactionList transactions, int year, ArrayList timeTransactions; // only year - if (period != null && number != -1) { - timeTransactions = transactions.getTransactionsByLastN(number, period); + if (period != null && number != -1 && period == "weeks") { + timeTransactions = transactions.getTransactionsByWeekRange(LocalDate.now(), number); + } else if (period != null && number != -1 && period == "months") { + timeTransactions = transactions.getTransactionsByMonthRange(LocalDate.now(), number); } else if (month == -1) { timeTransactions = transactions.getTransactionsByYear(year); } else if (year != -1) { diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 34b0fdaf1..0f2d10be7 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -33,7 +33,7 @@ public class TransactionList { private static final String LINE_SEPARATOR = System.lineSeparator(); //@@author chinhan99 - private ArrayList transactions; + private static ArrayList transactions; public TransactionList(TransactionList transactionList) { transactions = transactionList.getTransactions(); @@ -250,14 +250,14 @@ public String listCategoricalSavings() { //@@author paullowse /** - * Produce Categorical saving list for timeTransactions. + * Produces Categorical saving list for timeTransactions. * * @param timeTransactions An instance of the TransactionList class. - * @param year A specified year - * @param month A specified month - * @param period A specified period of time - * @param number A specified number of periods - * @return String output of transactions for the time period + * @param year A specified year. + * @param month A specified month. + * @param period A specified period of time. + * @param number A specified number of periods. + * @return String output of transactions for the time period. */ public String listTimeStats(ArrayList timeTransactions, int year, int month, String period, int number) { @@ -284,10 +284,10 @@ public String listTimeStats(ArrayList timeTransactions, int year, i } /** - * Produce Expense, Income and Savings statistics. + * Produces Expense, Income and Savings statistics. * * @param timeTransactions An instance of the TransactionList class. - * @return An amount arraylist of Expense and Income + * @return An amount arraylist of Expense and Income. */ public ArrayList processTimeSummaryStats(ArrayList timeTransactions) { int timeExpense = 0; @@ -312,37 +312,45 @@ public ArrayList processTimeSummaryStats(ArrayList timeTran //@@author chydarren /** - * Gets the range of dates for the last N number of weeks from occurring week. + * Gets all transactions recorded in between specified weeks, backdated from given date. * E.g. If the date is 21 October 2022 to backdate 2 weeks, the range will be 3 October to 16 October 2022. * - * @param numberOfWeeks N number of weeks to backdate, must be minimum 1 week. * @param date A specified date to backdate N weeks from occurring week. - * @return An array containing the start and end date. + * @param numberOfWeeks N number of weeks to backdate, must be minimum 1 week. + * @return An array list containing all transactions recorded in specified weeks. */ - public static LocalDate[] getWeekRange(LocalDate date, int numberOfWeeks) { + public static ArrayList getTransactionsByWeekRange(LocalDate date, int numberOfWeeks) { + ArrayList transactionsByWeekRange = new ArrayList<>(); + // Solution below adapted from https://stackoverflow.com/a/51356522 + // Gets the range of dates for the last N number of weeks from occurring week int dayOfWeek = date.getDayOfWeek().getValue(); LocalDate from = date.minusDays((dayOfWeek - 1) + (numberOfWeeks * 7)); LocalDate to = date.minusDays(dayOfWeek); - return new LocalDate[]{from, to}; + transactionsByWeekRange = getTransactionsByDateRange(new LocalDate[]{from, to}, transactionsByWeekRange); + return transactionsByWeekRange; } /** - * Gets the range of dates for the last N number of months from occurring month. + * Gets all transactions recorded in between specified months, backdated from given date. * E.g. If the date is 21 October 2022 to backdate 2 months, the range will be 1 August to 30 September 2022. * - * @param numberOfMonths N number of months to backdate, must be minimum 1 month. * @param date A specified date to backdate N months from occurring month. - * @return An array containing the start and end date. + * @param numberOfMonths N number of months to backdate, must be minimum 1 month. + * @return An array list containing all transactions recorded in specified months. */ - public static LocalDate[] getMonthRange(LocalDate date, int numberOfMonths) { + public static ArrayList getTransactionsByMonthRange(LocalDate date, int numberOfMonths) { + ArrayList transactionsByMonthRange = new ArrayList<>(); + // Solution below adapted from https://stackoverflow.com/a/51356522 + // Gets the range of dates for the last N number of months from occurring month LocalDate lastMonth = date.minusMonths(1); LocalDate from = date.minusMonths(numberOfMonths).withDayOfMonth(1); LocalDate to = lastMonth.withDayOfMonth(lastMonth.getMonth().maxLength()); - return new LocalDate[]{from, to}; + transactionsByMonthRange = getTransactionsByDateRange(new LocalDate[]{from, to}, transactionsByMonthRange); + return transactionsByMonthRange; } /** @@ -381,35 +389,24 @@ public ArrayList getTransactionsByMonth(int year, int month) { return transactionsByMonth; } - //@@author chydarren - /** * Gets all transactions recorded on the last N weeks or months. * - * @param numberOfType An integer that represents the last N number of weeks or months. - * @param type A string that represents the getting of N "weeks", or "months". + * @param dateRange A specified range of dates. * @return An array list containing all transactions recorded on the last N number of weeks or months. */ - public ArrayList getTransactionsByLastN(int numberOfType, String type) { - ArrayList transactionsByLastN = new ArrayList<>(); - LocalDate[] dateRange = {}; - - if (type.equals(WEEKS)) { - dateRange = getWeekRange(LocalDate.now(), numberOfType); - } else { - assert type.equals(MONTHS); - dateRange = getMonthRange(LocalDate.now(), numberOfType); - } + public static ArrayList getTransactionsByDateRange(LocalDate[] dateRange, + ArrayList transactionsByDateRange) { for (Transaction transaction : transactions) { LocalDate transactionDate = transaction.getDate(); // Transaction is added into the filtered array list if it falls within the expected date range if (!(transactionDate.isBefore(dateRange[START]) || transactionDate.isAfter(dateRange[END]))) { - transactionsByLastN.add(transaction); + transactionsByDateRange.add(transaction); } } - return transactionsByLastN; + return transactionsByDateRange; } //@@author brian-vb diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index 9dbeea07a..6c6695f05 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -27,10 +27,8 @@ */ public class CommandParser { //@@author chydarren - private static final String EMPTY_STRING = ""; private static final String DELIMITER = " "; private static final int SPLIT_POSITION = 2; - private static final Logger parserLogger = Logger.getLogger(ParameterParser.class.getName()); //@@author wcwy @@ -79,7 +77,7 @@ public static String[] splitInput(String fullCommandInput) { if (fullCommandInput.contains(DELIMITER)) { inputTokens = fullCommandInput.split(DELIMITER, SPLIT_POSITION); } else { - inputTokens = new String[]{fullCommandInput, EMPTY_STRING}; + inputTokens = new String[]{fullCommandInput, ""}; } return inputTokens; } diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index cb42095d3..0fdf9aafe 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -56,7 +56,6 @@ */ public class ParameterParser { //@@author chydarren - private static final String EMPTY_STRING = ""; private static final String DELIMITER = " "; private static final int SPLIT_POSITION = 2; private static final int MINIMUM_TAG_LENGTH = 2; @@ -496,20 +495,16 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt * @throws ListStatsInvalidStatsTypeException If the statistic type given is not supported. */ public static String parseStatsTypeTag(String parameter) throws ListStatsInvalidStatsTypeException { - String statsType; switch (parameter) { case "categories": - statsType = "categories"; - break; + return "categories"; case "time": - statsType = "time"; - break; + return "time"; default: parserLogger.log(Level.WARNING, "An invalid statistic type error is caught for the given parameter: " + parameter); throw new ListStatsInvalidStatsTypeException(); } - return statsType; } //@@author paullowse From e44eec052354fb2091b2fd3aac9d5abcdb6c763e Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 12:31:28 +0800 Subject: [PATCH 199/416] Edit some tags and exceptions for universal usage --- .../java/seedu/duke/command/CommandTag.java | 2 +- .../java/seedu/duke/command/ListCommand.java | 36 ++++++++++++++++--- .../java/seedu/duke/command/StatsCommand.java | 13 +++---- .../java/seedu/duke/common/ErrorMessages.java | 14 ++++---- .../java/seedu/duke/data/TransactionList.java | 2 -- .../exception/InputMissingTagException.java | 2 +- .../duke/exception/InvalidIndexException.java | 2 +- .../ListStatsMissingTagException.java | 2 +- .../exception/StatsInvalidMonthException.java | 2 +- .../exception/StatsInvalidYearException.java | 2 +- .../seedu/duke/parser/ParameterParser.java | 24 ++++++------- 11 files changed, 63 insertions(+), 38 deletions(-) diff --git a/src/main/java/seedu/duke/command/CommandTag.java b/src/main/java/seedu/duke/command/CommandTag.java index 08109f494..adc57979d 100644 --- a/src/main/java/seedu/duke/command/CommandTag.java +++ b/src/main/java/seedu/duke/command/CommandTag.java @@ -9,7 +9,7 @@ public class CommandTag { public static final String COMMAND_TAG_TRANSACTION_DESCRIPTION = "i/"; public static final String COMMAND_TAG_LIST_ENTRY_NUMBER = "e/"; public static final String COMMAND_TAG_HELP_OPTION = "o/"; - public static final String COMMAND_TAG_STATISTICS_TYPE = "s/"; + public static final String COMMAND_TAG_STATS_TYPE = "s/"; public static final String COMMAND_TAG_STATS_MONTH = "m/"; public static final String COMMAND_TAG_STATS_YEAR = "y/"; public static final String COMMAND_TAG_STATS_NUMBER = "n/"; diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index fda09bf6e..bccf1d4e6 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -3,7 +3,9 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.exception.InputMissingTagException; import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.ListStatsInvalidStatsTypeException; import seedu.duke.exception.MoolahException; import java.time.LocalDate; @@ -13,6 +15,8 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_YEAR; import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; @@ -26,7 +30,7 @@ public class ListCommand extends Command { // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To list all or some transactions based on selection."; // The guiding information for the usage of command - public static final String COMMAND_USAGE = "Usage: list [t/TYPE] [c/CATEGORY] [d/DATE]"; + public static final String COMMAND_USAGE = "Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR]"; // The formatting information for the parameters used by the command public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + LINE_SEPARATOR @@ -48,6 +52,8 @@ public class ListCommand extends Command { private String category; private LocalDate date; private String type; + private int month; + private int year; /** * Initialises the variables of the ListCommand class. @@ -56,6 +62,8 @@ public ListCommand() { category = ""; date = null; type = ""; + month = -1; + year = -1; } /** @@ -68,7 +76,9 @@ public String[] getOptionalTags() { String[] optionalTags = new String[]{ COMMAND_TAG_TRANSACTION_TYPE, COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_DATE + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_STATS_MONTH, + COMMAND_TAG_STATS_YEAR }; return optionalTags; } @@ -88,6 +98,16 @@ public void setDate(LocalDate date) { this.date = date; } + @Override + public void setStatsMonth(int month) { + this.month = month; + } + + @Override + public void setStatsYear(int year) { + this.year = year; + } + //@@author chydarren /** @@ -102,7 +122,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws listLogger.setLevel(Level.SEVERE); listLogger.log(Level.INFO, "Entering execution of the List command."); - listTransactions(transactions, type, category, date); + listTransactions(transactions, type, category, date, month, year); } /** @@ -114,8 +134,14 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param date Date of the transaction with format in "yyyyMMdd". * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. */ - private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date) - throws InputTransactionUnknownTypeException { + private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date, + int month, int year) + throws InputTransactionUnknownTypeException, InputMissingTagException { + if (month != -1 && year == -1) { + listLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); + throw new InputMissingTagException(); + } + String transactionsList = transactions.listTransactions(type, category, date); if (transactionsList.isEmpty()) { diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 355413fa1..60d568bdd 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -12,7 +12,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_PERIOD; @@ -52,12 +52,13 @@ public class StatsCommand extends Command { //@@author chydarren private static final Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); private String statsType; + + //@@author paullowse private int month = -1; private int year = -1; private String period = null; private int number = -1; - //@@author paullowse public StatsCommand() { } @@ -68,7 +69,7 @@ public StatsCommand() { */ @Override public String[] getMandatoryTags() { - String[] mandatoryTags = new String[]{COMMAND_TAG_STATISTICS_TYPE}; + String[] mandatoryTags = new String[]{COMMAND_TAG_STATS_TYPE}; return mandatoryTags; } @@ -131,9 +132,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws /** * Lists the statistics depending on the type of statistics requested. * - * @param statsType The type of statistics that is needed. - * @param transactions An instance of the TransactionList class. - * @throws MoolahException If the type of statistics is not recognised. + * @param statsType The type of statistics that is needed. + * @param transactions An instance of the TransactionList class. + * @throws MoolahException If the type of statistics is not recognised. */ private static void listStatsByStatsType(String statsType, TransactionList transactions, int month, int year, String period, int number) diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 2d6d8610e..b39f18333 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -7,20 +7,20 @@ public enum ErrorMessages { ERROR_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), ERROR_INPUT_UNSUPPORTED_TAG("Not supported tag(s) detected, please check your input!"), ERROR_INPUT_DUPLICATE_TAG("Duplicate tag(s) detected, please check your input!"), + ERROR_INPUT_MISSING_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), + ERROR_INPUT_INVALID_AMOUNT("Invalid amount, " + + "please ensure your amount is in positive numerals ($10000000 or less) only!"), ERROR_INPUT_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), ERROR_INPUT_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_INPUT_INVALID_TAG("Invalid tag(s) detected, please check your input!"), ERROR_INPUT_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), - ERROR_INPUT_MISSING_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), - ERROR_INVALID_INDEX("Invalid index, please ensure your index is correct!"), + ERROR_INPUT_INVALID_INDEX("Invalid index, please ensure your index is correct!"), + ERROR_INPUT_INVALID_MONTH("Invalid month, please check your input!"), + ERROR_INPUT_INVALID_YEAR("Invalid year, please check your input!"), + ERROR_INPUT_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_ENTRY_NUMBER_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), - ERROR_INPUT_INVALID_AMOUNT("Invalid amount, " - + "please ensure your amount is in positive numerals ($10000000 or less) only!"), - ERROR_ADD_COMMAND_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), - ERROR_STATS_COMMAND_INVALID_MONTH("Month of statistics given is invalid, please check your input!"), - ERROR_STATS_COMMAND_INVALID_YEAR("Year of statistics given is invalid, please check your input!"), ERROR_STATS_COMMAND_INVALID_PERIOD("Type of period given is invalid, please check your input!"), ERROR_STATS_COMMAND_INVALID_NUMBER("Number for period stats given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 0f2d10be7..b212000ae 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -26,8 +26,6 @@ public class TransactionList { private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; private static final String SYMBOL_DOLLAR = "$"; - private static final String WEEKS = "weeks"; - private static final String MONTHS = "months"; private static final int START = 0; private static final int END = 1; private static final String LINE_SEPARATOR = System.lineSeparator(); diff --git a/src/main/java/seedu/duke/exception/InputMissingTagException.java b/src/main/java/seedu/duke/exception/InputMissingTagException.java index 4983dfe44..5d9c6d988 100644 --- a/src/main/java/seedu/duke/exception/InputMissingTagException.java +++ b/src/main/java/seedu/duke/exception/InputMissingTagException.java @@ -10,6 +10,6 @@ public class InputMissingTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_MISSING_TAG.toString(); + return ErrorMessages.ERROR_INPUT_MISSING_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/InvalidIndexException.java b/src/main/java/seedu/duke/exception/InvalidIndexException.java index f2a036af9..5d9f3a690 100644 --- a/src/main/java/seedu/duke/exception/InvalidIndexException.java +++ b/src/main/java/seedu/duke/exception/InvalidIndexException.java @@ -10,6 +10,6 @@ public class InvalidIndexException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INVALID_INDEX.toString(); + return ErrorMessages.ERROR_INPUT_INVALID_INDEX.toString(); } } diff --git a/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java b/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java index fbe1f3dab..b2be07030 100644 --- a/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java +++ b/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java @@ -10,6 +10,6 @@ public class ListStatsMissingTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ADD_COMMAND_MISSING_TAG.toString(); + return ErrorMessages.ERROR_INPUT_MISSING_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java b/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java index b651bf55a..df52b0c1b 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java +++ b/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java @@ -10,6 +10,6 @@ public class StatsInvalidMonthException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_STATS_COMMAND_INVALID_MONTH.toString(); + return ErrorMessages.ERROR_INPUT_INVALID_MONTH.toString(); } } diff --git a/src/main/java/seedu/duke/exception/StatsInvalidYearException.java b/src/main/java/seedu/duke/exception/StatsInvalidYearException.java index c569f05d6..60962ffec 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidYearException.java +++ b/src/main/java/seedu/duke/exception/StatsInvalidYearException.java @@ -10,6 +10,6 @@ public class StatsInvalidYearException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_STATS_COMMAND_INVALID_YEAR.toString(); + return ErrorMessages.ERROR_INPUT_INVALID_YEAR.toString(); } } diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 0fdf9aafe..9260f064a 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -32,7 +32,7 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATISTICS_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_PERIOD; @@ -61,6 +61,9 @@ public class ParameterParser { private static final int MINIMUM_TAG_LENGTH = 2; private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; + private static final String CATEGORIES = "categories"; + private static final String TIME = "time"; + private static final String WEEKS = "weeks"; private static final String MONTHS = "months"; @@ -307,7 +310,7 @@ private static void setParameter(Command command, String tag, String parameter) case COMMAND_TAG_HELP_OPTION: command.setIsDetailedOption(parseHelpOptionTag(parameter)); break; - case COMMAND_TAG_STATISTICS_TYPE: + case COMMAND_TAG_STATS_TYPE: command.setStatsType(parseStatsTypeTag(parameter)); break; case COMMAND_TAG_STATS_MONTH: @@ -488,7 +491,7 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt //@@author chydarren /** - * Check if the type parameter is a valid statistic type and returns the parameter if it is valid. + * Checks if the type parameter is a valid statistic type and returns the parameter if it is valid. * * @param parameter The user input after the user tag. * @return The statistic type. @@ -496,10 +499,10 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt */ public static String parseStatsTypeTag(String parameter) throws ListStatsInvalidStatsTypeException { switch (parameter) { - case "categories": - return "categories"; - case "time": - return "time"; + case CATEGORIES: + return CATEGORIES; + case TIME: + return TIME; default: parserLogger.log(Level.WARNING, "An invalid statistic type error is caught for the given parameter: " + parameter); @@ -550,17 +553,14 @@ public static String parseStatsPeriodTag(String parameter) throws StatsInvalidPe String period; switch (parameter) { case WEEKS: - period = WEEKS; - break; + return WEEKS; case MONTHS: - period = MONTHS; - break; + return MONTHS; default: parserLogger.log(Level.WARNING, "An invalid statistic period error is caught for the given parameter: " + parameter); throw new StatsInvalidPeriodException(); } - return period; } public static int parseStatsNumberTag(String parameter) throws EntryNumberNotNumericException, From 562a2c4f2d2544bf94c9b1590188db7d9448eb51 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 12:47:00 +0800 Subject: [PATCH 200/416] Minor fix for Gradle test error --- src/main/java/seedu/duke/command/StatsCommand.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 60d568bdd..8ba9cf2f5 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -83,6 +83,7 @@ public String[] getOptionalTags() { }; return optionalTags; } + @Override public void setStatsType(String statsType) { this.statsType = statsType; From 928194071f900c767d5d663525df4edc1fae9637 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 12:52:06 +0800 Subject: [PATCH 201/416] Update EXPECTED.TXT to reflect changes for help on List command --- src/main/java/seedu/duke/command/StatsCommand.java | 2 +- text-ui-test/EXPECTED.TXT | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 8ba9cf2f5..24792b8e1 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -83,7 +83,7 @@ public String[] getOptionalTags() { }; return optionalTags; } - + @Override public void setStatsType(String statsType) { this.statsType = statsType; diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 89e2b921d..92b418cb3 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -25,7 +25,7 @@ Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION Command Word: LIST To list all or some transactions based on selection. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] Command Word: FIND To find specific transaction(s) based on any keywords inputted by the user. @@ -73,7 +73,7 @@ DESCRIPTION: More information regarding the transaction, written without any spa Command Word: LIST To list all or some transactions based on selection. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] Parameters information: (Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. (Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. From 69f1c29692301e83bf643abe5e058125508bdb3a Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 14:16:13 +0800 Subject: [PATCH 202/416] Rename exceptions and remove redundant ones, and update authorship affected changes to avoid RepoSense issues --- src/main/java/seedu/duke/Duke.java | 1 - src/main/java/seedu/duke/Storage.java | 12 +- .../java/seedu/duke/command/AddCommand.java | 14 +- .../java/seedu/duke/command/ByeCommand.java | 2 + src/main/java/seedu/duke/command/Command.java | 2 + .../java/seedu/duke/command/CommandTag.java | 10 +- .../seedu/duke/command/DeleteCommand.java | 12 +- .../java/seedu/duke/command/EditCommand.java | 6 +- .../java/seedu/duke/command/FindCommand.java | 2 + .../java/seedu/duke/command/HelpCommand.java | 2 + .../java/seedu/duke/command/ListCommand.java | 24 +-- .../java/seedu/duke/command/PurgeCommand.java | 2 + .../java/seedu/duke/command/StatsCommand.java | 28 ++-- .../java/seedu/duke/common/ErrorMessages.java | 33 ++-- .../java/seedu/duke/data/TransactionList.java | 29 +++- ...ndTransactionMissingKeywordsException.java | 1 + ....java => GlobalDuplicateTagException.java} | 5 +- ...ava => GlobalEmptyParameterException.java} | 5 +- .../GlobalInvalidCommandException.java | 16 ++ ....java => GlobalInvalidIndexException.java} | 5 +- ....java => GlobalInvalidMonthException.java} | 5 +- ...java => GlobalInvalidNumberException.java} | 5 +- ...java => GlobalInvalidPeriodException.java} | 31 ++-- ...n.java => GlobalInvalidYearException.java} | 5 +- ...on.java => GlobalMissingTagException.java} | 5 +- ...a => GlobalNumberNotNumericException.java} | 5 +- ...ava => GlobalUnsupportedTagException.java} | 5 +- ...n.java => HelpUnknownOptionException.java} | 3 +- ...nputTransactionInvalidAmountException.java | 7 +- ...utTransactionInvalidCategoryException.java | 3 +- .../InputTransactionInvalidDateException.java | 3 +- ...InputTransactionInvalidTypeException.java} | 5 +- .../exception/InvalidCommandException.java | 15 -- .../duke/exception/InvalidIndexException.java | 15 -- .../ListStatsInvalidStatsTypeException.java | 16 -- .../MaximumTransactionCountException.java | 1 + .../seedu/duke/exception/MoolahException.java | 1 + ...on.java => StatsInvalidTypeException.java} | 6 +- ...ava => StorageFileCorruptedException.java} | 3 +- .../exception/StorageWriteErrorException.java | 1 + .../java/seedu/duke/parser/CommandParser.java | 8 +- .../seedu/duke/parser/ParameterParser.java | 156 ++++++++++-------- .../duke/parser/ParameterParserTest.java | 4 +- 43 files changed, 274 insertions(+), 245 deletions(-) rename src/main/java/seedu/duke/exception/{StatsInvalidNumberException.java => GlobalDuplicateTagException.java} (66%) rename src/main/java/seedu/duke/exception/{EmptyParameterException.java => GlobalEmptyParameterException.java} (63%) create mode 100644 src/main/java/seedu/duke/exception/GlobalInvalidCommandException.java rename src/main/java/seedu/duke/exception/{StatsInvalidPeriodException.java => GlobalInvalidIndexException.java} (65%) rename src/main/java/seedu/duke/exception/{InputDuplicateTagException.java => GlobalInvalidMonthException.java} (64%) rename src/main/java/seedu/duke/exception/{InputUnsupportedTagException.java => GlobalInvalidNumberException.java} (66%) rename src/main/java/seedu/duke/exception/{ListStatsMissingTagException.java => GlobalInvalidPeriodException.java} (65%) rename src/main/java/seedu/duke/exception/{StatsInvalidMonthException.java => GlobalInvalidYearException.java} (66%) rename src/main/java/seedu/duke/exception/{StatsInvalidYearException.java => GlobalMissingTagException.java} (66%) rename src/main/java/seedu/duke/exception/{InputTransactionInvalidTagException.java => GlobalNumberNotNumericException.java} (63%) rename src/main/java/seedu/duke/exception/{EntryNumberNotNumericException.java => GlobalUnsupportedTagException.java} (64%) rename src/main/java/seedu/duke/exception/{UnknownHelpOptionException.java => HelpUnknownOptionException.java} (81%) rename src/main/java/seedu/duke/exception/{InputTransactionUnknownTypeException.java => InputTransactionInvalidTypeException.java} (65%) delete mode 100644 src/main/java/seedu/duke/exception/InvalidCommandException.java delete mode 100644 src/main/java/seedu/duke/exception/InvalidIndexException.java delete mode 100644 src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java rename src/main/java/seedu/duke/exception/{InputMissingTagException.java => StatsInvalidTypeException.java} (64%) rename src/main/java/seedu/duke/exception/{StorageInputCorruptedException.java => StorageFileCorruptedException.java} (79%) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index 7b09b1621..cd42c1bca 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -3,7 +3,6 @@ import seedu.duke.command.Command; import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.StorageWriteErrorException; import seedu.duke.parser.CommandParser; import java.io.IOException; diff --git a/src/main/java/seedu/duke/Storage.java b/src/main/java/seedu/duke/Storage.java index 3af22a821..f34653b45 100644 --- a/src/main/java/seedu/duke/Storage.java +++ b/src/main/java/seedu/duke/Storage.java @@ -5,7 +5,7 @@ import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.StorageInputCorruptedException; +import seedu.duke.exception.StorageFileCorruptedException; import seedu.duke.exception.StorageWriteErrorException; import seedu.duke.parser.ParameterParser; @@ -68,7 +68,7 @@ private static File checkIfFileExist() throws IOException { * Initializes the duke.txt by checking its existence, then store the data values to the program. * * @return The TransactionList storing entries which would be used in the program. - * @throws StorageInputCorruptedException If there are errors due to corrupted duke.txt data. + * @throws StorageFileCorruptedException If there are errors due to corrupted duke.txt data. * @throws StorageWriteErrorException If the file could not be created or could not be written. */ public TransactionList initializeFile() throws MoolahException { @@ -80,7 +80,7 @@ public TransactionList initializeFile() throws MoolahException { } catch (MoolahException e) { // Catch any parsing errors and throw the default StorageInputCorruptedException - throw new StorageInputCorruptedException(); + throw new StorageFileCorruptedException(); } catch (IOException e) { throw new StorageWriteErrorException(); } @@ -103,7 +103,7 @@ private void storeFileValuesLocally(TransactionList storedTransactions, Scanner String line = input.nextLine(); String[] splits = line.split(DELIMITER); if (splits.length != NUMBER_OF_STORED_PARAMETERS) { - throw new StorageInputCorruptedException(); + throw new StorageFileCorruptedException(); } String type = splits[0]; @@ -134,11 +134,11 @@ private void storeFileValuesLocally(TransactionList storedTransactions, Scanner storedTransactions.addIncomeDuringStorage(description, amount, category, date); break; default: - throw new StorageInputCorruptedException(); + throw new StorageFileCorruptedException(); } } catch (DateTimeParseException e) { // If the date format is incorrect, which is due to corrupted date information - throw new StorageInputCorruptedException(); + throw new StorageFileCorruptedException(); } } diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 54bf3ff88..f1fc76b81 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -1,3 +1,4 @@ +//@@author wcwy package seedu.duke.command; import seedu.duke.Storage; @@ -8,11 +9,12 @@ import seedu.duke.data.transaction.Transaction; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.InputTransactionInvalidTypeException; import seedu.duke.exception.MoolahException; import seedu.duke.exception.StorageWriteErrorException; import seedu.duke.exception.MaximumTransactionCountException; +//@@author chinhan99 import java.io.IOException; import java.time.LocalDate; import java.util.logging.Level; @@ -32,6 +34,7 @@ * Represents an add command object that will execute the operations for Add command. */ public class AddCommand extends Command { + //@@author chinhan99 private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "ADD"; @@ -56,7 +59,6 @@ public class AddCommand extends Command { // Detailed help description public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - //@@author chinhan99 private static final Logger addLogger = Logger.getLogger(AddCommand.class.getName()); //@@author paullowse @@ -135,7 +137,7 @@ public void setTransactionCreated(Transaction transaction) { * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. * @param storage An instance of the Storage class. - * @throws InputTransactionUnknownTypeException If the type of transaction is not recognised. + * @throws InputTransactionInvalidTypeException If the type of transaction is not recognised. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { @@ -190,9 +192,9 @@ private static void checkTransactionCapacity(TransactionList transactions) throw * * @param transactions The list of transactions in the application. * @returns A string containing the message banner based on the type of transaction created. - * @throws InputTransactionUnknownTypeException If the type of the transactions + * @throws InputTransactionInvalidTypeException If the type of the transactions */ - private String addTransaction(TransactionList transactions) throws InputTransactionUnknownTypeException { + private String addTransaction(TransactionList transactions) throws InputTransactionInvalidTypeException { String messageBanner = ""; Transaction transaction; switch (type) { @@ -210,7 +212,7 @@ private String addTransaction(TransactionList transactions) throws InputTransact break; default: addLogger.log(Level.SEVERE, "The parsed type of transaction stored in addCommand is unknown!"); - throw new InputTransactionUnknownTypeException(); + throw new InputTransactionInvalidTypeException(); } setTransactionCreated(transaction); return messageBanner; diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index cc7b3fa21..9a00b013a 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -1,3 +1,4 @@ +//@@author paullowse package seedu.duke.command; import seedu.duke.Storage; @@ -10,6 +11,7 @@ * Represents a bye command object that will execute the operations for Bye command. */ public class ByeCommand extends Command { + //@@author paullowse private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "BYE"; diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 2d6eafd98..f0a6e126d 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -1,3 +1,4 @@ +//@@author chinhan99 package seedu.duke.command; import seedu.duke.Storage; @@ -11,6 +12,7 @@ * Represents an object that can be inherited by other command objects. */ public abstract class Command { + //@@author wcwy // The command word used to trigger the execution of Moolah Manager's operations public static String COMMAND_WORD; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/CommandTag.java b/src/main/java/seedu/duke/command/CommandTag.java index adc57979d..7eef06f57 100644 --- a/src/main/java/seedu/duke/command/CommandTag.java +++ b/src/main/java/seedu/duke/command/CommandTag.java @@ -7,11 +7,11 @@ public class CommandTag { public static final String COMMAND_TAG_TRANSACTION_AMOUNT = "a/"; public static final String COMMAND_TAG_TRANSACTION_DATE = "d/"; public static final String COMMAND_TAG_TRANSACTION_DESCRIPTION = "i/"; - public static final String COMMAND_TAG_LIST_ENTRY_NUMBER = "e/"; + public static final String COMMAND_TAG_GLOBAL_ENTRY_NUMBER = "e/"; + public static final String COMMAND_TAG_GLOBAL_MONTH = "m/"; + public static final String COMMAND_TAG_GLOBAL_YEAR = "y/"; + public static final String COMMAND_TAG_GLOBAL_NUMBER = "n/"; + public static final String COMMAND_TAG_GLOBAL_PERIOD = "p/"; public static final String COMMAND_TAG_HELP_OPTION = "o/"; public static final String COMMAND_TAG_STATS_TYPE = "s/"; - public static final String COMMAND_TAG_STATS_MONTH = "m/"; - public static final String COMMAND_TAG_STATS_YEAR = "y/"; - public static final String COMMAND_TAG_STATS_NUMBER = "n/"; - public static final String COMMAND_TAG_STATS_PERIOD = "p/"; } \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index b9988ac46..0df8d73dc 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -1,9 +1,10 @@ +//@@author brian-vb package seedu.duke.command; import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.InvalidIndexException; +import seedu.duke.exception.GlobalInvalidIndexException; import seedu.duke.exception.MoolahException; import seedu.duke.exception.StorageWriteErrorException; @@ -11,13 +12,14 @@ import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; import static seedu.duke.common.InfoMessages.INFO_DELETE; /** * Represents a delete command object that will execute the operations for Delete command. */ public class DeleteCommand extends Command { + //@@author brian-vb private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "DELETE"; @@ -56,7 +58,7 @@ public DeleteCommand() { @Override public String[] getMandatoryTags() { // The mandatory tags that must exist in the user input - String[] mandatoryTags = new String[]{COMMAND_TAG_LIST_ENTRY_NUMBER}; + String[] mandatoryTags = new String[]{COMMAND_TAG_GLOBAL_ENTRY_NUMBER}; return mandatoryTags; } @@ -74,7 +76,7 @@ public void setEntryNumber(int entryNumber) { * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. * @param storage An instance of the Storage class. - * @throws InvalidIndexException If the index inputted is invalid. + * @throws GlobalInvalidIndexException If the index inputted is invalid. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { @@ -103,7 +105,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws } else { deleteLogger.log(Level.WARNING, "InvalidIndexException thrown when the index " + "is invalid."); - throw new InvalidIndexException(); + throw new GlobalInvalidIndexException(); } } catch (IOException e) { throw new StorageWriteErrorException(); diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index cb97ccc12..5957595a2 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -1,3 +1,4 @@ +//@@author brian-vb package seedu.duke.command; import seedu.duke.Storage; @@ -6,8 +7,8 @@ import java.time.LocalDate; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; @@ -17,6 +18,7 @@ * Represents an edit command object that will execute the operations for Edit command. */ public class EditCommand extends Command { + //@@author brian-vb private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "EDIT"; @@ -67,7 +69,7 @@ public EditCommand() { */ @Override public String[] getMandatoryTags() { - String[] mandatoryTags = new String[]{COMMAND_TAG_LIST_ENTRY_NUMBER}; + String[] mandatoryTags = new String[]{COMMAND_TAG_GLOBAL_ENTRY_NUMBER}; return mandatoryTags; } diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 31da3c378..ff0370cf2 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -1,3 +1,4 @@ +//@@author chydarren package seedu.duke.command; import seedu.duke.Storage; @@ -13,6 +14,7 @@ * Represents a find command object that will execute the operations for Find command. */ public class FindCommand extends Command { + //@@author chydarren private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "FIND"; diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 6039b099c..71ed7da18 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -1,3 +1,4 @@ +//@author wcwy package seedu.duke.command; import seedu.duke.Storage; @@ -10,6 +11,7 @@ * Represents a help command object that will execute the operations for Help command. */ public class HelpCommand extends Command { + //@@author wcwy private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "HELP"; diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index bccf1d4e6..738dd795c 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -1,22 +1,23 @@ +//@@author paullowse package seedu.duke.command; import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.InputMissingTagException; -import seedu.duke.exception.InputTransactionUnknownTypeException; -import seedu.duke.exception.ListStatsInvalidStatsTypeException; +import seedu.duke.exception.GlobalMissingTagException; +import seedu.duke.exception.InputTransactionInvalidTypeException; import seedu.duke.exception.MoolahException; import java.time.LocalDate; import java.util.logging.Logger; import java.util.logging.Level; +//@@author chydarren import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_YEAR; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; @@ -24,6 +25,7 @@ * Represents a list command object that will execute the operations for List command. */ public class ListCommand extends Command { + //@@author chydarren private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "LIST"; @@ -77,8 +79,8 @@ public String[] getOptionalTags() { COMMAND_TAG_TRANSACTION_TYPE, COMMAND_TAG_TRANSACTION_CATEGORY, COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_STATS_MONTH, - COMMAND_TAG_STATS_YEAR + COMMAND_TAG_GLOBAL_MONTH, + COMMAND_TAG_GLOBAL_YEAR }; return optionalTags; } @@ -132,17 +134,17 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param type The type of transaction. * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". - * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date, int month, int year) - throws InputTransactionUnknownTypeException, InputMissingTagException { + throws InputTransactionInvalidTypeException, GlobalMissingTagException { if (month != -1 && year == -1) { listLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); - throw new InputMissingTagException(); + throw new GlobalMissingTagException(); } - String transactionsList = transactions.listTransactions(type, category, date); + String transactionsList = transactions.listTransactions(type, category, date, month, year); if (transactionsList.isEmpty()) { listLogger.log(Level.INFO, "Transactions list is empty as there are no transactions available."); diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index d5c9a57ac..c7dac8ea3 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -1,3 +1,4 @@ +//@@author brian-vb package seedu.duke.command; import seedu.duke.Storage; @@ -19,6 +20,7 @@ * Represents a purge command object that will execute the operations for Purge command. */ public class PurgeCommand extends Command { + //@@author brian-vb private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "PURGE"; diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 24792b8e1..23850932c 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -1,10 +1,11 @@ +//@@author chydarren package seedu.duke.command; import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; -import seedu.duke.exception.ListStatsInvalidStatsTypeException; +import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; import java.time.LocalDate; @@ -12,11 +13,12 @@ import java.util.logging.Level; import java.util.logging.Logger; +//@@author paullowse import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_NUMBER; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_PERIOD; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_YEAR; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; import static seedu.duke.common.InfoMessages.INFO_STATS_EXPENSES; @@ -29,6 +31,7 @@ * Represents a get command object that will execute the operations for Get command. */ public class StatsCommand extends Command { + //@@author paullowse private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "STATS"; @@ -76,10 +79,10 @@ public String[] getMandatoryTags() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_STATS_MONTH, - COMMAND_TAG_STATS_YEAR, - COMMAND_TAG_STATS_NUMBER, - COMMAND_TAG_STATS_PERIOD, + COMMAND_TAG_GLOBAL_MONTH, + COMMAND_TAG_GLOBAL_YEAR, + COMMAND_TAG_GLOBAL_NUMBER, + COMMAND_TAG_GLOBAL_PERIOD, }; return optionalTags; } @@ -157,13 +160,13 @@ private static void listStatsByStatsType(String statsType, TransactionList trans statsTypeTimeSavings(transactions, year, month, period, number); } else { statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); - throw new ListStatsInvalidStatsTypeException(); + throw new StatsInvalidTypeException(); } break; //@@author chydarren default: statsLogger.log(Level.WARNING, "An exception has been caught due to an invalid stats type."); - throw new ListStatsInvalidStatsTypeException(); + throw new StatsInvalidTypeException(); } } @@ -216,7 +219,7 @@ public static void statsTypeTimeSavings(TransactionList transactions, int year, timeTransactions = transactions.getTransactionsByMonth(year, month); } else { statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); - throw new ListStatsInvalidStatsTypeException(); + throw new StatsInvalidTypeException(); } String timeSavingsList = transactions.listTimeStats(timeTransactions, year, month, period, number); @@ -239,7 +242,6 @@ public static void statsTypeTimeSavings(TransactionList transactions, int year, assert !timeSavingsList.isEmpty(); statsLogger.log(Level.INFO, "Monthly savings list is found to contain categories-amount pairs."); Ui.showStatsList(timeSavingsList, INFO_STATS_TIME.toString(), incomeMessage, expensesMessage, savingsMessage); - } //@@author paullowse diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index b39f18333..b60647ad4 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -4,25 +4,24 @@ * Provides enum variables for storing custom program error messages. */ public enum ErrorMessages { - ERROR_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), - ERROR_INPUT_UNSUPPORTED_TAG("Not supported tag(s) detected, please check your input!"), - ERROR_INPUT_DUPLICATE_TAG("Duplicate tag(s) detected, please check your input!"), - ERROR_INPUT_MISSING_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), - ERROR_INPUT_INVALID_AMOUNT("Invalid amount, " + ERROR_GLOBAL_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), + ERROR_GLOBAL_DUPLICATE_TAG("Duplicate tag(s) detected, please check your input!"), + ERROR_GLOBAL_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), + ERROR_GLOBAL_UNSUPPORTED_TAG("Not supported tag(s) detected, please check your input!"), + ERROR_GLOBAL_EMPTY_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), + ERROR_GLOBAL_INVALID_INDEX("Invalid index, please ensure your index is correct!"), + ERROR_GLOBAL_INVALID_MONTH("Invalid month, please check your input!"), + ERROR_GLOBAL_INVALID_YEAR("Invalid year, please check your input!"), + ERROR_GLOBAL_INVALID_PERIOD("Type of period given is invalid, please check your input!"), + ERROR_GLOBAL_INVALID_NUMBER("Number for period stats given is invalid, please check your input!"), + ERROR_GLOBAL_NUMBER_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), + ERROR_TRANSACTION_INVALID_AMOUNT("Invalid amount, " + "please ensure your amount is in positive numerals ($10000000 or less) only!"), - ERROR_INPUT_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), - ERROR_INPUT_INVALID_DATE("Invalid date, please ensure your date format is correct!"), - ERROR_INPUT_INVALID_TAG("Invalid tag(s) detected, please check your input!"), - ERROR_INPUT_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), - ERROR_INPUT_INVALID_INDEX("Invalid index, please ensure your index is correct!"), - ERROR_INPUT_INVALID_MONTH("Invalid month, please check your input!"), - ERROR_INPUT_INVALID_YEAR("Invalid year, please check your input!"), - ERROR_INPUT_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), - ERROR_ENTRY_NUMBER_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), + ERROR_TRANSACTION_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), + ERROR_TRANSACTION_INVALID_DATE("Invalid date, please ensure your date format is correct!"), + ERROR_TRANSACTION_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), - ERROR_STATS_COMMAND_INVALID_STATSTYPE("Type of statistics given is invalid, please check your input!"), - ERROR_STATS_COMMAND_INVALID_PERIOD("Type of period given is invalid, please check your input!"), - ERROR_STATS_COMMAND_INVALID_NUMBER("Number for period stats given is invalid, please check your input!"), + ERROR_STATS_COMMAND_INVALID_TYPE("Type of statistics given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. " + "To preserve data, please STOP the program and edit your data file correctly."), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b212000ae..381333e16 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -3,7 +3,7 @@ import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; import seedu.duke.data.transaction.Transaction; -import seedu.duke.exception.InputTransactionUnknownTypeException; +import seedu.duke.exception.InputTransactionInvalidTypeException; import java.time.LocalDate; import java.time.Year; @@ -136,7 +136,7 @@ public void addExpenseDuringStorage(String description, int amount, String categ * @param transaction The transaction record from the transactions list. * @param classType The transaction class type that is either Income or Expense. * @return A boolean value indicating whether transaction record belongs to the given class type. - * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ public boolean isTransactionInstance(Object transaction, String classType) throws ClassNotFoundException { return Class.forName(classType).isInstance(transaction); @@ -150,17 +150,17 @@ public boolean isTransactionInstance(Object transaction, String classType) throw * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". * @return A string containing the formatted transaction list. - * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ public boolean isMatchListFilters(Transaction transaction, String type, String category, - LocalDate date) throws InputTransactionUnknownTypeException { + LocalDate date) throws InputTransactionInvalidTypeException { boolean isMatch; try { isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) && (category.isEmpty() || transaction.getCategory().equals(category)) && (date == null || transaction.getDate().equals(date))); } catch (ClassNotFoundException e) { - throw new InputTransactionUnknownTypeException(); + throw new InputTransactionInvalidTypeException(); } return isMatch; } @@ -172,13 +172,24 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". * @return A string containing the formatted transaction list. - * @throws InputTransactionUnknownTypeException If class type cannot be found in the packages. + * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ - public String listTransactions(String type, String category, LocalDate date) - throws InputTransactionUnknownTypeException { + public String listTransactions(String type, String category, LocalDate date, int month, int year) + throws InputTransactionInvalidTypeException { String transactionsList = ""; + + ArrayList timeTransactions = new ArrayList<>(); + + if (year != -1 && month != -1) { + timeTransactions = getTransactionsByMonth(year, month); + } else if (year != -1) { + timeTransactions = getTransactionsByYear(year); + } else { + timeTransactions = transactions; + } + // Loops each transaction from the transactions list - for (Transaction transaction : transactions) { + for (Transaction transaction : timeTransactions) { if (isMatchListFilters(transaction, type, category, date)) { transactionsList += transaction.toString() + LINE_SEPARATOR; } diff --git a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java index f622df300..4e1a84a69 100644 --- a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java +++ b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java @@ -1,3 +1,4 @@ +//@@author chydarren package seedu.duke.exception; import seedu.duke.common.ErrorMessages; diff --git a/src/main/java/seedu/duke/exception/StatsInvalidNumberException.java b/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java similarity index 66% rename from src/main/java/seedu/duke/exception/StatsInvalidNumberException.java rename to src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java index 809f307d0..383718085 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidNumberException.java +++ b/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java @@ -1,8 +1,9 @@ +//@@author wcwy package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class StatsInvalidNumberException extends MoolahException { +public class GlobalDuplicateTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class StatsInvalidNumberException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_STATS_COMMAND_INVALID_NUMBER.toString(); + return ErrorMessages.ERROR_GLOBAL_DUPLICATE_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/EmptyParameterException.java b/src/main/java/seedu/duke/exception/GlobalEmptyParameterException.java similarity index 63% rename from src/main/java/seedu/duke/exception/EmptyParameterException.java rename to src/main/java/seedu/duke/exception/GlobalEmptyParameterException.java index 47c85536b..be40176e0 100644 --- a/src/main/java/seedu/duke/exception/EmptyParameterException.java +++ b/src/main/java/seedu/duke/exception/GlobalEmptyParameterException.java @@ -1,8 +1,9 @@ +//@@author chinhan99 package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class EmptyParameterException extends MoolahException { +public class GlobalEmptyParameterException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class EmptyParameterException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_MISSING_PARAMETER.toString(); + return ErrorMessages.ERROR_GLOBAL_EMPTY_PARAMETER.toString(); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidCommandException.java b/src/main/java/seedu/duke/exception/GlobalInvalidCommandException.java new file mode 100644 index 000000000..7d11727b5 --- /dev/null +++ b/src/main/java/seedu/duke/exception/GlobalInvalidCommandException.java @@ -0,0 +1,16 @@ +//@@author paullowse +package seedu.duke.exception; + +import static seedu.duke.common.ErrorMessages.ERROR_GLOBAL_INVALID_COMMAND; + +public class GlobalInvalidCommandException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ERROR_GLOBAL_INVALID_COMMAND.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/StatsInvalidPeriodException.java b/src/main/java/seedu/duke/exception/GlobalInvalidIndexException.java similarity index 65% rename from src/main/java/seedu/duke/exception/StatsInvalidPeriodException.java rename to src/main/java/seedu/duke/exception/GlobalInvalidIndexException.java index 7a1b21f57..ef7227cfa 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidPeriodException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidIndexException.java @@ -1,8 +1,9 @@ +//@@author brian-vb package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class StatsInvalidPeriodException extends MoolahException { +public class GlobalInvalidIndexException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class StatsInvalidPeriodException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_STATS_COMMAND_INVALID_PERIOD.toString(); + return ErrorMessages.ERROR_GLOBAL_INVALID_INDEX.toString(); } } diff --git a/src/main/java/seedu/duke/exception/InputDuplicateTagException.java b/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java similarity index 64% rename from src/main/java/seedu/duke/exception/InputDuplicateTagException.java rename to src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java index 7f8866a31..63aea92d9 100644 --- a/src/main/java/seedu/duke/exception/InputDuplicateTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java @@ -1,8 +1,9 @@ +//@@author chydarren package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class InputDuplicateTagException extends MoolahException { +public class GlobalInvalidMonthException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class InputDuplicateTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_DUPLICATE_TAG.toString(); + return ErrorMessages.ERROR_GLOBAL_INVALID_MONTH.toString(); } } diff --git a/src/main/java/seedu/duke/exception/InputUnsupportedTagException.java b/src/main/java/seedu/duke/exception/GlobalInvalidNumberException.java similarity index 66% rename from src/main/java/seedu/duke/exception/InputUnsupportedTagException.java rename to src/main/java/seedu/duke/exception/GlobalInvalidNumberException.java index 715fef4e6..6f6e75634 100644 --- a/src/main/java/seedu/duke/exception/InputUnsupportedTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidNumberException.java @@ -1,8 +1,9 @@ +//@@author brian-vb package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class InputUnsupportedTagException extends MoolahException { +public class GlobalInvalidNumberException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class InputUnsupportedTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_UNSUPPORTED_TAG.toString(); + return ErrorMessages.ERROR_GLOBAL_INVALID_NUMBER.toString(); } } diff --git a/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java b/src/main/java/seedu/duke/exception/GlobalInvalidPeriodException.java similarity index 65% rename from src/main/java/seedu/duke/exception/ListStatsMissingTagException.java rename to src/main/java/seedu/duke/exception/GlobalInvalidPeriodException.java index b2be07030..b2e2651ff 100644 --- a/src/main/java/seedu/duke/exception/ListStatsMissingTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidPeriodException.java @@ -1,15 +1,16 @@ -package seedu.duke.exception; - -import seedu.duke.common.ErrorMessages; - -public class ListStatsMissingTagException extends MoolahException { - /** - * Returns the error message of the exception to alert user of the exception. - * - * @return A string containing the error message - */ - @Override - public String getMessage() { - return ErrorMessages.ERROR_INPUT_MISSING_TAG.toString(); - } -} +//@@author paullowse +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class GlobalInvalidPeriodException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_GLOBAL_INVALID_PERIOD.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java b/src/main/java/seedu/duke/exception/GlobalInvalidYearException.java similarity index 66% rename from src/main/java/seedu/duke/exception/StatsInvalidMonthException.java rename to src/main/java/seedu/duke/exception/GlobalInvalidYearException.java index df52b0c1b..b2c90b4f7 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidMonthException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidYearException.java @@ -1,8 +1,9 @@ +//@@author paullowse package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class StatsInvalidMonthException extends MoolahException { +public class GlobalInvalidYearException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class StatsInvalidMonthException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_INVALID_MONTH.toString(); + return ErrorMessages.ERROR_GLOBAL_INVALID_YEAR.toString(); } } diff --git a/src/main/java/seedu/duke/exception/StatsInvalidYearException.java b/src/main/java/seedu/duke/exception/GlobalMissingTagException.java similarity index 66% rename from src/main/java/seedu/duke/exception/StatsInvalidYearException.java rename to src/main/java/seedu/duke/exception/GlobalMissingTagException.java index 60962ffec..a70d6588c 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidYearException.java +++ b/src/main/java/seedu/duke/exception/GlobalMissingTagException.java @@ -1,8 +1,9 @@ +//@@author wcwy package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class StatsInvalidYearException extends MoolahException { +public class GlobalMissingTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class StatsInvalidYearException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_INVALID_YEAR.toString(); + return ErrorMessages.ERROR_GLOBAL_MISSING_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidTagException.java b/src/main/java/seedu/duke/exception/GlobalNumberNotNumericException.java similarity index 63% rename from src/main/java/seedu/duke/exception/InputTransactionInvalidTagException.java rename to src/main/java/seedu/duke/exception/GlobalNumberNotNumericException.java index 7e7b92d61..089a363be 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalNumberNotNumericException.java @@ -1,8 +1,9 @@ +//@@author brian-vb package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class InputTransactionInvalidTagException extends MoolahException { +public class GlobalNumberNotNumericException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class InputTransactionInvalidTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_INVALID_TAG.toString(); + return ErrorMessages.ERROR_GLOBAL_NUMBER_NOT_NUMERIC.toString(); } } diff --git a/src/main/java/seedu/duke/exception/EntryNumberNotNumericException.java b/src/main/java/seedu/duke/exception/GlobalUnsupportedTagException.java similarity index 64% rename from src/main/java/seedu/duke/exception/EntryNumberNotNumericException.java rename to src/main/java/seedu/duke/exception/GlobalUnsupportedTagException.java index d7c4b49e0..524f953f0 100644 --- a/src/main/java/seedu/duke/exception/EntryNumberNotNumericException.java +++ b/src/main/java/seedu/duke/exception/GlobalUnsupportedTagException.java @@ -1,8 +1,9 @@ +//@@author chinhan99 package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class EntryNumberNotNumericException extends MoolahException { +public class GlobalUnsupportedTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class EntryNumberNotNumericException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_ENTRY_NUMBER_NOT_NUMERIC.toString(); + return ErrorMessages.ERROR_GLOBAL_UNSUPPORTED_TAG.toString(); } } diff --git a/src/main/java/seedu/duke/exception/UnknownHelpOptionException.java b/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java similarity index 81% rename from src/main/java/seedu/duke/exception/UnknownHelpOptionException.java rename to src/main/java/seedu/duke/exception/HelpUnknownOptionException.java index a97827fab..8967a770b 100644 --- a/src/main/java/seedu/duke/exception/UnknownHelpOptionException.java +++ b/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java @@ -1,8 +1,9 @@ +//@@author wcwy package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class UnknownHelpOptionException extends MoolahException { +public class HelpUnknownOptionException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java index 0bd57edf0..8a3a8b70f 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java @@ -1,8 +1,8 @@ +//@@author chinhan99 package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -//@@author chinhan99 public class InputTransactionInvalidAmountException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. @@ -11,8 +11,7 @@ public class InputTransactionInvalidAmountException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_INVALID_AMOUNT.toString(); + return ErrorMessages.ERROR_TRANSACTION_INVALID_AMOUNT.toString(); } -} -//@@author \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java index 41eadcabb..78823b273 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java @@ -1,3 +1,4 @@ +//@@author chinhan99 package seedu.duke.exception; import seedu.duke.common.ErrorMessages; @@ -10,7 +11,7 @@ public class InputTransactionInvalidCategoryException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_INVALID_CATEGORY.toString(); + return ErrorMessages.ERROR_TRANSACTION_INVALID_CATEGORY.toString(); } } diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java index fb3355b39..7756806bb 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java @@ -1,3 +1,4 @@ +//@@author wcwy package seedu.duke.exception; import seedu.duke.common.ErrorMessages; @@ -11,6 +12,6 @@ public class InputTransactionInvalidDateException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_INVALID_DATE.toString(); + return ErrorMessages.ERROR_TRANSACTION_INVALID_DATE.toString(); } } diff --git a/src/main/java/seedu/duke/exception/InputTransactionUnknownTypeException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java similarity index 65% rename from src/main/java/seedu/duke/exception/InputTransactionUnknownTypeException.java rename to src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java index 4605f4c7a..8d2a91c7c 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionUnknownTypeException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java @@ -1,8 +1,9 @@ +//@@author chydarren package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class InputTransactionUnknownTypeException extends MoolahException { +public class InputTransactionInvalidTypeException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +11,6 @@ public class InputTransactionUnknownTypeException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_INVALID_TYPE.toString(); + return ErrorMessages.ERROR_TRANSACTION_INVALID_TYPE.toString(); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/InvalidCommandException.java b/src/main/java/seedu/duke/exception/InvalidCommandException.java deleted file mode 100644 index 97706c5b4..000000000 --- a/src/main/java/seedu/duke/exception/InvalidCommandException.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.duke.exception; - -import static seedu.duke.common.ErrorMessages.ERROR_INVALID_COMMAND; - -public class InvalidCommandException extends MoolahException { - /** - * Returns the error message of the exception to alert user of the exception. - * - * @return A string containing the error message - */ - @Override - public String getMessage() { - return ERROR_INVALID_COMMAND.toString(); - } -} diff --git a/src/main/java/seedu/duke/exception/InvalidIndexException.java b/src/main/java/seedu/duke/exception/InvalidIndexException.java deleted file mode 100644 index 5d9f3a690..000000000 --- a/src/main/java/seedu/duke/exception/InvalidIndexException.java +++ /dev/null @@ -1,15 +0,0 @@ -package seedu.duke.exception; - -import seedu.duke.common.ErrorMessages; - -public class InvalidIndexException extends MoolahException { - /** - * Returns the error message of the exception to alert user of the exception. - * - * @return A string containing the error message - */ - @Override - public String getMessage() { - return ErrorMessages.ERROR_INPUT_INVALID_INDEX.toString(); - } -} diff --git a/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java b/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java deleted file mode 100644 index 1ffccfdf3..000000000 --- a/src/main/java/seedu/duke/exception/ListStatsInvalidStatsTypeException.java +++ /dev/null @@ -1,16 +0,0 @@ -package seedu.duke.exception; - -import seedu.duke.common.ErrorMessages; - -public class ListStatsInvalidStatsTypeException extends MoolahException { - - /** - * Returns the error message of the exception to alert user of the exception. - * - * @return A string containing the error message - */ - @Override - public String getMessage() { - return ErrorMessages.ERROR_STATS_COMMAND_INVALID_STATSTYPE.toString(); - } -} diff --git a/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java b/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java index 606a2ffa4..aa8c1899f 100644 --- a/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java +++ b/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java @@ -1,3 +1,4 @@ +//@@author wcwy package seedu.duke.exception; import seedu.duke.common.ErrorMessages; diff --git a/src/main/java/seedu/duke/exception/MoolahException.java b/src/main/java/seedu/duke/exception/MoolahException.java index d3ba3d9a7..87fe11a45 100644 --- a/src/main/java/seedu/duke/exception/MoolahException.java +++ b/src/main/java/seedu/duke/exception/MoolahException.java @@ -1,3 +1,4 @@ +//@@author wcwy package seedu.duke.exception; /** diff --git a/src/main/java/seedu/duke/exception/InputMissingTagException.java b/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java similarity index 64% rename from src/main/java/seedu/duke/exception/InputMissingTagException.java rename to src/main/java/seedu/duke/exception/StatsInvalidTypeException.java index 5d9c6d988..f37737efd 100644 --- a/src/main/java/seedu/duke/exception/InputMissingTagException.java +++ b/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java @@ -1,8 +1,10 @@ +//@@author chydarren package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class InputMissingTagException extends MoolahException { +public class StatsInvalidTypeException extends MoolahException { + /** * Returns the error message of the exception to alert user of the exception. * @@ -10,6 +12,6 @@ public class InputMissingTagException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_INPUT_MISSING_TAG.toString(); + return ErrorMessages.ERROR_STATS_COMMAND_INVALID_TYPE.toString(); } } diff --git a/src/main/java/seedu/duke/exception/StorageInputCorruptedException.java b/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java similarity index 79% rename from src/main/java/seedu/duke/exception/StorageInputCorruptedException.java rename to src/main/java/seedu/duke/exception/StorageFileCorruptedException.java index fb8e67419..f310b4a77 100644 --- a/src/main/java/seedu/duke/exception/StorageInputCorruptedException.java +++ b/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java @@ -1,8 +1,9 @@ +//@@author chinhan99 package seedu.duke.exception; import seedu.duke.common.ErrorMessages; -public class StorageInputCorruptedException extends MoolahException { +public class StorageFileCorruptedException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/StorageWriteErrorException.java b/src/main/java/seedu/duke/exception/StorageWriteErrorException.java index 764baf451..b188bd2fa 100644 --- a/src/main/java/seedu/duke/exception/StorageWriteErrorException.java +++ b/src/main/java/seedu/duke/exception/StorageWriteErrorException.java @@ -1,3 +1,4 @@ +//@@author chinhan99 package seedu.duke.exception; import seedu.duke.common.ErrorMessages; diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index 6c6695f05..cacc260bb 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -12,7 +12,7 @@ import seedu.duke.command.PurgeCommand; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.InvalidCommandException; +import seedu.duke.exception.GlobalInvalidCommandException; import java.util.logging.Level; import java.util.logging.Logger; @@ -89,9 +89,9 @@ public static String[] splitInput(String fullCommandInput) { * * @param commandWordInput The command word entered by user. * @return Command object created. - * @throws InvalidCommandException If the command word is not supported by the application. + * @throws GlobalInvalidCommandException If the command word is not supported by the application. */ - public static Command getCommand(String commandWordInput, String parameterInput) throws InvalidCommandException { + public static Command getCommand(String commandWordInput, String parameterInput) throws GlobalInvalidCommandException { // TODO: Remove parameter input once a solution is found for managing parameter that allows space Command command = null; switch (commandWordInput.toUpperCase()) { @@ -124,7 +124,7 @@ public static Command getCommand(String commandWordInput, String parameterInput) break; default: parserLogger.log(Level.WARNING, "An invalid command error is caught in this command"); - throw new InvalidCommandException(); + throw new GlobalInvalidCommandException(); } return command; } diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 9260f064a..50027ceef 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -1,26 +1,32 @@ +//@@author wcwy package seedu.duke.parser; import seedu.duke.command.Command; import seedu.duke.command.ListCommand; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -import seedu.duke.exception.EmptyParameterException; -import seedu.duke.exception.EntryNumberNotNumericException; -import seedu.duke.exception.InputDuplicateTagException; -import seedu.duke.exception.InputMissingTagException; + +//@@author chydarren +import seedu.duke.exception.GlobalEmptyParameterException; +import seedu.duke.exception.GlobalInvalidMonthException; +import seedu.duke.exception.GlobalInvalidNumberException; +import seedu.duke.exception.GlobalInvalidPeriodException; +import seedu.duke.exception.GlobalInvalidYearException; +import seedu.duke.exception.GlobalNumberNotNumericException; +import seedu.duke.exception.GlobalDuplicateTagException; +import seedu.duke.exception.GlobalMissingTagException; + +//@@author brian-vb import seedu.duke.exception.InputTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; -import seedu.duke.exception.InputTransactionUnknownTypeException; -import seedu.duke.exception.InputUnsupportedTagException; -import seedu.duke.exception.ListStatsInvalidStatsTypeException; +import seedu.duke.exception.InputTransactionInvalidTypeException; +import seedu.duke.exception.GlobalUnsupportedTagException; +import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.StatsInvalidMonthException; -import seedu.duke.exception.StatsInvalidNumberException; -import seedu.duke.exception.StatsInvalidPeriodException; -import seedu.duke.exception.StatsInvalidYearException; -import seedu.duke.exception.UnknownHelpOptionException; +import seedu.duke.exception.HelpUnknownOptionException; +//@@author wcwy import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -30,23 +36,27 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +//@@author paullowse import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; -import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_MONTH; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_NUMBER; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_PERIOD; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_YEAR; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; + +//@@author chinhan99 import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; + +//@@author chinhan99 import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; - /** * Parses the parameter portion of the user input and set the parameters into the Command object. * @@ -103,7 +113,7 @@ public static void parse(Command command, String parametersInput) throws MoolahE checkUnsupportedTagsNotExist(command, splits); // Might throw InputDuplicateTagException checkDuplicateTagsNotExist(splits); - // Might throw InputMissingParameterException + // Might throw InputEmptyParameterException checkParameterNotEmpty(splits); // The parameters input contains only the supported tags. @@ -120,15 +130,15 @@ public static void parse(Command command, String parametersInput) throws MoolahE * * @param command A command object created based on the command word given by user. * @param splits The user input after the command word, split into a list for every space found. - * @throws InputMissingTagException If there is a missing mandatory tag. + * @throws GlobalMissingTagException If there is a missing mandatory tag. */ - public static void checkMandatoryTagsExist(Command command, String[] splits) throws InputMissingTagException { + public static void checkMandatoryTagsExist(Command command, String[] splits) throws GlobalMissingTagException { String[] tags = command.getMandatoryTags(); for (String tag : tags) { boolean found = findMatchingTagAmongInputs(tag, splits); if (!found) { parserLogger.log(Level.WARNING, "A missing tag error is caught for the given tag: " + tag); - throw new InputMissingTagException(); + throw new GlobalMissingTagException(); } } } @@ -140,10 +150,10 @@ public static void checkMandatoryTagsExist(Command command, String[] splits) thr * * @param command A command object created based on the command word given by user. * @param splits The user input after the command word, split into a list for every space found. - * @throws InputUnsupportedTagException If there is an extra tag that is not recognised. + * @throws GlobalUnsupportedTagException If there is an extra tag that is not recognised. */ public static void checkUnsupportedTagsNotExist(Command command, String[] splits) - throws InputUnsupportedTagException { + throws GlobalUnsupportedTagException { String[] mandatoryTags = command.getMandatoryTags(); String[] optionalTags = command.getOptionalTags(); @@ -151,7 +161,7 @@ public static void checkUnsupportedTagsNotExist(Command command, String[] splits if (split.length() < MINIMUM_TAG_LENGTH) { // None of the tags is shorter than two characters parserLogger.log(Level.WARNING, "An unsupported tag error is caught for the given tag: " + split); - throw new InputUnsupportedTagException(); + throw new GlobalUnsupportedTagException(); } boolean hasFoundAmongMandatoryTag = findIfParameterTagAmongTags(split, mandatoryTags); boolean hasFoundAmongOptionalTag = findIfParameterTagAmongTags(split, optionalTags); @@ -161,7 +171,7 @@ public static void checkUnsupportedTagsNotExist(Command command, String[] splits // Found a tag entered by the user but does not exist in the supported tag for the command parserLogger.log(Level.WARNING, "An unsupported tag error is caught for the given tag: " + split); - throw new InputUnsupportedTagException(); + throw new GlobalUnsupportedTagException(); } } @@ -171,9 +181,9 @@ public static void checkUnsupportedTagsNotExist(Command command, String[] splits * Checks if the split user inputs contains a tag multiple times. * * @param splits The user input after the command word, split into a list for every space found. - * @throws InputDuplicateTagException If there is an extra of the same tag. + * @throws GlobalDuplicateTagException If there is an extra of the same tag. */ - public static void checkDuplicateTagsNotExist(String[] splits) throws InputDuplicateTagException { + public static void checkDuplicateTagsNotExist(String[] splits) throws GlobalDuplicateTagException { HashMap tagOccurenceMap = new HashMap<>(); for (String split : splits) { assert split.length() >= MINIMUM_TAG_LENGTH; @@ -182,7 +192,7 @@ public static void checkDuplicateTagsNotExist(String[] splits) throws InputDupli // The duplicated tag can be found in the hash map if (tagOccurenceMap.containsKey(tag)) { parserLogger.log(Level.WARNING, "An duplicate tag error is caught for the given tag: " + tag); - throw new InputDuplicateTagException(); + throw new GlobalDuplicateTagException(); } tagOccurenceMap.put(tag, 1); } @@ -195,14 +205,14 @@ public static void checkDuplicateTagsNotExist(String[] splits) throws InputDupli * If the split.length() is <= 2, it means that only the tag exists , and there is no parameter after the tag. * * @param splits The user input after the command word, split into a list for every space found. - * @throws EmptyParameterException If there exists a tag without parameter. + * @throws GlobalEmptyParameterException If there exists a tag without parameter. */ - public static void checkParameterNotEmpty(String[] splits) throws EmptyParameterException { + public static void checkParameterNotEmpty(String[] splits) throws GlobalEmptyParameterException { for (String split : splits) { if (split.length() == 2) { parserLogger.log(Level.WARNING, "An empty parameter error is caught for the " + "given tag input: " + split); - throw new EmptyParameterException(); + throw new GlobalEmptyParameterException(); } } } @@ -304,7 +314,7 @@ private static void setParameter(Command command, String tag, String parameter) case COMMAND_TAG_TRANSACTION_DESCRIPTION: command.setDescription(parameter); break; - case COMMAND_TAG_LIST_ENTRY_NUMBER: + case COMMAND_TAG_GLOBAL_ENTRY_NUMBER: command.setEntryNumber(parseEntryTag(parameter)); break; case COMMAND_TAG_HELP_OPTION: @@ -313,21 +323,21 @@ private static void setParameter(Command command, String tag, String parameter) case COMMAND_TAG_STATS_TYPE: command.setStatsType(parseStatsTypeTag(parameter)); break; - case COMMAND_TAG_STATS_MONTH: - command.setStatsMonth(parseStatsMonthTag(parameter)); + case COMMAND_TAG_GLOBAL_MONTH: + command.setStatsMonth(parseMonthTag(parameter)); break; - case COMMAND_TAG_STATS_YEAR: - command.setStatsYear(parseStatsYearTag(parameter)); + case COMMAND_TAG_GLOBAL_YEAR: + command.setStatsYear(parseYearTag(parameter)); break; - case COMMAND_TAG_STATS_NUMBER: - command.setStatsNumber(parseStatsNumberTag(parameter)); + case COMMAND_TAG_GLOBAL_NUMBER: + command.setStatsNumber(parseNumberTag(parameter)); break; - case COMMAND_TAG_STATS_PERIOD: - command.setStatsPeriod(parseStatsPeriodTag(parameter)); + case COMMAND_TAG_GLOBAL_PERIOD: + command.setStatsPeriod(parsePeriodTag(parameter)); break; default: parserLogger.log(Level.WARNING, "An unsupported tag exception is caught: " + tag); - throw new InputUnsupportedTagException(); + throw new GlobalUnsupportedTagException(); } } @@ -338,9 +348,9 @@ private static void setParameter(Command command, String tag, String parameter) * * @param parameter The user input after the user tag. * @return The class type if no exceptions are thrown. - * @throws InputTransactionUnknownTypeException If the transaction type provided is not supported. + * @throws InputTransactionInvalidTypeException If the transaction type provided is not supported. */ - public static String parseTypeTagForListing(String parameter) throws InputTransactionUnknownTypeException { + public static String parseTypeTagForListing(String parameter) throws InputTransactionInvalidTypeException { switch (parameter) { case "expense": return CLASS_TYPE_EXPENSE; @@ -349,7 +359,7 @@ public static String parseTypeTagForListing(String parameter) throws InputTransa default: parserLogger.log(Level.WARNING, "An invalid type error " + "is caught for the given parameter: " + parameter); - throw new InputTransactionUnknownTypeException(); + throw new InputTransactionInvalidTypeException(); } } @@ -360,16 +370,16 @@ public static String parseTypeTagForListing(String parameter) throws InputTransa * * @param parameter The user input after the user tag. * @return The user input after the user tag. - * @throws InputTransactionUnknownTypeException If the transaction type provided is not supported. + * @throws InputTransactionInvalidTypeException If the transaction type provided is not supported. */ - public static String parseTypeTagForAdding(String parameter) throws InputTransactionUnknownTypeException { + public static String parseTypeTagForAdding(String parameter) throws InputTransactionInvalidTypeException { boolean isExpense = parameter.equals(Expense.TRANSACTION_NAME); boolean isIncome = parameter.equals(Income.TRANSACTION_NAME); if (!isExpense && !isIncome) { parserLogger.log(Level.WARNING, "An invalid type error " + "is caught for the given parameter: " + parameter); - throw new InputTransactionUnknownTypeException(); + throw new InputTransactionInvalidTypeException(); } return parameter; @@ -462,7 +472,7 @@ public static int parseEntryTag(String parameter) throws MoolahException { } catch (NumberFormatException e) { parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + parameter); - throw new EntryNumberNotNumericException(); + throw new GlobalNumberNotNumericException(); } return index; @@ -475,16 +485,16 @@ public static int parseEntryTag(String parameter) throws MoolahException { * * @param parameter The user input after the user tag. * @return A boolean value indicating if the option selected by user is "detailed" - * @throws UnknownHelpOptionException If the help option parameter selected is not 'detailed'. + * @throws HelpUnknownOptionException If the help option parameter selected is not 'detailed'. */ - public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOptionException { + public static boolean parseHelpOptionTag(String parameter) throws HelpUnknownOptionException { boolean isValidHelpOption = parameter.equals("detailed"); if (isValidHelpOption) { return true; } else { parserLogger.log(Level.WARNING, "An invalid help option error is caught for the given parameter: " + parameter); - throw new UnknownHelpOptionException(); + throw new HelpUnknownOptionException(); } } @@ -495,9 +505,9 @@ public static boolean parseHelpOptionTag(String parameter) throws UnknownHelpOpt * * @param parameter The user input after the user tag. * @return The statistic type. - * @throws ListStatsInvalidStatsTypeException If the statistic type given is not supported. + * @throws StatsInvalidTypeException If the statistic type given is not supported. */ - public static String parseStatsTypeTag(String parameter) throws ListStatsInvalidStatsTypeException { + public static String parseStatsTypeTag(String parameter) throws StatsInvalidTypeException { switch (parameter) { case CATEGORIES: return CATEGORIES; @@ -506,50 +516,50 @@ public static String parseStatsTypeTag(String parameter) throws ListStatsInvalid default: parserLogger.log(Level.WARNING, "An invalid statistic type error is caught for the given parameter: " + parameter); - throw new ListStatsInvalidStatsTypeException(); + throw new StatsInvalidTypeException(); } } //@@author paullowse - public static int parseStatsMonthTag(String parameter) throws StatsInvalidMonthException, - EntryNumberNotNumericException { + public static int parseMonthTag(String parameter) throws GlobalInvalidMonthException, + GlobalNumberNotNumericException { int month; try { month = Integer.parseInt(parameter); } catch (NumberFormatException e) { - parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + parserLogger.log(Level.WARNING, "An invalid number error is caught for the given parameter: " + parameter); - throw new EntryNumberNotNumericException(); + throw new GlobalNumberNotNumericException(); } if (month > 12 || month < 0) { parserLogger.log(Level.WARNING, "An invalid month number error is caught for the given parameter: " + parameter); - throw new StatsInvalidMonthException(); + throw new GlobalInvalidMonthException(); } return month; } - public static int parseStatsYearTag(String parameter) throws StatsInvalidYearException, - EntryNumberNotNumericException { + public static int parseYearTag(String parameter) throws GlobalInvalidYearException, + GlobalNumberNotNumericException { int year; try { year = Integer.parseInt(parameter); } catch (NumberFormatException e) { - parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + parserLogger.log(Level.WARNING, "An invalid number error is caught for the given parameter: " + parameter); - throw new EntryNumberNotNumericException(); + throw new GlobalNumberNotNumericException(); } if (year < 0) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); - throw new StatsInvalidYearException(); + throw new GlobalInvalidYearException(); } return year; } - public static String parseStatsPeriodTag(String parameter) throws StatsInvalidPeriodException { + public static String parsePeriodTag(String parameter) throws GlobalInvalidPeriodException { String period; switch (parameter) { case WEEKS: @@ -557,26 +567,26 @@ public static String parseStatsPeriodTag(String parameter) throws StatsInvalidPe case MONTHS: return MONTHS; default: - parserLogger.log(Level.WARNING, "An invalid statistic period error is caught for the given parameter: " + parserLogger.log(Level.WARNING, "An invalid period error is caught for the given parameter: " + parameter); - throw new StatsInvalidPeriodException(); + throw new GlobalInvalidPeriodException(); } } - public static int parseStatsNumberTag(String parameter) throws EntryNumberNotNumericException, - StatsInvalidNumberException { + public static int parseNumberTag(String parameter) throws GlobalNumberNotNumericException, + GlobalInvalidNumberException { int statsNumber; try { statsNumber = Integer.parseInt(parameter); } catch (NumberFormatException e) { - parserLogger.log(Level.WARNING, "An invalid entry number error is caught for the given parameter: " + parserLogger.log(Level.WARNING, "An invalid number error is caught for the given parameter: " + parameter); - throw new EntryNumberNotNumericException(); + throw new GlobalNumberNotNumericException(); } if (statsNumber < 0) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); - throw new StatsInvalidNumberException(); + throw new GlobalInvalidNumberException(); } return statsNumber; } diff --git a/src/test/java/seedu/duke/parser/ParameterParserTest.java b/src/test/java/seedu/duke/parser/ParameterParserTest.java index 985120ee6..75686b233 100644 --- a/src/test/java/seedu/duke/parser/ParameterParserTest.java +++ b/src/test/java/seedu/duke/parser/ParameterParserTest.java @@ -5,7 +5,7 @@ import seedu.duke.exception.InputTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; -import seedu.duke.exception.EmptyParameterException; +import seedu.duke.exception.GlobalEmptyParameterException; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -53,7 +53,7 @@ public void parse_AddCommandEmptyDate_ExpectedException() { String parametersInput = "t/income c/bonus a/1 d/ i/thank_you_boss"; assertThrows( - EmptyParameterException.class, + GlobalEmptyParameterException.class, () -> ParameterParser.parse(addCommand, parametersInput) ); } From 3b9c50935f8337862fe3738376f909ce5f99a8dd Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 14:29:54 +0800 Subject: [PATCH 203/416] Minor fixes against Gradle test error --- src/main/java/seedu/duke/command/AddCommand.java | 2 +- src/main/java/seedu/duke/command/ByeCommand.java | 2 +- src/main/java/seedu/duke/command/Command.java | 2 +- src/main/java/seedu/duke/command/DeleteCommand.java | 2 +- src/main/java/seedu/duke/command/EditCommand.java | 2 +- src/main/java/seedu/duke/command/FindCommand.java | 2 +- src/main/java/seedu/duke/command/HelpCommand.java | 2 +- src/main/java/seedu/duke/command/ListCommand.java | 6 +++--- src/main/java/seedu/duke/command/PurgeCommand.java | 2 +- src/main/java/seedu/duke/command/StatsCommand.java | 10 +++++----- .../FindTransactionMissingKeywordsException.java | 2 +- .../duke/exception/GlobalDuplicateTagException.java | 2 +- .../duke/exception/GlobalEmptyParameterException.java | 2 +- .../duke/exception/GlobalInvalidCommandException.java | 2 +- .../duke/exception/GlobalInvalidIndexException.java | 2 +- .../duke/exception/GlobalInvalidMonthException.java | 2 +- .../duke/exception/GlobalInvalidNumberException.java | 2 +- .../duke/exception/GlobalInvalidPeriodException.java | 2 +- .../duke/exception/GlobalInvalidYearException.java | 2 +- .../duke/exception/GlobalMissingTagException.java | 2 +- .../exception/GlobalNumberNotNumericException.java | 2 +- .../duke/exception/GlobalUnsupportedTagException.java | 2 +- .../duke/exception/HelpUnknownOptionException.java | 2 +- .../InputTransactionInvalidAmountException.java | 2 +- .../InputTransactionInvalidCategoryException.java | 2 +- .../InputTransactionInvalidDateException.java | 2 +- .../InputTransactionInvalidTypeException.java | 2 +- .../exception/MaximumTransactionCountException.java | 2 +- .../java/seedu/duke/exception/MoolahException.java | 3 ++- .../duke/exception/StatsInvalidTypeException.java | 2 +- .../duke/exception/StorageFileCorruptedException.java | 2 +- .../duke/exception/StorageWriteErrorException.java | 2 +- src/main/java/seedu/duke/parser/CommandParser.java | 3 ++- src/main/java/seedu/duke/parser/ParameterParser.java | 2 +- 34 files changed, 42 insertions(+), 40 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index f1fc76b81..405a06c56 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -1,6 +1,6 @@ -//@@author wcwy package seedu.duke.command; +//@@author wcwy import seedu.duke.Storage; import seedu.duke.Ui; diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index 9a00b013a..94491fb4b 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -1,6 +1,6 @@ -//@@author paullowse package seedu.duke.command; +//@@author paullowse import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index f0a6e126d..7e1e49449 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -1,6 +1,6 @@ -//@@author chinhan99 package seedu.duke.command; +//@@author chinhan99 import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 0df8d73dc..2fe0355c8 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -1,6 +1,6 @@ -//@@author brian-vb package seedu.duke.command; +//@@author brian-vb import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 5957595a2..342f15cc9 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -1,6 +1,6 @@ -//@@author brian-vb package seedu.duke.command; +//@@author brian-vb import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index ff0370cf2..484bf7a6d 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -1,6 +1,6 @@ -//@@author chydarren package seedu.duke.command; +//@@author chydarren import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 71ed7da18..d3c35cb64 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -1,6 +1,6 @@ -//@author wcwy package seedu.duke.command; +//@author wcwy import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 738dd795c..34690b1d8 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -1,6 +1,6 @@ -//@@author paullowse package seedu.duke.command; +//@@author paullowse import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; @@ -79,8 +79,8 @@ public String[] getOptionalTags() { COMMAND_TAG_TRANSACTION_TYPE, COMMAND_TAG_TRANSACTION_CATEGORY, COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_GLOBAL_MONTH, - COMMAND_TAG_GLOBAL_YEAR + COMMAND_TAG_GLOBAL_MONTH, + COMMAND_TAG_GLOBAL_YEAR }; return optionalTags; } diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index c7dac8ea3..c70a705ae 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -1,6 +1,6 @@ -//@@author brian-vb package seedu.duke.command; +//@@author brian-vb import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 23850932c..cf4b9ff4d 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -1,6 +1,6 @@ -//@@author chydarren package seedu.duke.command; +//@@author chydarren import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; @@ -79,10 +79,10 @@ public String[] getMandatoryTags() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_GLOBAL_MONTH, - COMMAND_TAG_GLOBAL_YEAR, - COMMAND_TAG_GLOBAL_NUMBER, - COMMAND_TAG_GLOBAL_PERIOD, + COMMAND_TAG_GLOBAL_MONTH, + COMMAND_TAG_GLOBAL_YEAR, + COMMAND_TAG_GLOBAL_NUMBER, + COMMAND_TAG_GLOBAL_PERIOD, }; return optionalTags; } diff --git a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java index 4e1a84a69..823b933f5 100644 --- a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java +++ b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java @@ -1,6 +1,6 @@ -//@@author chydarren package seedu.duke.exception; +//@@author chydarren import seedu.duke.common.ErrorMessages; public class FindTransactionMissingKeywordsException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java b/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java index 383718085..5b9e9b4ae 100644 --- a/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java @@ -1,6 +1,6 @@ -//@@author wcwy package seedu.duke.exception; +//@@author wcwy import seedu.duke.common.ErrorMessages; public class GlobalDuplicateTagException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalEmptyParameterException.java b/src/main/java/seedu/duke/exception/GlobalEmptyParameterException.java index be40176e0..974f08789 100644 --- a/src/main/java/seedu/duke/exception/GlobalEmptyParameterException.java +++ b/src/main/java/seedu/duke/exception/GlobalEmptyParameterException.java @@ -1,6 +1,6 @@ -//@@author chinhan99 package seedu.duke.exception; +//@@author chinhan99 import seedu.duke.common.ErrorMessages; public class GlobalEmptyParameterException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidCommandException.java b/src/main/java/seedu/duke/exception/GlobalInvalidCommandException.java index 7d11727b5..55eee6e37 100644 --- a/src/main/java/seedu/duke/exception/GlobalInvalidCommandException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidCommandException.java @@ -1,6 +1,6 @@ -//@@author paullowse package seedu.duke.exception; +//@@author paullowse import static seedu.duke.common.ErrorMessages.ERROR_GLOBAL_INVALID_COMMAND; public class GlobalInvalidCommandException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidIndexException.java b/src/main/java/seedu/duke/exception/GlobalInvalidIndexException.java index ef7227cfa..9798485ec 100644 --- a/src/main/java/seedu/duke/exception/GlobalInvalidIndexException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidIndexException.java @@ -1,6 +1,6 @@ -//@@author brian-vb package seedu.duke.exception; +//@@author brian-vb import seedu.duke.common.ErrorMessages; public class GlobalInvalidIndexException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java b/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java index 63aea92d9..79e66ee00 100644 --- a/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java @@ -1,6 +1,6 @@ -//@@author chydarren package seedu.duke.exception; +//@@author chydarren import seedu.duke.common.ErrorMessages; public class GlobalInvalidMonthException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidNumberException.java b/src/main/java/seedu/duke/exception/GlobalInvalidNumberException.java index 6f6e75634..64b1070bc 100644 --- a/src/main/java/seedu/duke/exception/GlobalInvalidNumberException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidNumberException.java @@ -1,6 +1,6 @@ -//@@author brian-vb package seedu.duke.exception; +//@@author brian-vb import seedu.duke.common.ErrorMessages; public class GlobalInvalidNumberException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidPeriodException.java b/src/main/java/seedu/duke/exception/GlobalInvalidPeriodException.java index b2e2651ff..abaf2ab50 100644 --- a/src/main/java/seedu/duke/exception/GlobalInvalidPeriodException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidPeriodException.java @@ -1,6 +1,6 @@ -//@@author paullowse package seedu.duke.exception; +//@@author paullowse import seedu.duke.common.ErrorMessages; public class GlobalInvalidPeriodException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidYearException.java b/src/main/java/seedu/duke/exception/GlobalInvalidYearException.java index b2c90b4f7..4c279d546 100644 --- a/src/main/java/seedu/duke/exception/GlobalInvalidYearException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidYearException.java @@ -1,6 +1,6 @@ -//@@author paullowse package seedu.duke.exception; +//@@author paullowse import seedu.duke.common.ErrorMessages; public class GlobalInvalidYearException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalMissingTagException.java b/src/main/java/seedu/duke/exception/GlobalMissingTagException.java index a70d6588c..6b18985f8 100644 --- a/src/main/java/seedu/duke/exception/GlobalMissingTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalMissingTagException.java @@ -1,6 +1,6 @@ -//@@author wcwy package seedu.duke.exception; +//@@author wcwy import seedu.duke.common.ErrorMessages; public class GlobalMissingTagException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalNumberNotNumericException.java b/src/main/java/seedu/duke/exception/GlobalNumberNotNumericException.java index 089a363be..07aa06360 100644 --- a/src/main/java/seedu/duke/exception/GlobalNumberNotNumericException.java +++ b/src/main/java/seedu/duke/exception/GlobalNumberNotNumericException.java @@ -1,6 +1,6 @@ -//@@author brian-vb package seedu.duke.exception; +//@@author brian-vb import seedu.duke.common.ErrorMessages; public class GlobalNumberNotNumericException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/GlobalUnsupportedTagException.java b/src/main/java/seedu/duke/exception/GlobalUnsupportedTagException.java index 524f953f0..0810776d7 100644 --- a/src/main/java/seedu/duke/exception/GlobalUnsupportedTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalUnsupportedTagException.java @@ -1,6 +1,6 @@ -//@@author chinhan99 package seedu.duke.exception; +//@@author chinhan99 import seedu.duke.common.ErrorMessages; public class GlobalUnsupportedTagException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java b/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java index 8967a770b..1c4fbb121 100644 --- a/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java +++ b/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java @@ -1,6 +1,6 @@ -//@@author wcwy package seedu.duke.exception; +//@@author wcwy import seedu.duke.common.ErrorMessages; public class HelpUnknownOptionException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java index 8a3a8b70f..5a089dcdc 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidAmountException.java @@ -1,6 +1,6 @@ -//@@author chinhan99 package seedu.duke.exception; +//@@author chinhan99 import seedu.duke.common.ErrorMessages; public class InputTransactionInvalidAmountException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java index 78823b273..1d0dd9d71 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidCategoryException.java @@ -1,6 +1,6 @@ -//@@author chinhan99 package seedu.duke.exception; +//@@author chinhan99 import seedu.duke.common.ErrorMessages; public class InputTransactionInvalidCategoryException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java index 7756806bb..63dadcf22 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java @@ -1,6 +1,6 @@ -//@@author wcwy package seedu.duke.exception; +//@@author wcwy import seedu.duke.common.ErrorMessages; public class InputTransactionInvalidDateException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java index 8d2a91c7c..696e0afde 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java @@ -1,6 +1,6 @@ -//@@author chydarren package seedu.duke.exception; +//@@author chydarren import seedu.duke.common.ErrorMessages; public class InputTransactionInvalidTypeException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java b/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java index aa8c1899f..0bfd4010a 100644 --- a/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java +++ b/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java @@ -1,6 +1,6 @@ -//@@author wcwy package seedu.duke.exception; +//@@author wcwy import seedu.duke.common.ErrorMessages; public class MaximumTransactionCountException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/MoolahException.java b/src/main/java/seedu/duke/exception/MoolahException.java index 87fe11a45..57d134ca0 100644 --- a/src/main/java/seedu/duke/exception/MoolahException.java +++ b/src/main/java/seedu/duke/exception/MoolahException.java @@ -1,6 +1,7 @@ -//@@author wcwy package seedu.duke.exception; +//@@author wcwy + /** * Represents the base class of the exceptions defined for Moolah Manager. */ diff --git a/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java b/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java index f37737efd..dde30e309 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java +++ b/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java @@ -1,6 +1,6 @@ -//@@author chydarren package seedu.duke.exception; +//@@author chydarren import seedu.duke.common.ErrorMessages; public class StatsInvalidTypeException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java b/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java index f310b4a77..21a8569bf 100644 --- a/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java +++ b/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java @@ -1,6 +1,6 @@ -//@@author chinhan99 package seedu.duke.exception; +//@@author chinhan99 import seedu.duke.common.ErrorMessages; public class StorageFileCorruptedException extends MoolahException { diff --git a/src/main/java/seedu/duke/exception/StorageWriteErrorException.java b/src/main/java/seedu/duke/exception/StorageWriteErrorException.java index b188bd2fa..c22687b49 100644 --- a/src/main/java/seedu/duke/exception/StorageWriteErrorException.java +++ b/src/main/java/seedu/duke/exception/StorageWriteErrorException.java @@ -1,6 +1,6 @@ -//@@author chinhan99 package seedu.duke.exception; +//@@author chinhan99 import seedu.duke.common.ErrorMessages; public class StorageWriteErrorException extends MoolahException { diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index cacc260bb..cf07545dd 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -91,7 +91,8 @@ public static String[] splitInput(String fullCommandInput) { * @return Command object created. * @throws GlobalInvalidCommandException If the command word is not supported by the application. */ - public static Command getCommand(String commandWordInput, String parameterInput) throws GlobalInvalidCommandException { + public static Command getCommand(String commandWordInput, String parameterInput) throws + GlobalInvalidCommandException { // TODO: Remove parameter input once a solution is found for managing parameter that allows space Command command = null; switch (commandWordInput.toUpperCase()) { diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 50027ceef..09fb8d7f7 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -1,6 +1,6 @@ -//@@author wcwy package seedu.duke.parser; +//@@author wcwy import seedu.duke.command.Command; import seedu.duke.command.ListCommand; import seedu.duke.data.transaction.Expense; From 5b1e123082cd758995f54283bc28f68b7f9a69c0 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 21:33:19 +0800 Subject: [PATCH 204/416] Add ability to list transactions of a specific month or year --- .../java/seedu/duke/command/ListCommand.java | 21 ++++++++++++------- .../java/seedu/duke/common/ErrorMessages.java | 6 +++--- .../java/seedu/duke/data/TransactionList.java | 18 ++++++++++------ .../seedu/duke/parser/ParameterParser.java | 4 ++-- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 34690b1d8..c16033c3f 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -30,7 +30,8 @@ public class ListCommand extends Command { // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "LIST"; // The description for the usage of command - public static final String COMMAND_DESCRIPTION = "To list all or some transactions based on selection."; + public static final String COMMAND_DESCRIPTION = "To list all or some transactions based on selection." + + " Note that the tags will be joint in the filter based on the 'AND' operation."; // The guiding information for the usage of command public static final String COMMAND_USAGE = "Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR]"; // The formatting information for the parameters used by the command @@ -39,7 +40,13 @@ public class ListCommand extends Command { + "(Optional) TYPE - The type of transaction. Only \"income\" or \"expense\" is accepted." + LINE_SEPARATOR + "(Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted." - + LINE_SEPARATOR + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"."; + + LINE_SEPARATOR + + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + + LINE_SEPARATOR + + "(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that" + + " month must be accompanied by a year." + + LINE_SEPARATOR + + "(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted."; // Basic help description public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR @@ -76,11 +83,11 @@ public ListCommand() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_GLOBAL_MONTH, - COMMAND_TAG_GLOBAL_YEAR + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_GLOBAL_MONTH, + COMMAND_TAG_GLOBAL_YEAR }; return optionalTags; } diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index b60647ad4..db9c64a1c 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -10,11 +10,11 @@ public enum ErrorMessages { ERROR_GLOBAL_UNSUPPORTED_TAG("Not supported tag(s) detected, please check your input!"), ERROR_GLOBAL_EMPTY_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), ERROR_GLOBAL_INVALID_INDEX("Invalid index, please ensure your index is correct!"), - ERROR_GLOBAL_INVALID_MONTH("Invalid month, please check your input!"), - ERROR_GLOBAL_INVALID_YEAR("Invalid year, please check your input!"), + ERROR_GLOBAL_INVALID_MONTH("Invalid month, please check your input! Note: Month should be between 1 to 12."), + ERROR_GLOBAL_INVALID_YEAR("Invalid year, please check your input! Note: Year should be 1000 onwards."), ERROR_GLOBAL_INVALID_PERIOD("Type of period given is invalid, please check your input!"), ERROR_GLOBAL_INVALID_NUMBER("Number for period stats given is invalid, please check your input!"), - ERROR_GLOBAL_NUMBER_NOT_NUMERIC("Non-Numeric input detected, please enter a numerical amount!"), + ERROR_GLOBAL_NUMBER_NOT_NUMERIC("Non-integer detected, please enter a numerical integer!"), ERROR_TRANSACTION_INVALID_AMOUNT("Invalid amount, " + "please ensure your amount is in positive numerals ($10000000 or less) only!"), ERROR_TRANSACTION_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 381333e16..fda477043 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -153,7 +153,7 @@ public boolean isTransactionInstance(Object transaction, String classType) throw * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ public boolean isMatchListFilters(Transaction transaction, String type, String category, - LocalDate date) throws InputTransactionInvalidTypeException { + LocalDate date) throws InputTransactionInvalidTypeException { boolean isMatch; try { isMatch = ((type.isEmpty() || isTransactionInstance(transaction, type)) @@ -171,25 +171,31 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c * @param type The type of transaction. * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". + * @param year A specified year. + * @param month A specified month within the year. * @return A string containing the formatted transaction list. * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ public String listTransactions(String type, String category, LocalDate date, int month, int year) throws InputTransactionInvalidTypeException { - String transactionsList = ""; - - ArrayList timeTransactions = new ArrayList<>(); + ArrayList timeTransactions; + // Filters the transactions list by month or/and year first if (year != -1 && month != -1) { timeTransactions = getTransactionsByMonth(year, month); } else if (year != -1) { + assert month == -1; timeTransactions = getTransactionsByYear(year); } else { + // No filter month or/and year filter applied timeTransactions = transactions; } - // Loops each transaction from the transactions list + String transactionsList = ""; + + // Loops each transaction from the time transactions list for (Transaction transaction : timeTransactions) { + // Applies generic filter against each time transaction entry if (isMatchListFilters(transaction, type, category, date)) { transactionsList += transaction.toString() + LINE_SEPARATOR; } @@ -405,7 +411,7 @@ public ArrayList getTransactionsByMonth(int year, int month) { * @return An array list containing all transactions recorded on the last N number of weeks or months. */ public static ArrayList getTransactionsByDateRange(LocalDate[] dateRange, - ArrayList transactionsByDateRange) { + ArrayList transactionsByDateRange) { for (Transaction transaction : transactions) { LocalDate transactionDate = transaction.getDate(); diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 09fb8d7f7..5ac5c7ab8 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -533,7 +533,7 @@ public static int parseMonthTag(String parameter) throws GlobalInvalidMonthExcep throw new GlobalNumberNotNumericException(); } - if (month > 12 || month < 0) { + if (month > 12 || month <= 0) { parserLogger.log(Level.WARNING, "An invalid month number error is caught for the given parameter: " + parameter); throw new GlobalInvalidMonthException(); @@ -551,7 +551,7 @@ public static int parseYearTag(String parameter) throws GlobalInvalidYearExcepti + parameter); throw new GlobalNumberNotNumericException(); } - if (year < 0) { + if (year <= 999) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); throw new GlobalInvalidYearException(); From 3681e96500727880238eff9aafe8927a9c66bb18 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 21:38:29 +0800 Subject: [PATCH 205/416] Minor indentation fix for Gradle test issue --- src/main/java/seedu/duke/command/ListCommand.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index c16033c3f..958ecefc0 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -83,11 +83,11 @@ public ListCommand() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_GLOBAL_MONTH, - COMMAND_TAG_GLOBAL_YEAR + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_GLOBAL_MONTH, + COMMAND_TAG_GLOBAL_YEAR }; return optionalTags; } From ffdf3badceaf7e97c124ba42059d264319efee3d Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 21:43:58 +0800 Subject: [PATCH 206/416] Update EXPECTED.TXT to reflect minor change to help on List Command --- text-ui-test/EXPECTED.TXT | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 92b418cb3..31e90a3da 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -24,7 +24,7 @@ To add a new transaction entry, which could be either an "income" or an "expense Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION Command Word: LIST -To list all or some transactions based on selection. +To list all or some transactions based on selection. Note that the tags will be joint in the filter based on the 'AND' operation. Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] Command Word: FIND @@ -72,7 +72,7 @@ DATE: Date of the transaction. The format must be in "yyyyMMdd". DESCRIPTION: More information regarding the transaction, written without any space. Command Word: LIST -To list all or some transactions based on selection. +To list all or some transactions based on selection. Note that the tags will be joint in the filter based on the 'AND' operation. Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] Parameters information: (Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. From ad8397bfabc80c9ff11a3befd7dd10125f73eab9 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 21:46:26 +0800 Subject: [PATCH 207/416] Update EXPECTED.TXT again to reflect minor change to help on List Command --- text-ui-test/EXPECTED.TXT | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 31e90a3da..5122d4de7 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -78,6 +78,8 @@ Parameters information: (Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. (Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. (Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". +(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. +(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted. Command Word: FIND To find specific transaction(s) based on any keywords inputted by the user. From 48d87a44dbbdc62e066a2a6aff125120c980be35 Mon Sep 17 00:00:00 2001 From: chydarren Date: Sun, 23 Oct 2022 22:00:31 +0800 Subject: [PATCH 208/416] Organize authorship comments for UI class --- src/main/java/seedu/duke/Ui.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 0bf0f0df4..acb45af8c 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -92,6 +92,8 @@ public static void showHelp(String message) { printMessages(INFO_HELP_GREET.toString(), message); } + //@@author chydarren + /** * Prepares the transaction list messages to be displayed to the user. * @@ -102,6 +104,8 @@ public static void showList(String list, String message) { printMessages(message, list); } + //@@author paullowse + /** * Prepares the stats list messages to be displayed to the user. * @@ -113,6 +117,8 @@ public static void showStatsList(String list, String message, String incomeMessa printMessages(message, list, incomeMessage, expenseMessage, savingsMessage); } + //@@author chydarren + /** * Prepares the messages to be displayed to the user when add or delete has been performed on * the transaction list. @@ -130,6 +136,8 @@ public static void showTransactionAction(String message, String transactionDetai printMessages(message, transactionDetails); } + //@@author wcwy + /** * Returns the today's date in MMM dd yyyy format. * From c6da38dc842a959a7d6674dd7de8a197e8d6e593 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 00:07:06 +0800 Subject: [PATCH 209/416] Implement set budget feature Related features include parsing of budget and exceptions handling, creation of budget command for execution of setting the budget, and the addition of help and UI messages. --- src/main/java/seedu/duke/Ui.java | 21 ++--- .../java/seedu/duke/command/AddCommand.java | 2 +- .../seedu/duke/command/BudgetCommand.java | 94 +++++++++++++++++++ src/main/java/seedu/duke/command/Command.java | 3 + .../java/seedu/duke/command/CommandTag.java | 1 + .../java/seedu/duke/command/HelpCommand.java | 2 + .../java/seedu/duke/common/Constants.java | 4 +- .../java/seedu/duke/common/ErrorMessages.java | 6 +- .../java/seedu/duke/common/InfoMessages.java | 4 +- src/main/java/seedu/duke/data/Budget.java | 6 +- .../InputBudgetDuplicateException.java | 17 ++++ .../InputBudgetInvalidAmountException.java | 17 ++++ .../java/seedu/duke/parser/CommandParser.java | 4 + .../seedu/duke/parser/ParameterParser.java | 80 ++++++++++++---- text-ui-test/input.txt | 9 ++ 15 files changed, 231 insertions(+), 39 deletions(-) create mode 100644 src/main/java/seedu/duke/command/BudgetCommand.java create mode 100644 src/main/java/seedu/duke/exception/InputBudgetDuplicateException.java create mode 100644 src/main/java/seedu/duke/exception/InputBudgetInvalidAmountException.java diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index acb45af8c..1494544d6 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -7,12 +7,7 @@ import java.util.Scanner; import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; -import static seedu.duke.common.InfoMessages.INFO_CURRENT_BUDGET; -import static seedu.duke.common.InfoMessages.INFO_DIVIDER; -import static seedu.duke.common.InfoMessages.INFO_GREET; -import static seedu.duke.common.InfoMessages.INFO_HELP_GREET; -import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; -import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; +import static seedu.duke.common.InfoMessages.*; public class Ui { //@@author chydarren @@ -136,16 +131,16 @@ public static void showTransactionAction(String message, String transactionDetai printMessages(message, transactionDetails); } - //@@author wcwy + //@author wcwy /** - * Returns the today's date in MMM dd yyyy format. + * Prepares the messages to be displayed to the user when a new budget is set. * - * @return Formatted today's date + * @param currentBudget The string representation of the new budget set. */ - public static String showDateOfTheDay() { - LocalDate todayDate = LocalDate.now(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_OUTPUT_PATTERN.toString()); - return todayDate.format(formatter); + public static void showSetBudgetAcknowledgementMessage(String currentBudget) { + printMessages(INFO_BUDGET_SET_SUCCESSFUL.toString(), INFO_CURRENT_BUDGET + currentBudget); } + + } \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 405a06c56..03d59a62d 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -177,7 +177,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @throws MaximumTransactionCountException If the transaction list capacity has been reached. */ private static void checkTransactionCapacity(TransactionList transactions) throws MaximumTransactionCountException { - // The expected maximum number of transactions allowed to store is only one million. + // The expected maximum number of transactions allowed to store is only one million if (transactions.size() == MAX_TRANSACTIONS_COUNT) { addLogger.log(Level.WARNING, "A transaction is attempted to be stored beyond its capacity"); throw new MaximumTransactionCountException(); diff --git a/src/main/java/seedu/duke/command/BudgetCommand.java b/src/main/java/seedu/duke/command/BudgetCommand.java new file mode 100644 index 000000000..b03e4033c --- /dev/null +++ b/src/main/java/seedu/duke/command/BudgetCommand.java @@ -0,0 +1,94 @@ +package seedu.duke.command; + +//@@author wcwy + +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.Budget; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.MoolahException; + +import static seedu.duke.command.CommandTag.COMMAND_TAG_BUDGET_AMOUNT; + +/** + * Represents a budget command object that will set the user's monthly budget on the Budget command. + */ +public class BudgetCommand extends Command { + private static final String LINE_SEPARATOR = System.lineSeparator(); + // The command word used to trigger the execution of Moolah Manager's operations + public static final String COMMAND_WORD = "BUDGET"; + // The description for the usage of command + public static final String COMMAND_DESCRIPTION = "To set the amount of monthly budget."; + // The guiding information for the usage of command + public static final String COMMAND_USAGE = "Usage: budget b/BUDGET"; + // The formatting information for the parameters used by the command + public static final String COMMAND_PARAMETERS_INFO = "Parameters information: " + + LINE_SEPARATOR + + "BUDGET - Amount of monthly budget set."; + + // Basic budget description + public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR + + COMMAND_DESCRIPTION + LINE_SEPARATOR + COMMAND_USAGE + LINE_SEPARATOR; + // Detailed budget description + public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + + LINE_SEPARATOR; + + + //@@author wcwy + private long budgetAmount; + + /** + * Default constructor for BudgetCommand. Budget amount is initialised as 1000 (default value). + */ + public BudgetCommand() { + budgetAmount = 1000; + } + + /** + * To store the parsed monthly budget amount received from user input. + * + * @param budgetAmount Monthly budget amount + */ + @Override + public void setBudgetAmount(long budgetAmount) { + this.budgetAmount = budgetAmount; + } + + /** + * Gets the mandatory tags of the command. + * + * @return A string array containing all mandatory tags. + */ + @Override + public String[] getMandatoryTags() { + String[] mandatoryTags = new String[]{ + COMMAND_TAG_BUDGET_AMOUNT + }; + return mandatoryTags; + } + + /** + * + * @param transactions An instance of the TransactionList class. + * @param ui An instance of the Ui class. + * @param storage An instance of the Storage class. + * @throws MoolahException + */ + @Override + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + Budget.setBudget(budgetAmount); + ui.showSetBudgetAcknowledgementMessage(Long.toString(budgetAmount)); + } + + //@@author paullowse + + /** + * Enables the program to exit when the Bye command is issued. + * + * @return A boolean value that indicates whether the program shall exit. + */ + @Override + public boolean isExit() { + return false; + } +} diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 7e1e49449..be2bdfbe5 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -72,6 +72,9 @@ public void setDescription(String description) { public void setAmount(int amount) { } + public void setBudgetAmount(long amount) { + } + public void setCategory(String category) { } diff --git a/src/main/java/seedu/duke/command/CommandTag.java b/src/main/java/seedu/duke/command/CommandTag.java index 7eef06f57..a7648d6ee 100644 --- a/src/main/java/seedu/duke/command/CommandTag.java +++ b/src/main/java/seedu/duke/command/CommandTag.java @@ -14,4 +14,5 @@ public class CommandTag { public static final String COMMAND_TAG_GLOBAL_PERIOD = "p/"; public static final String COMMAND_TAG_HELP_OPTION = "o/"; public static final String COMMAND_TAG_STATS_TYPE = "s/"; + public static final String COMMAND_TAG_BUDGET_AMOUNT = "b/"; } \ No newline at end of file diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index d3c35cb64..8dfdb3b91 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -95,6 +95,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { */ private String generateBasicHelp() { String helpMessage = HelpCommand.COMMAND_HELP + LINE_SEPARATOR + + BudgetCommand.COMMAND_HELP + LINE_SEPARATOR + AddCommand.COMMAND_HELP + LINE_SEPARATOR + ListCommand.COMMAND_HELP + LINE_SEPARATOR + FindCommand.COMMAND_HELP + LINE_SEPARATOR @@ -114,6 +115,7 @@ private String generateBasicHelp() { */ private String generateDetailedHelp() { String helpMessage = HelpCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + + BudgetCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + AddCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + ListCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR + FindCommand.COMMAND_DETAILED_HELP + LINE_SEPARATOR diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index c6f89dbdd..ac6518a0b 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -1,5 +1,6 @@ package seedu.duke.common; +//@@author wcwy /** * Represents all the constant settings of the application. * Developers should update the constant in this file to allow for different limits for the application. @@ -7,7 +8,6 @@ *

Note that altering the values in this folder may result in integer overflow in the program in extreme cases. */ public class Constants { - //@@author wcwy /* WARNING: Editing the values below may result in integer overflow in 1. TransactionList.calculateMonthlyTotalExpense() @@ -21,7 +21,7 @@ public class Constants { public static int MIN_AMOUNT_VALUE = 0; public static int MAX_AMOUNT_VALUE = 10000000; - // The amount of transaction is allowed to be in the range or 0 <= x <= 10000000 + // The amount of transaction is allowed to be in the range or 1 <= x <= MAX_AMOUNT * MAX_TRANSACTION public static int MIN_BUDGET_VALUE = 1; public static long MAX_BUDGET_VALUE = Long.valueOf(MAX_TRANSACTIONS_COUNT) * Long.valueOf(MAX_AMOUNT_VALUE); } diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index db9c64a1c..914a9f613 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -27,7 +27,11 @@ public enum ErrorMessages { + "To preserve data, please STOP the program and edit your data file correctly."), ERROR_STORAGE_WRITE("Unable to write to Duke.txt. Please save your current Duke.txt file and restart Moolah"), ERROR_MAXIMUM_TRANSACTION_COUNT_REACHED("Unable to add transaction. " - + "The maximum allowed transaction size (1000000) has been reached."); + + "The maximum allowed transaction size (1000000) has been reached."), + + ERROR_INVALID_BUDGET("Invalid budget amount, please ensure your amount is in positive whole number" + + " of valid range only!"), + ERROR_DUPLICATE_BUDGET("Provided budget is the same as the originally set value."); //@@author chydarren public final String message; diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index af60e447b..c5e958b5b 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -28,9 +28,9 @@ public enum InfoMessages { INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."), + INFO_BUDGET_SET_SUCCESSFUL("You have successfully updated the budget."), INFO_CURRENT_BUDGET("Monthly budget set as: $"), - INFO_REMAINING_BUDGET("Budget remained for the month of transaction: $"), - INFO_TODAY_DATE("Today is "); + INFO_REMAINING_BUDGET("Budget remained for the month of transaction: $"); //@@author chydarren public final String message; diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index 0fa27ae5b..4f5bcf121 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -13,14 +13,14 @@ public class Budget { //@@author wcwy // Default value of the monthly budget is $1000 - private static int budget = 1000; + private static long budget = 1000; /** * Retrieves the budget value set for the current month. * * @return The budget value set by the user. */ - public static int getBudget() { + public static long getBudget() { return budget; } @@ -29,7 +29,7 @@ public static int getBudget() { * * @param budget The new value for budget. */ - public static void setBudget(int budget) { + public static void setBudget(long budget) { Budget.budget = budget; } diff --git a/src/main/java/seedu/duke/exception/InputBudgetDuplicateException.java b/src/main/java/seedu/duke/exception/InputBudgetDuplicateException.java new file mode 100644 index 000000000..1196232ef --- /dev/null +++ b/src/main/java/seedu/duke/exception/InputBudgetDuplicateException.java @@ -0,0 +1,17 @@ +package seedu.duke.exception; + +//@@author wcwy +import seedu.duke.common.ErrorMessages; + +public class InputBudgetDuplicateException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_DUPLICATE_BUDGET.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/InputBudgetInvalidAmountException.java b/src/main/java/seedu/duke/exception/InputBudgetInvalidAmountException.java new file mode 100644 index 000000000..fbeccf5b7 --- /dev/null +++ b/src/main/java/seedu/duke/exception/InputBudgetInvalidAmountException.java @@ -0,0 +1,17 @@ +package seedu.duke.exception; + +//@@author wcwy +import seedu.duke.common.ErrorMessages; + +public class InputBudgetInvalidAmountException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_INVALID_BUDGET.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/seedu/duke/parser/CommandParser.java b/src/main/java/seedu/duke/parser/CommandParser.java index cf07545dd..4bf7e358b 100644 --- a/src/main/java/seedu/duke/parser/CommandParser.java +++ b/src/main/java/seedu/duke/parser/CommandParser.java @@ -3,6 +3,7 @@ import seedu.duke.command.Command; import seedu.duke.command.AddCommand; import seedu.duke.command.ByeCommand; +import seedu.duke.command.BudgetCommand; import seedu.duke.command.DeleteCommand; import seedu.duke.command.EditCommand; import seedu.duke.command.FindCommand; @@ -120,6 +121,9 @@ public static Command getCommand(String commandWordInput, String parameterInput) case DeleteCommand.COMMAND_WORD: command = new DeleteCommand(); break; + case BudgetCommand.COMMAND_WORD: + command = new BudgetCommand(); + break; case ByeCommand.COMMAND_WORD: command = new ByeCommand(); break; diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 5ac5c7ab8..3637ea365 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -3,6 +3,7 @@ //@@author wcwy import seedu.duke.command.Command; import seedu.duke.command.ListCommand; +import seedu.duke.data.Budget; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; @@ -27,6 +28,8 @@ import seedu.duke.exception.HelpUnknownOptionException; //@@author wcwy +import seedu.duke.exception.InputBudgetInvalidAmountException; +import seedu.duke.exception.InputBudgetDuplicateException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -37,24 +40,12 @@ import java.util.regex.Pattern; //@@author paullowse -import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; -import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; -import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; -import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; -import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; //@@author chinhan99 -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; -import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; //@@author chinhan99 -import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; -import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; +import static seedu.duke.command.CommandTag.*; +import static seedu.duke.common.Constants.*; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; /** @@ -335,6 +326,9 @@ private static void setParameter(Command command, String tag, String parameter) case COMMAND_TAG_GLOBAL_PERIOD: command.setStatsPeriod(parsePeriodTag(parameter)); break; + case COMMAND_TAG_BUDGET_AMOUNT: + command.setBudgetAmount(parseBudgetTag(parameter)); + break; default: parserLogger.log(Level.WARNING, "An unsupported tag exception is caught: " + tag); throw new GlobalUnsupportedTagException(); @@ -413,10 +407,8 @@ public static String parseCategoryTag(String parameter) throws InputTransactionI * @throws InputTransactionInvalidAmountException If the transaction amount given is not a valid accepted integer. */ private static int parseAmountTag(String parameter) throws InputTransactionInvalidAmountException { - Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); - Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); try { - if (containAlphabet(parameter) || hasSpecialSymbols.find()) { + if (containAlphabet(parameter) || containSymbol(parameter)) { parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + parameter); throw new InputTransactionInvalidAmountException(); @@ -498,6 +490,47 @@ public static boolean parseHelpOptionTag(String parameter) throws HelpUnknownOpt } } + /** + * Parses the user parameter input for the budget amount and returns it as a long value. + * + *

Note: This method is created separately from parseAmountTag due to the difference in the data type for + * the parameter and its maximum amount. + * + *

For a new budget to be valid, it must satisfy the following requirements: + * 1. The budget should not contain any alphabets or symbols + * 2. The budget should be parsable into a long value + * 3. The budget range should fall within the minimum and maximum budget allowed + * 4. The budget should be different from the current budget + * + * @param parameter The user input after the user tag. + * @return The amount in long if no exceptions are thrown. + * @throws InputTransactionInvalidAmountException If the transaction amount given does not meet the 4 requirements. + */ + public static long parseBudgetTag(String parameter) throws MoolahException { + long newBudget; + if (containAlphabet(parameter) || containSymbol(parameter)) { + throw new InputBudgetInvalidAmountException(); + } + + // Long value is used here as the maximum of budget can go as high as 2 * (2^31 - 1) + try { + newBudget = Long.parseLong(parameter); + } catch (NumberFormatException e) { + parserLogger.log(Level.WARNING, "Invalid budget amount error found for the given parameter: " + parameter); + throw new InputBudgetInvalidAmountException(); + } + + if (newBudget < MIN_BUDGET_VALUE || newBudget > MAX_BUDGET_VALUE) { + throw new InputBudgetInvalidAmountException(); + } + + if (newBudget == Budget.getBudget()) { + throw new InputBudgetDuplicateException(); + } + + return newBudget; + } + //@@author chydarren /** @@ -624,4 +657,17 @@ public static boolean containAlphabet(String parameter) { } return false; } + + /** + * Checks if the parameter contains symbol characters. + * + * @param parameter The user input after the user tag. + * @return true if there are symbol characters within the parameter. + */ + public static boolean containSymbol(String parameter) { + Pattern specialSymbols = Pattern.compile("[!@#$%&*()_+=|<>?{}\\[\\]~-]"); + Matcher hasSpecialSymbols = specialSymbols.matcher(parameter); + + return hasSpecialSymbols.find(); + } } diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 63bce7157..21e200b74 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -5,6 +5,15 @@ help o/detailed blablabla help / help o/ help o/detailed o/detailed +budget +budget / +budget b/ +budget 123123 +budget b/12345678901234567890 +budget b/-123 +budget b/123:123 +budget b/0 +budget b/10000 stats stats blablabla stats / From c4c649868de28abb969035321962cb9c9a10b6ab Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 24 Oct 2022 00:11:02 +0800 Subject: [PATCH 210/416] Update Aboutus --- docs/AboutUs.md | 2 +- docs/team/paulsolomonlowsien.png | Bin 0 -> 29446 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/team/paulsolomonlowsien.png diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 7615523e3..fa3785ef9 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,6 +5,6 @@ Display | Name | Github Profile | Portfo ![](team/chuahanyongdarren.png) | Chua Han Yong Darren | [Github](https://github.com/chydarren) | [Portfolio](team/chuahanyongdarren.md) ![](team/chiathinhong.png) | Chia Thin Hong | [Github](https://github.com/wcwy) | [Portfolio](team/chiathinhong.md) ![](team/yongchinhan.jpg) | Yong Chin Han | [Github](https://github.com/chinhan99) | [Portfolio](team/yongchinhan.md) -![](https://via.placeholder.com/100.png?text=Photo) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](team/paullow.md) +![](team/paulsolomonlowsien.png) | Paul Low | [Github](https://github.com/paullowse) | [Portfolio](team/paullow.md) ![](team/brianwongyunlong.png) | Brian Wong Yun Long | [Github](https://github.com/brian-vb) | [Portfolio](team/brianwongyunlong.md) diff --git a/docs/team/paulsolomonlowsien.png b/docs/team/paulsolomonlowsien.png new file mode 100644 index 0000000000000000000000000000000000000000..ee1a9f29d29502d306080bc1c7d74ac74f7c8ec1 GIT binary patch literal 29446 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 z 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 literal 0 HcmV?d00001 From b252455c513efbebe8090c50ee078d2621c6adfd Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 01:03:20 +0800 Subject: [PATCH 211/416] Add Javadocs class header for exception classes --- .../seedu/duke/exception/GlobalDuplicateTagException.java | 3 +++ .../java/seedu/duke/exception/GlobalMissingTagException.java | 3 +++ .../java/seedu/duke/exception/HelpUnknownOptionException.java | 3 +++ .../seedu/duke/exception/InputBudgetDuplicateException.java | 3 +++ .../duke/exception/InputBudgetInvalidAmountException.java | 3 +++ .../duke/exception/InputTransactionInvalidDateException.java | 3 +++ .../duke/exception/MaximumTransactionCountException.java | 4 ++++ 7 files changed, 22 insertions(+) diff --git a/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java b/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java index 5b9e9b4ae..1852cfaae 100644 --- a/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalDuplicateTagException.java @@ -3,6 +3,9 @@ //@@author wcwy import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where a same tag is found more than once in the user input. + */ public class GlobalDuplicateTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/GlobalMissingTagException.java b/src/main/java/seedu/duke/exception/GlobalMissingTagException.java index 6b18985f8..24bdd19a5 100644 --- a/src/main/java/seedu/duke/exception/GlobalMissingTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalMissingTagException.java @@ -3,6 +3,9 @@ //@@author wcwy import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where a mandatory tag of the requested command is not found in the user input. + */ public class GlobalMissingTagException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java b/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java index 1c4fbb121..9eb208aa3 100644 --- a/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java +++ b/src/main/java/seedu/duke/exception/HelpUnknownOptionException.java @@ -3,6 +3,9 @@ //@@author wcwy import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the option chosen in help command is not "o/detailed". + */ public class HelpUnknownOptionException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/InputBudgetDuplicateException.java b/src/main/java/seedu/duke/exception/InputBudgetDuplicateException.java index 1196232ef..1a4d1dead 100644 --- a/src/main/java/seedu/duke/exception/InputBudgetDuplicateException.java +++ b/src/main/java/seedu/duke/exception/InputBudgetDuplicateException.java @@ -3,6 +3,9 @@ //@@author wcwy import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the budget value requested to be updated is the same as the current budget value. + */ public class InputBudgetDuplicateException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/InputBudgetInvalidAmountException.java b/src/main/java/seedu/duke/exception/InputBudgetInvalidAmountException.java index fbeccf5b7..6ee707f7e 100644 --- a/src/main/java/seedu/duke/exception/InputBudgetInvalidAmountException.java +++ b/src/main/java/seedu/duke/exception/InputBudgetInvalidAmountException.java @@ -3,6 +3,9 @@ //@@author wcwy import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the budget value provided by the user is not a valid acceptable value. + */ public class InputBudgetInvalidAmountException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java index 63dadcf22..0b130ed11 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidDateException.java @@ -3,6 +3,9 @@ //@@author wcwy import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the date provided for the transaction is not in the supported format. + */ public class InputTransactionInvalidDateException extends MoolahException { /** diff --git a/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java b/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java index 0bfd4010a..d98ee9715 100644 --- a/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java +++ b/src/main/java/seedu/duke/exception/MaximumTransactionCountException.java @@ -3,6 +3,10 @@ //@@author wcwy import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the number of transactions will exceed the maximum value set if a new transaction + * is added to the list. + */ public class MaximumTransactionCountException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. From e03db16f2a4a20a9070d0eb37e2ddf8bf64649c1 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 01:12:52 +0800 Subject: [PATCH 212/416] Fix coding style to comply with the rules in Java coding standards --- src/main/java/seedu/duke/Ui.java | 11 ++++++---- .../java/seedu/duke/command/AddCommand.java | 2 +- .../seedu/duke/command/BudgetCommand.java | 5 +++-- src/main/java/seedu/duke/data/Budget.java | 2 +- .../seedu/duke/parser/ParameterParser.java | 20 +++++++++++++++++-- 5 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 1494544d6..c731665a8 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -2,12 +2,15 @@ import seedu.duke.data.Budget; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import java.util.Scanner; -import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; -import static seedu.duke.common.InfoMessages.*; +import static seedu.duke.common.InfoMessages.INFO_CURRENT_BUDGET; +import static seedu.duke.common.InfoMessages.INFO_DIVIDER; +import static seedu.duke.common.InfoMessages.INFO_GREET; +import static seedu.duke.common.InfoMessages.INFO_HELP_GREET; +import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; +import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; +import static seedu.duke.common.InfoMessages.INFO_BUDGET_SET_SUCCESSFUL; public class Ui { //@@author chydarren diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 03d59a62d..8f1df9627 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -137,7 +137,7 @@ public void setTransactionCreated(Transaction transaction) { * @param ui An instance of the Ui class. * @param transactions An instance of the TransactionList class. * @param storage An instance of the Storage class. - * @throws InputTransactionInvalidTypeException If the type of transaction is not recognised. + * @throws InputTransactionInvalidTypeException If the type of transaction is not recognised or on storage error. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { diff --git a/src/main/java/seedu/duke/command/BudgetCommand.java b/src/main/java/seedu/duke/command/BudgetCommand.java index b03e4033c..9d9fe1116 100644 --- a/src/main/java/seedu/duke/command/BudgetCommand.java +++ b/src/main/java/seedu/duke/command/BudgetCommand.java @@ -62,17 +62,18 @@ public void setBudgetAmount(long budgetAmount) { @Override public String[] getMandatoryTags() { String[] mandatoryTags = new String[]{ - COMMAND_TAG_BUDGET_AMOUNT + COMMAND_TAG_BUDGET_AMOUNT }; return mandatoryTags; } /** + * Update the monthly budget value stored in the program to new value and display acknowledgement message. * * @param transactions An instance of the TransactionList class. * @param ui An instance of the Ui class. * @param storage An instance of the Storage class. - * @throws MoolahException + * @throws MoolahException If storage error is found. */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index 4f5bcf121..4e38ccfdf 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -4,6 +4,7 @@ import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; +//@@author wcwy /** * Represents the user's budget for the current month. * @@ -11,7 +12,6 @@ * Operations related to the budgets are also defined under this class. */ public class Budget { - //@@author wcwy // Default value of the monthly budget is $1000 private static long budget = 1000; diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 3637ea365..d6afec0f6 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -38,14 +38,30 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static seedu.duke.command.CommandTag.COMMAND_TAG_BUDGET_AMOUNT; +import static seedu.duke.common.Constants.MAX_BUDGET_VALUE; +import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; + //@@author paullowse +import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; //@@author chinhan99 +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; +import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; //@@author chinhan99 -import static seedu.duke.command.CommandTag.*; -import static seedu.duke.common.Constants.*; +import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; +import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; /** From 8beb5ff765afd5d50c08a7b98da6486bc994963c Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 03:48:35 +0800 Subject: [PATCH 213/416] Add Junit tests --- .../java/seedu/duke/command/AddCommand.java | 7 +- .../seedu/duke/command/AddCommandTest.java | 76 +++++++ .../seedu/duke/command/BudgetCommandTest.java | 35 ++++ .../seedu/duke/data/TransactionListTest.java | 81 ++++++++ .../data/transaction/TransactionTest.java | 7 + .../seedu/duke/parser/CommandParserTest.java | 33 +++ .../duke/parser/ParameterParserTest.java | 192 +++++++++++++++++- 7 files changed, 426 insertions(+), 5 deletions(-) create mode 100644 src/test/java/seedu/duke/command/AddCommandTest.java create mode 100644 src/test/java/seedu/duke/command/BudgetCommandTest.java create mode 100644 src/test/java/seedu/duke/data/TransactionListTest.java create mode 100644 src/test/java/seedu/duke/parser/CommandParserTest.java diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 8f1df9627..48f1a1ba0 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -83,6 +83,8 @@ public AddCommand(String type, String description, int amount, String category, this.date = date; } + //@@author wcwy + /** * Gets the mandatory tags of the command. * @@ -101,7 +103,6 @@ public String[] getMandatoryTags() { return mandatoryTags; } - //@@author wcwy @Override public void setType(String type) { this.type = type; @@ -176,7 +177,8 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param transactions The list of transactions in the application. * @throws MaximumTransactionCountException If the transaction list capacity has been reached. */ - private static void checkTransactionCapacity(TransactionList transactions) throws MaximumTransactionCountException { + public static void checkTransactionCapacity(TransactionList transactions) throws MaximumTransactionCountException { + assert transactions != null; // The expected maximum number of transactions allowed to store is only one million if (transactions.size() == MAX_TRANSACTIONS_COUNT) { addLogger.log(Level.WARNING, "A transaction is attempted to be stored beyond its capacity"); @@ -195,6 +197,7 @@ private static void checkTransactionCapacity(TransactionList transactions) throw * @throws InputTransactionInvalidTypeException If the type of the transactions */ private String addTransaction(TransactionList transactions) throws InputTransactionInvalidTypeException { + assert (transactions != null); String messageBanner = ""; Transaction transaction; switch (type) { diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java new file mode 100644 index 000000000..af31d43ca --- /dev/null +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -0,0 +1,76 @@ +package seedu.duke.command; + +import org.junit.jupiter.api.Test; +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Expense; +import seedu.duke.data.transaction.Income; +import seedu.duke.exception.MaximumTransactionCountException; +import seedu.duke.exception.MoolahException; + +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; +import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; + +public class AddCommandTest { + //@@author wcwy + @Test + public void checkTransactionCapacity_transactionListHasSpace_expectNoException() + throws MaximumTransactionCountException { + TransactionList transactions = new TransactionList(); + MAX_TRANSACTIONS_COUNT = 2; + transactions.addExpense("a", 1, "a", LocalDate.of(2022, 1, 1)); + AddCommand.checkTransactionCapacity(transactions); + + MAX_TRANSACTIONS_COUNT = 1000000; + } + + @Test + public void checkTransactionCapacity_transactionListFullSpace_exceptionThrown() + throws MaximumTransactionCountException { + TransactionList transactions = new TransactionList(); + MAX_TRANSACTIONS_COUNT = 2; + transactions.addExpense("a", 1, "a", LocalDate.of(2022, 1, 1)); + transactions.addExpense("a", 1, "a", LocalDate.of(2022, 1, 1)); + + assertThrows( + MaximumTransactionCountException.class, + () -> AddCommand.checkTransactionCapacity(transactions) + ); + + // Reset static variable back + MAX_TRANSACTIONS_COUNT = 1000000; + + } + + @Test + public void execute_addValidExpense_expectedSuccessfulExpenseAddition() throws MoolahException { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + AddCommand addCommand = new AddCommand("expense", "a", 1, "a", LocalDate.of(2022, 1, 1)); + addCommand.execute(transactions, ui, storage); + + Expense expense = new Expense("a", 1, "a", LocalDate.of(2022, 1, 1)); + + assertEquals(transactions.getEntry(0).toString(), expense.toString()); + } + + @Test + public void execute_addValidIncome_expectedSuccessfulIncomeAddition() throws MoolahException { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + AddCommand addCommand = new AddCommand("income", "a", 1, "a", LocalDate.of(2022, 1, 1)); + addCommand.execute(transactions, ui, storage); + + Income income = new Income("a", 1, "a", LocalDate.of(2022, 1, 1)); + + assertEquals(transactions.getEntry(0).toString(), income.toString()); + } + +} diff --git a/src/test/java/seedu/duke/command/BudgetCommandTest.java b/src/test/java/seedu/duke/command/BudgetCommandTest.java new file mode 100644 index 000000000..0f7fdd987 --- /dev/null +++ b/src/test/java/seedu/duke/command/BudgetCommandTest.java @@ -0,0 +1,35 @@ +package seedu.duke.command; + +import org.junit.jupiter.api.Test; +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.Budget; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.MoolahException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class BudgetCommandTest { + //@@author wcwy + @Test + public void execute_setValidBudget_expectedSuccessfulBudgetSet() throws MoolahException { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + + BudgetCommand budgetCommand = new BudgetCommand(); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), 1000); + + budgetCommand.setBudgetAmount(100000); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), 100000); + + budgetCommand.setBudgetAmount(Long.MAX_VALUE); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), Long.MAX_VALUE); + + // Reset budget back + Budget.setBudget(1000); + } +} diff --git a/src/test/java/seedu/duke/data/TransactionListTest.java b/src/test/java/seedu/duke/data/TransactionListTest.java new file mode 100644 index 000000000..6b48ea8f4 --- /dev/null +++ b/src/test/java/seedu/duke/data/TransactionListTest.java @@ -0,0 +1,81 @@ +package seedu.duke.data; + +import org.junit.jupiter.api.Test; +import seedu.duke.data.transaction.Expense; +import seedu.duke.data.transaction.Income; + +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class TransactionListTest { + //@@author wcwy + @Test + public void addExpense_addValidExpense_expectedSuccessfulExpenseAddition() { + TransactionList transactions = new TransactionList(); + transactions.addExpense("a", 1, "a", LocalDate.of(2022, 1, 1)); + Expense expense = new Expense("a", 1, "a", LocalDate.of(2022, 1, 1)); + assertEquals(transactions.getEntry(0).toString(), expense.toString()); + + transactions.addExpense("b", 2, "b", LocalDate.of(2222, 2, 2)); + expense = new Expense("b", 2, "b", LocalDate.of(2222, 2, 2)); + assertEquals(transactions.getEntry(1).toString(), expense.toString()); + } + + @Test + public void addIncome_addValidIncome_expectedSuccessfulIncomeAddition() { + TransactionList transactions = new TransactionList(); + transactions.addIncome("a", 1, "a", LocalDate.of(2022, 1, 1)); + Income income = new Income("a", 1, "a", LocalDate.of(2022, 1, 1)); + assertEquals(transactions.getEntry(0).toString(), income.toString()); + + transactions.addIncome("b", 2, "b", LocalDate.of(2222, 2, 2)); + income = new Income("b", 2, "b", LocalDate.of(2222, 2, 2)); + assertEquals(transactions.getEntry(1).toString(), income.toString()); + } + + @Test + public void calculateMonthlyTotalExpense_emptyTransactionList_expectedZero() { + TransactionList transactions = new TransactionList(); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2022, 1, 1)), 0); + } + + @Test + public void calculateMonthlyTotalExpense_noExpenseOnGivenMonth_expectedZero() { + TransactionList transactions = new TransactionList(); + transactions.addExpense("a", 1, "a", LocalDate.of(2022, 1, 1)); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2022, 2, 1)), 0); + } + + @Test + public void calculateMonthlyTotalExpense_multipleDateDifferentMonth_expectedCorrectAmountFromSameMonth() { + TransactionList transactions = new TransactionList(); + transactions.addExpense("a", 1, "a", LocalDate.of(2022, 1, 1)); + transactions.addExpense("a", 2, "a", LocalDate.of(2022, 2, 1)); + transactions.addExpense("a", 3, "a", LocalDate.of(2022, 3, 1)); + transactions.addExpense("a", 4, "a", LocalDate.of(2022, 4, 1)); + transactions.addExpense("a", 5, "a", LocalDate.of(2022, 5, 1)); + + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2022, 1, 31)), 1); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2022, 2, 01)), 2); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2022, 3, 02)), 3); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2022, 4, 03)), 4); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2022, 5, 04)), 5); + } + + @Test + public void calculateMonthlyTotalExpense_multipleDateDifferentYear_expectedCorrectAmountFromSameYearAndMonth() { + TransactionList transactions = new TransactionList(); + transactions.addExpense("a", 1, "a", LocalDate.of(2001, 1, 1)); + transactions.addExpense("a", 2, "a", LocalDate.of(2002, 1, 1)); + transactions.addExpense("a", 3, "a", LocalDate.of(2003, 1, 1)); + transactions.addExpense("a", 4, "a", LocalDate.of(2004, 1, 1)); + transactions.addExpense("a", 5, "a", LocalDate.of(2005, 1, 1)); + + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2001, 1, 31)), 1); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2002, 1, 01)), 2); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2003, 1, 02)), 3); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2004, 1, 03)), 4); + assertEquals(transactions.calculateMonthlyTotalExpense(LocalDate.of(2005, 1, 04)), 5); + } +} diff --git a/src/test/java/seedu/duke/data/transaction/TransactionTest.java b/src/test/java/seedu/duke/data/transaction/TransactionTest.java index 4f6d7e5ff..9790743d1 100644 --- a/src/test/java/seedu/duke/data/transaction/TransactionTest.java +++ b/src/test/java/seedu/duke/data/transaction/TransactionTest.java @@ -1,6 +1,7 @@ package seedu.duke.data.transaction; import org.junit.jupiter.api.Test; +import seedu.duke.data.TransactionList; import java.time.LocalDate; @@ -44,4 +45,10 @@ public void testSetCategory() { transaction.setCategory("Love"); assertEquals("Love", transaction.getCategory()); } + + //@@author wcwy + @Test + public void printFormattedDate_validDateFormat_expectCorrectFormatDate() { + assertEquals(transaction.printFormattedDate(), "Jan 01 2022"); + } } \ No newline at end of file diff --git a/src/test/java/seedu/duke/parser/CommandParserTest.java b/src/test/java/seedu/duke/parser/CommandParserTest.java new file mode 100644 index 000000000..e42e465b2 --- /dev/null +++ b/src/test/java/seedu/duke/parser/CommandParserTest.java @@ -0,0 +1,33 @@ +package seedu.duke.parser; + +import org.junit.jupiter.api.Test; +import seedu.duke.command.ByeCommand; +import seedu.duke.command.Command; +import seedu.duke.exception.GlobalInvalidCommandException; +import seedu.duke.exception.MoolahException; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class CommandParserTest { + //@@author wcwy + @Test + public void parse_unsupportedCommandWord_exceptionThrown() { + assertThrows( + GlobalInvalidCommandException.class, + () -> CommandParser.parse("rm -rf") + ); + } + + @Test + public void parse_supportedCommandWordLowerCase_expectCorrectCommandSubclass() throws MoolahException { + Command command = CommandParser.parse("bye"); + assertTrue(command instanceof ByeCommand); + } + + @Test + public void parse_supportedCommandWordRandomCase_expectCorrectCommandSubclass() throws MoolahException { + Command command = CommandParser.parse("bYe"); + assertTrue(command instanceof ByeCommand); + } +} diff --git a/src/test/java/seedu/duke/parser/ParameterParserTest.java b/src/test/java/seedu/duke/parser/ParameterParserTest.java index 75686b233..3b1508e57 100644 --- a/src/test/java/seedu/duke/parser/ParameterParserTest.java +++ b/src/test/java/seedu/duke/parser/ParameterParserTest.java @@ -2,12 +2,33 @@ import org.junit.jupiter.api.Test; import seedu.duke.command.AddCommand; +import seedu.duke.command.Command; +import seedu.duke.data.Budget; +import seedu.duke.data.transaction.Expense; +import seedu.duke.data.transaction.Income; +import seedu.duke.exception.GlobalEmptyParameterException; +import seedu.duke.exception.GlobalDuplicateTagException; +import seedu.duke.exception.GlobalMissingTagException; import seedu.duke.exception.InputTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; -import seedu.duke.exception.GlobalEmptyParameterException; +import seedu.duke.exception.InputTransactionInvalidTypeException; +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.HelpUnknownOptionException; +import seedu.duke.exception.InputBudgetInvalidAmountException; +import seedu.duke.exception.InputBudgetDuplicateException; +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; +import static seedu.duke.common.Constants.MAX_BUDGET_VALUE; +import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; +import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; +import static seedu.duke.parser.ParameterParser.parseBudgetTag; public class ParameterParserTest { @@ -48,7 +69,7 @@ public void execute_InvalidAmountInput_ExpectedException() { //@@author wcwy @Test - public void parse_AddCommandEmptyDate_ExpectedException() { + public void parse_addCommandEmptyDate_exceptionThrown() { AddCommand addCommand = new AddCommand(); String parametersInput = "t/income c/bonus a/1 d/ i/thank_you_boss"; @@ -59,7 +80,7 @@ public void parse_AddCommandEmptyDate_ExpectedException() { } @Test - public void parse_AddCommandInvalidDateFormat_ExpectedException() { + public void parse_addCommandInvalidDateFormat_exceptionThrown() { AddCommand addCommand = new AddCommand(); String parametersInput = "t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss"; @@ -68,4 +89,169 @@ public void parse_AddCommandInvalidDateFormat_ExpectedException() { () -> ParameterParser.parse(addCommand, parametersInput) ); } + + @Test + public void checkMandatoryTagsExist_allTagsExists_expectNoError() { + Command command = new AddCommand(); + String parametersInput = "t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss"; + String[] splits = parametersInput.split(" "); + assertDoesNotThrow( + () -> ParameterParser.checkMandatoryTagsExist(command, splits) + ); + } + + @Test + public void checkMandatoryTagsExist_allTagsExists_exceptionThrown() { + Command command = new AddCommand(); + String parametersInput = "t/income c/bonus a/1 d/2020-01-01"; + String[] splits = parametersInput.split(" "); + assertThrows( + GlobalMissingTagException.class, + () -> ParameterParser.checkMandatoryTagsExist(command, splits) + ); + } + + @Test + public void checkDuplicateTagsNotExist_noDuplicateTagExists_expectNoError() { + String parametersInput = "t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss"; + String[] splits = parametersInput.split(" "); + assertDoesNotThrow( + () -> ParameterParser.checkDuplicateTagsNotExist(splits) + ); + } + + + @Test + public void checkDuplicateTagsNotExist_duplicateTagsExists_exceptionThrown() { + String parametersInput = "t/income c/bonus a/1 d/2020-01-01 i/thank_you_boss i/thank_you_boss"; + String[] splits = parametersInput.split(" "); + assertThrows( + GlobalDuplicateTagException.class, + () -> ParameterParser.checkDuplicateTagsNotExist(splits) + ); + } + + @Test + public void parseTypeTagForAdding_typeIsAcceptable_expectTypeReturnBack() + throws InputTransactionInvalidTypeException { + assertEquals(ParameterParser.parseTypeTagForAdding("income"), Income.TRANSACTION_NAME); + assertEquals(ParameterParser.parseTypeTagForAdding("expense"), Expense.TRANSACTION_NAME); + } + + @Test + public void parseTypeTagForAdding_typeIsInvalid_exceptionThrown() { + assertThrows( + InputTransactionInvalidTypeException.class, + () -> ParameterParser.parseTypeTagForAdding("rAnD0m") + ); + } + + @Test + public void parseDateTag_validDateFormat_expectParsedDate() throws InputTransactionInvalidDateException { + assertEquals(ParameterParser.parseDateTag("02022222"), LocalDate.of(2222, 2, 2)); + assertEquals(ParameterParser.parseDateTag("29101999"), LocalDate.of(1999, 10, 29)); + } + + @Test + public void parseDateTag_invalidDateFormat_exceptionThrown() { + assertThrows( + InputTransactionInvalidDateException.class, + () -> ParameterParser.parseDateTag("rAnD0m") + ); + + assertThrows( + InputTransactionInvalidDateException.class, + () -> ParameterParser.parseDateTag("10291999") + ); + + assertThrows( + InputTransactionInvalidDateException.class, + () -> ParameterParser.parseDateTag("Oct 29 1999") + ); + + assertThrows( + InputTransactionInvalidDateException.class, + () -> ParameterParser.parseDateTag("29-10-1999") + ); + + assertThrows( + InputTransactionInvalidDateException.class, + () -> ParameterParser.parseDateTag("29-02-1999") + ); + } + + @Test + public void parseDateTag_emptyDateFormat_exceptionThrown() { + assertThrows( + InputTransactionInvalidDateException.class, + () -> ParameterParser.parseDateTag("") + ); + } + + @Test + public void parseHelpOptionTag_validHelpOption_expectTrue() throws HelpUnknownOptionException { + assertTrue(ParameterParser.parseHelpOptionTag("detailed")); + } + + @Test + public void parseHelpOptionTag_invalidHelpOption_exceptionThrown() { + assertThrows( + HelpUnknownOptionException.class, + () -> ParameterParser.parseHelpOptionTag("rAnD0m") + ); + } + + @Test + public void parseHelpOptionTag_emptyHelpOption_exceptionThrown() { + assertThrows( + HelpUnknownOptionException.class, + () -> ParameterParser.parseHelpOptionTag("") + ); + } + + @Test void parseBudgetTag_validBudget_expectBudgetParsedToLong() throws MoolahException { + assertEquals(parseBudgetTag("1000000000"), Long.parseLong("1000000000")); + assertEquals(parseBudgetTag(Integer.toString(MIN_BUDGET_VALUE)), Long.parseLong("1")); + assertEquals(parseBudgetTag(Long.toString(MAX_BUDGET_VALUE)), (long) MAX_TRANSACTIONS_COUNT * MAX_AMOUNT_VALUE); + } + + @Test + public void parseBudgetTag_budgetContainsAlphabets_exceptionThrown() { + assertThrows( + InputBudgetInvalidAmountException.class, + () -> ParameterParser.parseBudgetTag("0xffffffff") + ); + } + + @Test + public void parseBudgetTag_budgetContainsSymbols_exceptionThrown() { + assertThrows( + InputBudgetInvalidAmountException.class, + () -> ParameterParser.parseBudgetTag("1000000000-5") + ); + } + + @Test + public void parseBudgetTag_budgetOutOfAcceptedRange_exceptionThrown() { + assertThrows( + InputBudgetInvalidAmountException.class, + () -> ParameterParser.parseBudgetTag(Integer.toString(MIN_BUDGET_VALUE - 1)) + ); + + assertThrows( + InputBudgetInvalidAmountException.class, + () -> ParameterParser.parseBudgetTag(Long.toString(MAX_BUDGET_VALUE + 1)) + ); + } + + @Test + public void parseBudgetTag_budgetToSetSameAsCurrentBudget_exceptionThrown() { + String budgetAmount = Long.toString(Budget.getBudget()); + assertThrows( + InputBudgetDuplicateException.class, + () -> ParameterParser.parseBudgetTag(budgetAmount) + ); + } + + } From 806ac6ca6360f11de5342949dcd14e7dec4f55b8 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 03:48:55 +0800 Subject: [PATCH 214/416] Update test cases for budget command --- text-ui-test/EXPECTED.TXT | 48 +++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 5122d4de7..9e49130b0 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -19,6 +19,10 @@ To display a list of available commands with their respective expected parameter Type "help o/detailed" for a detailed version of all parameters used. Usage: help [o/detailed] +Command Word: BUDGET +To set the amount of monthly budget. +Usage: budget b/BUDGET + Command Word: ADD To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION @@ -61,6 +65,12 @@ Usage: help [o/detailed] Parameters information: (Optional) o/detailed - Detailed version of guide. +Command Word: BUDGET +To set the amount of monthly budget. +Usage: budget b/BUDGET +Parameters information: +BUDGET - Amount of monthly budget set. + Command Word: ADD To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION @@ -143,6 +153,34 @@ ____________________________________________________________ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ +Parameter behind tag(s) is found to be empty, please check your input! +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! +____________________________________________________________ +____________________________________________________________ +Invalid budget amount, please ensure your amount is in positive whole number of valid range only! +____________________________________________________________ +____________________________________________________________ +Invalid budget amount, please ensure your amount is in positive whole number of valid range only! +____________________________________________________________ +____________________________________________________________ +Invalid budget amount, please ensure your amount is in positive whole number of valid range only! +____________________________________________________________ +____________________________________________________________ +Invalid budget amount, please ensure your amount is in positive whole number of valid range only! +____________________________________________________________ +____________________________________________________________ +You have successfully updated the budget. +Monthly budget set as: $10000 +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! +____________________________________________________________ +____________________________________________________________ +Mandatory tag(s) missing, please check your input! +____________________________________________________________ +____________________________________________________________ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ @@ -166,17 +204,17 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][food] $20 at Sep 13 2022 | Description: NIL -Budget remained for the month of transaction: $980 +Budget remained for the month of transaction: $9980 ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary -Budget remained for the month of transaction: $-1020 +Budget remained for the month of transaction: $7980 ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare -Budget remained for the month of transaction: $999 +Budget remained for the month of transaction: $9999 ____________________________________________________________ ____________________________________________________________ Parameter behind tag(s) is found to be empty, please check your input! @@ -266,7 +304,7 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss -Budget remained for the month of transaction: $-9999001 +Budget remained for the month of transaction: $-9990001 ____________________________________________________________ ____________________________________________________________ Type of transaction given is invalid, please check your input! @@ -381,7 +419,7 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ -Budget remained for the month of transaction: $999 +Budget remained for the month of transaction: $9999 ____________________________________________________________ ____________________________________________________________ Not supported tag(s) detected, please check your input! From dddb950b2764357711fdac9ca33a82cda91f1a70 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 03:58:38 +0800 Subject: [PATCH 215/416] Bypass StorageWriteErrorException in test case --- src/test/java/seedu/duke/command/AddCommandTest.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/test/java/seedu/duke/command/AddCommandTest.java b/src/test/java/seedu/duke/command/AddCommandTest.java index af31d43ca..5b01e96fc 100644 --- a/src/test/java/seedu/duke/command/AddCommandTest.java +++ b/src/test/java/seedu/duke/command/AddCommandTest.java @@ -49,9 +49,10 @@ public void checkTransactionCapacity_transactionListFullSpace_exceptionThrown() @Test public void execute_addValidExpense_expectedSuccessfulExpenseAddition() throws MoolahException { - TransactionList transactions = new TransactionList(); Ui ui = new Ui(); Storage storage = new Storage(); + TransactionList transactions = new TransactionList(storage.initializeFile()); + transactions.purgeTransactions(); AddCommand addCommand = new AddCommand("expense", "a", 1, "a", LocalDate.of(2022, 1, 1)); addCommand.execute(transactions, ui, storage); @@ -62,9 +63,10 @@ public void execute_addValidExpense_expectedSuccessfulExpenseAddition() throws M @Test public void execute_addValidIncome_expectedSuccessfulIncomeAddition() throws MoolahException { - TransactionList transactions = new TransactionList(); Ui ui = new Ui(); Storage storage = new Storage(); + TransactionList transactions = new TransactionList(storage.initializeFile()); + transactions.purgeTransactions(); AddCommand addCommand = new AddCommand("income", "a", 1, "a", LocalDate.of(2022, 1, 1)); addCommand.execute(transactions, ui, storage); From e2407ae84b111b71626269aea4ea51aa4fdc554b Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 15:07:23 +0800 Subject: [PATCH 216/416] Add header comments for exception classes --- src/main/java/seedu/duke/Duke.java | 4 +--- .../exception/FindTransactionMissingKeywordsException.java | 3 +++ .../seedu/duke/exception/GlobalInvalidMonthException.java | 3 +++ .../duke/exception/InputTransactionInvalidTypeException.java | 3 +++ .../java/seedu/duke/exception/StatsInvalidTypeException.java | 3 +++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index cd42c1bca..194da73b0 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -5,8 +5,6 @@ import seedu.duke.exception.MoolahException; import seedu.duke.parser.CommandParser; -import java.io.IOException; - public class Duke { //@@author paullowse private Storage storage; @@ -16,8 +14,8 @@ public class Duke { //@@author chinhan99 public Duke() { // TODO: Add a file path when implementing storage feature ui = new Ui(); - storage = new Storage(); + try { transactions = new TransactionList(storage.initializeFile()); } catch (MoolahException e) { diff --git a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java index 823b933f5..48019df25 100644 --- a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java +++ b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java @@ -3,6 +3,9 @@ //@@author chydarren import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where there are missing keywords in the search expression. + */ public class FindTransactionMissingKeywordsException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java b/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java index 79e66ee00..eb6995bee 100644 --- a/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java @@ -3,6 +3,9 @@ //@@author chydarren import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the user input for month is not within 1 to 12. + */ public class GlobalInvalidMonthException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java index 696e0afde..9276679d1 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java @@ -3,6 +3,9 @@ //@@author chydarren import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the type given for a transaction is not Income or Expense. + */ public class InputTransactionInvalidTypeException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java b/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java index dde30e309..7fc144f40 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java +++ b/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java @@ -3,6 +3,9 @@ //@@author chydarren import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the type given for requesting statistics is not valid. + */ public class StatsInvalidTypeException extends MoolahException { /** From bea7be539fdf54f5a83895e1f670aa990652f5e7 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 15:08:24 +0800 Subject: [PATCH 217/416] Add functions to check month_year and period_number and replace magic some literals in checks --- src/main/java/seedu/duke/command/Command.java | 10 +- .../java/seedu/duke/command/ListCommand.java | 99 ++++++++++++++--- .../java/seedu/duke/command/StatsCommand.java | 105 ++++++++++++------ .../java/seedu/duke/data/TransactionList.java | 22 ++-- .../seedu/duke/parser/ParameterParser.java | 20 ++-- 5 files changed, 184 insertions(+), 72 deletions(-) diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index be2bdfbe5..24866bbef 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -90,17 +90,15 @@ public void setIsDetailedOption(boolean isDetailed) { public void setStatsType(String statsType) { } - public void setStatsMonth(int month) { + public void setGlobalMonth(int month) { } - public void setStatsYear(int year) { + public void setGlobalYear(int year) { } - public void setStatsNumber(int number) { + public void setGlobalNumber(int number) { } - public void setStatsPeriod(String period) { + public void setGlobalPeriod(String period) { } - - } diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 958ecefc0..8a1f81397 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -1,6 +1,6 @@ package seedu.duke.command; -//@@author paullowse +//@@author chydarren import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; @@ -12,12 +12,13 @@ import java.util.logging.Logger; import java.util.logging.Level; -//@@author chydarren import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; @@ -55,14 +56,22 @@ public class ListCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; //@@author chydarren - private static final Logger listLogger = Logger.getLogger(ListCommand.class.getName()); + private static final int UNDEFINED_PARAMETER = -1; + private static final int TRUE_AND = 1; + private static final int TRUE_OR = 2; + private static final int TRUE_INVALID_OR = 3; + private static final int FALSE = 0; - //@@author paullowse + private static Logger listLogger = Logger.getLogger(ListCommand.class.getName()); private String category; private LocalDate date; private String type; private int month; private int year; + private String period; + private int number; + + //@@author paullowse /** * Initialises the variables of the ListCommand class. @@ -71,8 +80,10 @@ public ListCommand() { category = ""; date = null; type = ""; - month = -1; - year = -1; + month = UNDEFINED_PARAMETER; + year = UNDEFINED_PARAMETER; + period = null; + number = UNDEFINED_PARAMETER; } /** @@ -87,7 +98,9 @@ public String[] getOptionalTags() { COMMAND_TAG_TRANSACTION_CATEGORY, COMMAND_TAG_TRANSACTION_DATE, COMMAND_TAG_GLOBAL_MONTH, - COMMAND_TAG_GLOBAL_YEAR + COMMAND_TAG_GLOBAL_YEAR, + COMMAND_TAG_GLOBAL_NUMBER, + COMMAND_TAG_GLOBAL_PERIOD }; return optionalTags; } @@ -108,17 +121,57 @@ public void setDate(LocalDate date) { } @Override - public void setStatsMonth(int month) { + public void setGlobalMonth(int month) { this.month = month; } @Override - public void setStatsYear(int year) { + public void setGlobalYear(int year) { this.year = year; } + @Override + public void setGlobalNumber(int number) { + this.number = number; + } + + @Override + public void setGlobalPeriod(String period) { + this.period = period; + } + //@@author chydarren + /** + * Checks if the input contains month or/and year tags. + * + * @return 1 if both tags are given, 2 if only month is given, 0 if both are not given. + */ + public int containMonthYear() { + if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (year != UNDEFINED_PARAMETER) { + return TRUE_OR; + } else if (month != UNDEFINED_PARAMETER) { + return TRUE_INVALID_OR; + } + return FALSE; + } + + /** + * Checks if the input contains period and/or number tags. + * + * @return 1 if both tags are given, 2 if either period/number is given, 0 if both are not given. + */ + public int containPeriodNumber() { + if (period != null && number != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (period != null || number != UNDEFINED_PARAMETER) { + return TRUE_OR; + } + return FALSE; + } + /** * Executes the operations related to the command. * @@ -131,7 +184,22 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws listLogger.setLevel(Level.SEVERE); listLogger.log(Level.INFO, "Entering execution of the List command."); - listTransactions(transactions, type, category, date, month, year); + // Throws an invalid list tags exception if there are both month/year and period/number + if (containMonthYear() == TRUE_OR && containPeriodNumber() == TRUE_OR) { + listLogger.log(Level.WARNING, "An exception has been caught as an invalid combination of tags " + + "has been given."); + // Throws a missing tag exception if month was given without a year + } else if (containMonthYear() == TRUE_INVALID_OR) { + listLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); + throw new GlobalMissingTagException(); + // Throws a missing tag if number and period was not given together + } else if (containPeriodNumber() == TRUE_OR) { + listLogger.log(Level.WARNING, "An exception has been caught as number and period needs to be " + + "given together."); + throw new GlobalMissingTagException(); + } + + listTransactions(transactions, type, category, date, month, year, number, period); } /** @@ -144,14 +212,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date, - int month, int year) - throws InputTransactionInvalidTypeException, GlobalMissingTagException { - if (month != -1 && year == -1) { - listLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); - throw new GlobalMissingTagException(); - } - - String transactionsList = transactions.listTransactions(type, category, date, month, year); + int month, int year, int number, String period) + throws InputTransactionInvalidTypeException { + String transactionsList = transactions.listTransactions(type, category, date, month, year, number, period); if (transactionsList.isEmpty()) { listLogger.log(Level.INFO, "Transactions list is empty as there are no transactions available."); diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index cf4b9ff4d..3b83b9eab 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -1,6 +1,6 @@ package seedu.duke.command; -//@@author chydarren +//@@author paullowse import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; @@ -13,7 +13,6 @@ import java.util.logging.Level; import java.util.logging.Logger; -//@@author paullowse import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; @@ -53,16 +52,30 @@ public class StatsCommand extends Command { + LINE_SEPARATOR; //@@author chydarren - private static final Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); + private static final int UNDEFINED_PARAMETER = -1; + private static final int TRUE_AND = 1; + private static final int TRUE_OR = 2; + private static final int TRUE_INVALID_OR = 3; + private static final int FALSE = 0; + private static final String CATEGORIES = "categories"; + private static final String TIME = "time"; + private static final String WEEKS = "weeks"; + private static final String MONTHS = "months"; + private static Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); private String statsType; + private int month; + private int year; + private String period; + private int number; //@@author paullowse - private int month = -1; - private int year = -1; - private String period = null; - private int number = -1; public StatsCommand() { + statsType = ""; + period = null; + month = UNDEFINED_PARAMETER; + year = UNDEFINED_PARAMETER; + number = UNDEFINED_PARAMETER; } /** @@ -93,30 +106,56 @@ public void setStatsType(String statsType) { } @Override - public void setStatsMonth(int month) { + public void setGlobalMonth(int month) { this.month = month; } @Override - public void setStatsYear(int year) { + public void setGlobalYear(int year) { this.year = year; } @Override - public void setStatsNumber(int number) { + public void setGlobalNumber(int number) { this.number = number; } @Override - public void setStatsPeriod(String period) { + public void setGlobalPeriod(String period) { this.period = period; } - public int getStatsYear() { - return year; + //@@author chydarren + + /** + * Checks if the input contains month or/and year tags. + * + * @return 1 if both tags are given, 2 if only month is given, 0 if both are not given. + */ + public int containMonthYear() { + if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (year != UNDEFINED_PARAMETER) { + return TRUE_OR; + } else if (month != UNDEFINED_PARAMETER) { + return TRUE_INVALID_OR; + } + return FALSE; } - //@@author chydarren + /** + * Checks if the input contains period and/or number tags. + * + * @return 1 if both tags are given, 2 if either period/number is given, 0 if both are not given. + */ + public int containPeriodNumber() { + if (period != null && number != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (period != null || number != UNDEFINED_PARAMETER) { + return TRUE_OR; + } + return FALSE; + } /** * Executes the operations related to the command. @@ -140,24 +179,29 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param transactions An instance of the TransactionList class. * @throws MoolahException If the type of statistics is not recognised. */ - private static void listStatsByStatsType(String statsType, TransactionList transactions, int month, + private void listStatsByStatsType(String statsType, TransactionList transactions, int month, int year, String period, int number) throws MoolahException { switch (statsType) { - case "categories": + case CATEGORIES: statsLogger.log(Level.INFO, "Stats type has been detected for categorical savings."); statsTypeCategoricalSavings(transactions); statsLogger.log(Level.INFO, "End of Stats command."); break; //@@author paullowse - case "time": + case TIME: statsLogger.log(Level.INFO, "Stats type has been detected for monthly savings."); - if (year == -1 && month == -1 && period != null && number != -1) { - statsLogger.log(Level.INFO, "Stats command uses lastNperiod."); + + // Stats command uses last N months or years + if (containPeriodNumber() == TRUE_AND && containMonthYear() == FALSE) { + statsLogger.log(Level.INFO, "Stats command uses last N months or years."); statsTypeTimeSavings(transactions, year, month, period, number); - } else if (year != -1 && period == null && number == -1) { + // Stats command uses either monthly or yearly + } else if (containPeriodNumber() == FALSE && (containMonthYear() == TRUE_OR + || containMonthYear() == TRUE_AND)) { statsLogger.log(Level.INFO, "Stats command uses either monthly or yearly."); statsTypeTimeSavings(transactions, year, month, period, number); + // Throws a missing tag if s/time was called without any relevant tags } else { statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); throw new StatsInvalidTypeException(); @@ -177,7 +221,7 @@ private static void listStatsByStatsType(String statsType, TransactionList trans * * @param transactions An instance of the TransactionList class. */ - public static void statsTypeCategoricalSavings(TransactionList transactions) { + public void statsTypeCategoricalSavings(TransactionList transactions) { String categoricalSavingsList = transactions.listCategoricalSavings(); if (categoricalSavingsList.isEmpty()) { @@ -204,19 +248,19 @@ public static void statsTypeCategoricalSavings(TransactionList transactions) { * @param number A specified number of periods. * @throws MoolahException If the type of statistics is not recognised. */ - public static void statsTypeTimeSavings(TransactionList transactions, int year, int month, + public void statsTypeTimeSavings(TransactionList transactions, int year, int month, String period, int number) throws MoolahException { ArrayList timeTransactions; // only year - if (period != null && number != -1 && period == "weeks") { + if (containPeriodNumber() == TRUE_AND && period == WEEKS) { timeTransactions = transactions.getTransactionsByWeekRange(LocalDate.now(), number); - } else if (period != null && number != -1 && period == "months") { + } else if (containPeriodNumber() == TRUE_AND && period == WEEKS) { timeTransactions = transactions.getTransactionsByMonthRange(LocalDate.now(), number); - } else if (month == -1) { - timeTransactions = transactions.getTransactionsByYear(year); - } else if (year != -1) { + } else if (containMonthYear() == TRUE_AND) { timeTransactions = transactions.getTransactionsByMonth(year, month); + } else if (containMonthYear() == TRUE_OR) { + timeTransactions = transactions.getTransactionsByYear(year); } else { statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); throw new StatsInvalidTypeException(); @@ -234,10 +278,9 @@ public static void statsTypeTimeSavings(TransactionList transactions, int year, amounts = transactions.processTimeSummaryStats(timeTransactions); String incomeMessage = INFO_STATS_SUMMARY_HEADER + LINE_SEPARATOR - + INFO_STATS_INCOME.toString() + amounts.get(0); - String expensesMessage = INFO_STATS_EXPENSES.toString() + amounts.get(1); - String savingsMessage = INFO_STATS_SAVINGS.toString() + amounts.get(2); - + + INFO_STATS_INCOME + amounts.get(0); + String expensesMessage = INFO_STATS_EXPENSES + amounts.get(1); + String savingsMessage = INFO_STATS_SAVINGS + amounts.get(2); assert !timeSavingsList.isEmpty(); statsLogger.log(Level.INFO, "Monthly savings list is found to contain categories-amount pairs."); diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index fda477043..8c5947134 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -26,8 +26,11 @@ public class TransactionList { private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; private static final String SYMBOL_DOLLAR = "$"; + private static final String INCOME = "income"; + private static final String EXPENSE = "expense"; private static final int START = 0; private static final int END = 1; + private static final int UNDEFINED_PARAMETER = -1; private static final String LINE_SEPARATOR = System.lineSeparator(); //@@author chinhan99 @@ -176,15 +179,16 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c * @return A string containing the formatted transaction list. * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ - public String listTransactions(String type, String category, LocalDate date, int month, int year) + public String listTransactions(String type, String category, LocalDate date, int month, int year, + int number, String period) throws InputTransactionInvalidTypeException { ArrayList timeTransactions; // Filters the transactions list by month or/and year first - if (year != -1 && month != -1) { + if (year != UNDEFINED_PARAMETER && month != UNDEFINED_PARAMETER) { timeTransactions = getTransactionsByMonth(year, month); - } else if (year != -1) { - assert month == -1; + } else if (year != UNDEFINED_PARAMETER) { + assert month == UNDEFINED_PARAMETER; timeTransactions = getTransactionsByYear(year); } else { // No filter month or/and year filter applied @@ -215,7 +219,7 @@ public String findTransactions(String keywords) { for (Transaction transaction : transactions) { // Includes only transactions that contain the keywords used in the search expression if (transaction.toString().contains(keywords)) { - transactionsList += transaction.toString() + LINE_SEPARATOR; + transactionsList += transaction + LINE_SEPARATOR; } } return transactionsList; @@ -278,10 +282,10 @@ public String listTimeStats(ArrayList timeTransactions, int year, i int number) { String timeSavingsList = ""; - if (period != null && number != -1) { + if (period != null && number != UNDEFINED_PARAMETER) { timeSavingsList += "The past " + number + " " + period + ": " + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; - } else if (month == -1) { + } else if (month == UNDEFINED_PARAMETER) { timeSavingsList += "Year: " + year + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; } else { @@ -309,9 +313,9 @@ public ArrayList processTimeSummaryStats(ArrayList timeTran int timeIncome = 0; for (Transaction entry : timeTransactions) { String category = entry.getType(); - if (category == "expense") { + if (category.equals(EXPENSE)) { timeExpense += entry.getAmount(); - } else if (category == "income") { + } else if (category.equals(INCOME)) { timeIncome += entry.getAmount(); } } diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index d6afec0f6..719fd4c80 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -42,7 +42,6 @@ import static seedu.duke.common.Constants.MAX_BUDGET_VALUE; import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; - //@@author paullowse import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; @@ -76,6 +75,11 @@ public class ParameterParser { private static final String DELIMITER = " "; private static final int SPLIT_POSITION = 2; private static final int MINIMUM_TAG_LENGTH = 2; + private static final int MINIMUM_YEAR = 1000; + private static final int SMALLEST_POSITIVE_INTEGER = 0; + private static final int JANUARY = 1; + private static final int DECEMBER = 12; + private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; private static final String CATEGORIES = "categories"; @@ -331,16 +335,16 @@ private static void setParameter(Command command, String tag, String parameter) command.setStatsType(parseStatsTypeTag(parameter)); break; case COMMAND_TAG_GLOBAL_MONTH: - command.setStatsMonth(parseMonthTag(parameter)); + command.setGlobalMonth(parseMonthTag(parameter)); break; case COMMAND_TAG_GLOBAL_YEAR: - command.setStatsYear(parseYearTag(parameter)); + command.setGlobalYear(parseYearTag(parameter)); break; case COMMAND_TAG_GLOBAL_NUMBER: - command.setStatsNumber(parseNumberTag(parameter)); + command.setGlobalNumber(parseNumberTag(parameter)); break; case COMMAND_TAG_GLOBAL_PERIOD: - command.setStatsPeriod(parsePeriodTag(parameter)); + command.setGlobalPeriod(parsePeriodTag(parameter)); break; case COMMAND_TAG_BUDGET_AMOUNT: command.setBudgetAmount(parseBudgetTag(parameter)); @@ -582,7 +586,7 @@ public static int parseMonthTag(String parameter) throws GlobalInvalidMonthExcep throw new GlobalNumberNotNumericException(); } - if (month > 12 || month <= 0) { + if (month > DECEMBER || month < JANUARY) { parserLogger.log(Level.WARNING, "An invalid month number error is caught for the given parameter: " + parameter); throw new GlobalInvalidMonthException(); @@ -600,7 +604,7 @@ public static int parseYearTag(String parameter) throws GlobalInvalidYearExcepti + parameter); throw new GlobalNumberNotNumericException(); } - if (year <= 999) { + if (year < MINIMUM_YEAR) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); throw new GlobalInvalidYearException(); @@ -632,7 +636,7 @@ public static int parseNumberTag(String parameter) throws GlobalNumberNotNumeric + parameter); throw new GlobalNumberNotNumericException(); } - if (statsNumber < 0) { + if (statsNumber < SMALLEST_POSITIVE_INTEGER) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); throw new GlobalInvalidNumberException(); From 1fc600db3050ac6f58d0756910e81c5065968406 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Mon, 24 Oct 2022 15:11:49 +0800 Subject: [PATCH 218/416] Add storage support for BudgetCommand --- src/main/java/seedu/duke/Storage.java | 157 ++++++++++++------ .../seedu/duke/command/BudgetCommand.java | 13 +- .../java/seedu/duke/command/EditCommand.java | 16 +- .../java/seedu/duke/common/ErrorMessages.java | 6 +- .../StorageFileCorruptedBudgetException.java | 17 ++ ...ageFileCorruptedTransactionException.java} | 4 +- text-ui-test/EXPECTED.TXT | 3 + 7 files changed, 157 insertions(+), 59 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/StorageFileCorruptedBudgetException.java rename src/main/java/seedu/duke/exception/{StorageFileCorruptedException.java => StorageFileCorruptedTransactionException.java} (66%) diff --git a/src/main/java/seedu/duke/Storage.java b/src/main/java/seedu/duke/Storage.java index f34653b45..75e4cf36d 100644 --- a/src/main/java/seedu/duke/Storage.java +++ b/src/main/java/seedu/duke/Storage.java @@ -2,10 +2,14 @@ import seedu.duke.command.Command; import seedu.duke.common.DateFormats; +import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.StorageFileCorruptedException; +import seedu.duke.exception.InputBudgetDuplicateException; +import seedu.duke.exception.InputBudgetInvalidAmountException; +import seedu.duke.exception.StorageFileCorruptedTransactionException; +import seedu.duke.exception.StorageFileCorruptedBudgetException; import seedu.duke.exception.StorageWriteErrorException; import seedu.duke.parser.ParameterParser; @@ -68,19 +72,22 @@ private static File checkIfFileExist() throws IOException { * Initializes the duke.txt by checking its existence, then store the data values to the program. * * @return The TransactionList storing entries which would be used in the program. - * @throws StorageFileCorruptedException If there are errors due to corrupted duke.txt data. - * @throws StorageWriteErrorException If the file could not be created or could not be written. + * @throws StorageFileCorruptedTransactionException If there are errors due to corrupted duke.txt data. + * @throws StorageWriteErrorException If the file could not be created or could not be written. */ public TransactionList initializeFile() throws MoolahException { try { File file = checkIfFileExist(); Scanner input = new Scanner(file); - storeFileValuesLocally(storedTransactions, input); + storeFileValuesLocally(input); Ui.printMessages("* duke.txt loaded successfully! *"); - } catch (MoolahException e) { - // Catch any parsing errors and throw the default StorageInputCorruptedException - throw new StorageFileCorruptedException(); + } catch (StorageFileCorruptedBudgetException e) { + // Catch any Budget parsing errors and throw the default StorageInputCorruptedBudgetException + throw new StorageFileCorruptedBudgetException(); + } catch (StorageFileCorruptedTransactionException e) { + // Catch any Transaction parsing errors and throw the default StorageInputCorruptedTransactionException + throw new StorageFileCorruptedTransactionException(); } catch (IOException e) { throw new StorageWriteErrorException(); } @@ -88,58 +95,102 @@ public TransactionList initializeFile() throws MoolahException { } /** - * Stores values from duke.txt to the program by parsing each line in the file. + * Stores budget value from duke.txt to the program by parsing the first line of the file. * - * @param storedTransactions The TransactionList to hold the stored values from the file. - * @param input The input from duke.txt to be processed. - * @throws MoolahException WHen there are parsing errors, due to corrupted data. + * @param monthlyBudget The budget value to be parsed. + * @throws MoolahException When there are parsing errors, due to corrupted data. */ + private void storeBudgetLocally(String monthlyBudget) throws MoolahException { + try { + Command command = null; + ParameterParser.parseBudgetTag(monthlyBudget); + Budget.setBudget(Long.parseLong(monthlyBudget)); - private void storeFileValuesLocally(TransactionList storedTransactions, Scanner input) throws MoolahException { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateFormats.DATE_STORAGE_OUTPUT_PATTERN.toString()); + } catch (InputBudgetDuplicateException e) { + Ui.printMessages("* Budget value remains as default *"); + } catch (InputBudgetInvalidAmountException e) { + throw new StorageFileCorruptedBudgetException(); + } + } + + /** + * Stores Transaction parameters from duke.txt to the program by parsing each line of the file after Budget value. + * + * @param lineString The individual lines from duke.txt which would be parsed. + * @throws MoolahException When there are parsing errors, due to corrupted data. + */ + private void storeTransactionsLocally(String lineString) throws MoolahException { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateFormats.DATE_STORAGE_OUTPUT_PATTERN.toString()); + String[] splits = lineString.split(DELIMITER); Command command = null; - while (input.hasNext()) { - String line = input.nextLine(); - String[] splits = line.split(DELIMITER); - if (splits.length != NUMBER_OF_STORED_PARAMETERS) { - throw new StorageFileCorruptedException(); - } - String type = splits[0]; - String category = splits[1]; - String amountString = splits[2]; - // Date has been formatted in duke.txt and must be synthesized into the correct string format before parsing - try { - LocalDate date = LocalDate.parse(splits[3], formatter); - String dateString = synthesizeDateString(date); - - - String description = splits[4]; - - String parametersInput = "t/" + type + " c/" + category + " a/" + amountString + " d/" + dateString - + " i/" + description; - command = getCommand("add", parametersInput); - - ParameterParser.parse(command, parametersInput); - - // amount would be converted into an integer before being used in the addition of transaction locally - int amount = Integer.parseInt(splits[2]); - - switch (type) { - case "expense": - storedTransactions.addExpenseDuringStorage(description, amount, category, date); - break; - case "income": - storedTransactions.addIncomeDuringStorage(description, amount, category, date); - break; - default: - throw new StorageFileCorruptedException(); - } - } catch (DateTimeParseException e) { - // If the date format is incorrect, which is due to corrupted date information - throw new StorageFileCorruptedException(); + if (splits.length != NUMBER_OF_STORED_PARAMETERS) { + throw new StorageFileCorruptedTransactionException(); + } + + String type = splits[0]; + String category = splits[1]; + String amountString = splits[2]; + // Date has been formatted in duke.txt and must be synthesized into the correct string format before parsing + try { + LocalDate date = LocalDate.parse(splits[3], formatter); + String dateString = synthesizeDateString(date); + + + String description = splits[4]; + + String parametersInput = "t/" + type + " c/" + category + " a/" + amountString + " d/" + dateString + + " i/" + description; + command = getCommand("add", parametersInput); + + ParameterParser.parse(command, parametersInput); + + // amount would be converted into an integer before being used in the addition of transaction locally + int amount = Integer.parseInt(splits[2]); + + switch (type) { + case "expense": + storedTransactions.addExpenseDuringStorage(description, amount, category, date); + break; + case "income": + storedTransactions.addIncomeDuringStorage(description, amount, category, date); + break; + default: + throw new StorageFileCorruptedTransactionException(); } + } catch (DateTimeParseException e) { + // If the date format is incorrect, which is due to corrupted date information + throw new StorageFileCorruptedTransactionException(); + } catch (MoolahException e) { + throw new StorageFileCorruptedTransactionException(); + } + } + + + /** + * Stores values from duke.txt to the program by parsing each line in the file. + * + * @param input The input from duke.txt to be processed. + * @throws MoolahException When there are parsing errors, due to corrupted data. + */ + + + private void storeFileValuesLocally(Scanner input) throws MoolahException { + + + // Processes the budget value located at the first line of the duke.txt file + if (input.hasNext()) { + String monthlyBudget = input.nextLine(); + storeBudgetLocally(monthlyBudget); + } else { + // Else, the file would be empty + Ui.printMessages("* Duke.txt file is currently empty and ready to be written *"); + } + + while (input.hasNext()) { + String line = input.nextLine(); + storeTransactionsLocally(line); } @@ -184,7 +235,9 @@ private String synthesizeDateString(LocalDate date) { */ public void writeToFile(ArrayList transactions) throws IOException { FileWriter fileWriter = new FileWriter(FILE_PATH); + long monthlyBudget = Budget.getBudget(); String transactionEntry = ""; + fileWriter.write(monthlyBudget + System.lineSeparator()); for (Transaction transaction : transactions) { transactionEntry = transaction.getType() + " | " + transaction.getCategory() + " | " diff --git a/src/main/java/seedu/duke/command/BudgetCommand.java b/src/main/java/seedu/duke/command/BudgetCommand.java index 9d9fe1116..3e1af70d7 100644 --- a/src/main/java/seedu/duke/command/BudgetCommand.java +++ b/src/main/java/seedu/duke/command/BudgetCommand.java @@ -7,6 +7,9 @@ import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageWriteErrorException; + +import java.io.IOException; import static seedu.duke.command.CommandTag.COMMAND_TAG_BUDGET_AMOUNT; @@ -77,8 +80,14 @@ public String[] getMandatoryTags() { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - Budget.setBudget(budgetAmount); - ui.showSetBudgetAcknowledgementMessage(Long.toString(budgetAmount)); + try { + Budget.setBudget(budgetAmount); + ui.showSetBudgetAcknowledgementMessage(Long.toString(budgetAmount)); + //@@author chinhan99 + storage.writeToFile(transactions.getTransactions()); + } catch (IOException e) { + throw new StorageWriteErrorException(); + } } //@@author paullowse diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 342f15cc9..f205c3112 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -1,9 +1,11 @@ package seedu.duke.command; //@@author brian-vb + import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.exception.MoolahException; import java.time.LocalDate; @@ -128,7 +130,7 @@ public void setDate(LocalDate date) { * @param storage An instance of the Storage class. */ @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) { + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { // Dummy output for test System.out.println(String.format("Entry number: %d\nType: %s\nDesc: %s\n$: %d\nCat: %s, Date: %s", entryNumber, @@ -137,6 +139,18 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { amount, category, date.toString())); + + + // Storage code to be utilised in EditCommand when the TransactionList contents are edited + + /* + try { + + } catch (IOException e) { + throw new StorageWriteErrorException(); + } + + */ } /** diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 914a9f613..22de6a8a1 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -23,8 +23,10 @@ public enum ErrorMessages { ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_TYPE("Type of statistics given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), - ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. " - + "To preserve data, please STOP the program and edit your data file correctly."), + ERROR_STORAGE_TRANSACTION_CORRUPTED("Transaction values corrupted." + + " To preserve data, please STOP the program and edit your data file correctly."), + ERROR_STORAGE_BUDGET_CORRUPTED("Budget parameter is invalid or missing." + + " To preserve data, please STOP the program and edit your data file correctly."), ERROR_STORAGE_WRITE("Unable to write to Duke.txt. Please save your current Duke.txt file and restart Moolah"), ERROR_MAXIMUM_TRANSACTION_COUNT_REACHED("Unable to add transaction. " + "The maximum allowed transaction size (1000000) has been reached."), diff --git a/src/main/java/seedu/duke/exception/StorageFileCorruptedBudgetException.java b/src/main/java/seedu/duke/exception/StorageFileCorruptedBudgetException.java new file mode 100644 index 000000000..ebbccb4c3 --- /dev/null +++ b/src/main/java/seedu/duke/exception/StorageFileCorruptedBudgetException.java @@ -0,0 +1,17 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class StorageFileCorruptedBudgetException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STORAGE_BUDGET_CORRUPTED.toString(); + } + +} diff --git a/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java b/src/main/java/seedu/duke/exception/StorageFileCorruptedTransactionException.java similarity index 66% rename from src/main/java/seedu/duke/exception/StorageFileCorruptedException.java rename to src/main/java/seedu/duke/exception/StorageFileCorruptedTransactionException.java index 21a8569bf..91a62317d 100644 --- a/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java +++ b/src/main/java/seedu/duke/exception/StorageFileCorruptedTransactionException.java @@ -3,7 +3,7 @@ //@@author chinhan99 import seedu.duke.common.ErrorMessages; -public class StorageFileCorruptedException extends MoolahException { +public class StorageFileCorruptedTransactionException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. @@ -12,7 +12,7 @@ public class StorageFileCorruptedException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_STORAGE_FILE_CORRUPTED.toString(); + return ErrorMessages.ERROR_STORAGE_TRANSACTION_CORRUPTED.toString(); } } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 9e49130b0..ffee52fa3 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,6 +5,9 @@ ____________________________________________________________ * Created new file for use * ____________________________________________________________ ____________________________________________________________ +* Duke.txt file is currently empty and ready to be written * +____________________________________________________________ +____________________________________________________________ * duke.txt loaded successfully! * ____________________________________________________________ ____________________________________________________________ From 99c178f7042a65a26a6b3ea2e4c0cdfb452bf25f Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Mon, 24 Oct 2022 15:27:27 +0800 Subject: [PATCH 219/416] Attempt to remove IO write issue in BudgetCommandTest --- .gitignore | 2 +- data/duke.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 data/duke.txt diff --git a/.gitignore b/.gitignore index 0c5ba6d7d..c685adf32 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,6 @@ src/main/resources/docs/ *.iml bin/ -/data/duke.txt + /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT diff --git a/data/duke.txt b/data/duke.txt new file mode 100644 index 000000000..2045006ed --- /dev/null +++ b/data/duke.txt @@ -0,0 +1 @@ +9223372036854775807 From cc21cc7017be84fbee3672f003ef11269685dc56 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Mon, 24 Oct 2022 15:51:46 +0800 Subject: [PATCH 220/416] Add exception handling to ignore printing of message in BudgetCommandTest --- .gitignore | 2 +- data/duke.txt | 2 +- .../seedu/duke/command/BudgetCommandTest.java | 36 +++++++++++-------- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index c685adf32..0c5ba6d7d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,6 @@ src/main/resources/docs/ *.iml bin/ - +/data/duke.txt /text-ui-test/ACTUAL.txt text-ui-test/EXPECTED-UNIX.TXT diff --git a/data/duke.txt b/data/duke.txt index 2045006ed..83b33d238 100644 --- a/data/duke.txt +++ b/data/duke.txt @@ -1 +1 @@ -9223372036854775807 +1000 diff --git a/src/test/java/seedu/duke/command/BudgetCommandTest.java b/src/test/java/seedu/duke/command/BudgetCommandTest.java index 0f7fdd987..95d16c35b 100644 --- a/src/test/java/seedu/duke/command/BudgetCommandTest.java +++ b/src/test/java/seedu/duke/command/BudgetCommandTest.java @@ -6,6 +6,9 @@ import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageWriteErrorException; + +import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -13,23 +16,28 @@ public class BudgetCommandTest { //@@author wcwy @Test public void execute_setValidBudget_expectedSuccessfulBudgetSet() throws MoolahException { - TransactionList transactions = new TransactionList(); - Ui ui = new Ui(); - Storage storage = new Storage(); + try { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); - BudgetCommand budgetCommand = new BudgetCommand(); - budgetCommand.execute(transactions, ui, storage); - assertEquals(Budget.getBudget(), 1000); + BudgetCommand budgetCommand = new BudgetCommand(); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), 1000); - budgetCommand.setBudgetAmount(100000); - budgetCommand.execute(transactions, ui, storage); - assertEquals(Budget.getBudget(), 100000); + budgetCommand.setBudgetAmount(100000); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), 100000); - budgetCommand.setBudgetAmount(Long.MAX_VALUE); - budgetCommand.execute(transactions, ui, storage); - assertEquals(Budget.getBudget(), Long.MAX_VALUE); + budgetCommand.setBudgetAmount(Long.MAX_VALUE); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), Long.MAX_VALUE); - // Reset budget back - Budget.setBudget(1000); + // Reset budget back to default + budgetCommand.setBudgetAmount(1000); + budgetCommand.execute(transactions, ui, storage); + } catch (StorageWriteErrorException ignore) { + // Ignores exception to prevent printing of error message + } } } From 59ba566748bb650aaecfdd4dd468d21318fce12f Mon Sep 17 00:00:00 2001 From: brian-vb Date: Mon, 24 Oct 2022 17:08:29 +0800 Subject: [PATCH 221/416] Added functionalities of Edit Command --- .../java/seedu/duke/command/EditCommand.java | 99 +++++++++++++++++-- .../java/seedu/duke/common/InfoMessages.java | 2 + .../java/seedu/duke/data/TransactionList.java | 12 +++ .../duke/data/transaction/Transaction.java | 6 ++ 4 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index cb97ccc12..4d70e1709 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -3,8 +3,16 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.InvalidIndexException; +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageWriteErrorException; + +import java.io.IOException; import java.time.LocalDate; +import java.util.logging.Level; +import java.util.logging.Logger; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_LIST_ENTRY_NUMBER; @@ -12,6 +20,7 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; +import static seedu.duke.common.InfoMessages.*; /** * Represents an edit command object that will execute the operations for Edit command. @@ -57,6 +66,8 @@ public class EditCommand extends Command { private String category; private LocalDate date; + private static final Logger editLogger = Logger.getLogger(DeleteCommand.class.getName()); + public EditCommand() { } @@ -126,15 +137,85 @@ public void setDate(LocalDate date) { * @param storage An instance of the Storage class. */ @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) { - // Dummy output for test - System.out.println(String.format("Entry number: %d\nType: %s\nDesc: %s\n$: %d\nCat: %s, Date: %s", - entryNumber, - type, - description, - amount, - category, - date.toString())); + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + try { + editLogger.setLevel(Level.SEVERE); + editLogger.log(Level.INFO, "Edit Command checks whether the index is valid " + + "before executing the command."); + int index = entryNumber; + Transaction entry = transactions.getEntry(index - 1); + boolean isInputValid = true; + int numberOfTransactions = transactions.size(); + + if ((index > numberOfTransactions) || (index <= 0)) { + isInputValid = false; + } + assert index > 0; + + if (isInputValid) { + String newType = type; + String newDescription = description; + int newAmount = amount; + LocalDate newDate = date; + String newCategory = category; + + if (newType == null) { + newType = entry.getType(); + } + + if (newType.equals("expense")) { + if (newDate == null) { + newDate = entry.getDate(); + } + + if (newDescription == null) { + newDescription = entry.getDescription(); + } + + if (newCategory == null) { + newCategory = entry.getCategory(); + } + + if (newAmount == 0) { + newAmount = entry.getAmount(); + } + transactions.deleteTransaction(index); + String message = transactions.editExpense(newDescription, newAmount, newCategory, newDate, index); + Ui.showTransactionAction(INFO_EDIT_EXPENSE.toString(), message); + editLogger.log(Level.INFO, "The requested transaction has been edited " + + "and the UI should display the confirmation message respectively."); + } else { + if (newDate == null) { + newDate = entry.getDate(); + } + + if (newDescription == null) { + newDescription = entry.getDescription(); + } + + if (newCategory == null) { + newCategory = entry.getCategory(); + } + + if (newAmount == 0) { + newAmount = entry.getAmount(); + } + transactions.deleteTransaction(index); + String message = transactions.editIncome(newDescription, newAmount, newCategory, newDate, index); + Ui.showTransactionAction(INFO_EDIT_INCOME.toString(), message); + editLogger.log(Level.INFO, "The requested transaction has been edited " + + "and the UI should display the confirmation message respectively."); + } + } else { + editLogger.log(Level.WARNING, "InvalidIndexException thrown when the index " + + "is invalid."); + throw new InvalidIndexException(); + } + storage.writeToFile(transactions.getTransactions()); + } catch (IOException e) { + throw new StorageWriteErrorException(); + } + editLogger.log(Level.INFO, "This is the end of the edit command."); } /** diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index af60e447b..a90b3ab59 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -7,6 +7,8 @@ public enum InfoMessages { INFO_DIVIDER("____________________________________________________________"), INFO_ADD_EXPENSE("I have added the following Expense transaction:"), INFO_ADD_INCOME("I have added the following Income transaction:"), + INFO_EDIT_EXPENSE("I have edited the following Expense transaction:"), + INFO_EDIT_INCOME("I have edited the following Income transaction:"), INFO_DELETE("I have deleted the following transaction:"), INFO_EXIT("Goodbye and see you soon."), INFO_GREET("Hello! I'm Moo and I will help you to manage your finances."), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b212000ae..da4d19847 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -83,6 +83,18 @@ public String deleteTransaction(int index) { return transaction.toString(); } + public String editExpense(String description, int amount, String category, LocalDate date, int index) { + Expense expense = new Expense(description, amount, category, date); + transactions.add(index - 1, expense); + return expense.toString(); + } + + public String editIncome(String description, int amount, String category, LocalDate date, int index) { + Income income = new Income(description, amount, category, date); + transactions.add(index - 1, income); + return income.toString(); + } + //@@author wcwy /** diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index a30f934d2..da94296b3 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -23,6 +23,8 @@ public abstract class Transaction { private int amount; private LocalDate date; + private String type; + public Transaction(String description, int amount, String category, LocalDate date) { this.description = description; this.amount = amount; @@ -85,4 +87,8 @@ public String toString() { return String.format("%s %s%d %s %s %s %s %s", printFormattedCategory(), SYMBOL_DOLLAR, amount, TEXT_AT, printFormattedDate(), SYMBOL_PIPE, TEXT_DESCRIPTION, description); } + + public void setType(String type) { + this.type = type; + } } \ No newline at end of file From e02f3545eecabc7dd3f48e6cbf550df4e62109d9 Mon Sep 17 00:00:00 2001 From: brian-vb <69341707+brian-vb@users.noreply.github.com> Date: Mon, 24 Oct 2022 18:14:18 +0800 Subject: [PATCH 222/416] Update EditCommand.java --- src/main/java/seedu/duke/command/EditCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 4d70e1709..d2f9a5edd 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -4,7 +4,7 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; -import seedu.duke.exception.InvalidIndexException; +import seedu.duke.exception.GlobalInvalidIndexException; import seedu.duke.exception.MoolahException; import seedu.duke.exception.StorageWriteErrorException; @@ -209,7 +209,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws } else { editLogger.log(Level.WARNING, "InvalidIndexException thrown when the index " + "is invalid."); - throw new InvalidIndexException(); + throw new GlobalInvalidIndexException(); } storage.writeToFile(transactions.getTransactions()); } catch (IOException e) { @@ -227,4 +227,4 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws public boolean isExit() { return false; } -} \ No newline at end of file +} From de1179d249d037d624155c36535202f2d45bf9d5 Mon Sep 17 00:00:00 2001 From: brian-vb <69341707+brian-vb@users.noreply.github.com> Date: Mon, 24 Oct 2022 18:16:38 +0800 Subject: [PATCH 223/416] Update EditCommand.java --- src/main/java/seedu/duke/command/EditCommand.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index d2f9a5edd..41dffa75c 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -20,7 +20,8 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; -import static seedu.duke.common.InfoMessages.*; +import static seedu.duke.common.InfoMessages.INFO_EDIT_EXPENSE; +import static seedu.duke.common.InfoMessages.INFO_EDIT_EXPENSE; /** * Represents an edit command object that will execute the operations for Edit command. From 8c6442d4043f7d2fbc5c75cc0a74525a71093a52 Mon Sep 17 00:00:00 2001 From: brian-vb <69341707+brian-vb@users.noreply.github.com> Date: Mon, 24 Oct 2022 18:18:44 +0800 Subject: [PATCH 224/416] Update EditCommand.java --- src/main/java/seedu/duke/command/EditCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 41dffa75c..c206869f0 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -21,7 +21,7 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; import static seedu.duke.common.InfoMessages.INFO_EDIT_EXPENSE; -import static seedu.duke.common.InfoMessages.INFO_EDIT_EXPENSE; +import static seedu.duke.common.InfoMessages.INFO_EDIT_INCOME; /** * Represents an edit command object that will execute the operations for Edit command. From a5ec21ac4a74f44c064a4c1608e6a9ca8e491591 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 18:49:50 +0800 Subject: [PATCH 225/416] Update exception handling for checking of missing tags in List and Stats command classes --- src/main/java/seedu/duke/command/Command.java | 2 +- .../duke/command/ListAndStatsCommand.java | 109 ++++++++++++++++++ .../java/seedu/duke/common/ErrorMessages.java | 3 + ...GlobalMissingPeriodNumberTagException.java | 19 +++ .../GlobalMissingYearTagException.java | 19 +++ 5 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 src/main/java/seedu/duke/command/ListAndStatsCommand.java create mode 100644 src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java create mode 100644 src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 24866bbef..364ff2806 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -54,7 +54,7 @@ public String[] getOptionalTags() { * @param storage An instance of the Storage class. */ public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; - + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/ListAndStatsCommand.java b/src/main/java/seedu/duke/command/ListAndStatsCommand.java new file mode 100644 index 000000000..b8d16d2dc --- /dev/null +++ b/src/main/java/seedu/duke/command/ListAndStatsCommand.java @@ -0,0 +1,109 @@ +package seedu.duke.command; + +import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.GlobalMissingPeriodNumberTagException; +import seedu.duke.exception.GlobalMissingYearTagException; +import seedu.duke.exception.GlobalUnsupportedTagException; +import seedu.duke.exception.MoolahException; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +public abstract class DateCommand extends Command { + private static final int UNDEFINED_PARAMETER = -1; + private static final int TRUE_AND = 1; + private static final int TRUE_OR = 2; + private static final int TRUE_INVALID_OR = 3; + private static final int FALSE = 0; + private static final String WEEKS = "weeks"; + private static final String MONTHS = "months"; + private static Logger dateLogger = Logger.getLogger(DateCommand.class.getName()); + + private int month; + private int year; + private String period; + private int number; + + public DateCommand(int month, int year, String period, int number) { + this.month = month; + this.year = year; + this.period = period; + this.number = number; + } + + /** + * Checks if the input contains month or/and year tags. + * + * @return 1 if both tags are given, 2 if only year is given, 3 if only month is given, + * 0 if both are not given. + */ + public int containMonthYear() { + if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (year != UNDEFINED_PARAMETER) { + return TRUE_OR; + } else if (month != UNDEFINED_PARAMETER) { + return TRUE_INVALID_OR; + } + return FALSE; + } + + /** + * Checks if the input contains period and/or number tags. + * + * @return 1 if both tags are given, 2 if either period/number is given, 0 if both are not given. + */ + public int containPeriodNumber() { + if (period != null && number != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (period != null || number != UNDEFINED_PARAMETER) { + return TRUE_OR; + } + return FALSE; + } + + /** + * + */ + public void parseDateIntervalsTags() throws MoolahException { + if (containMonthYear() != FALSE && containPeriodNumber() != FALSE) { + dateLogger.log(Level.WARNING, "An exception has been caught as an invalid combination of tags " + + "has been given."); + throw new GlobalUnsupportedTagException(); + } else if (containMonthYear() == TRUE_INVALID_OR) { + // Throws a missing tag if number and period was not given together + dateLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); + throw new GlobalMissingYearTagException(); + } else if (containPeriodNumber() == TRUE_OR) { + // Throws a missing tag if number and period was not given together + dateLogger.log(Level.WARNING, "An exception has been caught as number and period needs to be " + + "given together."); + throw new GlobalMissingPeriodNumberTagException(); + } + } + + /** + * Gets the complete transactions list by date intervals. + * + * @return An array list containing all transactions recorded in specified date interval. + */ + public ArrayList getTimeTransactions(TransactionList transactions) throws MoolahException { + ArrayList timeTransactions = transactions.getTransactions(); + + if (containMonthYear() == TRUE_AND) { + timeTransactions = transactions.getTransactionsByMonth(year, month); + } else if (containMonthYear() == TRUE_OR) { + assert month == UNDEFINED_PARAMETER; + timeTransactions = transactions.getTransactionsByYear(year); + } else if (containPeriodNumber() == TRUE_AND && period == MONTHS) { + timeTransactions = transactions.getTransactionsByMonthRange(LocalDate.now(), number); + } else if (containPeriodNumber() == TRUE_AND && period == WEEKS) { + timeTransactions = transactions.getTransactionsByWeekRange(LocalDate.now(), number); + } + + return timeTransactions; + } +} diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 914a9f613..a55b87844 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -7,6 +7,9 @@ public enum ErrorMessages { ERROR_GLOBAL_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), ERROR_GLOBAL_DUPLICATE_TAG("Duplicate tag(s) detected, please check your input!"), ERROR_GLOBAL_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), + ERROR_GLOBAL_MISSING_TAG_YEAR("Month tag must be accompanied by a year tag, please check your input!"), + ERROR_GLOBAL_MISSING_TAG_PERIODNUMBER("Period and number tags must be provided together, please check " + + "your input!"), ERROR_GLOBAL_UNSUPPORTED_TAG("Not supported tag(s) detected, please check your input!"), ERROR_GLOBAL_EMPTY_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), ERROR_GLOBAL_INVALID_INDEX("Invalid index, please ensure your index is correct!"), diff --git a/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java b/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java new file mode 100644 index 000000000..b1dd0ae49 --- /dev/null +++ b/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java @@ -0,0 +1,19 @@ +package seedu.duke.exception; + +//@@author chydarren +import seedu.duke.common.ErrorMessages; + +/** + * Represents the exception where period and number tags are not given as a pair in the input. + */ +public class GlobalMissingPeriodNumberTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_GLOBAL_MISSING_TAG_PERIODNUMBER.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java b/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java new file mode 100644 index 000000000..71be03879 --- /dev/null +++ b/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java @@ -0,0 +1,19 @@ +package seedu.duke.exception; + +//@@author chydarren +import seedu.duke.common.ErrorMessages; + +/** + * Represents the exception where a month tag is not accompanied by a year tag in the input. + */ +public class GlobalMissingYearTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_GLOBAL_MISSING_TAG_YEAR.toString(); + } +} From a044871b3b92645fafd04cc4b1665be2f445aa29 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 18:52:02 +0800 Subject: [PATCH 226/416] Add new checks for date interval tags and migrate getting of time transactions list to superclass --- src/main/java/seedu/duke/command/Command.java | 2 +- .../duke/command/ListAndStatsCommand.java | 52 +++++++-- .../java/seedu/duke/command/ListCommand.java | 103 +++------------- .../java/seedu/duke/command/StatsCommand.java | 110 +++--------------- .../java/seedu/duke/data/TransactionList.java | 20 +--- 5 files changed, 71 insertions(+), 216 deletions(-) diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index 364ff2806..24866bbef 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -54,7 +54,7 @@ public String[] getOptionalTags() { * @param storage An instance of the Storage class. */ public abstract void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException; - + /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/command/ListAndStatsCommand.java b/src/main/java/seedu/duke/command/ListAndStatsCommand.java index b8d16d2dc..b10988d5b 100644 --- a/src/main/java/seedu/duke/command/ListAndStatsCommand.java +++ b/src/main/java/seedu/duke/command/ListAndStatsCommand.java @@ -1,5 +1,6 @@ package seedu.duke.command; +//@@author chydarren import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.GlobalMissingPeriodNumberTagException; @@ -12,7 +13,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -public abstract class DateCommand extends Command { +public abstract class ListAndStatsCommand extends Command { private static final int UNDEFINED_PARAMETER = -1; private static final int TRUE_AND = 1; private static final int TRUE_OR = 2; @@ -20,20 +21,44 @@ public abstract class DateCommand extends Command { private static final int FALSE = 0; private static final String WEEKS = "weeks"; private static final String MONTHS = "months"; - private static Logger dateLogger = Logger.getLogger(DateCommand.class.getName()); + private static Logger datedTransactionsLogger = Logger.getLogger(ListAndStatsCommand.class.getName()); - private int month; - private int year; - private String period; - private int number; + //@@author paullowse + public int month; + public int year; + public String period; + public int number; - public DateCommand(int month, int year, String period, int number) { + public ListAndStatsCommand() { + this.month = UNDEFINED_PARAMETER; + this.year = UNDEFINED_PARAMETER; + this.period = null; + this.number = UNDEFINED_PARAMETER; + + datedTransactionsLogger.setLevel(Level.SEVERE); + } + + @Override + public void setGlobalMonth(int month) { this.month = month; + } + + @Override + public void setGlobalYear(int year) { this.year = year; - this.period = period; + } + + @Override + public void setGlobalNumber(int number) { this.number = number; } + @Override + public void setGlobalPeriod(String period) { + this.period = period; + } + + //@@author chydarren /** * Checks if the input contains month or/and year tags. * @@ -66,20 +91,22 @@ public int containPeriodNumber() { } /** + * Parses the tags related to tag intervals and checks if there are any error in their combinations. * + * @throws MoolahException If any of the below exception conditions are met. */ public void parseDateIntervalsTags() throws MoolahException { if (containMonthYear() != FALSE && containPeriodNumber() != FALSE) { - dateLogger.log(Level.WARNING, "An exception has been caught as an invalid combination of tags " + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as an invalid combination of tags " + "has been given."); throw new GlobalUnsupportedTagException(); } else if (containMonthYear() == TRUE_INVALID_OR) { // Throws a missing tag if number and period was not given together - dateLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); throw new GlobalMissingYearTagException(); } else if (containPeriodNumber() == TRUE_OR) { // Throws a missing tag if number and period was not given together - dateLogger.log(Level.WARNING, "An exception has been caught as number and period needs to be " + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as number and period needs to be " + "given together."); throw new GlobalMissingPeriodNumberTagException(); } @@ -88,9 +115,10 @@ public void parseDateIntervalsTags() throws MoolahException { /** * Gets the complete transactions list by date intervals. * + * @param transactions An instance of the TransactionList class. * @return An array list containing all transactions recorded in specified date interval. */ - public ArrayList getTimeTransactions(TransactionList transactions) throws MoolahException { + public ArrayList getTimeTransactions(TransactionList transactions) { ArrayList timeTransactions = transactions.getTransactions(); if (containMonthYear() == TRUE_AND) { diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 8a1f81397..702c5954c 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -4,11 +4,11 @@ import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.GlobalMissingTagException; -import seedu.duke.exception.InputTransactionInvalidTypeException; -import seedu.duke.exception.MoolahException; +import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.*; import java.time.LocalDate; +import java.util.ArrayList; import java.util.logging.Logger; import java.util.logging.Level; @@ -19,13 +19,14 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; + import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; /** * Represents a list command object that will execute the operations for List command. */ -public class ListCommand extends Command { +public class ListCommand extends ListAndStatsCommand { //@@author chydarren private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations @@ -56,20 +57,10 @@ public class ListCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; //@@author chydarren - private static final int UNDEFINED_PARAMETER = -1; - private static final int TRUE_AND = 1; - private static final int TRUE_OR = 2; - private static final int TRUE_INVALID_OR = 3; - private static final int FALSE = 0; - private static Logger listLogger = Logger.getLogger(ListCommand.class.getName()); private String category; private LocalDate date; private String type; - private int month; - private int year; - private String period; - private int number; //@@author paullowse @@ -77,13 +68,10 @@ public class ListCommand extends Command { * Initialises the variables of the ListCommand class. */ public ListCommand() { + super(); category = ""; date = null; type = ""; - month = UNDEFINED_PARAMETER; - year = UNDEFINED_PARAMETER; - period = null; - number = UNDEFINED_PARAMETER; } /** @@ -120,58 +108,8 @@ public void setDate(LocalDate date) { this.date = date; } - @Override - public void setGlobalMonth(int month) { - this.month = month; - } - - @Override - public void setGlobalYear(int year) { - this.year = year; - } - - @Override - public void setGlobalNumber(int number) { - this.number = number; - } - - @Override - public void setGlobalPeriod(String period) { - this.period = period; - } - //@@author chydarren - /** - * Checks if the input contains month or/and year tags. - * - * @return 1 if both tags are given, 2 if only month is given, 0 if both are not given. - */ - public int containMonthYear() { - if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { - return TRUE_AND; - } else if (year != UNDEFINED_PARAMETER) { - return TRUE_OR; - } else if (month != UNDEFINED_PARAMETER) { - return TRUE_INVALID_OR; - } - return FALSE; - } - - /** - * Checks if the input contains period and/or number tags. - * - * @return 1 if both tags are given, 2 if either period/number is given, 0 if both are not given. - */ - public int containPeriodNumber() { - if (period != null && number != UNDEFINED_PARAMETER) { - return TRUE_AND; - } else if (period != null || number != UNDEFINED_PARAMETER) { - return TRUE_OR; - } - return FALSE; - } - /** * Executes the operations related to the command. * @@ -184,22 +122,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws listLogger.setLevel(Level.SEVERE); listLogger.log(Level.INFO, "Entering execution of the List command."); - // Throws an invalid list tags exception if there are both month/year and period/number - if (containMonthYear() == TRUE_OR && containPeriodNumber() == TRUE_OR) { - listLogger.log(Level.WARNING, "An exception has been caught as an invalid combination of tags " - + "has been given."); - // Throws a missing tag exception if month was given without a year - } else if (containMonthYear() == TRUE_INVALID_OR) { - listLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); - throw new GlobalMissingTagException(); - // Throws a missing tag if number and period was not given together - } else if (containPeriodNumber() == TRUE_OR) { - listLogger.log(Level.WARNING, "An exception has been caught as number and period needs to be " - + "given together."); - throw new GlobalMissingTagException(); - } - - listTransactions(transactions, type, category, date, month, year, number, period); + // Checks if there are any error in the tag combinations related to DateIntervals + parseDateIntervalsTags(); + listTransactions(transactions, type, category, date); } /** @@ -209,12 +134,12 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param type The type of transaction. * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". - * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. + * @throws MoolahException If any type of exception has been caught within the function calls. */ - private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date, - int month, int year, int number, String period) - throws InputTransactionInvalidTypeException { - String transactionsList = transactions.listTransactions(type, category, date, month, year, number, period); + private void listTransactions(TransactionList transactions, String type, String category, LocalDate date) + throws MoolahException { + ArrayList timeTransactions = getTimeTransactions(transactions); + String transactionsList = transactions.listTransactions(timeTransactions, type, category, date); if (transactionsList.isEmpty()) { listLogger.log(Level.INFO, "Transactions list is empty as there are no transactions available."); diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 3b83b9eab..58d4c601a 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -8,7 +8,6 @@ import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; -import java.time.LocalDate; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; @@ -29,7 +28,7 @@ /** * Represents a get command object that will execute the operations for Get command. */ -public class StatsCommand extends Command { +public class StatsCommand extends ListAndStatsCommand { //@@author paullowse private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations @@ -52,30 +51,19 @@ public class StatsCommand extends Command { + LINE_SEPARATOR; //@@author chydarren - private static final int UNDEFINED_PARAMETER = -1; private static final int TRUE_AND = 1; private static final int TRUE_OR = 2; - private static final int TRUE_INVALID_OR = 3; private static final int FALSE = 0; private static final String CATEGORIES = "categories"; private static final String TIME = "time"; - private static final String WEEKS = "weeks"; - private static final String MONTHS = "months"; private static Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); private String statsType; - private int month; - private int year; - private String period; - private int number; //@@author paullowse public StatsCommand() { + super(); statsType = ""; - period = null; - month = UNDEFINED_PARAMETER; - year = UNDEFINED_PARAMETER; - number = UNDEFINED_PARAMETER; } /** @@ -100,63 +88,13 @@ public String[] getOptionalTags() { return optionalTags; } + //@@author chydarren + @Override public void setStatsType(String statsType) { this.statsType = statsType; } - @Override - public void setGlobalMonth(int month) { - this.month = month; - } - - @Override - public void setGlobalYear(int year) { - this.year = year; - } - - @Override - public void setGlobalNumber(int number) { - this.number = number; - } - - @Override - public void setGlobalPeriod(String period) { - this.period = period; - } - - //@@author chydarren - - /** - * Checks if the input contains month or/and year tags. - * - * @return 1 if both tags are given, 2 if only month is given, 0 if both are not given. - */ - public int containMonthYear() { - if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { - return TRUE_AND; - } else if (year != UNDEFINED_PARAMETER) { - return TRUE_OR; - } else if (month != UNDEFINED_PARAMETER) { - return TRUE_INVALID_OR; - } - return FALSE; - } - - /** - * Checks if the input contains period and/or number tags. - * - * @return 1 if both tags are given, 2 if either period/number is given, 0 if both are not given. - */ - public int containPeriodNumber() { - if (period != null && number != UNDEFINED_PARAMETER) { - return TRUE_AND; - } else if (period != null || number != UNDEFINED_PARAMETER) { - return TRUE_OR; - } - return FALSE; - } - /** * Executes the operations related to the command. * @@ -169,7 +107,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws statsLogger.setLevel(Level.SEVERE); statsLogger.log(Level.INFO, "Entering execution of the Stats command."); - listStatsByStatsType(statsType, transactions, month, year, period, number); + // Checks if there are any error in the tag combinations related to DateIntervals + parseDateIntervalsTags(); + listStatsByStatsType(statsType, transactions); } /** @@ -179,8 +119,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param transactions An instance of the TransactionList class. * @throws MoolahException If the type of statistics is not recognised. */ - private void listStatsByStatsType(String statsType, TransactionList transactions, int month, - int year, String period, int number) + private void listStatsByStatsType(String statsType, TransactionList transactions) throws MoolahException { switch (statsType) { case CATEGORIES: @@ -191,16 +130,15 @@ private void listStatsByStatsType(String statsType, TransactionList transactions //@@author paullowse case TIME: statsLogger.log(Level.INFO, "Stats type has been detected for monthly savings."); - // Stats command uses last N months or years if (containPeriodNumber() == TRUE_AND && containMonthYear() == FALSE) { statsLogger.log(Level.INFO, "Stats command uses last N months or years."); - statsTypeTimeSavings(transactions, year, month, period, number); + statsTypeTimeSavings(transactions); // Stats command uses either monthly or yearly } else if (containPeriodNumber() == FALSE && (containMonthYear() == TRUE_OR || containMonthYear() == TRUE_AND)) { statsLogger.log(Level.INFO, "Stats command uses either monthly or yearly."); - statsTypeTimeSavings(transactions, year, month, period, number); + statsTypeTimeSavings(transactions); // Throws a missing tag if s/time was called without any relevant tags } else { statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); @@ -241,30 +179,10 @@ public void statsTypeCategoricalSavings(TransactionList transactions) { * Calls transactions to get the necessary transaction list, convert the parameters into a String for output. * Produces info strings, list of categories and summary statistics. * - * @param transactions An instance of the TransactionList class. - * @param year A specified year. - * @param month A specified month. - * @param period A specified period of time. - * @param number A specified number of periods. - * @throws MoolahException If the type of statistics is not recognised. + * @param transactions An instance of the TransactionList class. */ - public void statsTypeTimeSavings(TransactionList transactions, int year, int month, - String period, int number) throws MoolahException { - - ArrayList timeTransactions; - // only year - if (containPeriodNumber() == TRUE_AND && period == WEEKS) { - timeTransactions = transactions.getTransactionsByWeekRange(LocalDate.now(), number); - } else if (containPeriodNumber() == TRUE_AND && period == WEEKS) { - timeTransactions = transactions.getTransactionsByMonthRange(LocalDate.now(), number); - } else if (containMonthYear() == TRUE_AND) { - timeTransactions = transactions.getTransactionsByMonth(year, month); - } else if (containMonthYear() == TRUE_OR) { - timeTransactions = transactions.getTransactionsByYear(year); - } else { - statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); - throw new StatsInvalidTypeException(); - } + public void statsTypeTimeSavings(TransactionList transactions) { + ArrayList timeTransactions = getTimeTransactions(transactions); String timeSavingsList = transactions.listTimeStats(timeTransactions, year, month, period, number); if (timeSavingsList.isEmpty()) { @@ -287,8 +205,6 @@ public void statsTypeTimeSavings(TransactionList transactions, int year, int mon Ui.showStatsList(timeSavingsList, INFO_STATS_TIME.toString(), incomeMessage, expensesMessage, savingsMessage); } - //@@author paullowse - /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 8c5947134..2986e8f72 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -28,6 +28,8 @@ public class TransactionList { private static final String SYMBOL_DOLLAR = "$"; private static final String INCOME = "income"; private static final String EXPENSE = "expense"; + private static final String MONTHS = "months"; + private static final String WEEKS = "weeks"; private static final int START = 0; private static final int END = 1; private static final int UNDEFINED_PARAMETER = -1; @@ -174,27 +176,11 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c * @param type The type of transaction. * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". - * @param year A specified year. - * @param month A specified month within the year. * @return A string containing the formatted transaction list. * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ - public String listTransactions(String type, String category, LocalDate date, int month, int year, - int number, String period) + public String listTransactions(ArrayList timeTransactions, String type, String category, LocalDate date) throws InputTransactionInvalidTypeException { - ArrayList timeTransactions; - - // Filters the transactions list by month or/and year first - if (year != UNDEFINED_PARAMETER && month != UNDEFINED_PARAMETER) { - timeTransactions = getTransactionsByMonth(year, month); - } else if (year != UNDEFINED_PARAMETER) { - assert month == UNDEFINED_PARAMETER; - timeTransactions = getTransactionsByYear(year); - } else { - // No filter month or/and year filter applied - timeTransactions = transactions; - } - String transactionsList = ""; // Loops each transaction from the time transactions list From a315c7a5be5ee5e2cbe12686d4d24dcf41fd5334 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 18:58:40 +0800 Subject: [PATCH 227/416] Fix coding style to deal with Gradle test errors --- .../seedu/duke/command/ListAndStatsCommand.java | 14 ++++++++------ src/main/java/seedu/duke/command/ListCommand.java | 2 +- src/main/java/seedu/duke/data/TransactionList.java | 3 ++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListAndStatsCommand.java b/src/main/java/seedu/duke/command/ListAndStatsCommand.java index b10988d5b..fb86d446b 100644 --- a/src/main/java/seedu/duke/command/ListAndStatsCommand.java +++ b/src/main/java/seedu/duke/command/ListAndStatsCommand.java @@ -59,11 +59,12 @@ public void setGlobalPeriod(String period) { } //@@author chydarren + /** * Checks if the input contains month or/and year tags. * * @return 1 if both tags are given, 2 if only year is given, 3 if only month is given, - * 0 if both are not given. + * 0 if both are not given. */ public int containMonthYear() { if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { @@ -97,17 +98,18 @@ public int containPeriodNumber() { */ public void parseDateIntervalsTags() throws MoolahException { if (containMonthYear() != FALSE && containPeriodNumber() != FALSE) { - datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as an invalid combination of tags " - + "has been given."); + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught " + + "as an invalid combination of tags has been given."); throw new GlobalUnsupportedTagException(); } else if (containMonthYear() == TRUE_INVALID_OR) { // Throws a missing tag if number and period was not given together - datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " + + "a month was given without a year."); throw new GlobalMissingYearTagException(); } else if (containPeriodNumber() == TRUE_OR) { // Throws a missing tag if number and period was not given together - datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as number and period needs to be " - + "given together."); + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " + + "number and period needs to be given together."); throw new GlobalMissingPeriodNumberTagException(); } } diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 702c5954c..42a13fa2e 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -5,7 +5,7 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; -import seedu.duke.exception.*; +import seedu.duke.exception.MoolahException; import java.time.LocalDate; import java.util.ArrayList; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 2986e8f72..f80cb9658 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -179,7 +179,8 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c * @return A string containing the formatted transaction list. * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ - public String listTransactions(ArrayList timeTransactions, String type, String category, LocalDate date) + public String listTransactions(ArrayList timeTransactions, String type, + String category, LocalDate date) throws InputTransactionInvalidTypeException { String transactionsList = ""; From c317319ffb34ef1ec813cf573691adb971d11256 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 19:13:35 +0800 Subject: [PATCH 228/416] Fix merge issues and conflicts --- docs/diagram/FindCommandClassDiagram.puml | 27 +++ src/main/java/seedu/duke/Duke.java | 4 +- src/main/java/seedu/duke/Storage.java | 157 ++++++++++++------ .../seedu/duke/command/BudgetCommand.java | 13 +- src/main/java/seedu/duke/command/Command.java | 10 +- .../java/seedu/duke/command/EditCommand.java | 107 ++++++++++-- .../duke/command/ListAndStatsCommand.java | 139 ++++++++++++++++ .../java/seedu/duke/command/ListCommand.java | 56 +++---- .../java/seedu/duke/command/StatsCommand.java | 109 ++++-------- .../java/seedu/duke/common/ErrorMessages.java | 9 +- .../java/seedu/duke/common/InfoMessages.java | 2 + .../java/seedu/duke/data/TransactionList.java | 45 ++--- .../duke/data/transaction/Transaction.java | 6 + ...ndTransactionMissingKeywordsException.java | 3 + .../GlobalInvalidMonthException.java | 3 + ...GlobalMissingPeriodNumberTagException.java | 19 +++ .../GlobalMissingYearTagException.java | 19 +++ .../InputTransactionInvalidTypeException.java | 3 + .../exception/StatsInvalidTypeException.java | 3 + .../StorageFileCorruptedBudgetException.java | 17 ++ ...ageFileCorruptedTransactionException.java} | 4 +- .../seedu/duke/parser/ParameterParser.java | 20 ++- .../seedu/duke/command/BudgetCommandTest.java | 36 ++-- text-ui-test/EXPECTED.TXT | 3 + 24 files changed, 585 insertions(+), 229 deletions(-) create mode 100644 docs/diagram/FindCommandClassDiagram.puml create mode 100644 src/main/java/seedu/duke/command/ListAndStatsCommand.java create mode 100644 src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java create mode 100644 src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java create mode 100644 src/main/java/seedu/duke/exception/StorageFileCorruptedBudgetException.java rename src/main/java/seedu/duke/exception/{StorageFileCorruptedException.java => StorageFileCorruptedTransactionException.java} (66%) diff --git a/docs/diagram/FindCommandClassDiagram.puml b/docs/diagram/FindCommandClassDiagram.puml new file mode 100644 index 000000000..f2cda6228 --- /dev/null +++ b/docs/diagram/FindCommandClassDiagram.puml @@ -0,0 +1,27 @@ +@startuml +'https://plantuml.com/class-diagram + +abstract class AbstractList +abstract AbstractCollection +interface List +interface Collection + +List <|-- AbstractList +Collection <|-- AbstractCollection + +Collection <|- List +AbstractCollection <|- AbstractList +AbstractList <|-- ArrayList + +class ArrayList { +Object[] elementData +size() +} + +enum TimeUnit { +DAYS +HOURS +MINUTES +} + +@enduml \ No newline at end of file diff --git a/src/main/java/seedu/duke/Duke.java b/src/main/java/seedu/duke/Duke.java index cd42c1bca..194da73b0 100644 --- a/src/main/java/seedu/duke/Duke.java +++ b/src/main/java/seedu/duke/Duke.java @@ -5,8 +5,6 @@ import seedu.duke.exception.MoolahException; import seedu.duke.parser.CommandParser; -import java.io.IOException; - public class Duke { //@@author paullowse private Storage storage; @@ -16,8 +14,8 @@ public class Duke { //@@author chinhan99 public Duke() { // TODO: Add a file path when implementing storage feature ui = new Ui(); - storage = new Storage(); + try { transactions = new TransactionList(storage.initializeFile()); } catch (MoolahException e) { diff --git a/src/main/java/seedu/duke/Storage.java b/src/main/java/seedu/duke/Storage.java index f34653b45..75e4cf36d 100644 --- a/src/main/java/seedu/duke/Storage.java +++ b/src/main/java/seedu/duke/Storage.java @@ -2,10 +2,14 @@ import seedu.duke.command.Command; import seedu.duke.common.DateFormats; +import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.StorageFileCorruptedException; +import seedu.duke.exception.InputBudgetDuplicateException; +import seedu.duke.exception.InputBudgetInvalidAmountException; +import seedu.duke.exception.StorageFileCorruptedTransactionException; +import seedu.duke.exception.StorageFileCorruptedBudgetException; import seedu.duke.exception.StorageWriteErrorException; import seedu.duke.parser.ParameterParser; @@ -68,19 +72,22 @@ private static File checkIfFileExist() throws IOException { * Initializes the duke.txt by checking its existence, then store the data values to the program. * * @return The TransactionList storing entries which would be used in the program. - * @throws StorageFileCorruptedException If there are errors due to corrupted duke.txt data. - * @throws StorageWriteErrorException If the file could not be created or could not be written. + * @throws StorageFileCorruptedTransactionException If there are errors due to corrupted duke.txt data. + * @throws StorageWriteErrorException If the file could not be created or could not be written. */ public TransactionList initializeFile() throws MoolahException { try { File file = checkIfFileExist(); Scanner input = new Scanner(file); - storeFileValuesLocally(storedTransactions, input); + storeFileValuesLocally(input); Ui.printMessages("* duke.txt loaded successfully! *"); - } catch (MoolahException e) { - // Catch any parsing errors and throw the default StorageInputCorruptedException - throw new StorageFileCorruptedException(); + } catch (StorageFileCorruptedBudgetException e) { + // Catch any Budget parsing errors and throw the default StorageInputCorruptedBudgetException + throw new StorageFileCorruptedBudgetException(); + } catch (StorageFileCorruptedTransactionException e) { + // Catch any Transaction parsing errors and throw the default StorageInputCorruptedTransactionException + throw new StorageFileCorruptedTransactionException(); } catch (IOException e) { throw new StorageWriteErrorException(); } @@ -88,58 +95,102 @@ public TransactionList initializeFile() throws MoolahException { } /** - * Stores values from duke.txt to the program by parsing each line in the file. + * Stores budget value from duke.txt to the program by parsing the first line of the file. * - * @param storedTransactions The TransactionList to hold the stored values from the file. - * @param input The input from duke.txt to be processed. - * @throws MoolahException WHen there are parsing errors, due to corrupted data. + * @param monthlyBudget The budget value to be parsed. + * @throws MoolahException When there are parsing errors, due to corrupted data. */ + private void storeBudgetLocally(String monthlyBudget) throws MoolahException { + try { + Command command = null; + ParameterParser.parseBudgetTag(monthlyBudget); + Budget.setBudget(Long.parseLong(monthlyBudget)); - private void storeFileValuesLocally(TransactionList storedTransactions, Scanner input) throws MoolahException { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateFormats.DATE_STORAGE_OUTPUT_PATTERN.toString()); + } catch (InputBudgetDuplicateException e) { + Ui.printMessages("* Budget value remains as default *"); + } catch (InputBudgetInvalidAmountException e) { + throw new StorageFileCorruptedBudgetException(); + } + } + + /** + * Stores Transaction parameters from duke.txt to the program by parsing each line of the file after Budget value. + * + * @param lineString The individual lines from duke.txt which would be parsed. + * @throws MoolahException When there are parsing errors, due to corrupted data. + */ + private void storeTransactionsLocally(String lineString) throws MoolahException { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DateFormats.DATE_STORAGE_OUTPUT_PATTERN.toString()); + String[] splits = lineString.split(DELIMITER); Command command = null; - while (input.hasNext()) { - String line = input.nextLine(); - String[] splits = line.split(DELIMITER); - if (splits.length != NUMBER_OF_STORED_PARAMETERS) { - throw new StorageFileCorruptedException(); - } - String type = splits[0]; - String category = splits[1]; - String amountString = splits[2]; - // Date has been formatted in duke.txt and must be synthesized into the correct string format before parsing - try { - LocalDate date = LocalDate.parse(splits[3], formatter); - String dateString = synthesizeDateString(date); - - - String description = splits[4]; - - String parametersInput = "t/" + type + " c/" + category + " a/" + amountString + " d/" + dateString - + " i/" + description; - command = getCommand("add", parametersInput); - - ParameterParser.parse(command, parametersInput); - - // amount would be converted into an integer before being used in the addition of transaction locally - int amount = Integer.parseInt(splits[2]); - - switch (type) { - case "expense": - storedTransactions.addExpenseDuringStorage(description, amount, category, date); - break; - case "income": - storedTransactions.addIncomeDuringStorage(description, amount, category, date); - break; - default: - throw new StorageFileCorruptedException(); - } - } catch (DateTimeParseException e) { - // If the date format is incorrect, which is due to corrupted date information - throw new StorageFileCorruptedException(); + if (splits.length != NUMBER_OF_STORED_PARAMETERS) { + throw new StorageFileCorruptedTransactionException(); + } + + String type = splits[0]; + String category = splits[1]; + String amountString = splits[2]; + // Date has been formatted in duke.txt and must be synthesized into the correct string format before parsing + try { + LocalDate date = LocalDate.parse(splits[3], formatter); + String dateString = synthesizeDateString(date); + + + String description = splits[4]; + + String parametersInput = "t/" + type + " c/" + category + " a/" + amountString + " d/" + dateString + + " i/" + description; + command = getCommand("add", parametersInput); + + ParameterParser.parse(command, parametersInput); + + // amount would be converted into an integer before being used in the addition of transaction locally + int amount = Integer.parseInt(splits[2]); + + switch (type) { + case "expense": + storedTransactions.addExpenseDuringStorage(description, amount, category, date); + break; + case "income": + storedTransactions.addIncomeDuringStorage(description, amount, category, date); + break; + default: + throw new StorageFileCorruptedTransactionException(); } + } catch (DateTimeParseException e) { + // If the date format is incorrect, which is due to corrupted date information + throw new StorageFileCorruptedTransactionException(); + } catch (MoolahException e) { + throw new StorageFileCorruptedTransactionException(); + } + } + + + /** + * Stores values from duke.txt to the program by parsing each line in the file. + * + * @param input The input from duke.txt to be processed. + * @throws MoolahException When there are parsing errors, due to corrupted data. + */ + + + private void storeFileValuesLocally(Scanner input) throws MoolahException { + + + // Processes the budget value located at the first line of the duke.txt file + if (input.hasNext()) { + String monthlyBudget = input.nextLine(); + storeBudgetLocally(monthlyBudget); + } else { + // Else, the file would be empty + Ui.printMessages("* Duke.txt file is currently empty and ready to be written *"); + } + + while (input.hasNext()) { + String line = input.nextLine(); + storeTransactionsLocally(line); } @@ -184,7 +235,9 @@ private String synthesizeDateString(LocalDate date) { */ public void writeToFile(ArrayList transactions) throws IOException { FileWriter fileWriter = new FileWriter(FILE_PATH); + long monthlyBudget = Budget.getBudget(); String transactionEntry = ""; + fileWriter.write(monthlyBudget + System.lineSeparator()); for (Transaction transaction : transactions) { transactionEntry = transaction.getType() + " | " + transaction.getCategory() + " | " diff --git a/src/main/java/seedu/duke/command/BudgetCommand.java b/src/main/java/seedu/duke/command/BudgetCommand.java index 9d9fe1116..3e1af70d7 100644 --- a/src/main/java/seedu/duke/command/BudgetCommand.java +++ b/src/main/java/seedu/duke/command/BudgetCommand.java @@ -7,6 +7,9 @@ import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageWriteErrorException; + +import java.io.IOException; import static seedu.duke.command.CommandTag.COMMAND_TAG_BUDGET_AMOUNT; @@ -77,8 +80,14 @@ public String[] getMandatoryTags() { */ @Override public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { - Budget.setBudget(budgetAmount); - ui.showSetBudgetAcknowledgementMessage(Long.toString(budgetAmount)); + try { + Budget.setBudget(budgetAmount); + ui.showSetBudgetAcknowledgementMessage(Long.toString(budgetAmount)); + //@@author chinhan99 + storage.writeToFile(transactions.getTransactions()); + } catch (IOException e) { + throw new StorageWriteErrorException(); + } } //@@author paullowse diff --git a/src/main/java/seedu/duke/command/Command.java b/src/main/java/seedu/duke/command/Command.java index be2bdfbe5..24866bbef 100644 --- a/src/main/java/seedu/duke/command/Command.java +++ b/src/main/java/seedu/duke/command/Command.java @@ -90,17 +90,15 @@ public void setIsDetailedOption(boolean isDetailed) { public void setStatsType(String statsType) { } - public void setStatsMonth(int month) { + public void setGlobalMonth(int month) { } - public void setStatsYear(int year) { + public void setGlobalYear(int year) { } - public void setStatsNumber(int number) { + public void setGlobalNumber(int number) { } - public void setStatsPeriod(String period) { + public void setGlobalPeriod(String period) { } - - } diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 342f15cc9..c16708044 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -1,11 +1,22 @@ package seedu.duke.command; //@@author brian-vb + import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.GlobalInvalidIndexException; + +import seedu.duke.exception.MoolahException; + +import seedu.duke.exception.StorageWriteErrorException; + +import java.io.IOException; import java.time.LocalDate; +import java.util.logging.Level; +import java.util.logging.Logger; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; @@ -13,6 +24,8 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; +import static seedu.duke.common.InfoMessages.INFO_EDIT_EXPENSE; +import static seedu.duke.common.InfoMessages.INFO_EDIT_INCOME; /** * Represents an edit command object that will execute the operations for Edit command. @@ -59,6 +72,8 @@ public class EditCommand extends Command { private String category; private LocalDate date; + private static final Logger editLogger = Logger.getLogger(DeleteCommand.class.getName()); + public EditCommand() { } @@ -128,15 +143,87 @@ public void setDate(LocalDate date) { * @param storage An instance of the Storage class. */ @Override - public void execute(TransactionList transactions, Ui ui, Storage storage) { - // Dummy output for test - System.out.println(String.format("Entry number: %d\nType: %s\nDesc: %s\n$: %d\nCat: %s, Date: %s", - entryNumber, - type, - description, - amount, - category, - date.toString())); + public void execute(TransactionList transactions, Ui ui, Storage storage) throws MoolahException { + + try { + editLogger.setLevel(Level.SEVERE); + editLogger.log(Level.INFO, "Edit Command checks whether the index is valid " + + "before executing the command."); + int index = entryNumber; + Transaction entry = transactions.getEntry(index - 1); + boolean isInputValid = true; + int numberOfTransactions = transactions.size(); + + if ((index > numberOfTransactions) || (index <= 0)) { + isInputValid = false; + } + assert index > 0; + + if (isInputValid) { + String newType = type; + String newDescription = description; + int newAmount = amount; + LocalDate newDate = date; + String newCategory = category; + + if (newType == null) { + newType = entry.getType(); + } + + if (newType.equals("expense")) { + if (newDate == null) { + newDate = entry.getDate(); + } + + if (newDescription == null) { + newDescription = entry.getDescription(); + } + + if (newCategory == null) { + newCategory = entry.getCategory(); + } + + if (newAmount == 0) { + newAmount = entry.getAmount(); + } + transactions.deleteTransaction(index); + String message = transactions.editExpense(newDescription, newAmount, newCategory, newDate, index); + Ui.showTransactionAction(INFO_EDIT_EXPENSE.toString(), message); + editLogger.log(Level.INFO, "The requested transaction has been edited " + + "and the UI should display the confirmation message respectively."); + } else { + if (newDate == null) { + newDate = entry.getDate(); + } + + if (newDescription == null) { + newDescription = entry.getDescription(); + } + + if (newCategory == null) { + newCategory = entry.getCategory(); + } + + if (newAmount == 0) { + newAmount = entry.getAmount(); + } + transactions.deleteTransaction(index); + String message = transactions.editIncome(newDescription, newAmount, newCategory, newDate, index); + Ui.showTransactionAction(INFO_EDIT_INCOME.toString(), message); + editLogger.log(Level.INFO, "The requested transaction has been edited " + + "and the UI should display the confirmation message respectively."); + } + } else { + editLogger.log(Level.WARNING, "InvalidIndexException thrown when the index " + + "is invalid."); + throw new GlobalInvalidIndexException(); + } + storage.writeToFile(transactions.getTransactions()); + } catch (IOException e) { + throw new StorageWriteErrorException(); + } + editLogger.log(Level.INFO, "This is the end of the edit command."); + } /** @@ -148,4 +235,4 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) { public boolean isExit() { return false; } -} \ No newline at end of file +} diff --git a/src/main/java/seedu/duke/command/ListAndStatsCommand.java b/src/main/java/seedu/duke/command/ListAndStatsCommand.java new file mode 100644 index 000000000..718fb48df --- /dev/null +++ b/src/main/java/seedu/duke/command/ListAndStatsCommand.java @@ -0,0 +1,139 @@ +package seedu.duke.command; + +//@@author chydarren +import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.GlobalMissingPeriodNumberTagException; +import seedu.duke.exception.GlobalMissingYearTagException; +import seedu.duke.exception.GlobalUnsupportedTagException; +import seedu.duke.exception.MoolahException; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +public abstract class ListAndStatsCommand extends Command { + private static final int UNDEFINED_PARAMETER = -1; + private static final int TRUE_AND = 1; + private static final int TRUE_OR = 2; + private static final int TRUE_INVALID_OR = 3; + private static final int FALSE = 0; + private static final String WEEKS = "weeks"; + private static final String MONTHS = "months"; + private static Logger datedTransactionsLogger = Logger.getLogger(ListAndStatsCommand.class.getName()); + + //@@author paullowse + public int month; + public int year; + public String period; + public int number; + + public ListAndStatsCommand() { + this.month = UNDEFINED_PARAMETER; + this.year = UNDEFINED_PARAMETER; + this.period = null; + this.number = UNDEFINED_PARAMETER; + + datedTransactionsLogger.setLevel(Level.SEVERE); + } + + @Override + public void setGlobalMonth(int month) { + this.month = month; + } + + @Override + public void setGlobalYear(int year) { + this.year = year; + } + + @Override + public void setGlobalNumber(int number) { + this.number = number; + } + + @Override + public void setGlobalPeriod(String period) { + this.period = period; + } + + //@@author chydarren + + /** + * Checks if the input contains month or/and year tags. + * + * @return 1 if both tags are given, 2 if only year is given, 3 if only month is given, + * 0 if both are not given. + */ + public int containMonthYear() { + if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (year != UNDEFINED_PARAMETER) { + return TRUE_OR; + } else if (month != UNDEFINED_PARAMETER) { + return TRUE_INVALID_OR; + } + return FALSE; + } + + /** + * Checks if the input contains period and/or number tags. + * + * @return 1 if both tags are given, 2 if either period/number is given, 0 if both are not given. + */ + public int containPeriodNumber() { + if (period != null && number != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (period != null || number != UNDEFINED_PARAMETER) { + return TRUE_OR; + } + return FALSE; + } + + /** + * Parses the tags related to tag intervals and checks if there are any error in their combinations. + * + * @throws MoolahException If any of the below exception conditions are met. + */ + public void parseDateIntervalsTags() throws MoolahException { + if (containMonthYear() != FALSE && containPeriodNumber() != FALSE) { + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught " + + "as an invalid combination of tags has been given."); + throw new GlobalUnsupportedTagException(); + } else if (containMonthYear() == TRUE_INVALID_OR) { + // Throws a missing tag if number and period was not given together + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " + + "a month was given without a year."); + throw new GlobalMissingYearTagException(); + } else if (containPeriodNumber() == TRUE_OR) { + // Throws a missing tag if number and period was not given together + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " + + "number and period needs to be given together."); + throw new GlobalMissingPeriodNumberTagException(); + } + } + + /** + * Gets the complete transactions list by date intervals. + * + * @param transactions An instance of the TransactionList class. + * @return An array list containing all transactions recorded in specified date interval. + */ + public ArrayList getTimeTransactions(TransactionList transactions) { + ArrayList timeTransactions = transactions.getTransactions(); + + if (containMonthYear() == TRUE_AND) { + timeTransactions = transactions.getTransactionsByMonth(year, month); + } else if (containMonthYear() == TRUE_OR) { + assert month == UNDEFINED_PARAMETER; + timeTransactions = transactions.getTransactionsByYear(year); + } else if (containPeriodNumber() == TRUE_AND && period == MONTHS) { + timeTransactions = transactions.getTransactionsByMonthRange(LocalDate.now(), number); + } else if (containPeriodNumber() == TRUE_AND && period == WEEKS) { + timeTransactions = transactions.getTransactionsByWeekRange(LocalDate.now(), number); + } + + return timeTransactions; + } +} diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 958ecefc0..42a13fa2e 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -1,30 +1,32 @@ package seedu.duke.command; -//@@author paullowse +//@@author chydarren import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; -import seedu.duke.exception.GlobalMissingTagException; -import seedu.duke.exception.InputTransactionInvalidTypeException; +import seedu.duke.data.transaction.Transaction; import seedu.duke.exception.MoolahException; import java.time.LocalDate; +import java.util.ArrayList; import java.util.logging.Logger; import java.util.logging.Level; -//@@author chydarren import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; +import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; + import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; /** * Represents a list command object that will execute the operations for List command. */ -public class ListCommand extends Command { +public class ListCommand extends ListAndStatsCommand { //@@author chydarren private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations @@ -55,24 +57,21 @@ public class ListCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; //@@author chydarren - private static final Logger listLogger = Logger.getLogger(ListCommand.class.getName()); - - //@@author paullowse + private static Logger listLogger = Logger.getLogger(ListCommand.class.getName()); private String category; private LocalDate date; private String type; - private int month; - private int year; + + //@@author paullowse /** * Initialises the variables of the ListCommand class. */ public ListCommand() { + super(); category = ""; date = null; type = ""; - month = -1; - year = -1; } /** @@ -87,7 +86,9 @@ public String[] getOptionalTags() { COMMAND_TAG_TRANSACTION_CATEGORY, COMMAND_TAG_TRANSACTION_DATE, COMMAND_TAG_GLOBAL_MONTH, - COMMAND_TAG_GLOBAL_YEAR + COMMAND_TAG_GLOBAL_YEAR, + COMMAND_TAG_GLOBAL_NUMBER, + COMMAND_TAG_GLOBAL_PERIOD }; return optionalTags; } @@ -107,16 +108,6 @@ public void setDate(LocalDate date) { this.date = date; } - @Override - public void setStatsMonth(int month) { - this.month = month; - } - - @Override - public void setStatsYear(int year) { - this.year = year; - } - //@@author chydarren /** @@ -131,7 +122,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws listLogger.setLevel(Level.SEVERE); listLogger.log(Level.INFO, "Entering execution of the List command."); - listTransactions(transactions, type, category, date, month, year); + // Checks if there are any error in the tag combinations related to DateIntervals + parseDateIntervalsTags(); + listTransactions(transactions, type, category, date); } /** @@ -141,17 +134,12 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param type The type of transaction. * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". - * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. + * @throws MoolahException If any type of exception has been caught within the function calls. */ - private static void listTransactions(TransactionList transactions, String type, String category, LocalDate date, - int month, int year) - throws InputTransactionInvalidTypeException, GlobalMissingTagException { - if (month != -1 && year == -1) { - listLogger.log(Level.WARNING, "An exception has been caught as a month was given without a year."); - throw new GlobalMissingTagException(); - } - - String transactionsList = transactions.listTransactions(type, category, date, month, year); + private void listTransactions(TransactionList transactions, String type, String category, LocalDate date) + throws MoolahException { + ArrayList timeTransactions = getTimeTransactions(transactions); + String transactionsList = transactions.listTransactions(timeTransactions, type, category, date); if (transactionsList.isEmpty()) { listLogger.log(Level.INFO, "Transactions list is empty as there are no transactions available."); diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index cf4b9ff4d..58d4c601a 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -1,6 +1,6 @@ package seedu.duke.command; -//@@author chydarren +//@@author paullowse import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; @@ -8,12 +8,10 @@ import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; -import java.time.LocalDate; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; -//@@author paullowse import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; @@ -30,7 +28,7 @@ /** * Represents a get command object that will execute the operations for Get command. */ -public class StatsCommand extends Command { +public class StatsCommand extends ListAndStatsCommand { //@@author paullowse private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations @@ -53,16 +51,19 @@ public class StatsCommand extends Command { + LINE_SEPARATOR; //@@author chydarren - private static final Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); + private static final int TRUE_AND = 1; + private static final int TRUE_OR = 2; + private static final int FALSE = 0; + private static final String CATEGORIES = "categories"; + private static final String TIME = "time"; + private static Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); private String statsType; //@@author paullowse - private int month = -1; - private int year = -1; - private String period = null; - private int number = -1; public StatsCommand() { + super(); + statsType = ""; } /** @@ -87,37 +88,13 @@ public String[] getOptionalTags() { return optionalTags; } + //@@author chydarren + @Override public void setStatsType(String statsType) { this.statsType = statsType; } - @Override - public void setStatsMonth(int month) { - this.month = month; - } - - @Override - public void setStatsYear(int year) { - this.year = year; - } - - @Override - public void setStatsNumber(int number) { - this.number = number; - } - - @Override - public void setStatsPeriod(String period) { - this.period = period; - } - - public int getStatsYear() { - return year; - } - - //@@author chydarren - /** * Executes the operations related to the command. * @@ -130,7 +107,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws statsLogger.setLevel(Level.SEVERE); statsLogger.log(Level.INFO, "Entering execution of the Stats command."); - listStatsByStatsType(statsType, transactions, month, year, period, number); + // Checks if there are any error in the tag combinations related to DateIntervals + parseDateIntervalsTags(); + listStatsByStatsType(statsType, transactions); } /** @@ -140,24 +119,27 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param transactions An instance of the TransactionList class. * @throws MoolahException If the type of statistics is not recognised. */ - private static void listStatsByStatsType(String statsType, TransactionList transactions, int month, - int year, String period, int number) + private void listStatsByStatsType(String statsType, TransactionList transactions) throws MoolahException { switch (statsType) { - case "categories": + case CATEGORIES: statsLogger.log(Level.INFO, "Stats type has been detected for categorical savings."); statsTypeCategoricalSavings(transactions); statsLogger.log(Level.INFO, "End of Stats command."); break; //@@author paullowse - case "time": + case TIME: statsLogger.log(Level.INFO, "Stats type has been detected for monthly savings."); - if (year == -1 && month == -1 && period != null && number != -1) { - statsLogger.log(Level.INFO, "Stats command uses lastNperiod."); - statsTypeTimeSavings(transactions, year, month, period, number); - } else if (year != -1 && period == null && number == -1) { + // Stats command uses last N months or years + if (containPeriodNumber() == TRUE_AND && containMonthYear() == FALSE) { + statsLogger.log(Level.INFO, "Stats command uses last N months or years."); + statsTypeTimeSavings(transactions); + // Stats command uses either monthly or yearly + } else if (containPeriodNumber() == FALSE && (containMonthYear() == TRUE_OR + || containMonthYear() == TRUE_AND)) { statsLogger.log(Level.INFO, "Stats command uses either monthly or yearly."); - statsTypeTimeSavings(transactions, year, month, period, number); + statsTypeTimeSavings(transactions); + // Throws a missing tag if s/time was called without any relevant tags } else { statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); throw new StatsInvalidTypeException(); @@ -177,7 +159,7 @@ private static void listStatsByStatsType(String statsType, TransactionList trans * * @param transactions An instance of the TransactionList class. */ - public static void statsTypeCategoricalSavings(TransactionList transactions) { + public void statsTypeCategoricalSavings(TransactionList transactions) { String categoricalSavingsList = transactions.listCategoricalSavings(); if (categoricalSavingsList.isEmpty()) { @@ -197,30 +179,10 @@ public static void statsTypeCategoricalSavings(TransactionList transactions) { * Calls transactions to get the necessary transaction list, convert the parameters into a String for output. * Produces info strings, list of categories and summary statistics. * - * @param transactions An instance of the TransactionList class. - * @param year A specified year. - * @param month A specified month. - * @param period A specified period of time. - * @param number A specified number of periods. - * @throws MoolahException If the type of statistics is not recognised. + * @param transactions An instance of the TransactionList class. */ - public static void statsTypeTimeSavings(TransactionList transactions, int year, int month, - String period, int number) throws MoolahException { - - ArrayList timeTransactions; - // only year - if (period != null && number != -1 && period == "weeks") { - timeTransactions = transactions.getTransactionsByWeekRange(LocalDate.now(), number); - } else if (period != null && number != -1 && period == "months") { - timeTransactions = transactions.getTransactionsByMonthRange(LocalDate.now(), number); - } else if (month == -1) { - timeTransactions = transactions.getTransactionsByYear(year); - } else if (year != -1) { - timeTransactions = transactions.getTransactionsByMonth(year, month); - } else { - statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); - throw new StatsInvalidTypeException(); - } + public void statsTypeTimeSavings(TransactionList transactions) { + ArrayList timeTransactions = getTimeTransactions(transactions); String timeSavingsList = transactions.listTimeStats(timeTransactions, year, month, period, number); if (timeSavingsList.isEmpty()) { @@ -234,18 +196,15 @@ public static void statsTypeTimeSavings(TransactionList transactions, int year, amounts = transactions.processTimeSummaryStats(timeTransactions); String incomeMessage = INFO_STATS_SUMMARY_HEADER + LINE_SEPARATOR - + INFO_STATS_INCOME.toString() + amounts.get(0); - String expensesMessage = INFO_STATS_EXPENSES.toString() + amounts.get(1); - String savingsMessage = INFO_STATS_SAVINGS.toString() + amounts.get(2); - + + INFO_STATS_INCOME + amounts.get(0); + String expensesMessage = INFO_STATS_EXPENSES + amounts.get(1); + String savingsMessage = INFO_STATS_SAVINGS + amounts.get(2); assert !timeSavingsList.isEmpty(); statsLogger.log(Level.INFO, "Monthly savings list is found to contain categories-amount pairs."); Ui.showStatsList(timeSavingsList, INFO_STATS_TIME.toString(), incomeMessage, expensesMessage, savingsMessage); } - //@@author paullowse - /** * Enables the program to exit when the Bye command is issued. * diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 914a9f613..a1b003caf 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -7,6 +7,9 @@ public enum ErrorMessages { ERROR_GLOBAL_INVALID_COMMAND("Invalid command, please enter if you need the list of commands."), ERROR_GLOBAL_DUPLICATE_TAG("Duplicate tag(s) detected, please check your input!"), ERROR_GLOBAL_MISSING_TAG("Mandatory tag(s) missing, please check your input!"), + ERROR_GLOBAL_MISSING_TAG_YEAR("Month tag must be accompanied by a year tag, please check your input!"), + ERROR_GLOBAL_MISSING_TAG_PERIODNUMBER("Period and number tags must be provided together, please check " + + "your input!"), ERROR_GLOBAL_UNSUPPORTED_TAG("Not supported tag(s) detected, please check your input!"), ERROR_GLOBAL_EMPTY_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), ERROR_GLOBAL_INVALID_INDEX("Invalid index, please ensure your index is correct!"), @@ -23,8 +26,10 @@ public enum ErrorMessages { ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_TYPE("Type of statistics given is invalid, please check your input!"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), - ERROR_STORAGE_FILE_CORRUPTED("Duke.txt corrupted. " - + "To preserve data, please STOP the program and edit your data file correctly."), + ERROR_STORAGE_TRANSACTION_CORRUPTED("Transaction values corrupted." + + " To preserve data, please STOP the program and edit your data file correctly."), + ERROR_STORAGE_BUDGET_CORRUPTED("Budget parameter is invalid or missing." + + " To preserve data, please STOP the program and edit your data file correctly."), ERROR_STORAGE_WRITE("Unable to write to Duke.txt. Please save your current Duke.txt file and restart Moolah"), ERROR_MAXIMUM_TRANSACTION_COUNT_REACHED("Unable to add transaction. " + "The maximum allowed transaction size (1000000) has been reached."), diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index c5e958b5b..5b120fdee 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -7,6 +7,8 @@ public enum InfoMessages { INFO_DIVIDER("____________________________________________________________"), INFO_ADD_EXPENSE("I have added the following Expense transaction:"), INFO_ADD_INCOME("I have added the following Income transaction:"), + INFO_EDIT_EXPENSE("I have edited the following Expense transaction:"), + INFO_EDIT_INCOME("I have edited the following Income transaction:"), INFO_DELETE("I have deleted the following transaction:"), INFO_EXIT("Goodbye and see you soon."), INFO_GREET("Hello! I'm Moo and I will help you to manage your finances."), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index fda477043..9d7dd4d47 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -26,8 +26,13 @@ public class TransactionList { private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; private static final String SYMBOL_DOLLAR = "$"; + private static final String INCOME = "income"; + private static final String EXPENSE = "expense"; + private static final String MONTHS = "months"; + private static final String WEEKS = "weeks"; private static final int START = 0; private static final int END = 1; + private static final int UNDEFINED_PARAMETER = -1; private static final String LINE_SEPARATOR = System.lineSeparator(); //@@author chinhan99 @@ -83,6 +88,18 @@ public String deleteTransaction(int index) { return transaction.toString(); } + public String editExpense(String description, int amount, String category, LocalDate date, int index) { + Expense expense = new Expense(description, amount, category, date); + transactions.add(index - 1, expense); + return expense.toString(); + } + + public String editIncome(String description, int amount, String category, LocalDate date, int index) { + Income income = new Income(description, amount, category, date); + transactions.add(index - 1, income); + return income.toString(); + } + //@@author wcwy /** @@ -171,26 +188,12 @@ public boolean isMatchListFilters(Transaction transaction, String type, String c * @param type The type of transaction. * @param category A category for the transaction. * @param date Date of the transaction with format in "yyyyMMdd". - * @param year A specified year. - * @param month A specified month within the year. * @return A string containing the formatted transaction list. * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. */ - public String listTransactions(String type, String category, LocalDate date, int month, int year) + public String listTransactions(ArrayList timeTransactions, String type, + String category, LocalDate date) throws InputTransactionInvalidTypeException { - ArrayList timeTransactions; - - // Filters the transactions list by month or/and year first - if (year != -1 && month != -1) { - timeTransactions = getTransactionsByMonth(year, month); - } else if (year != -1) { - assert month == -1; - timeTransactions = getTransactionsByYear(year); - } else { - // No filter month or/and year filter applied - timeTransactions = transactions; - } - String transactionsList = ""; // Loops each transaction from the time transactions list @@ -215,7 +218,7 @@ public String findTransactions(String keywords) { for (Transaction transaction : transactions) { // Includes only transactions that contain the keywords used in the search expression if (transaction.toString().contains(keywords)) { - transactionsList += transaction.toString() + LINE_SEPARATOR; + transactionsList += transaction + LINE_SEPARATOR; } } return transactionsList; @@ -278,10 +281,10 @@ public String listTimeStats(ArrayList timeTransactions, int year, i int number) { String timeSavingsList = ""; - if (period != null && number != -1) { + if (period != null && number != UNDEFINED_PARAMETER) { timeSavingsList += "The past " + number + " " + period + ": " + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; - } else if (month == -1) { + } else if (month == UNDEFINED_PARAMETER) { timeSavingsList += "Year: " + year + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; } else { @@ -309,9 +312,9 @@ public ArrayList processTimeSummaryStats(ArrayList timeTran int timeIncome = 0; for (Transaction entry : timeTransactions) { String category = entry.getType(); - if (category == "expense") { + if (category.equals(EXPENSE)) { timeExpense += entry.getAmount(); - } else if (category == "income") { + } else if (category.equals(INCOME)) { timeIncome += entry.getAmount(); } } diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index a30f934d2..da94296b3 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -23,6 +23,8 @@ public abstract class Transaction { private int amount; private LocalDate date; + private String type; + public Transaction(String description, int amount, String category, LocalDate date) { this.description = description; this.amount = amount; @@ -85,4 +87,8 @@ public String toString() { return String.format("%s %s%d %s %s %s %s %s", printFormattedCategory(), SYMBOL_DOLLAR, amount, TEXT_AT, printFormattedDate(), SYMBOL_PIPE, TEXT_DESCRIPTION, description); } + + public void setType(String type) { + this.type = type; + } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java index 823b933f5..48019df25 100644 --- a/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java +++ b/src/main/java/seedu/duke/exception/FindTransactionMissingKeywordsException.java @@ -3,6 +3,9 @@ //@@author chydarren import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where there are missing keywords in the search expression. + */ public class FindTransactionMissingKeywordsException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java b/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java index 79e66ee00..eb6995bee 100644 --- a/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java +++ b/src/main/java/seedu/duke/exception/GlobalInvalidMonthException.java @@ -3,6 +3,9 @@ //@@author chydarren import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the user input for month is not within 1 to 12. + */ public class GlobalInvalidMonthException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java b/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java new file mode 100644 index 000000000..6f7629e91 --- /dev/null +++ b/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java @@ -0,0 +1,19 @@ +package seedu.duke.exception; + +//@@author chydarren +import seedu.duke.common.ErrorMessages; + +/** + * Represents the exception where period and number tags are not given as a pair in the input. + */ +public class GlobalMissingPeriodNumberTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_GLOBAL_MISSING_TAG_PERIODNUMBER.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java b/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java new file mode 100644 index 000000000..e54d9d4ce --- /dev/null +++ b/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java @@ -0,0 +1,19 @@ +package seedu.duke.exception; + +//@@author chydarren +import seedu.duke.common.ErrorMessages; + +/** + * Represents the exception where a month tag is not accompanied by a year tag in the input. + */ +public class GlobalMissingYearTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_GLOBAL_MISSING_TAG_YEAR.toString(); + } +} diff --git a/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java b/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java index 696e0afde..9276679d1 100644 --- a/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java +++ b/src/main/java/seedu/duke/exception/InputTransactionInvalidTypeException.java @@ -3,6 +3,9 @@ //@@author chydarren import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the type given for a transaction is not Income or Expense. + */ public class InputTransactionInvalidTypeException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. diff --git a/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java b/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java index dde30e309..7fc144f40 100644 --- a/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java +++ b/src/main/java/seedu/duke/exception/StatsInvalidTypeException.java @@ -3,6 +3,9 @@ //@@author chydarren import seedu.duke.common.ErrorMessages; +/** + * Represents the exception where the type given for requesting statistics is not valid. + */ public class StatsInvalidTypeException extends MoolahException { /** diff --git a/src/main/java/seedu/duke/exception/StorageFileCorruptedBudgetException.java b/src/main/java/seedu/duke/exception/StorageFileCorruptedBudgetException.java new file mode 100644 index 000000000..ebbccb4c3 --- /dev/null +++ b/src/main/java/seedu/duke/exception/StorageFileCorruptedBudgetException.java @@ -0,0 +1,17 @@ +package seedu.duke.exception; + +import seedu.duke.common.ErrorMessages; + +public class StorageFileCorruptedBudgetException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STORAGE_BUDGET_CORRUPTED.toString(); + } + +} diff --git a/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java b/src/main/java/seedu/duke/exception/StorageFileCorruptedTransactionException.java similarity index 66% rename from src/main/java/seedu/duke/exception/StorageFileCorruptedException.java rename to src/main/java/seedu/duke/exception/StorageFileCorruptedTransactionException.java index 21a8569bf..91a62317d 100644 --- a/src/main/java/seedu/duke/exception/StorageFileCorruptedException.java +++ b/src/main/java/seedu/duke/exception/StorageFileCorruptedTransactionException.java @@ -3,7 +3,7 @@ //@@author chinhan99 import seedu.duke.common.ErrorMessages; -public class StorageFileCorruptedException extends MoolahException { +public class StorageFileCorruptedTransactionException extends MoolahException { /** * Returns the error message of the exception to alert user of the exception. @@ -12,7 +12,7 @@ public class StorageFileCorruptedException extends MoolahException { */ @Override public String getMessage() { - return ErrorMessages.ERROR_STORAGE_FILE_CORRUPTED.toString(); + return ErrorMessages.ERROR_STORAGE_TRANSACTION_CORRUPTED.toString(); } } diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index d6afec0f6..719fd4c80 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -42,7 +42,6 @@ import static seedu.duke.common.Constants.MAX_BUDGET_VALUE; import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; - //@@author paullowse import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; @@ -76,6 +75,11 @@ public class ParameterParser { private static final String DELIMITER = " "; private static final int SPLIT_POSITION = 2; private static final int MINIMUM_TAG_LENGTH = 2; + private static final int MINIMUM_YEAR = 1000; + private static final int SMALLEST_POSITIVE_INTEGER = 0; + private static final int JANUARY = 1; + private static final int DECEMBER = 12; + private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; private static final String CATEGORIES = "categories"; @@ -331,16 +335,16 @@ private static void setParameter(Command command, String tag, String parameter) command.setStatsType(parseStatsTypeTag(parameter)); break; case COMMAND_TAG_GLOBAL_MONTH: - command.setStatsMonth(parseMonthTag(parameter)); + command.setGlobalMonth(parseMonthTag(parameter)); break; case COMMAND_TAG_GLOBAL_YEAR: - command.setStatsYear(parseYearTag(parameter)); + command.setGlobalYear(parseYearTag(parameter)); break; case COMMAND_TAG_GLOBAL_NUMBER: - command.setStatsNumber(parseNumberTag(parameter)); + command.setGlobalNumber(parseNumberTag(parameter)); break; case COMMAND_TAG_GLOBAL_PERIOD: - command.setStatsPeriod(parsePeriodTag(parameter)); + command.setGlobalPeriod(parsePeriodTag(parameter)); break; case COMMAND_TAG_BUDGET_AMOUNT: command.setBudgetAmount(parseBudgetTag(parameter)); @@ -582,7 +586,7 @@ public static int parseMonthTag(String parameter) throws GlobalInvalidMonthExcep throw new GlobalNumberNotNumericException(); } - if (month > 12 || month <= 0) { + if (month > DECEMBER || month < JANUARY) { parserLogger.log(Level.WARNING, "An invalid month number error is caught for the given parameter: " + parameter); throw new GlobalInvalidMonthException(); @@ -600,7 +604,7 @@ public static int parseYearTag(String parameter) throws GlobalInvalidYearExcepti + parameter); throw new GlobalNumberNotNumericException(); } - if (year <= 999) { + if (year < MINIMUM_YEAR) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); throw new GlobalInvalidYearException(); @@ -632,7 +636,7 @@ public static int parseNumberTag(String parameter) throws GlobalNumberNotNumeric + parameter); throw new GlobalNumberNotNumericException(); } - if (statsNumber < 0) { + if (statsNumber < SMALLEST_POSITIVE_INTEGER) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); throw new GlobalInvalidNumberException(); diff --git a/src/test/java/seedu/duke/command/BudgetCommandTest.java b/src/test/java/seedu/duke/command/BudgetCommandTest.java index 0f7fdd987..95d16c35b 100644 --- a/src/test/java/seedu/duke/command/BudgetCommandTest.java +++ b/src/test/java/seedu/duke/command/BudgetCommandTest.java @@ -6,6 +6,9 @@ import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StorageWriteErrorException; + +import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -13,23 +16,28 @@ public class BudgetCommandTest { //@@author wcwy @Test public void execute_setValidBudget_expectedSuccessfulBudgetSet() throws MoolahException { - TransactionList transactions = new TransactionList(); - Ui ui = new Ui(); - Storage storage = new Storage(); + try { + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); - BudgetCommand budgetCommand = new BudgetCommand(); - budgetCommand.execute(transactions, ui, storage); - assertEquals(Budget.getBudget(), 1000); + BudgetCommand budgetCommand = new BudgetCommand(); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), 1000); - budgetCommand.setBudgetAmount(100000); - budgetCommand.execute(transactions, ui, storage); - assertEquals(Budget.getBudget(), 100000); + budgetCommand.setBudgetAmount(100000); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), 100000); - budgetCommand.setBudgetAmount(Long.MAX_VALUE); - budgetCommand.execute(transactions, ui, storage); - assertEquals(Budget.getBudget(), Long.MAX_VALUE); + budgetCommand.setBudgetAmount(Long.MAX_VALUE); + budgetCommand.execute(transactions, ui, storage); + assertEquals(Budget.getBudget(), Long.MAX_VALUE); - // Reset budget back - Budget.setBudget(1000); + // Reset budget back to default + budgetCommand.setBudgetAmount(1000); + budgetCommand.execute(transactions, ui, storage); + } catch (StorageWriteErrorException ignore) { + // Ignores exception to prevent printing of error message + } } } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 9e49130b0..ffee52fa3 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -5,6 +5,9 @@ ____________________________________________________________ * Created new file for use * ____________________________________________________________ ____________________________________________________________ +* Duke.txt file is currently empty and ready to be written * +____________________________________________________________ +____________________________________________________________ * duke.txt loaded successfully! * ____________________________________________________________ ____________________________________________________________ From cd2e303e95715f98c4cf2915ba73b071876ac489 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 19:22:53 +0800 Subject: [PATCH 229/416] Fix merge issues and conflicts --- .../duke/command/ListAndStatsCommand.java | 142 ++++++++++++++++++ ...GlobalMissingPeriodNumberTagException.java | 22 +++ .../GlobalMissingYearTagException.java | 22 +++ 3 files changed, 186 insertions(+) diff --git a/src/main/java/seedu/duke/command/ListAndStatsCommand.java b/src/main/java/seedu/duke/command/ListAndStatsCommand.java index 718fb48df..8028e15f5 100644 --- a/src/main/java/seedu/duke/command/ListAndStatsCommand.java +++ b/src/main/java/seedu/duke/command/ListAndStatsCommand.java @@ -1,3 +1,4 @@ +<<<<<<< HEAD package seedu.duke.command; //@@author chydarren @@ -137,3 +138,144 @@ public ArrayList getTimeTransactions(TransactionList transactions) return timeTransactions; } } +======= +package seedu.duke.command; + +//@@author chydarren +import seedu.duke.data.TransactionList; +import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.GlobalMissingPeriodNumberTagException; +import seedu.duke.exception.GlobalMissingYearTagException; +import seedu.duke.exception.GlobalUnsupportedTagException; +import seedu.duke.exception.MoolahException; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +public abstract class ListAndStatsCommand extends Command { + private static final int UNDEFINED_PARAMETER = -1; + private static final int TRUE_AND = 1; + private static final int TRUE_OR = 2; + private static final int TRUE_INVALID_OR = 3; + private static final int FALSE = 0; + private static final String WEEKS = "weeks"; + private static final String MONTHS = "months"; + private static Logger datedTransactionsLogger = Logger.getLogger(ListAndStatsCommand.class.getName()); + + //@@author paullowse + public int month; + public int year; + public String period; + public int number; + + public ListAndStatsCommand() { + this.month = UNDEFINED_PARAMETER; + this.year = UNDEFINED_PARAMETER; + this.period = null; + this.number = UNDEFINED_PARAMETER; + + datedTransactionsLogger.setLevel(Level.SEVERE); + } + + @Override + public void setGlobalMonth(int month) { + this.month = month; + } + + @Override + public void setGlobalYear(int year) { + this.year = year; + } + + @Override + public void setGlobalNumber(int number) { + this.number = number; + } + + @Override + public void setGlobalPeriod(String period) { + this.period = period; + } + + //@@author chydarren + + /** + * Checks if the input contains month or/and year tags. + * + * @return 1 if both tags are given, 2 if only year is given, 3 if only month is given, + * 0 if both are not given. + */ + public int containMonthYear() { + if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (year != UNDEFINED_PARAMETER) { + return TRUE_OR; + } else if (month != UNDEFINED_PARAMETER) { + return TRUE_INVALID_OR; + } + return FALSE; + } + + /** + * Checks if the input contains period and/or number tags. + * + * @return 1 if both tags are given, 2 if either period/number is given, 0 if both are not given. + */ + public int containPeriodNumber() { + if (period != null && number != UNDEFINED_PARAMETER) { + return TRUE_AND; + } else if (period != null || number != UNDEFINED_PARAMETER) { + return TRUE_OR; + } + return FALSE; + } + + /** + * Parses the tags related to tag intervals and checks if there are any error in their combinations. + * + * @throws MoolahException If any of the below exception conditions are met. + */ + public void parseDateIntervalsTags() throws MoolahException { + if (containMonthYear() != FALSE && containPeriodNumber() != FALSE) { + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught " + + "as an invalid combination of tags has been given."); + throw new GlobalUnsupportedTagException(); + } else if (containMonthYear() == TRUE_INVALID_OR) { + // Throws a missing tag if number and period was not given together + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " + + "a month was given without a year."); + throw new GlobalMissingYearTagException(); + } else if (containPeriodNumber() == TRUE_OR) { + // Throws a missing tag if number and period was not given together + datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " + + "number and period needs to be given together."); + throw new GlobalMissingPeriodNumberTagException(); + } + } + + /** + * Gets the complete transactions list by date intervals. + * + * @param transactions An instance of the TransactionList class. + * @return An array list containing all transactions recorded in specified date interval. + */ + public ArrayList getTimeTransactions(TransactionList transactions) { + ArrayList timeTransactions = transactions.getTransactions(); + + if (containMonthYear() == TRUE_AND) { + timeTransactions = transactions.getTransactionsByMonth(year, month); + } else if (containMonthYear() == TRUE_OR) { + assert month == UNDEFINED_PARAMETER; + timeTransactions = transactions.getTransactionsByYear(year); + } else if (containPeriodNumber() == TRUE_AND && period == MONTHS) { + timeTransactions = transactions.getTransactionsByMonthRange(LocalDate.now(), number); + } else if (containPeriodNumber() == TRUE_AND && period == WEEKS) { + timeTransactions = transactions.getTransactionsByWeekRange(LocalDate.now(), number); + } + + return timeTransactions; + } +} +>>>>>>> 12d727e7faae9053200db9f37950fe2d61309a05 diff --git a/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java b/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java index 6f7629e91..7c4a4e535 100644 --- a/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalMissingPeriodNumberTagException.java @@ -1,3 +1,4 @@ +<<<<<<< HEAD package seedu.duke.exception; //@@author chydarren @@ -17,3 +18,24 @@ public String getMessage() { return ErrorMessages.ERROR_GLOBAL_MISSING_TAG_PERIODNUMBER.toString(); } } +======= +package seedu.duke.exception; + +//@@author chydarren +import seedu.duke.common.ErrorMessages; + +/** + * Represents the exception where period and number tags are not given as a pair in the input. + */ +public class GlobalMissingPeriodNumberTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_GLOBAL_MISSING_TAG_PERIODNUMBER.toString(); + } +} +>>>>>>> 12d727e7faae9053200db9f37950fe2d61309a05 diff --git a/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java b/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java index e54d9d4ce..30985c018 100644 --- a/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java +++ b/src/main/java/seedu/duke/exception/GlobalMissingYearTagException.java @@ -1,3 +1,4 @@ +<<<<<<< HEAD package seedu.duke.exception; //@@author chydarren @@ -17,3 +18,24 @@ public String getMessage() { return ErrorMessages.ERROR_GLOBAL_MISSING_TAG_YEAR.toString(); } } +======= +package seedu.duke.exception; + +//@@author chydarren +import seedu.duke.common.ErrorMessages; + +/** + * Represents the exception where a month tag is not accompanied by a year tag in the input. + */ +public class GlobalMissingYearTagException extends MoolahException { + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_GLOBAL_MISSING_TAG_YEAR.toString(); + } +} +>>>>>>> 12d727e7faae9053200db9f37950fe2d61309a05 From 2234522178e60b641143f527402538e268800cfc Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 21:39:36 +0800 Subject: [PATCH 230/416] Implement money managing tips feature for add command The program will now check if the monthly budget has been exceeded on every add command and to display corresponding messages and tips to the users. --- src/main/java/seedu/duke/Ui.java | 2 +- .../java/seedu/duke/command/AddCommand.java | 4 +- .../java/seedu/duke/common/DateFormats.java | 1 + .../java/seedu/duke/common/InfoMessages.java | 11 +- src/main/java/seedu/duke/data/Budget.java | 103 +++++++++++++++++- 5 files changed, 112 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index c731665a8..1affbecea 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -126,7 +126,7 @@ public static void showStatsList(String list, String message, String incomeMessa * @param budgetInfo A message that contains the monthly budget information. */ public static void showTransactionAction(String message, String transactionDetails, String budgetInfo) { - printMessages(message, transactionDetails, INFO_REMAINING_BUDGET + budgetInfo); + printMessages(message, transactionDetails, budgetInfo); } // A temporary overload method for backward-compatibility for delete command diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 48f1a1ba0..a70e433a9 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -153,9 +153,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws checkTransactionCapacity(transactions); String messageBanner = addTransaction(transactions); long addedMonthExpenseSum = transactions.calculateMonthlyTotalExpense(date); - String budgetLeft = Budget.getBudgetLeft(addedMonthExpenseSum); + String budgetInfo = Budget.generateBudgetRemainingMessage(addedMonthExpenseSum, true, date); - Ui.showTransactionAction(messageBanner, transactionCreated.toString(), budgetLeft); + Ui.showTransactionAction(messageBanner, transactionCreated.toString(), budgetInfo); //@@author chinhan99 storage.writeToFile(transactions.getTransactions()); diff --git a/src/main/java/seedu/duke/common/DateFormats.java b/src/main/java/seedu/duke/common/DateFormats.java index 26b36145e..f20c67016 100644 --- a/src/main/java/seedu/duke/common/DateFormats.java +++ b/src/main/java/seedu/duke/common/DateFormats.java @@ -7,6 +7,7 @@ public enum DateFormats { //@@author wcwy DATE_INPUT_PATTERN("ddMMyyyy"), DATE_OUTPUT_PATTERN("MMM dd yyyy"), + MONTH_YEAR_OUTPUT_PATTERN("MMM yyyy"), DATE_STORAGE_OUTPUT_PATTERN("yyyy-MM-dd"); public final String message; diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 5b120fdee..b04398c15 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -5,6 +5,12 @@ */ public enum InfoMessages { INFO_DIVIDER("____________________________________________________________"), + INFO_LINE_SEPARATOR(System.lineSeparator()), + INFO_SPACE(" "), + INFO_FULL_STOP("."), + INFO_FULL_STOP_SPACE(". "), + INFO_COLON_SPACE(": "), + INFO_DOLLAR_SIGN("$"), INFO_ADD_EXPENSE("I have added the following Expense transaction:"), INFO_ADD_INCOME("I have added the following Income transaction:"), INFO_EDIT_EXPENSE("I have edited the following Expense transaction:"), @@ -32,7 +38,10 @@ public enum InfoMessages { INFO_PURGE_WARNING("Are you sure you want to proceed with this command? Please enter 'Y' to confirm."), INFO_BUDGET_SET_SUCCESSFUL("You have successfully updated the budget."), INFO_CURRENT_BUDGET("Monthly budget set as: $"), - INFO_REMAINING_BUDGET("Budget remained for the month of transaction: $"); + INFO_REMAINING_BUDGET("Budget remained for "), + INFO_EXCEEDING_BUDGET("Budget exceeded for "), + INFO_BUDGET_EXCEEDED_TIPS("Consider spending less!"), + INFO_BUDGET_NOT_EXCEEDED_TIPS("Keep it up!"); //@@author chydarren public final String message; diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index 4e38ccfdf..f7a54625d 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -1,10 +1,17 @@ package seedu.duke.data; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +import static java.lang.Math.abs; import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; +import static seedu.duke.common.DateFormats.MONTH_YEAR_OUTPUT_PATTERN; +import static seedu.duke.common.InfoMessages.*; //@@author wcwy + /** * Represents the user's budget for the current month. * @@ -34,14 +41,39 @@ public static void setBudget(long budget) { } /** - * Returns the amount of budget left in the month, as a string. + * Generates a budget remaining message based on the total monthly expense given. * - *

If the total amount of expenses is higher than the budget, a negative value in string will be returned. + *

Caller method can choose to either to generate it with a tips behind or without it + * by setting the boolean withTips correspondingly. * * @param totalMonthlyExpense The long value representing the total sum of a monthly expense. - * @return A string value representing the amount of budget left. + * @param withTips The boolean value to indicate whether tips need to be appended behind the message. + * @param date The date of transaction. + * @return The message generated based on the budget remained for the given date. */ - public static String getBudgetLeft(long totalMonthlyExpense) { + public static String generateBudgetRemainingMessage(long totalMonthlyExpense, boolean withTips, LocalDate date) { + long budgetLeft = calculateBudgetLeft(totalMonthlyExpense); + boolean hasExceededBudget = hasExceededBudget(budgetLeft); + String budgetMonthAndYear = retrieveFormattedMonthAndYear(date); + + String message = getBudgetLeftMessage(budgetLeft, hasExceededBudget, budgetMonthAndYear); + + if (withTips) { + message += INFO_FULL_STOP_SPACE + getMoneyManagingTips(hasExceededBudget); + } + + return message; + } + + /** + * Calculates and returns the amount of budget left in the month, as a long value. + * + *

If the total amount of expenses is higher than the budget, a negative value will be returned. + * + * @param totalMonthlyExpense The long value representing the total sum of a monthly expense. + * @return A long value representing the amount of budget left. + */ + private static long calculateBudgetLeft(long totalMonthlyExpense) { /* Since the maximum number of transaction is 1000000, maximum amount of expense is 10000000, and minimum is 1, the lowest possible budget left value is @@ -53,6 +85,67 @@ public static String getBudgetLeft(long totalMonthlyExpense) { assert (Long.valueOf(MAX_AMOUNT_VALUE) * Long.valueOf(MAX_TRANSACTIONS_COUNT) > Long.valueOf(MAX_AMOUNT_VALUE)); assert (MIN_BUDGET_VALUE > 0); - return Long.toString(budget - totalMonthlyExpense); + return budget - totalMonthlyExpense; + } + + /** + * Checks if the budget has been exceeded. + * + *

If the budget is negative, it means that the total monthly expense is higher than the budget left. + * Thus, the budget is exceeded and returns true. + * + *

If the budget is not positive, it means that the total monthly expense is lower than or equals to the + * budget left. Thus budget is not yet exceeded and returns false. + * + * @param budgetLeft A long value indicating the difference of total monthly expense and monthly budget. + * @return A boolean value indicating whether the budget has been exceeded for the month. + */ + private static boolean hasExceededBudget(long budgetLeft) { + return budgetLeft < 0; + } + + /** + * Retrieves a message to inform user if the budget has been exceeded. + * + * @param budgetLeft A long value indicating the difference of total monthly expense and monthly budget. + * @param hasExceededBudget A boolean value indicating whether the budget has been exceeded for the month. + * @return A budget remaining or exceeding message. + */ + private static String getBudgetLeftMessage(long budgetLeft, boolean hasExceededBudget, String monthAndYear) { + if (hasExceededBudget) { + assert budgetLeft < 0; + // The absolute value of budget left will be the amount of budget exceeded + return INFO_EXCEEDING_BUDGET+ monthAndYear + INFO_COLON_SPACE + INFO_DOLLAR_SIGN + + abs(budgetLeft); + } else { + assert budgetLeft >= 0; + return INFO_REMAINING_BUDGET + monthAndYear + INFO_COLON_SPACE + INFO_DOLLAR_SIGN + + budgetLeft; + } + } + + /** + * Retrieves a money managing tips based on whether budget has been exceeded. + * + * @param hasExceededBudget A boolean value indicating whether the budget has been exceeded for the month. + * @return A string containing a money managing tips to the user. + */ + private static String getMoneyManagingTips(boolean hasExceededBudget) { + if (hasExceededBudget) { + return INFO_BUDGET_EXCEEDED_TIPS.toString(); + } else { + return INFO_BUDGET_NOT_EXCEEDED_TIPS.toString(); + } + } + + /** + * Retrieves a formatted string containing the month and year of a date + * + * @param date Date of the transaction to be considered for the budget. + * @return A string containing the formatted output for month and year. + */ + private static String retrieveFormattedMonthAndYear(LocalDate date) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(MONTH_YEAR_OUTPUT_PATTERN.toString()); + return date.format(formatter); } } From d6435f64bb704a045836b4e7a36dafed74ac1b84 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 22:00:40 +0800 Subject: [PATCH 231/416] Update budget display on the welcome message Current month's expense and budget will be calculated to find the difference which will be displayed to the user. --- src/main/java/seedu/duke/Ui.java | 2 +- .../java/seedu/duke/command/AddCommand.java | 4 ++- .../java/seedu/duke/common/DateFormats.java | 14 ++++++++ src/main/java/seedu/duke/data/Budget.java | 33 +++++++++++-------- .../java/seedu/duke/data/TransactionList.java | 2 +- 5 files changed, 38 insertions(+), 17 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 1affbecea..f7fc9e2fd 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -74,7 +74,7 @@ public static void showInfoMessage(String message) { * Prepares the greeting messages to be displayed to the user. */ public static void showGreeting() { - printMessages(INFO_GREET.toString(), INFO_CURRENT_BUDGET.toString() + Budget.getBudget(), + printMessages(INFO_GREET.toString(), Budget.generateCurrentMonthBudgetRemainingMessage(), INFO_HELP_PROMPT.toString() ); } diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index a70e433a9..44e9f5b2c 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -4,6 +4,7 @@ import seedu.duke.Storage; import seedu.duke.Ui; +import seedu.duke.common.DateFormats; import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; @@ -153,7 +154,8 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws checkTransactionCapacity(transactions); String messageBanner = addTransaction(transactions); long addedMonthExpenseSum = transactions.calculateMonthlyTotalExpense(date); - String budgetInfo = Budget.generateBudgetRemainingMessage(addedMonthExpenseSum, true, date); + String budgetInfo = Budget.generateBudgetRemainingMessage(addedMonthExpenseSum, true, + DateFormats.retrieveFormattedMonthAndYear(date)); Ui.showTransactionAction(messageBanner, transactionCreated.toString(), budgetInfo); diff --git a/src/main/java/seedu/duke/common/DateFormats.java b/src/main/java/seedu/duke/common/DateFormats.java index f20c67016..7cb68b295 100644 --- a/src/main/java/seedu/duke/common/DateFormats.java +++ b/src/main/java/seedu/duke/common/DateFormats.java @@ -1,5 +1,8 @@ package seedu.duke.common; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + /** * Provides enum variables for the approved date formats for input and output. */ @@ -29,4 +32,15 @@ public enum DateFormats { public String toString() { return message; } + + /** + * Retrieves a formatted string containing the month and year of a date + * + * @param date Date of the transaction to be considered for the budget. + * @return A string containing the formatted output for month and year. + */ + public static String retrieveFormattedMonthAndYear(LocalDate date) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(MONTH_YEAR_OUTPUT_PATTERN.toString()); + return date.format(formatter); + } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index f7a54625d..59f9891f3 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -8,7 +8,13 @@ import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; import static seedu.duke.common.DateFormats.MONTH_YEAR_OUTPUT_PATTERN; -import static seedu.duke.common.InfoMessages.*; +import static seedu.duke.common.InfoMessages.INFO_COLON_SPACE; +import static seedu.duke.common.InfoMessages.INFO_EXCEEDING_BUDGET; +import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; +import static seedu.duke.common.InfoMessages.INFO_BUDGET_EXCEEDED_TIPS; +import static seedu.duke.common.InfoMessages.INFO_BUDGET_NOT_EXCEEDED_TIPS; +import static seedu.duke.common.InfoMessages.INFO_FULL_STOP_SPACE; +import static seedu.duke.common.InfoMessages.INFO_DOLLAR_SIGN; //@@author wcwy @@ -51,12 +57,11 @@ public static void setBudget(long budget) { * @param date The date of transaction. * @return The message generated based on the budget remained for the given date. */ - public static String generateBudgetRemainingMessage(long totalMonthlyExpense, boolean withTips, LocalDate date) { + public static String generateBudgetRemainingMessage(long totalMonthlyExpense, boolean withTips, String monthYear) { long budgetLeft = calculateBudgetLeft(totalMonthlyExpense); boolean hasExceededBudget = hasExceededBudget(budgetLeft); - String budgetMonthAndYear = retrieveFormattedMonthAndYear(date); - String message = getBudgetLeftMessage(budgetLeft, hasExceededBudget, budgetMonthAndYear); + String message = getBudgetLeftMessage(budgetLeft, hasExceededBudget, monthYear); if (withTips) { message += INFO_FULL_STOP_SPACE + getMoneyManagingTips(hasExceededBudget); @@ -65,6 +70,15 @@ public static String generateBudgetRemainingMessage(long totalMonthlyExpense, bo return message; } + public static String generateCurrentMonthBudgetRemainingMessage() { + LocalDate todayDate = LocalDate.now(); + String monthYear = "current month"; + long currentMonthTotalExpense = TransactionList.calculateMonthlyTotalExpense(todayDate); + String message = generateBudgetRemainingMessage(currentMonthTotalExpense, false, monthYear); + + return message; + } + /** * Calculates and returns the amount of budget left in the month, as a long value. * @@ -138,14 +152,5 @@ private static String getMoneyManagingTips(boolean hasExceededBudget) { } } - /** - * Retrieves a formatted string containing the month and year of a date - * - * @param date Date of the transaction to be considered for the budget. - * @return A string containing the formatted output for month and year. - */ - private static String retrieveFormattedMonthAndYear(LocalDate date) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern(MONTH_YEAR_OUTPUT_PATTERN.toString()); - return date.format(formatter); - } + } diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 9d7dd4d47..48d70e7c4 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -444,7 +444,7 @@ public void purgeTransactions() { * @param date A date object in which the monthly total expenses calculated is based on. * @return A long value indicating the amount of expenses spent in the month. */ - public long calculateMonthlyTotalExpense(LocalDate date) { + public static long calculateMonthlyTotalExpense(LocalDate date) { long totalExpense = 0; int month = date.getMonthValue(); int year = date.getYear(); From 4c2d9591def7b90075ebf45531a6740d543fa19b Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 22:08:01 +0800 Subject: [PATCH 232/416] Add updated help information for List and Stats commands --- .../duke/command/ListAndStatsCommand.java | 10 +++- .../java/seedu/duke/command/ListCommand.java | 21 +++++++-- .../java/seedu/duke/command/StatsCommand.java | 47 +++++++++++++++++-- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListAndStatsCommand.java b/src/main/java/seedu/duke/command/ListAndStatsCommand.java index b97d3340c..5ffe9ba75 100644 --- a/src/main/java/seedu/duke/command/ListAndStatsCommand.java +++ b/src/main/java/seedu/duke/command/ListAndStatsCommand.java @@ -13,7 +13,11 @@ import java.util.logging.Level; import java.util.logging.Logger; +/** + * Represents an object that can be inherited by List and Stats command objects. + */ public abstract class ListAndStatsCommand extends Command { + //@@author chydarren private static final int UNDEFINED_PARAMETER = -1; private static final int TRUE_AND = 1; private static final int TRUE_OR = 2; @@ -35,6 +39,7 @@ public ListAndStatsCommand() { this.period = null; this.number = UNDEFINED_PARAMETER; + //@@author chydarren datedTransactionsLogger.setLevel(Level.SEVERE); } @@ -98,16 +103,17 @@ public int containPeriodNumber() { */ public void parseDateIntervalsTags() throws MoolahException { if (containMonthYear() != FALSE && containPeriodNumber() != FALSE) { + // Throws an unsupported tag exception if tags are not supposed to be used together datedTransactionsLogger.log(Level.WARNING, "An exception has been caught " + "as an invalid combination of tags has been given."); throw new GlobalUnsupportedTagException(); } else if (containMonthYear() == TRUE_INVALID_OR) { - // Throws a missing tag if number and period was not given together + // Throws a missing tag exception if number and period was not given together datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " + "a month was given without a year."); throw new GlobalMissingYearTagException(); } else if (containPeriodNumber() == TRUE_OR) { - // Throws a missing tag if number and period was not given together + // Throws a missing tag exception if number and period was not given together datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " + "number and period needs to be given together."); throw new GlobalMissingPeriodNumberTagException(); diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 42a13fa2e..85d873416 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -33,9 +33,10 @@ public class ListCommand extends ListAndStatsCommand { public static final String COMMAND_WORD = "LIST"; // The description for the usage of command public static final String COMMAND_DESCRIPTION = "To list all or some transactions based on selection." - + " Note that the tags will be joint in the filter based on the 'AND' operation."; + + " Tags will be joined in the filter based on the 'AND' operation."; // The guiding information for the usage of command - public static final String COMMAND_USAGE = "Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR]"; + public static final String COMMAND_USAGE = "Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] " + + "[p/PERIOD] [n/NUMBER]"; // The formatting information for the parameters used by the command public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + LINE_SEPARATOR @@ -45,10 +46,20 @@ public class ListCommand extends ListAndStatsCommand { + LINE_SEPARATOR + "(Optional) DATE: Date of the transaction. The format must be in \"yyyyMMdd\"." + LINE_SEPARATOR - + "(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that" - + " month must be accompanied by a year." + + "(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that " + + "month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] " + + "tags." + LINE_SEPARATOR - + "(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted."; + + "(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted." + + "This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags." + + LINE_SEPARATOR + + "(Optional) PERIOD: Period of the transaction. Only \"weeks\" or \"months\" is accepted. Note that" + + "period must be accompanied by a number to backdate from. This tag cannot be used together with " + + "[m/MONTH] or [y/YEAR] tags." + + LINE_SEPARATOR + + "(Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note that" + + "number must be accompanied by a period that represents weeks or months. This tag cannot be used " + + "together with [m/MONTH] or [y/YEAR] tags."; // Basic help description public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 58d4c601a..8b8d53355 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -5,6 +5,7 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.InputTransactionInvalidTypeException; import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; @@ -18,6 +19,7 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; +import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS; import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; import static seedu.duke.common.InfoMessages.INFO_STATS_EXPENSES; import static seedu.duke.common.InfoMessages.INFO_STATS_INCOME; @@ -34,14 +36,28 @@ public class StatsCommand extends ListAndStatsCommand { // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "STATS"; // The description for the usage of command - public static final String COMMAND_DESCRIPTION = "To get statistics of the transactions such " - + "as the total savings for each category, summary of expenditure, etc."; + public static final String COMMAND_DESCRIPTION = "To get statistics of the transactions such" + + " as the total savings for each category, summary of expenditure over a time period."; // The guiding information for the usage of command - public static final String COMMAND_USAGE = "Usage: stats s/STATISTICS_TYPE"; + public static final String COMMAND_USAGE = "Usage: stats s/STATS_TYPE [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER]"; // The formatting information for the parameters used by the command public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + LINE_SEPARATOR - + "STATISTICS_TYPE: The type of statistics to be generated. Only \"categories\" is accepted."; + + "STATISTICS_TYPE: The type of statistics to be generated. Only \"categories\" or \"time\" is accepted." + + "(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that " + + "month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] " + + "tags." + + LINE_SEPARATOR + + "(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted." + + "This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags." + + LINE_SEPARATOR + + "(Optional) PERIOD: Period of the transaction. Only \"weeks\" or \"months\" is accepted. Note that " + + "period must be accompanied by a number to backdate from. This tag cannot be used together with " + + "[m/MONTH] or [y/YEAR] tags." + + LINE_SEPARATOR + + "(Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note that" + + "number must be accompanied by a period that represents weeks or months. This tag cannot be used " + + "together with [m/MONTH] or [y/YEAR] tags."; // Basic help description public static final String COMMAND_HELP = "Command Word: " + COMMAND_WORD + LINE_SEPARATOR @@ -56,6 +72,7 @@ public class StatsCommand extends ListAndStatsCommand { private static final int FALSE = 0; private static final String CATEGORIES = "categories"; private static final String TIME = "time"; + private static final String MONTHS = "months"; private static Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); private String statsType; @@ -122,6 +139,9 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws private void listStatsByStatsType(String statsType, TransactionList transactions) throws MoolahException { switch (statsType) { + case MONTHS: + statsTypeMonthlyExpenditure(transactions); + break; case CATEGORIES: statsLogger.log(Level.INFO, "Stats type has been detected for categorical savings."); statsTypeCategoricalSavings(transactions); @@ -173,6 +193,25 @@ public void statsTypeCategoricalSavings(TransactionList transactions) { Ui.showList(categoricalSavingsList, INFO_STATS_CATEGORIES.toString()); } + /** + * Display the statistics requested for the amount of expenditure and savings accumulated over different months. + * + * @param transactions An instance of the TransactionList class. + */ + public void statsTypeMonthlyExpenditure(TransactionList transactions) throws InputTransactionInvalidTypeException { + String monthlyExpenditureList = transactions.listMonthlyExpenditure(); + + if (monthlyExpenditureList.isEmpty()) { + statsLogger.log(Level.INFO, "Monthly expenditure list is empty as there are no transactions available."); + Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); + return; + } + + assert !monthlyExpenditureList.isEmpty(); + statsLogger.log(Level.INFO, "Monthly expenditure list is found to contain month-expenditure pairs."); + Ui.showList(monthlyExpenditureList, INFO_STATS_MONTHS.toString()); + } + //@@author paullowse /** From 34dd0cf7d0b25f7984081454927f4ffb1ca50f15 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 22:13:25 +0800 Subject: [PATCH 233/416] Add ability to view the monthly expenditure and comments related to spending habits in expenditure summary --- .../java/seedu/duke/command/StatsCommand.java | 3 +- .../java/seedu/duke/common/DateFormats.java | 1 + .../java/seedu/duke/common/InfoMessages.java | 6 + .../java/seedu/duke/data/TransactionList.java | 103 +++++++++++++++++- .../seedu/duke/parser/ParameterParser.java | 2 + 5 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 8b8d53355..af6103c1f 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -43,7 +43,8 @@ public class StatsCommand extends ListAndStatsCommand { // The formatting information for the parameters used by the command public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + LINE_SEPARATOR - + "STATISTICS_TYPE: The type of statistics to be generated. Only \"categories\" or \"time\" is accepted." + + "STATISTICS_TYPE: The type of statistics to be generated. Only \"categories\", \"months\" or \"time\" " + + "is accepted." + "(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that " + "month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] " + "tags." diff --git a/src/main/java/seedu/duke/common/DateFormats.java b/src/main/java/seedu/duke/common/DateFormats.java index 26b36145e..cb8b495c2 100644 --- a/src/main/java/seedu/duke/common/DateFormats.java +++ b/src/main/java/seedu/duke/common/DateFormats.java @@ -7,6 +7,7 @@ public enum DateFormats { //@@author wcwy DATE_INPUT_PATTERN("ddMMyyyy"), DATE_OUTPUT_PATTERN("MMM dd yyyy"), + DATE_MONTH_PATTERN("MMM yyyy"), DATE_STORAGE_OUTPUT_PATTERN("yyyy-MM-dd"); public final String message; diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 5b120fdee..4183155d6 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -20,6 +20,12 @@ public enum InfoMessages { INFO_LIST_UNFILTERED("There are no transaction records that match your search expression."), INFO_STATS_EMPTY("There are no statistics available yet for the given statistics type."), INFO_STATS_CATEGORIES("Here are the total savings for each category:"), + INFO_STATS_MONTHS("Here is the expenditure summary for each month:"), + INFO_STATS_MONTHS_COMMENT_ONE("Excellent! You saved quite a lot this month."), + INFO_STATS_MONTHS_COMMENT_TWO("Wow, keep up the good work. You saved at least 80% of your income."), + INFO_STATS_MONTHS_COMMENT_THREE("Good effort, you saved at least half of your income."), + INFO_STATS_MONTHS_COMMENT_FOUR("Hmm, it spent quite a sum. Try saving more in future."), + INFO_STATS_MONTHS_COMMENT_FIVE("You spent too much. Try to manage your expense within your budget."), INFO_STATS_TIME("Here are the total savings and expenses for"), INFO_STATS_INCOME("Total Income = "), INFO_STATS_EXPENSES("Total Expenses = "), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 9d7dd4d47..2dd935e6d 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -8,13 +8,20 @@ import java.time.LocalDate; import java.time.Year; import java.time.YearMonth; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.stream.Collectors; import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; +import static seedu.duke.common.DateFormats.DATE_MONTH_PATTERN; import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES_HEADER; +import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_ONE; +import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_TWO; +import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_THREE; +import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_FOUR; +import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_FIVE; /** * Represents a list of transactions added by the user into the application. @@ -28,8 +35,8 @@ public class TransactionList { private static final String SYMBOL_DOLLAR = "$"; private static final String INCOME = "income"; private static final String EXPENSE = "expense"; - private static final String MONTHS = "months"; - private static final String WEEKS = "weeks"; + private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; + private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; private static final int START = 0; private static final int END = 1; private static final int UNDEFINED_PARAMETER = -1; @@ -265,6 +272,98 @@ public String listCategoricalSavings() { return categoricalSavingsList; } + /** + * Reads the transactions list and adds each amount to the month and year in monthly savings hashmap. + * + * @param monthlyExpenditure A hashmap containing all month-expenditure pair for total expenditure and savings. + * @return A hashmap containing all month-expenditure pair for total expenditure and savings. + */ + public HashMap processMonthlyExpenditure(HashMap monthlyExpenditure) + throws InputTransactionInvalidTypeException { + for (Transaction transaction : transactions) { + String date = transaction.getDate().format(DateTimeFormatter.ofPattern(DATE_MONTH_PATTERN.toString())); + int income = 0; + int expense = 0; + + // Checks whether transaction is Income or Expense and adds to respective amount + try { + if (isTransactionInstance(transaction, CLASS_TYPE_INCOME)) { + income = transaction.getAmount(); + } else { + expense = - transaction.getAmount(); + } + } catch (ClassNotFoundException e) { + throw new InputTransactionInvalidTypeException(); + } + + // Creates a new month and year with starter amounts if not exists in hashmap + if (!monthlyExpenditure.containsKey(date)) { + monthlyExpenditure.put(date, new int[]{income, expense, income + expense}); + continue; + } + + // Adds amounts to existing month and year in hashmap + int updatedIncome = monthlyExpenditure.get(date)[0] + income; + int updatedExpense = monthlyExpenditure.get(date)[1] + expense; + + monthlyExpenditure.put(date, new int[]{updatedIncome, updatedExpense, + updatedIncome + updatedExpense}); + } + + return monthlyExpenditure; + } + + /** + * Gets comment related to spending habit each month. + * + * @param savingsPercentage The percentage savings based against the total income. + * @return A string containing the comment related to the spending habit for the month. + */ + public String getSpendingHabitComment(int savingsPercentage) { + System.out.println(savingsPercentage); + if (savingsPercentage >= 100) { + return INFO_STATS_MONTHS_COMMENT_ONE.toString(); + } else if (savingsPercentage >= 75) { + return INFO_STATS_MONTHS_COMMENT_TWO.toString(); + } else if (savingsPercentage >= 50) { + return INFO_STATS_MONTHS_COMMENT_THREE.toString(); + } else if (savingsPercentage >= 25) { + return INFO_STATS_MONTHS_COMMENT_FOUR.toString(); + } + return INFO_STATS_MONTHS_COMMENT_FIVE.toString(); + } + + /** + * Calculates and stores total expenditure for each month and year into a hashmap. + * + * @return A hashmap containing all month-expenditure pair for total expenditure and savings. + * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. + */ + public String listMonthlyExpenditure() throws InputTransactionInvalidTypeException { + String monthlyExpenditureList = ""; + HashMap monthlyExpenditure = new HashMap<>(); + // Adds each amount from transactions list to the month and year in monthly expenditure hashmap + monthlyExpenditure = processMonthlyExpenditure(monthlyExpenditure); + + // Formats every entry in the hashmap into a monthly expenditure summary list + for (HashMap.Entry entry : monthlyExpenditure.entrySet()) { + monthlyExpenditureList += String.format("%s%s%s%s", PREFIX_CATEGORY, entry.getKey(), POSTFIX_CATEGORY, + LINE_SEPARATOR); + monthlyExpenditureList += String.format("%s%s%s%s", "Income: ", SYMBOL_DOLLAR, entry.getValue()[0], + LINE_SEPARATOR); + monthlyExpenditureList += String.format("%s%s%s%s", "Expense: ", SYMBOL_DOLLAR, entry.getValue()[1], + LINE_SEPARATOR); + monthlyExpenditureList += String.format("%s%s%s%s", "Savings: ", SYMBOL_DOLLAR, entry.getValue()[2], + LINE_SEPARATOR, LINE_SEPARATOR); + + int savingsPercentage = 100 * entry.getValue()[2] / entry.getValue()[0]; + monthlyExpenditureList += String.format("%s%s%s%s", "Spending Habit: ", + getSpendingHabitComment(savingsPercentage), LINE_SEPARATOR, LINE_SEPARATOR); + } + + return monthlyExpenditureList; + } + //@@author paullowse /** diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 719fd4c80..a8275dee0 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -564,6 +564,8 @@ public static String parseStatsTypeTag(String parameter) throws StatsInvalidType switch (parameter) { case CATEGORIES: return CATEGORIES; + case MONTHS: + return MONTHS; case TIME: return TIME; default: From ba667e4b0becd9bc0fb2d1ea4a774f9b98da2306 Mon Sep 17 00:00:00 2001 From: chydarren Date: Mon, 24 Oct 2022 22:13:59 +0800 Subject: [PATCH 234/416] Update EXPECTED.TXT to reflect changes to help information --- text-ui-test/EXPECTED.TXT | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index ffee52fa3..c03998379 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -31,16 +31,16 @@ To add a new transaction entry, which could be either an "income" or an "expense Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION Command Word: LIST -To list all or some transactions based on selection. Note that the tags will be joint in the filter based on the 'AND' operation. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] +To list all or some transactions based on selection. Tags will be joined in the filter based on the 'AND' operation. +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER] Command Word: FIND To find specific transaction(s) based on any keywords inputted by the user. Usage: find KEYWORDS Command Word: STATS -To get statistics of the transactions such as the total savings for each category, summary of expenditure, etc. -Usage: stats s/STATISTICS_TYPE +To get statistics of the transactions such as the total savings for each category, summary of expenditure over a time period. +Usage: stats s/STATS_TYPE [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER] Command Word: EDIT To edit a specific entry in the list of transactions. @@ -85,14 +85,16 @@ DATE: Date of the transaction. The format must be in "yyyyMMdd". DESCRIPTION: More information regarding the transaction, written without any space. Command Word: LIST -To list all or some transactions based on selection. Note that the tags will be joint in the filter based on the 'AND' operation. -Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] +To list all or some transactions based on selection. Tags will be joined in the filter based on the 'AND' operation. +Usage: list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER] Parameters information: (Optional) TYPE - The type of transaction. Only "income" or "expense" is accepted. (Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. (Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". -(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. -(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted. +(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. +(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted.This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. +(Optional) PERIOD: Period of the transaction. Only "weeks" or "months" is accepted. Note thatperiod must be accompanied by a number to backdate from. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. +(Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note thatnumber must be accompanied by a period that represents weeks or months. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. Command Word: FIND To find specific transaction(s) based on any keywords inputted by the user. @@ -101,10 +103,13 @@ Parameters information: KEYWORDS: Any partial or full keyword(s) that matches the details of the transaction, such as type, category, amount, date or description. Command Word: STATS -To get statistics of the transactions such as the total savings for each category, summary of expenditure, etc. -Usage: stats s/STATISTICS_TYPE +To get statistics of the transactions such as the total savings for each category, summary of expenditure over a time period. +Usage: stats s/STATS_TYPE [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER] Parameters information: -STATISTICS_TYPE: The type of statistics to be generated. Only "categories" is accepted. +STATISTICS_TYPE: The type of statistics to be generated. Only "categories", "months" or "time" is accepted.(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. +(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted.This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. +(Optional) PERIOD: Period of the transaction. Only "weeks" or "months" is accepted. Note that period must be accompanied by a number to backdate from. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. +(Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note thatnumber must be accompanied by a period that represents weeks or months. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. Command Word: EDIT To edit a specific entry in the list of transactions. From ccb22e70bd4d9ff5933bc3a890ac256b3a5acf74 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 22:48:04 +0800 Subject: [PATCH 235/416] Add budget managing tips on delete and edit command --- src/main/java/seedu/duke/Ui.java | 5 ----- .../seedu/duke/command/DeleteCommand.java | 15 ++++++++++++-- .../java/seedu/duke/command/EditCommand.java | 20 +++++++++++++++---- src/main/java/seedu/duke/data/Budget.java | 6 ++---- .../java/seedu/duke/data/TransactionList.java | 10 ++++++---- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index f7fc9e2fd..a599d59a8 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -129,11 +129,6 @@ public static void showTransactionAction(String message, String transactionDetai printMessages(message, transactionDetails, budgetInfo); } - // A temporary overload method for backward-compatibility for delete command - public static void showTransactionAction(String message, String transactionDetails) { - printMessages(message, transactionDetails); - } - //@author wcwy /** diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 2fe0355c8..7603a7b7e 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -3,12 +3,15 @@ //@@author brian-vb import seedu.duke.Storage; import seedu.duke.Ui; +import seedu.duke.common.DateFormats; +import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.exception.GlobalInvalidIndexException; import seedu.duke.exception.MoolahException; import seedu.duke.exception.StorageWriteErrorException; import java.io.IOException; +import java.time.LocalDate; import java.util.logging.Level; import java.util.logging.Logger; @@ -95,10 +98,18 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws if ((index > numberOfTransactions) || (index <= 0)) { isInputValid = false; } + + assert index > 0; if (isInputValid) { - String transaction = transactions.deleteTransaction(index); - Ui.showTransactionAction(INFO_DELETE.toString(), transaction); + LocalDate date = transactions.getEntry(index - 1).getDate(); + String transaction = transactions.deleteTransaction(index - 1); + + long addedMonthExpenseSum = transactions.calculateMonthlyTotalExpense(date); + String budgetInfo = Budget.generateBudgetRemainingMessage(addedMonthExpenseSum, true, + DateFormats.retrieveFormattedMonthAndYear(date)); + + Ui.showTransactionAction(INFO_DELETE.toString(), transaction, budgetInfo); deleteLogger.log(Level.INFO, "The requested transaction has been deleted " + "and the UI should display the confirmation message respectively."); storage.writeToFile(transactions.getTransactions()); diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index c16708044..946b70cf0 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -4,6 +4,8 @@ import seedu.duke.Storage; import seedu.duke.Ui; +import seedu.duke.common.DateFormats; +import seedu.duke.data.Budget; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; @@ -186,9 +188,14 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws if (newAmount == 0) { newAmount = entry.getAmount(); } - transactions.deleteTransaction(index); + transactions.deleteTransaction(index - 1); String message = transactions.editExpense(newDescription, newAmount, newCategory, newDate, index); - Ui.showTransactionAction(INFO_EDIT_EXPENSE.toString(), message); + + long addedMonthExpenseSum = transactions.calculateMonthlyTotalExpense(newDate); + String budgetInfo = Budget.generateBudgetRemainingMessage(addedMonthExpenseSum, true, + DateFormats.retrieveFormattedMonthAndYear(newDate)); + + Ui.showTransactionAction(INFO_EDIT_EXPENSE.toString(), message, budgetInfo); editLogger.log(Level.INFO, "The requested transaction has been edited " + "and the UI should display the confirmation message respectively."); } else { @@ -207,9 +214,14 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws if (newAmount == 0) { newAmount = entry.getAmount(); } - transactions.deleteTransaction(index); + transactions.deleteTransaction(index - 1); String message = transactions.editIncome(newDescription, newAmount, newCategory, newDate, index); - Ui.showTransactionAction(INFO_EDIT_INCOME.toString(), message); + + long addedMonthExpenseSum = transactions.calculateMonthlyTotalExpense(newDate); + String budgetInfo = Budget.generateBudgetRemainingMessage(addedMonthExpenseSum, true, + DateFormats.retrieveFormattedMonthAndYear(newDate)); + + Ui.showTransactionAction(INFO_EDIT_INCOME.toString(), message, budgetInfo); editLogger.log(Level.INFO, "The requested transaction has been edited " + "and the UI should display the confirmation message respectively."); } diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index 59f9891f3..a48d4e4a6 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -1,13 +1,11 @@ package seedu.duke.data; import java.time.LocalDate; -import java.time.format.DateTimeFormatter; import static java.lang.Math.abs; import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; -import static seedu.duke.common.DateFormats.MONTH_YEAR_OUTPUT_PATTERN; import static seedu.duke.common.InfoMessages.INFO_COLON_SPACE; import static seedu.duke.common.InfoMessages.INFO_EXCEEDING_BUDGET; import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; @@ -54,7 +52,7 @@ public static void setBudget(long budget) { * * @param totalMonthlyExpense The long value representing the total sum of a monthly expense. * @param withTips The boolean value to indicate whether tips need to be appended behind the message. - * @param date The date of transaction. + * @param monthYear The date of transaction. * @return The message generated based on the budget remained for the given date. */ public static String generateBudgetRemainingMessage(long totalMonthlyExpense, boolean withTips, String monthYear) { @@ -129,7 +127,7 @@ private static String getBudgetLeftMessage(long budgetLeft, boolean hasExceededB if (hasExceededBudget) { assert budgetLeft < 0; // The absolute value of budget left will be the amount of budget exceeded - return INFO_EXCEEDING_BUDGET+ monthAndYear + INFO_COLON_SPACE + INFO_DOLLAR_SIGN + return INFO_EXCEEDING_BUDGET + monthAndYear + INFO_COLON_SPACE + INFO_DOLLAR_SIGN + abs(budgetLeft); } else { assert budgetLeft >= 0; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 48d70e7c4..b33cdeffa 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -28,8 +28,6 @@ public class TransactionList { private static final String SYMBOL_DOLLAR = "$"; private static final String INCOME = "income"; private static final String EXPENSE = "expense"; - private static final String MONTHS = "months"; - private static final String WEEKS = "weeks"; private static final int START = 0; private static final int END = 1; private static final int UNDEFINED_PARAMETER = -1; @@ -83,8 +81,8 @@ public int size() { * @return A string tht states the details of the deleted transaction. */ public String deleteTransaction(int index) { - Transaction transaction = transactions.get(index - 1); - transactions.remove(index - 1); + Transaction transaction = transactions.get(index); + transactions.remove(index); return transaction.toString(); } @@ -449,6 +447,10 @@ public static long calculateMonthlyTotalExpense(LocalDate date) { int month = date.getMonthValue(); int year = date.getYear(); for (Transaction transaction : transactions) { + // As sum of spending is to be calculated, thus only expense type transaction will be considered + if (!(transaction instanceof Expense)) { + continue; + } if (transaction.getDate().getMonthValue() == month && transaction.getDate().getYear() == year) { /* Since the maximum number of transaction is 1000000 and maximum amount of expense is 10000000, From 968b2d9efda23751291c471b74841bf8e8ca5371 Mon Sep 17 00:00:00 2001 From: wcwy Date: Mon, 24 Oct 2022 23:01:26 +0800 Subject: [PATCH 236/416] Remove duplicate declaration for line separator and dollar sign --- src/main/java/seedu/duke/command/AddCommand.java | 2 +- src/main/java/seedu/duke/command/BudgetCommand.java | 2 +- src/main/java/seedu/duke/command/ByeCommand.java | 3 ++- src/main/java/seedu/duke/command/DeleteCommand.java | 3 ++- src/main/java/seedu/duke/command/EditCommand.java | 3 ++- src/main/java/seedu/duke/command/FindCommand.java | 3 ++- src/main/java/seedu/duke/command/HelpCommand.java | 3 ++- src/main/java/seedu/duke/command/ListCommand.java | 2 +- src/main/java/seedu/duke/command/PurgeCommand.java | 2 +- src/main/java/seedu/duke/command/StatsCommand.java | 4 ++-- src/main/java/seedu/duke/common/InfoMessages.java | 12 ++++++------ src/main/java/seedu/duke/data/Budget.java | 12 ++++++------ src/main/java/seedu/duke/data/TransactionList.java | 10 +++++----- .../java/seedu/duke/data/transaction/Category.java | 6 ++++-- .../seedu/duke/data/transaction/Transaction.java | 4 ++-- 15 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/main/java/seedu/duke/command/AddCommand.java b/src/main/java/seedu/duke/command/AddCommand.java index 44e9f5b2c..723e83c40 100644 --- a/src/main/java/seedu/duke/command/AddCommand.java +++ b/src/main/java/seedu/duke/command/AddCommand.java @@ -30,13 +30,13 @@ import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; import static seedu.duke.common.InfoMessages.INFO_ADD_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_ADD_INCOME; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; /** * Represents an add command object that will execute the operations for Add command. */ public class AddCommand extends Command { //@@author chinhan99 - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "ADD"; diff --git a/src/main/java/seedu/duke/command/BudgetCommand.java b/src/main/java/seedu/duke/command/BudgetCommand.java index 3e1af70d7..1a4e6f2df 100644 --- a/src/main/java/seedu/duke/command/BudgetCommand.java +++ b/src/main/java/seedu/duke/command/BudgetCommand.java @@ -12,12 +12,12 @@ import java.io.IOException; import static seedu.duke.command.CommandTag.COMMAND_TAG_BUDGET_AMOUNT; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; /** * Represents a budget command object that will set the user's monthly budget on the Budget command. */ public class BudgetCommand extends Command { - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "BUDGET"; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index 94491fb4b..e09e67705 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -6,13 +6,14 @@ import seedu.duke.data.TransactionList; import static seedu.duke.common.InfoMessages.INFO_EXIT; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; + /** * Represents a bye command object that will execute the operations for Bye command. */ public class ByeCommand extends Command { //@@author paullowse - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "BYE"; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 7603a7b7e..588c15624 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -17,13 +17,14 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; import static seedu.duke.common.InfoMessages.INFO_DELETE; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; + /** * Represents a delete command object that will execute the operations for Delete command. */ public class DeleteCommand extends Command { //@@author brian-vb - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "DELETE"; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 946b70cf0..e2888e91c 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -28,13 +28,14 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; import static seedu.duke.common.InfoMessages.INFO_EDIT_EXPENSE; import static seedu.duke.common.InfoMessages.INFO_EDIT_INCOME; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; + /** * Represents an edit command object that will execute the operations for Edit command. */ public class EditCommand extends Command { //@@author brian-vb - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "EDIT"; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 484bf7a6d..033ce6d63 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -9,13 +9,14 @@ import static seedu.duke.common.InfoMessages.INFO_LIST_FILTERED; import static seedu.duke.common.InfoMessages.INFO_LIST_UNFILTERED; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; + /** * Represents a find command object that will execute the operations for Find command. */ public class FindCommand extends Command { //@@author chydarren - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "FIND"; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/HelpCommand.java b/src/main/java/seedu/duke/command/HelpCommand.java index 8dfdb3b91..532d49a0d 100644 --- a/src/main/java/seedu/duke/command/HelpCommand.java +++ b/src/main/java/seedu/duke/command/HelpCommand.java @@ -6,13 +6,14 @@ import seedu.duke.data.TransactionList; import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; + /** * Represents a help command object that will execute the operations for Help command. */ public class HelpCommand extends Command { //@@author wcwy - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "HELP"; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 42a13fa2e..55d77389b 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -22,13 +22,13 @@ import static seedu.duke.common.InfoMessages.INFO_LIST; import static seedu.duke.common.InfoMessages.INFO_LIST_EMPTY; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; /** * Represents a list command object that will execute the operations for List command. */ public class ListCommand extends ListAndStatsCommand { //@@author chydarren - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "LIST"; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/PurgeCommand.java b/src/main/java/seedu/duke/command/PurgeCommand.java index c70a705ae..4839e4f95 100644 --- a/src/main/java/seedu/duke/command/PurgeCommand.java +++ b/src/main/java/seedu/duke/command/PurgeCommand.java @@ -15,13 +15,13 @@ import static seedu.duke.common.InfoMessages.INFO_PURGE_ABORT; import static seedu.duke.common.InfoMessages.INFO_PURGE_EMPTY; import static seedu.duke.common.InfoMessages.INFO_PURGE_WARNING; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; /** * Represents a purge command object that will execute the operations for Purge command. */ public class PurgeCommand extends Command { //@@author brian-vb - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "PURGE"; // The description for the usage of command diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 58d4c601a..4ffe8e13b 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -24,13 +24,13 @@ import static seedu.duke.common.InfoMessages.INFO_STATS_SAVINGS; import static seedu.duke.common.InfoMessages.INFO_STATS_SUMMARY_HEADER; import static seedu.duke.common.InfoMessages.INFO_STATS_TIME; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; /** * Represents a get command object that will execute the operations for Get command. */ public class StatsCommand extends ListAndStatsCommand { //@@author paullowse - private static final String LINE_SEPARATOR = System.lineSeparator(); // The command word used to trigger the execution of Moolah Manager's operations public static final String COMMAND_WORD = "STATS"; // The description for the usage of command @@ -195,7 +195,7 @@ public void statsTypeTimeSavings(TransactionList transactions) { ArrayList amounts; amounts = transactions.processTimeSummaryStats(timeTransactions); - String incomeMessage = INFO_STATS_SUMMARY_HEADER + LINE_SEPARATOR + String incomeMessage = INFO_STATS_SUMMARY_HEADER + LINE_SEPARATOR.toString() + INFO_STATS_INCOME + amounts.get(0); String expensesMessage = INFO_STATS_EXPENSES + amounts.get(1); String savingsMessage = INFO_STATS_SAVINGS + amounts.get(2); diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index b04398c15..eaf73e5a4 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -4,13 +4,13 @@ * Provides enum variables for storing custom program information messages. */ public enum InfoMessages { + LINE_SEPARATOR(System.lineSeparator()), + SPACE(" "), + FULL_STOP("."), + FULL_STOP_SPACE(". "), + COLON_SPACE(": "), + DOLLAR_SIGN("$"), INFO_DIVIDER("____________________________________________________________"), - INFO_LINE_SEPARATOR(System.lineSeparator()), - INFO_SPACE(" "), - INFO_FULL_STOP("."), - INFO_FULL_STOP_SPACE(". "), - INFO_COLON_SPACE(": "), - INFO_DOLLAR_SIGN("$"), INFO_ADD_EXPENSE("I have added the following Expense transaction:"), INFO_ADD_INCOME("I have added the following Income transaction:"), INFO_EDIT_EXPENSE("I have edited the following Expense transaction:"), diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index a48d4e4a6..15dce06aa 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -6,13 +6,13 @@ import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; -import static seedu.duke.common.InfoMessages.INFO_COLON_SPACE; import static seedu.duke.common.InfoMessages.INFO_EXCEEDING_BUDGET; import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; import static seedu.duke.common.InfoMessages.INFO_BUDGET_EXCEEDED_TIPS; import static seedu.duke.common.InfoMessages.INFO_BUDGET_NOT_EXCEEDED_TIPS; -import static seedu.duke.common.InfoMessages.INFO_FULL_STOP_SPACE; -import static seedu.duke.common.InfoMessages.INFO_DOLLAR_SIGN; +import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; +import static seedu.duke.common.InfoMessages.COLON_SPACE; +import static seedu.duke.common.InfoMessages.FULL_STOP_SPACE; //@@author wcwy @@ -62,7 +62,7 @@ public static String generateBudgetRemainingMessage(long totalMonthlyExpense, bo String message = getBudgetLeftMessage(budgetLeft, hasExceededBudget, monthYear); if (withTips) { - message += INFO_FULL_STOP_SPACE + getMoneyManagingTips(hasExceededBudget); + message += FULL_STOP_SPACE + getMoneyManagingTips(hasExceededBudget); } return message; @@ -127,11 +127,11 @@ private static String getBudgetLeftMessage(long budgetLeft, boolean hasExceededB if (hasExceededBudget) { assert budgetLeft < 0; // The absolute value of budget left will be the amount of budget exceeded - return INFO_EXCEEDING_BUDGET + monthAndYear + INFO_COLON_SPACE + INFO_DOLLAR_SIGN + return INFO_EXCEEDING_BUDGET + monthAndYear + COLON_SPACE + DOLLAR_SIGN + abs(budgetLeft); } else { assert budgetLeft >= 0; - return INFO_REMAINING_BUDGET + monthAndYear + INFO_COLON_SPACE + INFO_DOLLAR_SIGN + return INFO_REMAINING_BUDGET + monthAndYear + COLON_SPACE + DOLLAR_SIGN + budgetLeft; } } diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index b33cdeffa..c77d7c4f4 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -15,6 +15,8 @@ import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES_HEADER; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; +import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; /** * Represents a list of transactions added by the user into the application. @@ -25,13 +27,11 @@ public class TransactionList { //@@author chydarren private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; - private static final String SYMBOL_DOLLAR = "$"; private static final String INCOME = "income"; private static final String EXPENSE = "expense"; private static final int START = 0; private static final int END = 1; private static final int UNDEFINED_PARAMETER = -1; - private static final String LINE_SEPARATOR = System.lineSeparator(); //@@author chinhan99 private static ArrayList transactions; @@ -216,7 +216,7 @@ public String findTransactions(String keywords) { for (Transaction transaction : transactions) { // Includes only transactions that contain the keywords used in the search expression if (transaction.toString().contains(keywords)) { - transactionsList += transaction + LINE_SEPARATOR; + transactionsList += transaction + LINE_SEPARATOR.toString(); } } return transactionsList; @@ -257,7 +257,7 @@ public String listCategoricalSavings() { // Formats every entry in the hashmap into a categorical savings list for (HashMap.Entry entry : categoricalSavings.entrySet()) { categoricalSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getKey(), - POSTFIX_CATEGORY, SYMBOL_DOLLAR, entry.getValue(), LINE_SEPARATOR); + POSTFIX_CATEGORY, DOLLAR_SIGN, entry.getValue(), LINE_SEPARATOR); } return categoricalSavingsList; @@ -293,7 +293,7 @@ public String listTimeStats(ArrayList timeTransactions, int year, i // Formats every entry in the hashmap into a categorical savings list for (Transaction entry : timeTransactions) { timeSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getCategory(), - POSTFIX_CATEGORY, SYMBOL_DOLLAR, entry.getAmount(), LINE_SEPARATOR); + POSTFIX_CATEGORY, DOLLAR_SIGN, entry.getAmount(), LINE_SEPARATOR); } return timeSavingsList; diff --git a/src/main/java/seedu/duke/data/transaction/Category.java b/src/main/java/seedu/duke/data/transaction/Category.java index 0bdc9d19e..15a772fdc 100644 --- a/src/main/java/seedu/duke/data/transaction/Category.java +++ b/src/main/java/seedu/duke/data/transaction/Category.java @@ -1,10 +1,12 @@ package seedu.duke.data.transaction; + +import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; + public class Category { //@@author chydarren private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; - private static final String SYMBOL_DOLLAR = "$"; private String category; private int amount; @@ -32,6 +34,6 @@ public void setAmount(int amount) { @Override public String toString() { return String.format("%s%s%s %s%d", PREFIX_CATEGORY, category, POSTFIX_CATEGORY, - SYMBOL_DOLLAR, amount); + DOLLAR_SIGN, amount); } } \ No newline at end of file diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index da94296b3..0ff60158a 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -4,6 +4,7 @@ import java.time.format.DateTimeFormatter; import static seedu.duke.common.DateFormats.DATE_OUTPUT_PATTERN; +import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; /** * Represents a transaction made by the user, which could be either an income or an expense. @@ -12,7 +13,6 @@ public abstract class Transaction { //@@author chydarren private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; - private static final String SYMBOL_DOLLAR = "$"; private static final String SYMBOL_PIPE = "|"; private static final String TEXT_AT = "at"; private static final String TEXT_DESCRIPTION = "Description:"; @@ -84,7 +84,7 @@ public String printFormattedCategory() { @Override public String toString() { - return String.format("%s %s%d %s %s %s %s %s", printFormattedCategory(), SYMBOL_DOLLAR, + return String.format("%s %s%d %s %s %s %s %s", printFormattedCategory(), DOLLAR_SIGN, amount, TEXT_AT, printFormattedDate(), SYMBOL_PIPE, TEXT_DESCRIPTION, description); } From fb500fa1a6997223c898fc37428cfd16b44f3775 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Mon, 24 Oct 2022 23:05:32 +0800 Subject: [PATCH 237/416] Add Storage description for Developer Guide --- docs/DeveloperGuide.md | 37 +++++++-- docs/diagram/StorageComponent.puml | 76 +++++++++++++++++++ docs/images/StorageComponentClassDiagram.png | Bin 0 -> 158333 bytes 3 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 docs/diagram/StorageComponent.puml create mode 100644 docs/images/StorageComponentClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index ce4cc4e73..102a25f5e 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -128,8 +128,22 @@ under each command section below. _Written by: Chia Thin Hong_ ### Storage Component +The `Storage` component is a standalone class. It utilises its sub-methods and methods from external classes to perform it's read and write functions. -_Written by: Author name_ +The structure of `Storage` can be seen below. +

+ +
+ Simplified Class Diagram for Storage Component +

+ +1. `Duke` initializes `Storage` and `Storage#initializeFile` is called. +2. During the initialization , parser methods from `CommandParser` and `ParameterParser` would be used to process the entries within `Duke.txt`. +Methods from `Budget` and `TransactionList` would be used for the storage of `Budget` amount and `TransactionList` entries into the program. +3. `TransactionList` is returned to `Duke` after the storage of entries within `Duke.txt`. +4. After initialization and upon user input, `Command` classes such as `AddCommand`can call for `Storage#writeToFile` method in order to update the contents within `Duke.txt`. + +_Written by: Yong Chin Han_ ### Parser Component The Parser component comprises of two main parsers: `CommandParser` and `ParameterParser`. Together, both these @@ -276,7 +290,7 @@ These are the important operations performed within the `AddCommand` class, with It is used externally by ParameterParser to verify if the user input contains the mandatory command tags, to correctly store the Transaction object in the program. -Written by: Yong Chin Han +_Written by: Yong Chin Han_ ### Edit Command @@ -450,12 +464,25 @@ This is how the command works: _Written by: Brian Wong Yun Long_ ### Storage Operations +The Storage class is a standalone class that contains methods used for the storage of Transaction entries and the Budget value. -#### Reading From a File +The class is first called by `Duke` during the initialising of the `TransactionList`. In this process, duke.txt's existance will be verified. +1. If the file does not exist, an empty `Duke.txt` file would be created for the program to use. +2. If the file exists, it's values would be parsed to verify if they have been corrupted. If corrupted, the storage of values would halt and error messages would be shown to prompt user to correct file issues. +Else, the values would update the program's `Budget` and the entries in `TransactionList`without any issues. -#### Writing To a File +#### Reading From Duke.txt +This specific operation is done during the initializing of the program. The `Budget` value and `TransactionList` entries would be parsed before their values are added into the program. +`Storage#initializeFile` is called by `Duke`. +`Storage#checkIfFileExist` is used to check if `Duke.txt` exists, and creates a new `Duke.txt` file if it does not exist. +`Storage#storeFileValuesLocally` uses sub-methods and methods from external classes to parse each line entry within `Duke.txt` and store the values in the program. -_Written by: Author name_ +#### Writing To Duke.txt +This operation is done whenever the `TransactionList` entries or `Budget` value is changed via any of the `Command` classes; +(e.g. Add, Delete, Purge , Edit and Budget commands). +The method `Storage#writeToFile` is used to update changes in `Budget` or `TransactionList`. + +_Written by: Yong Chin Han_ ### Logging Operations diff --git a/docs/diagram/StorageComponent.puml b/docs/diagram/StorageComponent.puml new file mode 100644 index 000000000..7bbf1df6b --- /dev/null +++ b/docs/diagram/StorageComponent.puml @@ -0,0 +1,76 @@ +@startuml +'https://plantuml.com/sequence-diagram + +hide circle +skinparam classAttributeIconSize 0 + +class Storage{ + - DIRECTORY_PATH: String {static} + - FILE_PATH: String {static} + - DELIMITER: String {static} + - NUMBER_OF_STORED_PARAMETERS: int {static} + - TENS: int {static} + - HUNDREDS: int {static} + - THOUSANDS: int {static} + - storedTransactions: TransactionList {static} + + + - initializeFile(): TransactionList{static} + - checkIfFileExist(): File{static} + - storeBudgetLocally(){static} + - storeTransactionsLocally(){static} + - storeFileValuesLocally(){static} + - synthesizeDateString(): String{static} + - writeToFile() + + + +} +class "TransactionList"{ ++ addExpenseDuringStorage() ++ addIncomeDuringStorage() +} + +class "ParameterParser"{ ++ parse() ++parseBudgetTag() +} +class "CommandParser"{ ++ getCommand():Command + +} + +class "Budget"{ ++ getBudget():long ++ setBudget() +} + +class "AddCommand"{ ++ execute() +} +class "EditCommand"{ ++ execute() +} +class "DeleteCommand"{ ++ execute() +} +class "PurgeCommand"{ ++ execute() +} +class "BudgetCommand"{ ++ execute() +} + +Duke <.. Storage: Initializes > +Storage --> "ParameterParser": uses methods from > +Storage --> "CommandParser": uses methods from > +Storage --> "TransactionList":uses methods from > +Storage --> "Budget":uses methods from > +Storage <-right- "AddCommand":writes to < +Storage <-- "EditCommand":writes to < +Storage <-right- "DeleteCommand":writes to < +Storage <-left- "PurgeCommand":writes to < +Storage <-left- "BudgetCommand":writes to < + + +@enduml \ No newline at end of file diff --git a/docs/images/StorageComponentClassDiagram.png b/docs/images/StorageComponentClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..b0113602c7473a339736a833b91075e9a16abcef GIT binary patch literal 158333 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

Uuq-L2;}NQIlp6mLYRyw_M@0_JQ#v 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

Operations related to modifying the list of transactions are defined under this class. * These operations include adding, listing, modifying, deleting and purging. */ public class TransactionList { - //@@author chydarren private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; private static final String INCOME = "income"; private static final String EXPENSE = "expense"; - private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; - private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; + private static final int HUNDRED_PERCENT = 100; + private static final int SEVENTY_FIVE_PERCENT = 75; + private static final int FIFTY_PERCENT = 50; + private static final int TWENTY_FIVE_PERCENT = 25; private static final int START = 0; private static final int END = 1; private static final int UNDEFINED_PARAMETER = -1; @@ -253,9 +263,9 @@ public HashMap processCategoricalSavings(HashMap processMonthlyExpenditure(HashMap monthlyExpenditure) - throws InputTransactionInvalidTypeException { + public HashMap processMonthlyExpenditure(HashMap monthlyExpenditure) { for (Transaction transaction : transactions) { String date = transaction.getDate().format(DateTimeFormatter.ofPattern(DATE_MONTH_PATTERN.toString())); - int income = 0; - int expense = 0; - - // Checks whether transaction is Income or Expense and adds to respective amount - try { - if (isTransactionInstance(transaction, CLASS_TYPE_INCOME)) { - income = transaction.getAmount(); - } else { - expense = - transaction.getAmount(); - } - } catch (ClassNotFoundException e) { - throw new InputTransactionInvalidTypeException(); + int income = MIN_AMOUNT_VALUE; + int expense = MIN_AMOUNT_VALUE; + + // Checks whether transaction is Income or Expense and places in respective amount + if (transaction instanceof Income) { + income = transaction.getAmount(); + } else { + expense = transaction.getAmount(); } // Creates a new month and year with starter amounts if not exists in hashmap if (!monthlyExpenditure.containsKey(date)) { - monthlyExpenditure.put(date, new int[]{income, expense, income + expense}); + monthlyExpenditure.put(date, new int[]{income, expense, income - expense}); continue; } @@ -307,7 +312,7 @@ public HashMap processMonthlyExpenditure(HashMap m int updatedExpense = monthlyExpenditure.get(date)[1] + expense; monthlyExpenditure.put(date, new int[]{updatedIncome, updatedExpense, - updatedIncome + updatedExpense}); + updatedIncome - updatedExpense}); } return monthlyExpenditure; @@ -316,30 +321,31 @@ public HashMap processMonthlyExpenditure(HashMap m /** * Gets comment related to spending habit each month. * - * @param savingsPercentage The percentage savings based against the total income. + * @param income Income value of the transaction in numerical form. + * @param savings Savings value of the transaction in numerical form. * @return A string containing the comment related to the spending habit for the month. */ - public String getSpendingHabitComment(int savingsPercentage) { - System.out.println(savingsPercentage); - if (savingsPercentage >= 100) { - return INFO_STATS_MONTHS_COMMENT_ONE.toString(); - } else if (savingsPercentage >= 75) { - return INFO_STATS_MONTHS_COMMENT_TWO.toString(); - } else if (savingsPercentage >= 50) { - return INFO_STATS_MONTHS_COMMENT_THREE.toString(); - } else if (savingsPercentage >= 25) { - return INFO_STATS_MONTHS_COMMENT_FOUR.toString(); + public String getSpendingHabitComment(int income, int savings) { + int savingsPercentage = HUNDRED_PERCENT * savings / income; + + if (savingsPercentage >= HUNDRED_PERCENT) { + return INFO_STATS_HABIT_VERY_HIGH_SAVINGS.toString(); + } else if (savingsPercentage >= SEVENTY_FIVE_PERCENT) { + return INFO_STATS_HABIT_HIGH_SAVINGS.toString(); + } else if (savingsPercentage >= FIFTY_PERCENT) { + return INFO_STATS_HABIT_MEDIUM_SAVINGS.toString(); + } else if (savingsPercentage >= TWENTY_FIVE_PERCENT) { + return INFO_STATS_HABIT_LOW_SAVINGS.toString(); } - return INFO_STATS_MONTHS_COMMENT_FIVE.toString(); + return INFO_STATS_HABIT_VERY_LOW_SAVINGS.toString(); } /** - * Calculates and stores total expenditure for each month and year into a hashmap. + * Formats the hashmap of monthly expenditure into a monthly expenditure list. * - * @return A hashmap containing all month-expenditure pair for total expenditure and savings. - * @throws InputTransactionInvalidTypeException If class type cannot be found in the packages. + * @return A string that represents the formatted monthly expenditure list. */ - public String listMonthlyExpenditure() throws InputTransactionInvalidTypeException { + public String listMonthlyExpenditure() { String monthlyExpenditureList = ""; HashMap monthlyExpenditure = new HashMap<>(); // Adds each amount from transactions list to the month and year in monthly expenditure hashmap @@ -349,16 +355,14 @@ public String listMonthlyExpenditure() throws InputTransactionInvalidTypeExcepti for (HashMap.Entry entry : monthlyExpenditure.entrySet()) { monthlyExpenditureList += String.format("%s%s%s%s", PREFIX_CATEGORY, entry.getKey(), POSTFIX_CATEGORY, LINE_SEPARATOR); - monthlyExpenditureList += String.format("%s%s%s%s", "Income: ", DOLLAR_SIGN, entry.getValue()[0], - LINE_SEPARATOR); - monthlyExpenditureList += String.format("%s%s%s%s", "Expense: ", DOLLAR_SIGN, entry.getValue()[1], - LINE_SEPARATOR); - monthlyExpenditureList += String.format("%s%s%s%s", "Savings: ", DOLLAR_SIGN, entry.getValue()[2], - LINE_SEPARATOR, LINE_SEPARATOR); - - int savingsPercentage = 100 * entry.getValue()[2] / entry.getValue()[0]; + monthlyExpenditureList += String.format("%s%s%s%s%s", INFO_INCOME, COLON_SPACE, DOLLAR_SIGN, + entry.getValue()[0], LINE_SEPARATOR); + monthlyExpenditureList += String.format("%s%s%s%s%s", INFO_EXPENSE, COLON_SPACE, DOLLAR_SIGN, + entry.getValue()[1], LINE_SEPARATOR); + monthlyExpenditureList += String.format("%s%s%s%s%s", INFO_SAVINGS, COLON_SPACE, DOLLAR_SIGN, + entry.getValue()[2], LINE_SEPARATOR); monthlyExpenditureList += String.format("%s%s%s%s", "Spending Habit: ", - getSpendingHabitComment(savingsPercentage), LINE_SEPARATOR, LINE_SEPARATOR); + getSpendingHabitComment(entry.getValue()[2], entry.getValue()[0]), LINE_SEPARATOR, LINE_SEPARATOR); } return monthlyExpenditureList; @@ -378,26 +382,26 @@ public String listMonthlyExpenditure() throws InputTransactionInvalidTypeExcepti */ public String listTimeStats(ArrayList timeTransactions, int year, int month, String period, int number) { - String timeSavingsList = ""; + String timeInsightsList = ""; if (period != null && number != UNDEFINED_PARAMETER) { - timeSavingsList += "The past " + number + " " + period + ": " + LINE_SEPARATOR + LINE_SEPARATOR + timeInsightsList += "The past " + number + " " + period + ": " + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; } else if (month == UNDEFINED_PARAMETER) { - timeSavingsList += "Year: " + year + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + timeInsightsList += "Year: " + year + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; } else { - timeSavingsList += "Year: " + year + ", Month: " + month + timeInsightsList += "Year: " + year + ", Month: " + month + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; } - // Formats every entry in the hashmap into a categorical savings list + // Formats every entry in the hashmap into a time insights list for (Transaction entry : timeTransactions) { - timeSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getCategory(), + timeInsightsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getCategory(), POSTFIX_CATEGORY, DOLLAR_SIGN, entry.getAmount(), LINE_SEPARATOR); } - return timeSavingsList; + return timeInsightsList; } /** @@ -407,8 +411,8 @@ public String listTimeStats(ArrayList timeTransactions, int year, i * @return An amount arraylist of Expense and Income. */ public ArrayList processTimeSummaryStats(ArrayList timeTransactions) { - int timeExpense = 0; - int timeIncome = 0; + int timeExpense = MIN_AMOUNT_VALUE; + int timeIncome = MIN_AMOUNT_VALUE; for (Transaction entry : timeTransactions) { String category = entry.getType(); if (category.equals(EXPENSE)) { From f6af3ab15a9a866dc638dbb94414fea824724645 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 09:07:14 +0800 Subject: [PATCH 245/416] Update EXPECTED.TXT to reflect minor change to the help information for Stats command --- text-ui-test/EXPECTED.TXT | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 50d861d04..9611765b3 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -107,8 +107,8 @@ Command Word: STATS To get statistics of the transactions such as the total savings for each category, summary of expenditure over a time period. Usage: stats s/STATS_TYPE [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER] Parameters information: -STATISTICS_TYPE: The type of statistics to be generated. Only "categories", "months" or "time" is accepted.(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. -(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted.This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. +STATISTICS_TYPE: The type of statistics to be generated. Only "categorical_savings", "monthly_expenditure" or "time_insights" is accepted. +(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. (Optional) PERIOD: Period of the transaction. Only "weeks" or "months" is accepted. Note that period must be accompanied by a number to backdate from. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. (Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note thatnumber must be accompanied by a period that represents weeks or months. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. From dbc71350904a38efc7c1fd60f93ec444c868ab7e Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 09:14:41 +0800 Subject: [PATCH 246/416] Update input.txt and EXPECTED.TXT to reflect change for StatsType tag in Stats command --- text-ui-test/EXPECTED.TXT | 19 ++++++++++++++++++- text-ui-test/input.txt | 9 +++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 9611765b3..038302375 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -109,6 +109,7 @@ Usage: stats s/STATS_TYPE [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER] Parameters information: STATISTICS_TYPE: The type of statistics to be generated. Only "categorical_savings", "monthly_expenditure" or "time_insights" is accepted. (Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. +(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted.This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. (Optional) PERIOD: Period of the transaction. Only "weeks" or "months" is accepted. Note that period must be accompanied by a number to backdate from. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. (Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note thatnumber must be accompanied by a period that represents weeks or months. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. @@ -283,11 +284,27 @@ Here are your transaction records: ____________________________________________________________ ____________________________________________________________ -Here are the total savings for each category: +Here are your requested statistics for the categorical_savings type: [transport] $1 [salary] $2000 [food] $20 +____________________________________________________________ +____________________________________________________________ +Here are your requested statistics for the monthly_expenditure type: +[Sep 2022] +Income: $2000 +Expense: $20 +Savings: $1980 +Spending Habit: Excellent! You saved quite a lot this month. + +[Oct 2022] +Income: $0 +Expense: $1 +Savings: $-1 +Spending Habit: Oops, you spent too much. Do manage your expenses within your constraints. + + ____________________________________________________________ ____________________________________________________________ Keyword(s) for your search expression missing, please check your input! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 21e200b74..2e97a06de 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -19,9 +19,9 @@ stats blablabla stats / stats s/ stats s/invalid -stats s/categories blablabla -stats s/categories s/categories -stats s/categories +stats s/categorical_savings blablabla +stats s/categorical_savings s/categorical_savings +stats s/categorical_savings list add t/expense c/food a/20 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary @@ -42,7 +42,8 @@ list c/food d/30092022 list c/food d/13092022 list t/income d/ list t/income d/30092022 -stats s/categories +stats s/categorical_savings +stats s/monthly_expenditure find find bus_fare find TOTALLY_NON_EXISTING_STRING From 2d6f4658eb3b6417214f595b481e1ce9066433e0 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 25 Oct 2022 10:25:35 +0800 Subject: [PATCH 247/416] Add Stats fix --- .../java/seedu/duke/command/StatsCommand.java | 13 ++-- .../java/seedu/duke/data/TransactionList.java | 10 +-- text-ui-test/EXPECTED.TXT | 68 +++++++++++++++++++ text-ui-test/input.txt | 8 +++ 4 files changed, 91 insertions(+), 8 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index ab142fcf5..86f57393a 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -1,23 +1,25 @@ package seedu.duke.command; //@@author paullowse + import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; +import seedu.duke.exception.GlobalMissingTagException; import seedu.duke.exception.GlobalUnsupportedTagException; -import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; +import seedu.duke.exception.StatsInvalidTypeException; import java.util.ArrayList; import java.util.logging.Level; import java.util.logging.Logger; -import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_MONTH; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; +import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; import static seedu.duke.common.InfoMessages.COLON_SPACE; import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; import static seedu.duke.common.InfoMessages.INFO_EXPENSE; @@ -126,9 +128,12 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws statsLogger.log(Level.INFO, "Entering execution of the Stats command."); // Throws an unsupported tag exception if non-time_insights tag is using date intervals tags - if (!statsType.equals(TIME_INSIGHTS) && (containMonthYear() != FALSE - || containPeriodNumber() != FALSE)) { + if (!statsType.equals(TIME_INSIGHTS) && (containMonthYear() != FALSE || containPeriodNumber() != FALSE)) { + // Throws an unsupported tag exception if non-time_insights tag is using date intervals tags throw new GlobalUnsupportedTagException(); + } else if (statsType.equals(TIME_INSIGHTS) && containMonthYear() == FALSE && containPeriodNumber() == FALSE) { + // Throws a missing mandatory tag(s) exception if there are no related tags for time_insights + throw new GlobalMissingTagException(); } // Checks if there are any error in the tag combinations related to DateIntervals diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 0e70c7ec4..bcc33470d 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -395,11 +395,13 @@ public String listTimeStats(ArrayList timeTransactions, int year, i + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; } + String categoricalList = listCategoricalSavings(); + timeInsightsList += categoricalList; // Formats every entry in the hashmap into a time insights list - for (Transaction entry : timeTransactions) { - timeInsightsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getCategory(), - POSTFIX_CATEGORY, DOLLAR_SIGN, entry.getAmount(), LINE_SEPARATOR); - } + //for (Transaction entry : timeTransactions) { + // timeInsightsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getCategory(), + // POSTFIX_CATEGORY, DOLLAR_SIGN, entry.getAmount(), LINE_SEPARATOR); + //} return timeInsightsList; } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 038302375..75a81536d 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -305,6 +305,74 @@ Savings: $-1 Spending Habit: Oops, you spent too much. Do manage your expenses within your constraints. +____________________________________________________________ +____________________________________________________________ +Here are the total savings and expenses for +Year: 2022 + +-----CATEGORIES----- +[transport] $1 +[salary] $2000 +[food] $20 + +-----EXPENDITURE----- +Income: $2000 +Expense: $21 +Savings: $1979 +____________________________________________________________ +____________________________________________________________ +Here are the total savings and expenses for +Year: 2022, Month: 10 + +-----CATEGORIES----- +[transport] $1 +[salary] $2000 +[food] $20 + +-----EXPENDITURE----- +Income: $0 +Expense: $1 +Savings: $-1 +____________________________________________________________ +____________________________________________________________ +Month tag must be accompanied by a year tag, please check your input! +____________________________________________________________ +____________________________________________________________ +Here are the total savings and expenses for +The past 3 weeks: + +-----CATEGORIES----- +[transport] $1 +[salary] $2000 +[food] $20 + +-----EXPENDITURE----- +Income: $0 +Expense: $0 +Savings: $0 +____________________________________________________________ +____________________________________________________________ +Here are the total savings and expenses for +The past 4 months: + +-----CATEGORIES----- +[transport] $1 +[salary] $2000 +[food] $20 + +-----EXPENDITURE----- +Income: $2000 +Expense: $20 +Savings: $1980 +____________________________________________________________ +____________________________________________________________ +Type of period given is invalid, please check your input! +____________________________________________________________ +____________________________________________________________ +Period and number tags must be provided together, please check your input! +____________________________________________________________ +____________________________________________________________ +Period and number tags must be provided together, please check your input! ____________________________________________________________ ____________________________________________________________ Keyword(s) for your search expression missing, please check your input! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 2e97a06de..2d9085e64 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -44,6 +44,14 @@ list t/income d/ list t/income d/30092022 stats s/categorical_savings stats s/monthly_expenditure +stats s/time_insights y/2022 +stats s/time_insights y/2022 m/10 +stats s/time_insights m/10 +stats s/time_insights p/weeks n/3 +stats s/time_insights p/months n/4 +stats s/time_insights p/mont n/4 +stats s/time_insights n/4 +stats s/time_insights p/months find find bus_fare find TOTALLY_NON_EXISTING_STRING From 769bf38ef02f40e82397c9e3553496857b2aabd8 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 13:01:22 +0800 Subject: [PATCH 248/416] Add JUnit Tests for V2.0 Part I --- .../java/seedu/duke/command/FindCommand.java | 2 +- .../duke/command/ListAndStatsCommand.java | 14 +-- .../java/seedu/duke/command/StatsCommand.java | 3 +- .../seedu/duke/command/FindCommandTest.java | 32 +++++ .../seedu/duke/command/ListCommandTest.java | 111 ++++++++++++++++++ .../seedu/duke/command/StatsCommandTest.java | 72 ++++++++++++ .../seedu/duke/parser/CommandParserTest.java | 18 ++- .../duke/parser/ParameterParserTest.java | 41 +++++-- 8 files changed, 271 insertions(+), 22 deletions(-) create mode 100644 src/test/java/seedu/duke/command/FindCommandTest.java create mode 100644 src/test/java/seedu/duke/command/ListCommandTest.java create mode 100644 src/test/java/seedu/duke/command/StatsCommandTest.java diff --git a/src/main/java/seedu/duke/command/FindCommand.java b/src/main/java/seedu/duke/command/FindCommand.java index 033ce6d63..e47865408 100644 --- a/src/main/java/seedu/duke/command/FindCommand.java +++ b/src/main/java/seedu/duke/command/FindCommand.java @@ -55,7 +55,7 @@ public FindCommand(String keywords) { * @param keywords A string containing the keywords used in the search expression. * @throws FindTransactionMissingKeywordsException If a user does not enter a search expression for Find. */ - public void checkFindFormat(String keywords) throws FindTransactionMissingKeywordsException { + public static void checkFindFormat(String keywords) throws FindTransactionMissingKeywordsException { if (keywords.isBlank()) { throw new FindTransactionMissingKeywordsException(); } diff --git a/src/main/java/seedu/duke/command/ListAndStatsCommand.java b/src/main/java/seedu/duke/command/ListAndStatsCommand.java index 93271a5c1..d015562a0 100644 --- a/src/main/java/seedu/duke/command/ListAndStatsCommand.java +++ b/src/main/java/seedu/duke/command/ListAndStatsCommand.java @@ -28,10 +28,10 @@ public abstract class ListAndStatsCommand extends Command { private static Logger listStatsLogger = Logger.getLogger(ListAndStatsCommand.class.getName()); //@@author paullowse - public int month; - public int year; - public String period; - public int number; + public static int month; + public static int year; + public static String period; + public static int number; public ListAndStatsCommand() { this.month = UNDEFINED_PARAMETER; @@ -72,7 +72,7 @@ public void setGlobalPeriod(String period) { * @return 1 if both tags are given, 2 if only year is given, 3 if only month is given, * 0 if both are not given. */ - public int containMonthYear() { + public static int containMonthYear() { if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { return CONTAIN_BOTH; } else if (year != UNDEFINED_PARAMETER) { @@ -89,7 +89,7 @@ public int containMonthYear() { * * @return 1 if both tags are given, 2 if either period/number is given, 0 if both are not given. */ - public int containPeriodNumber() { + public static int containPeriodNumber() { if (period != null && number != UNDEFINED_PARAMETER) { return CONTAIN_BOTH; } else if (period != null || number != UNDEFINED_PARAMETER) { @@ -103,7 +103,7 @@ public int containPeriodNumber() { * * @throws MoolahException If any of the below exception conditions are met. */ - public void parseDateIntervalsTags() throws MoolahException { + public static void parseDateIntervalsTags() throws MoolahException { if (containMonthYear() != FALSE && containPeriodNumber() != FALSE) { // Throws an unsupported tag exception if tags are not supposed to be used together listStatsLogger.log(Level.WARNING, "Exception occurred as an invalid combination " diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 86f57393a..963f3493e 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -127,7 +127,6 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws statsLogger.setLevel(Level.SEVERE); statsLogger.log(Level.INFO, "Entering execution of the Stats command."); - // Throws an unsupported tag exception if non-time_insights tag is using date intervals tags if (!statsType.equals(TIME_INSIGHTS) && (containMonthYear() != FALSE || containPeriodNumber() != FALSE)) { // Throws an unsupported tag exception if non-time_insights tag is using date intervals tags throw new GlobalUnsupportedTagException(); @@ -147,7 +146,7 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws * @param transactions An instance of the TransactionList class. * @throws MoolahException If the type of statistics is not recognised. */ - private void listStatsByStatsType(TransactionList transactions) throws MoolahException { + public void listStatsByStatsType(TransactionList transactions) throws MoolahException { switch (statsType) { case MONTHLY_EXPENDITURE: case CATEGORICAL_SAVINGS: diff --git a/src/test/java/seedu/duke/command/FindCommandTest.java b/src/test/java/seedu/duke/command/FindCommandTest.java new file mode 100644 index 000000000..aa5420e8c --- /dev/null +++ b/src/test/java/seedu/duke/command/FindCommandTest.java @@ -0,0 +1,32 @@ +package seedu.duke.command; + +import org.junit.jupiter.api.Test; +import seedu.duke.exception.FindTransactionMissingKeywordsException; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +public class FindCommandTest { + //@@author chydarren + @Test + public void checkFindFormat_noKeywords_exceptionThrown() { + assertThrows( + FindTransactionMissingKeywordsException.class, + () -> FindCommand.checkFindFormat("") + ); + } + + @Test + public void checkFindFormat_oneKeyword_expectNoError() { + assertDoesNotThrow( + () -> FindCommand.checkFindFormat("Moolah") + ); + } + + @Test + public void checkFindFormat_moreThanOneKeyword_expectNoError() { + assertDoesNotThrow( + () -> FindCommand.checkFindFormat("Moolah Manager") + ); + } +} diff --git a/src/test/java/seedu/duke/command/ListCommandTest.java b/src/test/java/seedu/duke/command/ListCommandTest.java new file mode 100644 index 000000000..d53e3f371 --- /dev/null +++ b/src/test/java/seedu/duke/command/ListCommandTest.java @@ -0,0 +1,111 @@ +package seedu.duke.command; + +import org.junit.jupiter.api.Test; +import seedu.duke.exception.GlobalMissingPeriodNumberTagException; +import seedu.duke.exception.GlobalMissingYearTagException; +import seedu.duke.exception.GlobalUnsupportedTagException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +public class ListCommandTest { + //@@author chydarren + @Test + public void containMonthYear_YearAndMonth_expectContainBoth() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalYear(2022); + listCommand.setGlobalMonth(10); + + assertEquals(listCommand.containMonthYear(), 1); + } + + @Test + public void containMonthYear_YearOnly_expectContainEither() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalYear(2022); + + assertEquals(listCommand.containMonthYear(), 2); + } + + @Test + public void containMonthYear_MonthOnly_expectContainEitherInvalid() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalMonth(10); + + assertEquals(listCommand.containMonthYear(), 3); + } + + @Test + public void containPeriodNumber_PeriodAndNumber_expectContainBoth() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalPeriod("weeks"); + listCommand.setGlobalNumber(5); + + assertEquals(listCommand.containPeriodNumber(), 1); + } + + @Test + public void containPeriodNumber_PeriodOnly_expectContainEither() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalPeriod("months"); + + assertEquals(listCommand.containPeriodNumber(), 2); + } + + @Test + public void parseDateIntervalsTags_useYearAndPeriodNumberTags_exceptionThrown() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalYear(2022); + listCommand.setGlobalPeriod("months"); + listCommand.setGlobalNumber(5); + + assertThrows( + GlobalUnsupportedTagException.class, + () -> listCommand.parseDateIntervalsTags() + ); + } + + @Test + public void parseDateIntervalsTags_useNumberWithoutPeriodTag_exceptionThrown() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalNumber(1); + + assertThrows( + GlobalMissingPeriodNumberTagException.class, + () -> listCommand.parseDateIntervalsTags() + ); + } + + @Test + public void parseDateIntervalsTags_usePeriodWithoutNumberTag_exceptionThrown() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalPeriod("weeks"); + + assertThrows( + GlobalMissingPeriodNumberTagException.class, + () -> listCommand.parseDateIntervalsTags() + ); + } + + @Test + public void parseDateIntervalsTags_useMonthWithoutYearTag_exceptionThrown() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalMonth(10); + + assertThrows( + GlobalMissingYearTagException.class, + () -> listCommand.parseDateIntervalsTags() + ); + } + + @Test + public void parseDateIntervalsTags_useYearWithoutMonthTag_expectNoError() { + ListCommand listCommand = new ListCommand(); + listCommand.setGlobalYear(2010); + + assertDoesNotThrow( + () -> listCommand.parseDateIntervalsTags() + ); + } +} diff --git a/src/test/java/seedu/duke/command/StatsCommandTest.java b/src/test/java/seedu/duke/command/StatsCommandTest.java new file mode 100644 index 000000000..6cf0beffd --- /dev/null +++ b/src/test/java/seedu/duke/command/StatsCommandTest.java @@ -0,0 +1,72 @@ +package seedu.duke.command; + +import org.junit.jupiter.api.Test; +import seedu.duke.Storage; +import seedu.duke.Ui; +import seedu.duke.data.TransactionList; +import seedu.duke.exception.GlobalMissingTagException; +import seedu.duke.exception.GlobalUnsupportedTagException; +import seedu.duke.exception.StatsInvalidTypeException; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class StatsCommandTest { + //@@author chydarren + @Test + public void execute_useMonthYearTagsNotForTimeInsights_exceptionThrown() { + StatsCommand statsCommand = new StatsCommand(); + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + + statsCommand.setGlobalMonth(3); + statsCommand.setGlobalYear(2012); + statsCommand.setStatsType("categorical_savings"); + + assertThrows( + GlobalUnsupportedTagException.class, + () -> statsCommand.execute(transactions, ui, storage) + ); + } + + @Test + public void execute_useNoTagsForTimeInsights_exceptionThrown() { + StatsCommand statsCommand = new StatsCommand(); + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + + statsCommand.setStatsType("time_insights"); + + assertThrows( + GlobalMissingTagException.class, + () -> statsCommand.execute(transactions, ui, storage) + ); + } + + @Test + public void listStatsByStatsType_useInvalidStatsType_exceptionThrown() { + StatsCommand statsCommand = new StatsCommand(); + TransactionList transactions = new TransactionList(); + + statsCommand.setStatsType("moolah"); + + assertThrows( + StatsInvalidTypeException.class, + () -> statsCommand.listStatsByStatsType(transactions) + ); + } + + @Test + public void listStatsByStatsType_useCategoricalSavingsStatsType_noError() { + StatsCommand statsCommand = new StatsCommand(); + TransactionList transactions = new TransactionList(); + + statsCommand.setStatsType("categorical_savings"); + + assertDoesNotThrow( + () -> statsCommand.listStatsByStatsType(transactions) + ); + } +} diff --git a/src/test/java/seedu/duke/parser/CommandParserTest.java b/src/test/java/seedu/duke/parser/CommandParserTest.java index e42e465b2..ef07070ba 100644 --- a/src/test/java/seedu/duke/parser/CommandParserTest.java +++ b/src/test/java/seedu/duke/parser/CommandParserTest.java @@ -6,8 +6,9 @@ import seedu.duke.exception.GlobalInvalidCommandException; import seedu.duke.exception.MoolahException; -import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; public class CommandParserTest { //@@author wcwy @@ -30,4 +31,19 @@ public void parse_supportedCommandWordRandomCase_expectCorrectCommandSubclass() Command command = CommandParser.parse("bYe"); assertTrue(command instanceof ByeCommand); } + + //@@author chydarren + @Test + public void splitInput_commandWordWithoutParameter_expectOneToken() { + String[] inputTokens = CommandParser.splitInput("bye"); + assertEquals(inputTokens[0], "bye"); + assertEquals(inputTokens[1], ""); + } + + @Test + public void splitInput_commandWordWithParameter_expectTwoTokens() { + String[] inputTokens = CommandParser.splitInput("find helloworld"); + assertEquals(inputTokens[0], "find"); + assertEquals(inputTokens[1], "helloworld"); + } } diff --git a/src/test/java/seedu/duke/parser/ParameterParserTest.java b/src/test/java/seedu/duke/parser/ParameterParserTest.java index 3b1508e57..f7d2d3880 100644 --- a/src/test/java/seedu/duke/parser/ParameterParserTest.java +++ b/src/test/java/seedu/duke/parser/ParameterParserTest.java @@ -6,17 +6,7 @@ import seedu.duke.data.Budget; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -import seedu.duke.exception.GlobalEmptyParameterException; -import seedu.duke.exception.GlobalDuplicateTagException; -import seedu.duke.exception.GlobalMissingTagException; -import seedu.duke.exception.InputTransactionInvalidAmountException; -import seedu.duke.exception.InputTransactionInvalidCategoryException; -import seedu.duke.exception.InputTransactionInvalidDateException; -import seedu.duke.exception.InputTransactionInvalidTypeException; -import seedu.duke.exception.MoolahException; -import seedu.duke.exception.HelpUnknownOptionException; -import seedu.duke.exception.InputBudgetInvalidAmountException; -import seedu.duke.exception.InputBudgetDuplicateException; +import seedu.duke.exception.*; import java.time.LocalDate; @@ -253,5 +243,34 @@ public void parseBudgetTag_budgetToSetSameAsCurrentBudget_exceptionThrown() { ); } + //@@author chydarren + @Test + public void parseTypeTagForListing_invalidInputTransactionType_exceptionThrown() { + assertThrows( + InputTransactionInvalidTypeException.class, + () -> ParameterParser.parseTypeTagForListing("savings") + ); + } + + @Test + public void parseTypeTagForListing_expenseTransactionType_expectClassTypeExpense() + throws InputTransactionInvalidTypeException { + assertEquals(ParameterParser.parseTypeTagForListing("expense"), + "seedu.duke.data.transaction.Expense"); + } + @Test + public void parseStatsTypeTag_invalidStatsType_exceptionThrown() { + assertThrows( + StatsInvalidTypeException.class, + () -> ParameterParser.parseStatsTypeTag("categories") + ); + } + + @Test + public void parseStatsTypeTag_monthlyExpenditureStatsType_expectMonthlyExpenditure() + throws StatsInvalidTypeException { + assertEquals(ParameterParser.parseStatsTypeTag("monthly_expenditure"), + "monthly_expenditure"); + } } From 9a828d442cde0cc4f5d0073b461131b4df3ad291 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 13:07:47 +0800 Subject: [PATCH 249/416] Rectify minor issues for Gradle coding style --- .../seedu/duke/command/ListCommandTest.java | 18 ++++++++-------- .../seedu/duke/command/StatsCommandTest.java | 21 ++++++++++--------- .../duke/parser/ParameterParserTest.java | 21 ++++++++++++++----- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/test/java/seedu/duke/command/ListCommandTest.java b/src/test/java/seedu/duke/command/ListCommandTest.java index d53e3f371..a264f7747 100644 --- a/src/test/java/seedu/duke/command/ListCommandTest.java +++ b/src/test/java/seedu/duke/command/ListCommandTest.java @@ -61,8 +61,8 @@ public void parseDateIntervalsTags_useYearAndPeriodNumberTags_exceptionThrown() listCommand.setGlobalNumber(5); assertThrows( - GlobalUnsupportedTagException.class, - () -> listCommand.parseDateIntervalsTags() + GlobalUnsupportedTagException.class, + () -> listCommand.parseDateIntervalsTags() ); } @@ -72,8 +72,8 @@ public void parseDateIntervalsTags_useNumberWithoutPeriodTag_exceptionThrown() { listCommand.setGlobalNumber(1); assertThrows( - GlobalMissingPeriodNumberTagException.class, - () -> listCommand.parseDateIntervalsTags() + GlobalMissingPeriodNumberTagException.class, + () -> listCommand.parseDateIntervalsTags() ); } @@ -83,8 +83,8 @@ public void parseDateIntervalsTags_usePeriodWithoutNumberTag_exceptionThrown() { listCommand.setGlobalPeriod("weeks"); assertThrows( - GlobalMissingPeriodNumberTagException.class, - () -> listCommand.parseDateIntervalsTags() + GlobalMissingPeriodNumberTagException.class, + () -> listCommand.parseDateIntervalsTags() ); } @@ -94,8 +94,8 @@ public void parseDateIntervalsTags_useMonthWithoutYearTag_exceptionThrown() { listCommand.setGlobalMonth(10); assertThrows( - GlobalMissingYearTagException.class, - () -> listCommand.parseDateIntervalsTags() + GlobalMissingYearTagException.class, + () -> listCommand.parseDateIntervalsTags() ); } @@ -105,7 +105,7 @@ public void parseDateIntervalsTags_useYearWithoutMonthTag_expectNoError() { listCommand.setGlobalYear(2010); assertDoesNotThrow( - () -> listCommand.parseDateIntervalsTags() + () -> listCommand.parseDateIntervalsTags() ); } } diff --git a/src/test/java/seedu/duke/command/StatsCommandTest.java b/src/test/java/seedu/duke/command/StatsCommandTest.java index 6cf0beffd..091a67312 100644 --- a/src/test/java/seedu/duke/command/StatsCommandTest.java +++ b/src/test/java/seedu/duke/command/StatsCommandTest.java @@ -16,17 +16,18 @@ public class StatsCommandTest { @Test public void execute_useMonthYearTagsNotForTimeInsights_exceptionThrown() { StatsCommand statsCommand = new StatsCommand(); - TransactionList transactions = new TransactionList(); - Ui ui = new Ui(); - Storage storage = new Storage(); statsCommand.setGlobalMonth(3); statsCommand.setGlobalYear(2012); statsCommand.setStatsType("categorical_savings"); + TransactionList transactions = new TransactionList(); + Ui ui = new Ui(); + Storage storage = new Storage(); + assertThrows( - GlobalUnsupportedTagException.class, - () -> statsCommand.execute(transactions, ui, storage) + GlobalUnsupportedTagException.class, + () -> statsCommand.execute(transactions, ui, storage) ); } @@ -40,8 +41,8 @@ public void execute_useNoTagsForTimeInsights_exceptionThrown() { statsCommand.setStatsType("time_insights"); assertThrows( - GlobalMissingTagException.class, - () -> statsCommand.execute(transactions, ui, storage) + GlobalMissingTagException.class, + () -> statsCommand.execute(transactions, ui, storage) ); } @@ -53,8 +54,8 @@ public void listStatsByStatsType_useInvalidStatsType_exceptionThrown() { statsCommand.setStatsType("moolah"); assertThrows( - StatsInvalidTypeException.class, - () -> statsCommand.listStatsByStatsType(transactions) + StatsInvalidTypeException.class, + () -> statsCommand.listStatsByStatsType(transactions) ); } @@ -66,7 +67,7 @@ public void listStatsByStatsType_useCategoricalSavingsStatsType_noError() { statsCommand.setStatsType("categorical_savings"); assertDoesNotThrow( - () -> statsCommand.listStatsByStatsType(transactions) + () -> statsCommand.listStatsByStatsType(transactions) ); } } diff --git a/src/test/java/seedu/duke/parser/ParameterParserTest.java b/src/test/java/seedu/duke/parser/ParameterParserTest.java index f7d2d3880..acc0dd3ee 100644 --- a/src/test/java/seedu/duke/parser/ParameterParserTest.java +++ b/src/test/java/seedu/duke/parser/ParameterParserTest.java @@ -6,7 +6,18 @@ import seedu.duke.data.Budget; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -import seedu.duke.exception.*; +import seedu.duke.exception.GlobalEmptyParameterException; +import seedu.duke.exception.GlobalDuplicateTagException; +import seedu.duke.exception.GlobalMissingTagException; +import seedu.duke.exception.InputTransactionInvalidAmountException; +import seedu.duke.exception.InputTransactionInvalidCategoryException; +import seedu.duke.exception.InputTransactionInvalidDateException; +import seedu.duke.exception.InputTransactionInvalidTypeException; +import seedu.duke.exception.MoolahException; +import seedu.duke.exception.HelpUnknownOptionException; +import seedu.duke.exception.InputBudgetInvalidAmountException; +import seedu.duke.exception.InputBudgetDuplicateException; +import seedu.duke.exception.StatsInvalidTypeException; import java.time.LocalDate; @@ -247,8 +258,8 @@ public void parseBudgetTag_budgetToSetSameAsCurrentBudget_exceptionThrown() { @Test public void parseTypeTagForListing_invalidInputTransactionType_exceptionThrown() { assertThrows( - InputTransactionInvalidTypeException.class, - () -> ParameterParser.parseTypeTagForListing("savings") + InputTransactionInvalidTypeException.class, + () -> ParameterParser.parseTypeTagForListing("savings") ); } @@ -262,8 +273,8 @@ public void parseTypeTagForListing_expenseTransactionType_expectClassTypeExpense @Test public void parseStatsTypeTag_invalidStatsType_exceptionThrown() { assertThrows( - StatsInvalidTypeException.class, - () -> ParameterParser.parseStatsTypeTag("categories") + StatsInvalidTypeException.class, + () -> ParameterParser.parseStatsTypeTag("categories") ); } From 7c1df25130e4f1f39d0f84345afd0916c9dd5ebf Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 13:24:48 +0800 Subject: [PATCH 250/416] Reclaim authorship for Brian in EditCommand class --- src/main/java/seedu/duke/command/EditCommand.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index 12f546238..93fd4ea0d 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -74,8 +74,10 @@ public class EditCommand extends Command { private String category; private LocalDate date; + //@@brian-vb private static final Logger editLogger = Logger.getLogger(DeleteCommand.class.getName()); + //@@paullowse public EditCommand() { } @@ -137,6 +139,7 @@ public void setDate(LocalDate date) { this.date = date; } + //@@author brian-vb /** * Executes the "edit" command. * From 69e5e032e1ad2b719546e22648b54908b79ddf51 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 25 Oct 2022 18:17:33 +0800 Subject: [PATCH 251/416] Describe program architecture in developer guide Add architecture diagram to aid the description. Add sequence diagram as a simple illustration of the interactions between components. --- docs/DeveloperGuide.md | 25 ++++++++-- docs/diagram/ArchitectureSequenceDiagram.puml | 44 ++++++++++++++++++ docs/images/ArchitectureDiagram.png | Bin 0 -> 112434 bytes docs/images/ArchitectureSequenceDiagram.png | Bin 0 -> 28083 bytes 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 docs/diagram/ArchitectureSequenceDiagram.puml create mode 100644 docs/images/ArchitectureDiagram.png create mode 100644 docs/images/ArchitectureSequenceDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 102a25f5e..756b0b021 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -69,11 +69,30 @@ _Written by: Paul Low_ ## Design -{Describe the design of the product. Use UML diagrams and short code snippets where applicable.} - ### Architecture +![Architecture Diagram](images/ArchitectureDiagram.png) +The Architecture Diagram shown above explains the high-level design of Moolah Manager. +The `Duke` class contain the main method which holds the responsibility for the following: +1. On application launch, it will initialise the `UI`, `Storage` and `Data` components. +2. During application execution, it will interact with `UI`, `Parser`, `Command` components +to execute the command entered by the users. +3. On any exception caught, it will handle the exception and interact with the `UI` to display the error message. + +`Common` represents a collection of classes or enums used by multiple components. + +The rest of the application consists of six components: + - `UI`: The user interface of Moolah Manager + - `Parser`: Parser for user's entered command. + - `Command`: The command executor. + - `Data`: Holds the data of the application in memory. + - `Storage`: File I/O to store the data onto the hard disk. + +#### How the architecture components interact with each other +The sequence diagram below shows how the components interact on command `budget b/1000`. +![Architecture Interaction](images/ArchitectureSequenceDiagram.png) +The section below gives more detailed description of each component. -_Written by: Author name_ +_Written by: Chia Thin Hong_ ### Command Component diff --git a/docs/diagram/ArchitectureSequenceDiagram.puml b/docs/diagram/ArchitectureSequenceDiagram.puml new file mode 100644 index 000000000..d1925ef81 --- /dev/null +++ b/docs/diagram/ArchitectureSequenceDiagram.puml @@ -0,0 +1,44 @@ +@startuml +!include Style.puml + +Participant ":Duke" as duke +Participant ":Parser" as parser +Participant ":Command" as command +Participant ":Data" as data +Participant ":Storage" as storage +Participant ":Ui" as ui +actor User + +activate duke +duke -> ui:readCommand() +activate ui +User -> ui:"budget b/1000" +return input:String + +duke -> parser:parse(input) +activate parser + create command + parser -> command:new BudgetCommand() + activate command + return + parser -> command:setBudget(1000) + activate command + return +return command:Command + +duke -> command:execute() +activate command + command -> data:setBudget(1000) + activate data + return + + command -> storage:writeToFile() + activate storage + return + + command -> ui:displayMessage() + activate ui + return +return + +@enduml \ No newline at end of file diff --git a/docs/images/ArchitectureDiagram.png b/docs/images/ArchitectureDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..9959bb0bb61c24bf3eb5f03bac03b7ffadae1935 GIT binary patch literal 112434 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 x7B5(~cH^A`m7LecIBjTceUq1!^`@z|xwVa% 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 bZx(&^M2lg+n@1CT4P{BNGW*@+@WuZE9%i^5 literal 0 HcmV?d00001 From 7a7a1fb6e826bf566a63912f55bbf1e194dcb9ba Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 25 Oct 2022 21:12:11 +0800 Subject: [PATCH 252/416] Update data component in developer guide for budget class --- docs/DeveloperGuide.md | 26 +++---- docs/diagram/DataComponentClassDiagram.puml | 73 ++++++++++-------- .../TransactionListSequenceDiagram.puml | 28 ++++--- docs/images/DataComponentClassDiagram.png | Bin 54711 -> 55857 bytes .../images/TransactionListSequenceDiagram.png | Bin 41423 -> 38931 bytes .../duke/data/transaction/Transaction.java | 2 + 6 files changed, 75 insertions(+), 54 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 756b0b021..bd59ab38a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -101,7 +101,11 @@ _Written by: Author name_ ### Data Component The data component is represented by a `data` package which consists of all the classes that is part of the data stored -by Moolah Manager. Within the `data` package, a transaction package and a transactionList class is stored. +by Moolah Manager. Within the `data` package, a transaction package, a budget class and a transactionList class is +stored. + +The `budget` class is a representation of the monthly budget of the users. Operations related to viewing the budget and +differences from budget is implemented within this class. The `transactionList` class is a representation of a list of transactions, and the operations related to the `transactionList` implemented within this class. @@ -110,28 +114,21 @@ Within the transaction package, the following classes are stored: 1. Transaction 2. Income 3. Expense -4. Category The structure of the data component in Moolah Manager is illustrated in the class diagram below: ![Data Component Class Diagram](images/DataComponentClassDiagram.png) -From the class diagram, it can be seen that the transactionList mainly contain methods for CRUD operations to the list, -such as getting, adding, editing, deleting and purging of transaction(s) from the list. - -The `Transaction` class is the abstract classes of an `Income` or an `Expense`. The `Category` represents a category of -a transaction. Within the transaction class and its subclasses, getters and setters are used to access the private -variables. These classes override the toString() method for a self-defined print format when the transactions are -displayed. +From the class diagram, it can be seen that the transactionList contain the methods for CRUD operations to the list, +such as getting, adding, editing, deleting and purging of transaction(s) in the list. - - -A more detailed explanation on the implementation on the transactions can be viewed under Section +The `Transaction` class is the abstract classes of an `Income` or an `Expense`. A more detailed explanation on the +implementation on the transactions can be viewed under Section [Implementation for Transaction](#implementation-for-transaction). #### How the data component interacts - When MoolahManager starts running, the `Duke` class will initialize a `Storage` object which will attempt to -read from the file and initialize a `transactionList`. The temporary `transactionList` containing all the stored +read from the file and initialize both `budget` and `transactionList`. The temporary `transactionList` containing all the stored transaction records will be returned by the `Storage`. Based on the whether the initialization is successful, the corresponding constructor will be called to initialize a `transactionList` object which will be used throughout the application running time to hold the `transactions` added. @@ -142,6 +139,8 @@ Based on the whether the initialization is successful, the corresponding constru class and can be deleted by a `deleteCommand` or `purgeCommand` class. These interactions are described in further detail under each command section below. +- The monthly budget can be updated by `budget` command. + _Written by: Chia Thin Hong_ @@ -512,6 +511,7 @@ for different classes such as `parserLogger` and `addLogger` to set the log mess **Logging Levels**: +* `ERROR`: An unexpected control flow captured * `WARNING`: An exception has been caught by the app * `INFO`: Information details what the app has done diff --git a/docs/diagram/DataComponentClassDiagram.puml b/docs/diagram/DataComponentClassDiagram.puml index 0375dbfa0..2043dea89 100644 --- a/docs/diagram/DataComponentClassDiagram.puml +++ b/docs/diagram/DataComponentClassDiagram.puml @@ -1,27 +1,19 @@ @startuml !include Style.puml -class Category{ - -} - -class CategoryList{ - -} class TransactionList { - + TransactionList() - + TransactionList(TransactionList) - + getEntry(int) : Transaction - + size() : int + + getEntry(int index):Transaction + addExpense(description:String, amount:int, category:String, date:LocalDate) : String + addIncome(description:String, amount:int, category:String, date:LocalDate) : String + listTransactions(type: String, category:String, date:LocalDate) : String + + getTransactionsByMonth(year: int, month:int) : String + findTransaction(String) : String + deleteTransaction(int) : String + purgeTransactions() + } abstract class "Transaction"{ @@ -29,46 +21,63 @@ abstract class "Transaction"{ - description: String - amount: int - date: LocalDate - + Transaction(description:String, amount:int, - category:String, date:LocalDate) - + getType() : String {abstract} - + getDescription() : String + + Expense(description:String, amount:int, + category:String, date:LocalDate) + setDescription(String) - + getAmount() : int + setAmount(int) - + getCategory() : String + setCategory(String) - + getDate() : LocalDate + setDate(LocalDate) - + printFormattedDate() : String - + printFormattedCategory() : String - + toString() : String } class Income { - + TRANSACTION_NAME: String = "income"{static} - + ICON_INCOME: String = "[+]" {static} + Income(description:String, amount:int, category:String, date:LocalDate) - getType() : String - getIcon() : String - toString() : String } class Expense { - + TRANSACTION_NAME: String = "expense"{static} - + ICON_INCOME: String = "[-]" {static} + Expense(description:String, amount:int, category:String, date:LocalDate) - getType() : String - getIcon() : String - toString() : String +} + +abstract class Command { + + execute() +} + +abstract class Storage { + + initializeFile() + - storeFileValuesLocally() +} + + +class Budget{ + - budget : long {static} + + getBudget() : long {static} + + setBudget(long) {static} +} + +together { + class Command + class Income + class Expense +} + + +together { + class Storage + class TransactionList + class Budget } Income -up-|> "Transaction" Expense -up-|> "Transaction" -TransactionList -> "*" "Transaction" : contains > +TransactionList "1" *-right> "*" "Transaction" : contains +Storage -[dashed]down-> "Budget" : initialize > +Storage -[dashed]up-> "TransactionList" : initialize > +Command -[dashed]down-> "Budget" : set > +Command -[dashed]up-> "TransactionList" : add, edit, delete, purge, find, list, stats > + + @enduml \ No newline at end of file diff --git a/docs/diagram/TransactionListSequenceDiagram.puml b/docs/diagram/TransactionListSequenceDiagram.puml index a0a413867..bb6de76a1 100644 --- a/docs/diagram/TransactionListSequenceDiagram.puml +++ b/docs/diagram/TransactionListSequenceDiagram.puml @@ -1,15 +1,16 @@ @startuml !include Style.puml -actor Main + Participant ":Duke" as duke Participant ":Storage" as storage Participant "storedTransactions:\nTransactionList" as storedList +Participant ":Budget" as budget Participant "transactionList:\nTransactionList" as transactionList Participant "transactionList:\nTransactionList" as emptyTransactionList -create duke -Main -> duke:Duke() + + activate duke create storage duke -> storage:Storage() @@ -22,12 +23,21 @@ activate duke duke -> storage:initializeFile() activate storage - storage -> storedList:Add transactions - note right - Minimal notation. - Details hidden for adding transactions - from file read into storedTransactions. - end note + storage -> budget:setBudget() + activate budget + return + loop Until End-of-File + alt isExpense + storage -> storedList:addExpenseDuringStorage() + activate storedList + return + else isIncome + storage -> storedList:addIncomeDuringStorage() + activate storedList + return + end + end + return stored:TransactionList destroy storedList diff --git a/docs/images/DataComponentClassDiagram.png b/docs/images/DataComponentClassDiagram.png index 633b74db25792e222afdf9061b190cc312f3596d..07dd4a02828633693403e1baa23a0964ec73a3d7 100644 GIT binary patch literal 55857 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 diff --git a/docs/images/TransactionListSequenceDiagram.png b/docs/images/TransactionListSequenceDiagram.png index 7c87b4ec7cc4cb30f9525fe90a0b6d90fd3786ad..2c4d41beaede9a95d5c7cc3fe7da751f27b7f6a8 100644 GIT binary patch literal 38931 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

FRi$A>~xl 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 Date: Tue, 25 Oct 2022 21:39:52 +0800 Subject: [PATCH 253/416] Add sequence diagram for help command --- docs/DeveloperGuide.md | 7 ++++--- docs/diagram/HelpSequenceDiagram.puml | 27 ++++++++++++++++++++++++++ docs/images/HelpSequenceDiagram.png | Bin 0 -> 18300 bytes 3 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 docs/diagram/HelpSequenceDiagram.puml create mode 100644 docs/images/HelpSequenceDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index bd59ab38a..677370954 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -264,10 +264,11 @@ The structure of the application focusing on the help command is illustrated in For each command subclass, they will implement the getHelpMessage() and getDetailedHelpMessage() methods. These methods will contain their corresponding HelpMessage Enum that stores the help messages as strings inside the enum. -In the help classes, during the execute() call, it will call either generateBasicHelp() or generateDetailedHelp() method -based on the help option given by the user. +In the help command, during the execute() call, it will call either generateBasicHelp() or generateDetailedHelp() method +based on the help option chosen by the user. + +![Data Component Class Diagram](images/HelpSequenceDiagram.png) - _Written by: Chia Thin Hong_ diff --git a/docs/diagram/HelpSequenceDiagram.puml b/docs/diagram/HelpSequenceDiagram.puml new file mode 100644 index 000000000..cfbf3a429 --- /dev/null +++ b/docs/diagram/HelpSequenceDiagram.puml @@ -0,0 +1,27 @@ +@startuml +!include Style.puml + +Participant ":Duke" as duke +Participant ":HelpCommand" as command +Participant ":Ui" as ui +actor User + +duke -> command:execute() +activate command + alt parameter o/detailed found + command -> command : generateBasicHelp() + activate command + return message:String + else parameter o/detailed not found + command -> command :generateDetailedHelp() + activate command + return message:String + end + command -> ui :showHelp(message) + activate ui + ui -> User + return + +return + +@enduml \ No newline at end of file diff --git a/docs/images/HelpSequenceDiagram.png b/docs/images/HelpSequenceDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..bb146a83ec064a3e683feb4286e0df8b4fa31de9 GIT binary patch literal 18300 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 b%brD?T(wfT(FcAH4%Rg#HN_%%qu~DqQzW`T literal 0 HcmV?d00001 From fbada29e67d96213dafb8bcbd8b074e5647838b5 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 25 Oct 2022 22:00:06 +0800 Subject: [PATCH 254/416] Add description for budget command --- docs/DeveloperGuide.md | 38 ++++++++++++++++--- src/main/java/seedu/duke/data/Budget.java | 2 +- .../java/seedu/duke/data/TransactionList.java | 2 +- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 677370954..23fae1a01 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -15,6 +15,7 @@ * [Overview for Transaction](#overview-for-transaction) * [Implementation for Transaction](#implementation-for-transaction) * [Help Command](#help-command) + * [Budget Command](#budget-command) * [Add Command](#add-command) * [Edit Command](#edit-command) * [List Command](#list-command) @@ -252,24 +253,49 @@ Some important operations are performed within the `TransactionList` class, whic _Written by: Chua Han Yong Darren_ ### Help Command -The help command displays the help message to the users to guide them on the usage and provide descriptions for each -available command. +The help command displays the help message to the users to guide them on the usage and provide descriptions for each +available command. -The help command can be run as `help` or `help o/detailed`, where the latter will display a more detailed version of -help messages to the users. +The help command can be run as `help` or `help o/detailed`, where the latter will display a more detailed version of +help messages to the users. The structure of the application focusing on the help command is illustrated in the class diagram below: ![Data Component Class Diagram](images/HelpClassDiagram.png) -For each command subclass, they will implement the getHelpMessage() and getDetailedHelpMessage() methods. These methods +For each command subclass, they will implement the getHelpMessage() and getDetailedHelpMessage() methods. These methods will contain their corresponding HelpMessage Enum that stores the help messages as strings inside the enum. In the help command, during the execute() call, it will call either generateBasicHelp() or generateDetailedHelp() method -based on the help option chosen by the user. +based on the help option chosen by the user. ![Data Component Class Diagram](images/HelpSequenceDiagram.png) +_Written by: Chia Thin Hong_ + +### Budget Command + +The budget command allows user to set a new monthly budget. The range of accepted budget value is stored in the +`common/Constants.java` file, whereby the content of the file is as such: + +``` +public static int MAX_TRANSACTIONS_COUNT = 1000000; +public static int MIN_AMOUNT_VALUE = 0; +public static int MAX_AMOUNT_VALUE = 10000000; +public static int MIN_BUDGET_VALUE = 1; +public static long MAX_BUDGET_VALUE = Long.valueOf(MAX_TRANSACTIONS_COUNT) * Long.valueOf(MAX_AMOUNT_VALUE); +``` + +Under the default setting, the acceptable range of the monthly budget, is 0 < budget <= 10000000000000, which is 10^13 +and it ensures that no integer overflow will occur as the `long` data type is used. + +To set a new budget, user can use the command `budget b/AMOUNT` where the `AMOUNT` tag is any whole number within the +valid range above. + +The interaction of the components on setting a budget can be seen in the sequence diagram under +[Architecture](#How-the-architecture-components-interact-with-each-other). + + _Written by: Chia Thin Hong_ ### Add Command diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index bcc61bf73..dfa20aac1 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -100,7 +100,7 @@ private static long calculateBudgetLeft(long totalMonthlyExpense) { /* Since the maximum number of transaction is 1000000, maximum amount of expense is 10000000, and minimum is 1, the lowest possible budget left value is - 1 - (10^6 * 10^7) = -10^15 + 1 > Long.MIN_VALUE (approx -9.22 * 10^18) + 1 - (10^6 * 10^7) = -10^13 + 1 > Long.MIN_VALUE (approx -9.22 * 10^18) Thus, this function is safe from integer overflow UNLESS the values in common.Constants.java is altered. */ diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index bcc33470d..4e3b93b33 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -561,7 +561,7 @@ public static long calculateMonthlyTotalExpense(LocalDate date) { if (transaction.getDate().getMonthValue() == month && transaction.getDate().getYear() == year) { /* Since the maximum number of transaction is 1000000 and maximum amount of expense is 10000000, - the highest possible expense value is 10^6 * 10^7 = 10^15 < Long.MAX_VALUE (approx 9.22 * 10^18) + the highest possible expense value is 10^6 * 10^7 = 10^13 < Long.MAX_VALUE (approx 9.22 * 10^18) Therefore, this function is safe from integer overflow UNLESS the max values in common.Constants.java is altered. */ From 1edbf61c45f77fdf6216bec8b9b5ee2f492bd8c0 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Tue, 25 Oct 2022 22:25:09 +0800 Subject: [PATCH 255/416] Add Preface and Acknowledgements --- docs/DeveloperGuide.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 23fae1a01..36910f2a9 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -37,15 +37,21 @@ ## Preface -{Provide brief details of the Moolah Manager application and the purpose of the Developer Guide} +Moolah Manager is a desktop app for managing your finances, optimised for use via a Command Line Interface (CLI). Designed for people who are +fast typists, it can help to process day-to-day transactions, namely your incomes and expenses. These can help you to see all your transactions +and provide you with a valuable insight into your spending habits. -_Written by: Author name_ +This document is meant to assist potential users and developers in understanding how our program works. + +_Written by: Brian Wong Yun Long_ ## Acknowledgements -{List here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +The format of this development guide was adapted from [[SE-EDU AddressBook Level 3 Developer Guide]](https://se-education.org/addressbook-level3/DeveloperGuide.html) -_Written by: Author name_ +Some code used in this program were reused and adapted from our individual projects from the CS2113 IP. + +_Written by: Brian Wong Yun Long_ ## Setting Up the Project From 7ee210f8da72f84111b0d07e4ef1f4a8f3267f51 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Tue, 25 Oct 2022 22:54:32 +0800 Subject: [PATCH 256/416] Add Command Component and Overview for Transaction --- docs/DeveloperGuide.md | 44 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 36910f2a9..2ecb8530c 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -103,7 +103,35 @@ _Written by: Chia Thin Hong_ ### Command Component -_Written by: Author name_ +The command component is represented by a `command` package which consists of all the classes that is part of the data stored +by Moolah Manager. Within the `command` package, there are many classes, each corresponding to all of our commands which are +supported by the application. + +The `AddCommand` class contains the operations pertaining to adding a transaction into the list of transactions. + +The `BudgetCommand` class contains the operations pertaining to setting the budget for the user. + +The `ByeCommand` class contains the operations pertaining to exiting the program. + +The `CommandTag` class consists of all the tags that the program parses. + +The `DeleteCommand` class contains the operations pertaining to deleting a transaction from the list of transactions. + +The `EditCommand` class contains the operations pertaining to editing a transaction from the list of transactions. + +The `FindCommand` class contains the operations pertaining to searching the list of transactions for transactions that match the inputted keywords. + +The `HelpCommand` class contains the operations pertaining to displaying help messages for the user. + +The `ListCommand` class contains the operations pertaining to listing all transactions. + +The `PurgeCommand` class contains the operations pertaining to deleting all transaction from the list of transactions. + +The `StatsCommand` class contains the operations pertaining to getting statistics based on your list of transactions. + +The structure of the command component in Moolah Manager is illustrated in the class diagram below: + +_Written by: Brian Wong Yun Long_ ### Data Component @@ -217,9 +245,19 @@ _Written by: Author name_ ### Overview for Transaction -{Give a brief overview of the Transaction features (i.e. purpose of each command) in Moolah Manager application.} +The following commands are accepted in Moolah Manager: +1) `Add` - Adds an `Income` or `expense` type transaction into the list of transaction. [Add Command](#add-command) +2) `Budget` - Adds an `Budget` into Moolah Manager, which sets the basis for financial tracking. [Budget Command](#budget-command) +3) `Bye` - Exits Moolah Manager. [Bye Command](#bye-command) +4) `Delete` - Deletes an `Income` or `expense` type transaction from the list of transaction. [Delete Command](#delete-command) +5) `Edit` - Edits an `Income` or `expense` type transaction from the list of transaction. [Edit Command](#edit-command) +6) `Find` - Searches for an `Income` or `expense` type transaction from the list of transaction given keywords. [Find Command](#find-command) +7) `Help` - Outputs the usage of commands of Moolah Manager. [Help Command](#help-command) +8) `List` - List all transactions. [List Command](#list-command) +9) `Purge` - Deletes all transactions from the list of transaction. [Purge Command](#purge-command) +10) `Stats` - View statistics on transactions based on the list of transactions in Moolah Manager. [Stats Command](#stats-command) -_Written by: Author name_ +_Written by: Brian Wong Yun Long_ ### Implementation for Transaction From 97eeb2523cb25711ae4e9f89b2f96cb0ffaf2425 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Tue, 25 Oct 2022 23:25:02 +0800 Subject: [PATCH 257/416] Add Diagram --- docs/DeveloperGuide.md | 1 + .../diagram/CommandComponentClassDiagram.puml | 131 ++++++++++++++++++ docs/images/CommandComponentClassDiagram.png | Bin 0 -> 82525 bytes 3 files changed, 132 insertions(+) create mode 100644 docs/diagram/CommandComponentClassDiagram.puml create mode 100644 docs/images/CommandComponentClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 2ecb8530c..51226bcc0 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -130,6 +130,7 @@ The `PurgeCommand` class contains the operations pertaining to deleting all tran The `StatsCommand` class contains the operations pertaining to getting statistics based on your list of transactions. The structure of the command component in Moolah Manager is illustrated in the class diagram below: +![Command Component Class Diagram](images/CommandComponentClassDiagram.png) _Written by: Brian Wong Yun Long_ diff --git a/docs/diagram/CommandComponentClassDiagram.puml b/docs/diagram/CommandComponentClassDiagram.puml new file mode 100644 index 000000000..52ea36d06 --- /dev/null +++ b/docs/diagram/CommandComponentClassDiagram.puml @@ -0,0 +1,131 @@ +@startuml +!include Style.puml + +class "{abstract}\n Command" { + + COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - type: String + - category: String + - description: String + - amount: int + - date: LocalDate + + +getMandatoryTags() + +setBudgetAmount() + +{abstract}execute() +} + +class AddCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - type: String + - category: String + - description: String + - amount: int + - date: LocalDate + + +getMandatoryTags() + +{abstract}execute() +} + +class ByeCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + + +{abstract}execute() +} + +class DeleteCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - entryNumber: int + + +getMandatoryTags() + +setEntryNumber(): int + +{abstract}execute() +} + +class ListCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - type: String + - category: String + - date: LocalDate + + -listTransactions: TransactionList + +{abstract}execute() +} + +class PurgeCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + + +isEmpty(): TransactionList + +{abstract}execute() +} + +class FindCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - keywords: String + + +checkFindFormat() : String + +{abstract}execute() +} + +class HelpCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - isDetailed : Boolean + + -generateBasicHelp() : String + -generateDetailedHelp() : String + +{abstract}execute() +} + +class BudgetCommand { ++ COMMAND_WORD: String + + COMMAND_DESCRIPTION: String + + COMMAND_USAGE: String + + COMMAND_PARAMETERS_INFO: String + + - budgetAmount: long + + +getMandatoryTags() + +setBudgetAmount() + +{abstract}execute() +} + +FindCommand --|> "{abstract}\n Command" +ByeCommand -up-|> "{abstract}\n Command" +AddCommand -up-|> "{abstract}\n Command" +DeleteCommand --|> "{abstract}\n Command" +ListCommand --|> "{abstract}\n Command" +PurgeCommand -up-|> "{abstract}\n Command" +HelpCommand -left-|> "{abstract}\n Command" +BudgetCommand -right-|> "{abstract}\n Command" +@enduml \ No newline at end of file diff --git a/docs/images/CommandComponentClassDiagram.png b/docs/images/CommandComponentClassDiagram.png new file mode 100644 index 0000000000000000000000000000000000000000..a2ba03e1cf3185ca7c8a2d34604be7eaf35f4e11 GIT binary patch literal 82525 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 Date: Wed, 26 Oct 2022 15:15:17 +0800 Subject: [PATCH 258/416] Add heading structure for User Guide --- docs/UserGuide.md | 106 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 95 insertions(+), 11 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index abd9fbe89..70f18afc0 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,42 +1,126 @@ # User Guide +- [Introduction](#introduction) +- [About This Guide](#about-this-guide) + * [What is in Moolah Manager](#what-is-in-moolah-manager) + * [Command Format](#command-format) +- [Getting Started](#getting-started) + * [Setting Up](#setting-up) + * [Viewing Help: `help`](#viewing-help---help-) +- [Managing Transactions](#managing-transactions) + * [Adding a Transaction: `add`](#adding-a-transaction---add-) + * [Editing a Transaction: `edit`](#editing-a-transaction---edit-) + * [Listing the Transactions: `list`](#listing-the-transactions---list-) + * [Searching for Transactions: `find`](#searching-for-transactions---find-) + * [Deleting a Transaction: `delete`](#deleting-a-transaction---delete-) + * [Purging all Transactions: `purge`](#purging-all-transactions---purge-) +- [Budgeting and Financial Insights](#budgeting-and-financial-insights) + * [Viewing the Statistics: `stats`](#viewing-the-statistics---stats-) + * [Managing the Budget: `budget`](#managing-the-budget---budget-) +- [General](#general) + * [Persistent Data](#persistent-data) + * [Exiting the Program: `exit`](#exiting-the-program---exit-) +- [Command Summary](#command-summary) +- [FAQ](#faq) + ## Introduction -{Give a product intro} +_Written by: Chua Han Yong Darren_ + +## About This Guide + +### What is in Moolah Manager + +_Written by: Paul Low_ + +### Command Format -## Quick Start +_Written by: Chia Thin Hong_ + +## Getting Started + +### Setting Up {Give steps to get started quickly} 1. Ensure that you have Java 11 or above installed. 1. Down the latest version of `Duke` from [here](http://link.to/duke). -## Features +_Written by: Chua Han Yong Darren_ + +### Viewing Help: `help` + +_Written by: Chia Thin Hong_ -{Give detailed description of each feature} +## Managing Transactions + +### Adding a Transaction: `add` -### Adding a todo: `todo` Adds a new item to the list of todo items. Format: `todo n/TODO_NAME d/DEADLINE` * The `DEADLINE` can be in a natural language format. -* The `TODO_NAME` cannot contain punctuation. +* The `TODO_NAME` cannot contain punctuation. -Example of usage: +Example of usage: `todo n/Write the rest of the User Guide d/next week` `todo n/Refactor the User Guide to remove passive voice d/13/04/2020` -## FAQ +_Written by: Yong Chin Han_ -**Q**: How do I transfer my data to another computer? +### Editing a Transaction: `edit` -**A**: {your answer here} +_Written by: Brian Wong Yun Long_ + +### Listing the Transactions: `list` + +_Written by: Chua Han Yong Darren_ + +### Searching for Transactions: `find` + +_Written by: Chua Han Yong Darren_ + +### Deleting a Transaction: `delete` + +_Written by: Brian Wong Yun Long_ + +### Purging all Transactions: `purge` + +_Written by: Brian Wong Yun Long_ + +## Budgeting and Financial Insights + +### Viewing the Statistics: `stats` + +_Written by: Paul Low_ + +### Managing the Budget: `budget` + +_Written by: Chia Thin Hong_ + +## Others + +### Persistent Data + +_Written by: Yong Chin Han_ + +### Exiting the Program: `exit` + +_Written by: Brian Wong Yun Long_ ## Command Summary {Give a 'cheat sheet' of commands here} -* Add todo `todo n/TODO_NAME d/DEADLINE` +_Written by: Yong Chin Han_ + +## FAQ + +**Q**: How do I transfer my data to another computer? + +**A**: {your answer here} + +_Written by: Paul Low_ \ No newline at end of file From 2df27a455ea4197a13bc4c84fc3f06b6a2dac35e Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 26 Oct 2022 15:23:59 +0800 Subject: [PATCH 259/416] Amend Duke and description to Moolah Manager --- docs/README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index bbcc99c1e..800a58dfb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,12 @@ -# Duke +# Moolah Manager -{Give product intro here} +Financial bookkeeping via traditional mobile applications has always been a hassle due to the repetitive clicks needed to manage monetary transactions. With Moolah Manager, you will be encouraged to take ownership of managing your finances via a time-saving and efficient command-line (CLI) interface. + +The main features of Moolah Manager include: + +- Managing records of monetary transactions +- Gathering financial insights such as categorical savings and periodic expenditure +- General budgeting and reminders about spending Useful links: * [User Guide](UserGuide.md) From 0076531fd4a2bd1523bfad771ba9c84a8a856b6b Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 26 Oct 2022 17:46:21 +0800 Subject: [PATCH 260/416] Update the developer guide's appendices based on previous team meeting notes --- docs/DeveloperGuide.md | 104 ++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 23 deletions(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 51226bcc0..4dbc2b98b 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -37,19 +37,19 @@ ## Preface -Moolah Manager is a desktop app for managing your finances, optimised for use via a Command Line Interface (CLI). Designed for people who are -fast typists, it can help to process day-to-day transactions, namely your incomes and expenses. These can help you to see all your transactions -and provide you with a valuable insight into your spending habits. +Moolah Manager is a desktop app for managing one's finances, optimised for use via a Command Line Interface (CLI). Designed for IT professionals who are +fast typists, it can help to process day-to-day monetary transactions that are classified into income and expense. Users can expect to get an overview of their transactions +at a glance and be provided with valuable insights into their spending habits. They are also encouraged to set budget goals to minimise their spending. -This document is meant to assist potential users and developers in understanding how our program works. +This document is meant assist developers in understanding how our program works. _Written by: Brian Wong Yun Long_ ## Acknowledgements -The format of this development guide was adapted from [[SE-EDU AddressBook Level 3 Developer Guide]](https://se-education.org/addressbook-level3/DeveloperGuide.html) +The format of this development guide was adapted from [SE-EDU AddressBook Level 3 Developer Guide](https://se-education.org/addressbook-level3/DeveloperGuide.html). -Some code used in this program were reused and adapted from our individual projects from the CS2113 IP. +Some parts of the source code in this program were reused and adapted from the team's individual projects during the CS2113 IP phase. _Written by: Brian Wong Yun Long_ @@ -345,7 +345,6 @@ _Written by: Chia Thin Hong_ ### Add Command - **This feature allows the local and external (handled by Storage class) storage of transaction entries by the user.** The `AddCommand` inherits properties from the abstract `Command` class. The inheritance of `Command` from `AddCommand` is @@ -384,13 +383,10 @@ _Written by: Yong Chin Han_ ### Edit Command -{Describe the implementation for the Edit Command} - -_Written by: Author name_ +_Written by: Brian Wong Yun Long_ ### List Command - The full command for list is `list [t/TYPE] [c/CATEGORY] [d/DATE]` For example, if 'list' is called, all transactions that are present in Moolah Manager will be listed out Adding tags such as type, category and date will list all transactions to that category @@ -447,7 +443,7 @@ _Written by: Chua Han Yong Darren_ {Describe the implementation for the Stats Command} -_Written by: Author name_ +_Written by: Chua Han Yong Darren_ ### Delete Command @@ -576,8 +572,6 @@ _Written by: Yong Chin Han_ ### Logging Operations -{Describe how logging is performed in the developer code} - Our team used `java.util.logging` package for the purposes of logging. We instantiated various objects for different classes such as `parserLogger` and `addLogger` to set the log messages. @@ -593,27 +587,91 @@ _Written by: Paul Low_ ### Target user profile -{Describe the target user profile} +Moolah Manager is developed for IT professionals who prefer using Command Line Interface (CLI) applications +to quickly track and update their daily monetary transactions. They ought to be reasonably comfortable in typing over +mouse interactions and can type fast. ### Value proposition -{Describe the value proposition: what problem does it solve?} +Financial bookkeeping using a mobile application is often a hassle due to repetitive clicks. Moolah Manager boasts a +time-saving CLI that encourages individuals to take ownership of tracking and reviewing their daily or monthly +transactions in an efficient and effective way. Moreover, it facilitates budget planning to prevent overspending. ## Appendix B: User Stories -| Version | As a ... | I want to ... | So that I can ... | -|---------|----------|---------------------------|-------------------------------------------------------------| -| v1.0 | new user | see usage instructions | refer to them when I forget how to use the application | -| v2.0 | user | find a to-do item by name | locate a to-do without having to go through the entire list | +| Version | As a ... | I want to ... | So that I can ... | +|---------|------------------|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| +| v1.0 | fast-typing user | type my own actions instead of having to click through different GUI pages | have a more convenient way of managing my spending | +| v1.0 | new user | have similar features be grouped in the same family | navigate the application easily | +| v1.0 | new user | list usable commands | better utilize the application when I unfamiliar with the commands | +| v1.0 | user | add my income and daily expense into the application | keep a record of my transaction history | +| v1.0 | user | add my salary into the application | gather insights from a trend of my income | +| v1.0 | user | add a category to each type of spending | have an organised view of my financial statements | +| v1.0 | user | view my daily expenditure | plan how I want to spend my remaining income throughout the rest of the week | +| v1.0 | user | know which category of expenses I spend the most on | better allocate my income for essential needs | +| v1.0 | careless user | delete my spending or expenses in the application | remove inputs that are false or outdated | +| v1.0 | careless user | receive an error message when entering a wrong command | be aware that I need to rectify my incorrect input | +| v1.0 | forgetful user | find a specific transaction | recall how much I spent or earned for a particular situation | +| v1.0 | busy user | purge all my transactions at one go | refrain from deleting my transactions one by one when needed | +| v2.0 | new user | be guided at the initial stage of using the application | make good use of the features | +| v2.0 | frequent user | input a file with all my expenses for the application to retrieve data | be more efficient and do not need to manually type my financial records into the command prompt | +| v2.0 | frequent user | save my input history into a file | have the inputs automatically read again in future without having to re-enter similar expenses each time I use the application | +| v2.0 | user | gather a summary of my expenditure over a time period (i.e., daily, weekly, monthly) | better understand my spending habits | +| v2.0 | user | know the amount of savings tabulated from income and expenditure after each month | review my spending and plan my budget for the next month | +| v2.0 | user | gather individual insights of different time periods after adding my transactions | analyze and reflect on the way I am managing my income and expenses | +| v2.0 | user | view recommended money-managing tips from the application | better improve my money-managing habits | +| v2.0 | user | set up and update my budget | limit my spending against a budget | +| v2.0 | user | archive my financial transactions from the previous years | focus on transactions that matter only for the current year | +| v2.0 | careless user | modify my spending or expenses in the application | rectify any false input | +| v2.0 | forgetful user | receive reminders on how I should spend my allowance | be consciously aware of my budget constraints | ## Appendix C: Non-Functional Requirements -{Give non-functional requirements} +1. Should work on common operating systems including Windows, macOSX and Linux as long as it has Java 11 or above installed. +2. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +3. Does not require an active connection to the Internet to use the application. +4. Should respond to commands within 3 seconds with no noticeable sluggishness in performance for typical usage. ## Appendix D: Glossary -* *glossary item* - Definition +- **Transaction:** An instance when someone makes or receives a payment including deposits, withdrawals, and exchanges +- **Budget:** An estimate of income or expenditure for a set period of time +- **Income:** Payment received from others for work or personal purpose +- **Expense:** Payment made to others for a purpose +- **Savings:** Portion of income that is not spent on current expenditures ## Appendix E: Instructions for manual testing -{Give instructions on how to do a manual product testing e.g., how to load sample data to be used for testing} +### Launch and Shutdown + +- Initial Launch + 1. Download the latest [duke.jar](https://github.com/AY2223S1-CS2113-W12-2/tp/releases/download/v2.0/duke.jar) and + copy it into a separate directory. + 2. Ensure that Java 11 has been installed and configured on your operating system. + 3. Launch a command prompt or terminal and run the command `java -jar duke.jar`.
+ **Expected:** Moolah Manager will display a greeting message and a remaining budget for the current month. + A data file, `duke.txt` may be loaded if it exists in `./data/` directory. + +- Shutdown + 1. Type `exit` to quit the program.
+ **Expected:** Moolah Manager will terminate and displays a goodbye message. + +### Storage + +- Loading Data + 1. Launch the application and change the state of the program, such as adding a new transaction. Close the window. + 2. Re-launch the application.
+ **Expected:** Moolah Manager will load the `duke.txt` data file and the state of the program is the same as when it was closed. + +- Missing Data File + 1. As per the instructions from loading data, check there is a `duke.txt` data file in `./data/` directory. + 2. In `./data/` directory, delete `duke.txt`. + 3. Re-launch the application.
+ **Expected:** No data file will be loaded into the application, and user may not see the former state of the program. + +- Corrupted Data File + 1. As per the instructions from loading data, check there is a `duke.txt` data file in `./data/` directory. + 2. In `./data/` directory, open `duke.txt` and try corrupting the records by e.g., removing the first pipe symbol from + the first row. Save the changes to the file. + 3. Re-launch the application.
+ **Expected:** The data file will not be loaded into the application, and user will be prompted that the file \ No newline at end of file From a236e8792ff0ca56dc96165c56f1f5bdc5cc3c3e Mon Sep 17 00:00:00 2001 From: chydarren Date: Wed, 26 Oct 2022 17:52:19 +0800 Subject: [PATCH 261/416] Add a full stop at the last line of DG --- docs/DeveloperGuide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 4dbc2b98b..f330b339a 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -674,4 +674,4 @@ transactions in an efficient and effective way. Moreover, it facilitates budget 2. In `./data/` directory, open `duke.txt` and try corrupting the records by e.g., removing the first pipe symbol from the first row. Save the changes to the file. 3. Re-launch the application.
- **Expected:** The data file will not be loaded into the application, and user will be prompted that the file \ No newline at end of file + **Expected:** The data file will not be loaded into the application, and user will be prompted that the file. \ No newline at end of file From 1204b1e9da27a02d6eb899f773b438766b6d1af9 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:44:22 +0800 Subject: [PATCH 262/416] Add Command cheat sheet to User Guide --- docs/UserGuide.md | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 70f18afc0..f6e503957 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -113,7 +113,44 @@ _Written by: Brian Wong Yun Long_ ## Command Summary -{Give a 'cheat sheet' of commands here} + +| Command | Command Syntax | Example | +|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------| +| help | help | help | +| help (detailed) | help o/detailed | help o/detailed | +| add | add t/TRANSACTION_TYPE c/CATEGORY_TYPE a/TRANSACTION_AMOUNT d/TRANSACTION_DATE i/ADDITIONAL_INFO | add t/expense c/transport a/1 d/02102022 i/bus_fare
add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss | +| list | list | list | +| list ( with filters ) | list c/OPTIONAL_CATEGORY d/OPTIONAL_DATE t/OPTIONAL_TRANSACTION_TYPE | list c/food d/13092022
list t/income d/30092022 | +| statistics for categorical savings | stats s/categorical_savings | stats s/categorical_savings | +| statistics for monthly expenditure | stats s/monthly_expenditure | stats s/monthly_expenditure | +| statistics for time insight of a specific year OR month of specific year | stats s/time_insights y/YEAR_NUMBER m/OPTIONAL_MONTH | stats s/time_insights y/2022
stats s/time_insights y/2002 m/10 | +| statistics for time insight for the PAST periods from current date | stats s/time_insights p/PERIODS n/NUMBER_OF_PERIODS | stats s/time_insights p/weeks n/3
stats s/time_insights p/months n/4 | +| budget | budget b/MONTHLY_BUDGET | budget b/9999999999999
budget b/1 | +| delete | delete e/TASK_NUMBER | delete e/3 | +| purge | purge | purge | +| find | find KEYWORD(s) | find bus_fare
find transport
find Sep 13 | +| bye | bye | bye | + +Mandatory Tags + +* The `TRANSACTION_TYPE` is either `"expense"` or `"income"`. +* The `CATEGORY_TYPE` is a one-word parameter flexibly defined by the user. [ No numerals, symbols or spacings are allowed ] +* The `TRANSACTION_AMOUNT` is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] +* The `TRANSACTION_DATE` MUST be in ddMMyyyy format. +* The `ADDITIONAL_INFO` is a single limitless parameter defined by the user. [ Spacings are not allowed ] +* The `TASK_NUMBER` is the entry value which is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] +* The `KEYWORD` are parameter values within Search-fields that would be searched. [ Available Search-fields: date, transaction type, category , amount, information. Cross-search across different Search-fields NOT supported ] +* The `MONTHLY_BUDGET` is a positive numeral that is above 0 and below a Trillion (1000000000000) . [ No alphabets, symbols or spacings allowed ] +* The `YEAR_NUMBER` is the year in yyyy format. +* The `PERIODS` is the selection of either periods in `"weeks"` or `"months"`. +* The `NUMBER_OF_PERIODS` is the number of periods to view. + + +Optional tags +* The `OPTIONAL_MONTH` is the month value in numerical form where January is represented by '1'. [ from 1 - 12 ] +* The `OPTIONAL_CATEGORY` is the category label for the transactions under the same transaction category +* The `OPTIONAL_DATE` is the date in ddMMyyyy format. +* The `OPTIONAL_TRANSACTION_TYPE` is either `"expense"` or `"income"`. _Written by: Yong Chin Han_ From 7ca0e1d893b350fba33cabfc954faaf49f9b3a66 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 19:47:59 +0800 Subject: [PATCH 263/416] Update minimum transaction value --- src/main/java/seedu/duke/common/Constants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index fd6f9600d..c7f5f35b8 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -19,7 +19,7 @@ public class Constants { public static int MAX_TRANSACTIONS_COUNT = 1000000; // The amount of one transaction is allowed to be in the range or 0 <= x <= 10000000 - public static int MIN_AMOUNT_VALUE = 0; + public static int MIN_AMOUNT_VALUE = 1; public static int MAX_AMOUNT_VALUE = 10000000; // The amount of transaction is allowed to be in the range or 1 <= x <= MAX_AMOUNT * MAX_TRANSACTION From 543cf797d2ff277b6e7798f87478ca219b685597 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 20:49:48 +0800 Subject: [PATCH 264/416] Update list description in Command Summary and Fix Arithmatic exception in StatsCommand --- docs/UserGuide.md | 41 ++++++++++--------- .../java/seedu/duke/command/ListCommand.java | 2 +- .../java/seedu/duke/common/Constants.java | 2 + .../java/seedu/duke/data/TransactionList.java | 39 +++++++++--------- text-ui-test/EXPECTED.TXT | 26 ++++++------ 5 files changed, 58 insertions(+), 52 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index f6e503957..350acb959 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -114,22 +114,22 @@ _Written by: Brian Wong Yun Long_ ## Command Summary -| Command | Command Syntax | Example | -|--------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------| -| help | help | help | -| help (detailed) | help o/detailed | help o/detailed | -| add | add t/TRANSACTION_TYPE c/CATEGORY_TYPE a/TRANSACTION_AMOUNT d/TRANSACTION_DATE i/ADDITIONAL_INFO | add t/expense c/transport a/1 d/02102022 i/bus_fare
add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss | -| list | list | list | -| list ( with filters ) | list c/OPTIONAL_CATEGORY d/OPTIONAL_DATE t/OPTIONAL_TRANSACTION_TYPE | list c/food d/13092022
list t/income d/30092022 | -| statistics for categorical savings | stats s/categorical_savings | stats s/categorical_savings | -| statistics for monthly expenditure | stats s/monthly_expenditure | stats s/monthly_expenditure | -| statistics for time insight of a specific year OR month of specific year | stats s/time_insights y/YEAR_NUMBER m/OPTIONAL_MONTH | stats s/time_insights y/2022
stats s/time_insights y/2002 m/10 | -| statistics for time insight for the PAST periods from current date | stats s/time_insights p/PERIODS n/NUMBER_OF_PERIODS | stats s/time_insights p/weeks n/3
stats s/time_insights p/months n/4 | -| budget | budget b/MONTHLY_BUDGET | budget b/9999999999999
budget b/1 | -| delete | delete e/TASK_NUMBER | delete e/3 | -| purge | purge | purge | -| find | find KEYWORD(s) | find bus_fare
find transport
find Sep 13 | -| bye | bye | bye | +| Command | Command Syntax | Example | +|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| help | help | help | +| help (detailed) | help o/detailed | help o/detailed | +| add | add t/TRANSACTION_TYPE c/CATEGORY_TYPE a/TRANSACTION_AMOUNT d/TRANSACTION_DATE i/ADDITIONAL_INFO | add t/expense c/transport a/1 d/02102022 i/bus_fare
add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss | +| list | list | list | +| list ( with filters ) | list c/OPTIONAL_CATEGORY d/OPTIONAL_DATE t/OPTIONAL_TRANSACTION_TYPE m/OPTIONAL_MONTH y/OPTIONAL_YEAR p/OPTIONAL_PERIODS n/OPTIONAL_NUMBER_OF_PERIODS | list c/food d/13092022
list t/income d/30092022
list p/months n/1
list t/income d/30092022 y/2022
list t/income d/30092022 y/2022 m/9 | +| statistics for categorical savings | stats s/categorical_savings | stats s/categorical_savings | +| statistics for monthly expenditure | stats s/monthly_expenditure | stats s/monthly_expenditure | +| statistics for time insight of a specific year OR month of specific year | stats s/time_insights y/YEAR_NUMBER m/OPTIONAL_MONTH | stats s/time_insights y/2022
stats s/time_insights y/2002 m/10 | +| statistics for time insight for the PAST periods from current date | stats s/time_insights p/PERIODS n/NUMBER_OF_PERIODS | stats s/time_insights p/weeks n/3
stats s/time_insights p/months n/4 | +| budget | budget b/MONTHLY_BUDGET | budget b/9999999999999
budget b/1 | +| delete | delete e/TASK_NUMBER | delete e/3 | +| purge | purge | purge | +| find | find KEYWORD(s) | find bus_fare
find transport
find Sep 13 | +| bye | bye | bye | Mandatory Tags @@ -142,14 +142,17 @@ Mandatory Tags * The `KEYWORD` are parameter values within Search-fields that would be searched. [ Available Search-fields: date, transaction type, category , amount, information. Cross-search across different Search-fields NOT supported ] * The `MONTHLY_BUDGET` is a positive numeral that is above 0 and below a Trillion (1000000000000) . [ No alphabets, symbols or spacings allowed ] * The `YEAR_NUMBER` is the year in yyyy format. -* The `PERIODS` is the selection of either periods in `"weeks"` or `"months"`. -* The `NUMBER_OF_PERIODS` is the number of periods to view. +* The `PERIODS` is the selection of either periods in `"weeks"` or `"months"`. [Used with "n/NUMBER_OF_PERIODS" ONLY] +* The `NUMBER_OF_PERIODS` is the number of periods to view. [Used with "p/PERIODS" ONLY] Optional tags -* The `OPTIONAL_MONTH` is the month value in numerical form where January is represented by '1'. [ from 1 - 12 ] +* The `OPTIONAL_MONTH` is the month value in numerical form where January is represented by '1'. [ from 1 - 12 . Must be used with y/OPTIONAL_YEAR or y/YEAR_NUMBER ] * The `OPTIONAL_CATEGORY` is the category label for the transactions under the same transaction category * The `OPTIONAL_DATE` is the date in ddMMyyyy format. +* The `OPTIONAL_YEAR` is the year in yyyy format.[ Not recommended to use with d/ tag , since if the year is different from the year in the date d/ tag, nothing would show.] +* The `OPTIONAL_PERIODS` is the selection of either periods in `"weeks"` or `"months"`. [Used with "n/OPTIONAL_NUMBER_OF_PERIODS" ] +* The `OPTIONAL_NUMBER_OF_PERIODS`is the number of periods to view. [Used with "p/OPTIONAL_PERIODS"] * The `OPTIONAL_TRANSACTION_TYPE` is either `"expense"` or `"income"`. _Written by: Yong Chin Han_ diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 44666a63e..5c94e9014 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -53,7 +53,7 @@ public class ListCommand extends ListAndStatsCommand { + "(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted." + "This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags." + LINE_SEPARATOR - + "(Optional) PERIOD: Period of the transaction. Only \"weeks\" or \"months\" is accepted. Note that" + + "(Optional) PERIOD: Period of the transaction. Only \"weeks\" or \"months\" is accepted. Note that " + "period must be accompanied by a number to backdate from. This tag cannot be used together with " + "[m/MONTH] or [y/YEAR] tags." + LINE_SEPARATOR diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index c7f5f35b8..e36333bba 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -22,7 +22,9 @@ public class Constants { public static int MIN_AMOUNT_VALUE = 1; public static int MAX_AMOUNT_VALUE = 10000000; + // The amount of transaction is allowed to be in the range or 1 <= x <= MAX_AMOUNT * MAX_TRANSACTION public static int MIN_BUDGET_VALUE = 1; + public static long MAX_BUDGET_VALUE = Long.valueOf(MAX_TRANSACTIONS_COUNT) * Long.valueOf(MAX_AMOUNT_VALUE); } diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 4e3b93b33..9bef7bbd1 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -311,8 +311,7 @@ public HashMap processMonthlyExpenditure(HashMap m int updatedIncome = monthlyExpenditure.get(date)[0] + income; int updatedExpense = monthlyExpenditure.get(date)[1] + expense; - monthlyExpenditure.put(date, new int[]{updatedIncome, updatedExpense, - updatedIncome - updatedExpense}); + monthlyExpenditure.put(date, new int[]{updatedIncome, updatedExpense, updatedIncome - updatedExpense}); } return monthlyExpenditure; @@ -326,16 +325,18 @@ public HashMap processMonthlyExpenditure(HashMap m * @return A string containing the comment related to the spending habit for the month. */ public String getSpendingHabitComment(int income, int savings) { - int savingsPercentage = HUNDRED_PERCENT * savings / income; - - if (savingsPercentage >= HUNDRED_PERCENT) { - return INFO_STATS_HABIT_VERY_HIGH_SAVINGS.toString(); - } else if (savingsPercentage >= SEVENTY_FIVE_PERCENT) { - return INFO_STATS_HABIT_HIGH_SAVINGS.toString(); - } else if (savingsPercentage >= FIFTY_PERCENT) { - return INFO_STATS_HABIT_MEDIUM_SAVINGS.toString(); - } else if (savingsPercentage >= TWENTY_FIVE_PERCENT) { - return INFO_STATS_HABIT_LOW_SAVINGS.toString(); + if (income >= MIN_AMOUNT_VALUE) { + int savingsPercentage = HUNDRED_PERCENT * savings / income; + + if (savingsPercentage >= HUNDRED_PERCENT) { + return INFO_STATS_HABIT_VERY_HIGH_SAVINGS.toString(); + } else if (savingsPercentage >= SEVENTY_FIVE_PERCENT) { + return INFO_STATS_HABIT_HIGH_SAVINGS.toString(); + } else if (savingsPercentage >= FIFTY_PERCENT) { + return INFO_STATS_HABIT_MEDIUM_SAVINGS.toString(); + } else if (savingsPercentage >= TWENTY_FIVE_PERCENT) { + return INFO_STATS_HABIT_LOW_SAVINGS.toString(); + } } return INFO_STATS_HABIT_VERY_LOW_SAVINGS.toString(); } @@ -345,7 +346,7 @@ public String getSpendingHabitComment(int income, int savings) { * * @return A string that represents the formatted monthly expenditure list. */ - public String listMonthlyExpenditure() { + public String listMonthlyExpenditure() { String monthlyExpenditureList = ""; HashMap monthlyExpenditure = new HashMap<>(); // Adds each amount from transactions list to the month and year in monthly expenditure hashmap @@ -373,11 +374,11 @@ public String listMonthlyExpenditure() { /** * Produces Categorical saving list for timeTransactions. * - * @param timeTransactions An instance of the TransactionList class. - * @param year A specified year. - * @param month A specified month. - * @param period A specified period of time. - * @param number A specified number of periods. + * @param timeTransactions An instance of the TransactionList class. + * @param year A specified year. + * @param month A specified month. + * @param period A specified period of time. + * @param number A specified number of periods. * @return String output of transactions for the time period. */ public String listTimeStats(ArrayList timeTransactions, int year, int month, String period, @@ -409,7 +410,7 @@ public String listTimeStats(ArrayList timeTransactions, int year, i /** * Produces Expense, Income and Savings statistics. * - * @param timeTransactions An instance of the TransactionList class. + * @param timeTransactions An instance of the TransactionList class. * @return An amount arraylist of Expense and Income. */ public ArrayList processTimeSummaryStats(ArrayList timeTransactions) { diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 75a81536d..a19bb3825 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -94,7 +94,7 @@ Parameters information: (Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". (Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. (Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted.This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. -(Optional) PERIOD: Period of the transaction. Only "weeks" or "months" is accepted. Note thatperiod must be accompanied by a number to backdate from. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. +(Optional) PERIOD: Period of the transaction. Only "weeks" or "months" is accepted. Note that period must be accompanied by a number to backdate from. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. (Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note thatnumber must be accompanied by a period that represents weeks or months. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. Command Word: FIND @@ -293,15 +293,15 @@ ____________________________________________________________ ____________________________________________________________ Here are your requested statistics for the monthly_expenditure type: [Sep 2022] -Income: $2000 -Expense: $20 +Income: $2001 +Expense: $21 Savings: $1980 Spending Habit: Excellent! You saved quite a lot this month. [Oct 2022] -Income: $0 +Income: $1 Expense: $1 -Savings: $-1 +Savings: $0 Spending Habit: Oops, you spent too much. Do manage your expenses within your constraints. @@ -316,8 +316,8 @@ Year: 2022 [food] $20 -----EXPENDITURE----- -Income: $2000 -Expense: $21 +Income: $2001 +Expense: $22 Savings: $1979 ____________________________________________________________ ____________________________________________________________ @@ -330,8 +330,8 @@ Year: 2022, Month: 10 [food] $20 -----EXPENDITURE----- -Income: $0 -Expense: $1 +Income: $1 +Expense: $2 Savings: $-1 ____________________________________________________________ ____________________________________________________________ @@ -347,8 +347,8 @@ The past 3 weeks: [food] $20 -----EXPENDITURE----- -Income: $0 -Expense: $0 +Income: $1 +Expense: $1 Savings: $0 ____________________________________________________________ ____________________________________________________________ @@ -361,8 +361,8 @@ The past 4 months: [food] $20 -----EXPENDITURE----- -Income: $2000 -Expense: $20 +Income: $2001 +Expense: $21 Savings: $1980 ____________________________________________________________ ____________________________________________________________ From ece5e2b0fe234dbf385d1585600fcb29a9c8b069 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 20:53:37 +0800 Subject: [PATCH 265/416] Update List description in Command Summary --- docs/UserGuide.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 350acb959..bf3541583 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,24 +2,24 @@ - [Introduction](#introduction) - [About This Guide](#about-this-guide) - * [What is in Moolah Manager](#what-is-in-moolah-manager) - * [Command Format](#command-format) + * [What is in Moolah Manager](#what-is-in-moolah-manager) + * [Command Format](#command-format) - [Getting Started](#getting-started) - * [Setting Up](#setting-up) - * [Viewing Help: `help`](#viewing-help---help-) + * [Setting Up](#setting-up) + * [Viewing Help: `help`](#viewing-help---help-) - [Managing Transactions](#managing-transactions) - * [Adding a Transaction: `add`](#adding-a-transaction---add-) - * [Editing a Transaction: `edit`](#editing-a-transaction---edit-) - * [Listing the Transactions: `list`](#listing-the-transactions---list-) - * [Searching for Transactions: `find`](#searching-for-transactions---find-) - * [Deleting a Transaction: `delete`](#deleting-a-transaction---delete-) - * [Purging all Transactions: `purge`](#purging-all-transactions---purge-) + * [Adding a Transaction: `add`](#adding-a-transaction---add-) + * [Editing a Transaction: `edit`](#editing-a-transaction---edit-) + * [Listing the Transactions: `list`](#listing-the-transactions---list-) + * [Searching for Transactions: `find`](#searching-for-transactions---find-) + * [Deleting a Transaction: `delete`](#deleting-a-transaction---delete-) + * [Purging all Transactions: `purge`](#purging-all-transactions---purge-) - [Budgeting and Financial Insights](#budgeting-and-financial-insights) - * [Viewing the Statistics: `stats`](#viewing-the-statistics---stats-) - * [Managing the Budget: `budget`](#managing-the-budget---budget-) + * [Viewing the Statistics: `stats`](#viewing-the-statistics---stats-) + * [Managing the Budget: `budget`](#managing-the-budget---budget-) - [General](#general) - * [Persistent Data](#persistent-data) - * [Exiting the Program: `exit`](#exiting-the-program---exit-) + * [Persistent Data](#persistent-data) + * [Exiting the Program: `exit`](#exiting-the-program---exit-) - [Command Summary](#command-summary) - [FAQ](#faq) @@ -27,13 +27,13 @@ _Written by: Chua Han Yong Darren_ -## About This Guide +## About This Guide ### What is in Moolah Manager _Written by: Paul Low_ -### Command Format +### Command Format _Written by: Chia Thin Hong_ @@ -133,7 +133,7 @@ _Written by: Brian Wong Yun Long_ Mandatory Tags -* The `TRANSACTION_TYPE` is either `"expense"` or `"income"`. +* The `TRANSACTION_TYPE` is either `"expense"` or `"income"`. * The `CATEGORY_TYPE` is a one-word parameter flexibly defined by the user. [ No numerals, symbols or spacings are allowed ] * The `TRANSACTION_AMOUNT` is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] * The `TRANSACTION_DATE` MUST be in ddMMyyyy format. @@ -157,7 +157,7 @@ Optional tags _Written by: Yong Chin Han_ -## FAQ +## FAQ **Q**: How do I transfer my data to another computer? From f30ed74dfdeb03e0f591f1bc5532ba969fe5f312 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 20:57:00 +0800 Subject: [PATCH 266/416] Update ListCommand COMMAND_PARAMETERS_INFO --- .../java/seedu/duke/command/ListCommand.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 5c94e9014..66c601e7f 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -50,14 +50,14 @@ public class ListCommand extends ListAndStatsCommand { + "month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] " + "tags." + LINE_SEPARATOR - + "(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted." + + "(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted. " + "This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags." + LINE_SEPARATOR + "(Optional) PERIOD: Period of the transaction. Only \"weeks\" or \"months\" is accepted. Note that " + "period must be accompanied by a number to backdate from. This tag cannot be used together with " + "[m/MONTH] or [y/YEAR] tags." + LINE_SEPARATOR - + "(Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note that" + + "(Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note that " + "number must be accompanied by a period that represents weeks or months. This tag cannot be used " + "together with [m/MONTH] or [y/YEAR] tags."; @@ -93,13 +93,13 @@ public ListCommand() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_GLOBAL_MONTH, - COMMAND_TAG_GLOBAL_YEAR, - COMMAND_TAG_GLOBAL_NUMBER, - COMMAND_TAG_GLOBAL_PERIOD + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_GLOBAL_MONTH, + COMMAND_TAG_GLOBAL_YEAR, + COMMAND_TAG_GLOBAL_NUMBER, + COMMAND_TAG_GLOBAL_PERIOD }; return optionalTags; } From 494531535f6342e8b1a320026c4438f8acaf983c Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 21:04:20 +0800 Subject: [PATCH 267/416] Update test files and reformat lines in ListCommand --- src/main/java/seedu/duke/command/ListCommand.java | 15 ++++++++------- text-ui-test/EXPECTED.TXT | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 66c601e7f..b9b417321 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -1,6 +1,7 @@ package seedu.duke.command; //@@author chydarren + import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.TransactionList; @@ -93,13 +94,13 @@ public ListCommand() { @Override public String[] getOptionalTags() { String[] optionalTags = new String[]{ - COMMAND_TAG_TRANSACTION_TYPE, - COMMAND_TAG_TRANSACTION_CATEGORY, - COMMAND_TAG_TRANSACTION_DATE, - COMMAND_TAG_GLOBAL_MONTH, - COMMAND_TAG_GLOBAL_YEAR, - COMMAND_TAG_GLOBAL_NUMBER, - COMMAND_TAG_GLOBAL_PERIOD + COMMAND_TAG_TRANSACTION_TYPE, + COMMAND_TAG_TRANSACTION_CATEGORY, + COMMAND_TAG_TRANSACTION_DATE, + COMMAND_TAG_GLOBAL_MONTH, + COMMAND_TAG_GLOBAL_YEAR, + COMMAND_TAG_GLOBAL_NUMBER, + COMMAND_TAG_GLOBAL_PERIOD }; return optionalTags; } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index a19bb3825..195745dc9 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -93,9 +93,9 @@ Parameters information: (Optional) CATEGORY: A category for the transaction. Only string containing alphabets is accepted. (Optional) DATE: Date of the transaction. The format must be in "yyyyMMdd". (Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. -(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted.This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. +(Optional) YEAR: Year of the transaction. Only integers from 1000 onwards are accepted. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] tags. (Optional) PERIOD: Period of the transaction. Only "weeks" or "months" is accepted. Note that period must be accompanied by a number to backdate from. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. -(Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note thatnumber must be accompanied by a period that represents weeks or months. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. +(Optional) NUMBER: Last number of weeks or months. Only positive integers are accepted. Note that number must be accompanied by a period that represents weeks or months. This tag cannot be used together with [m/MONTH] or [y/YEAR] tags. Command Word: FIND To find specific transaction(s) based on any keywords inputted by the user. From 028567623cba1181b6e4bbbae97254edd37a964f Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 21:17:00 +0800 Subject: [PATCH 268/416] Update MIN_AMOUNT_VALUE --- .../java/seedu/duke/common/Constants.java | 2 +- .../java/seedu/duke/data/TransactionList.java | 2 +- text-ui-test/EXPECTED.TXT | 24 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index e36333bba..e524a4c74 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -19,7 +19,7 @@ public class Constants { public static int MAX_TRANSACTIONS_COUNT = 1000000; // The amount of one transaction is allowed to be in the range or 0 <= x <= 10000000 - public static int MIN_AMOUNT_VALUE = 1; + public static int MIN_AMOUNT_VALUE = 0; public static int MAX_AMOUNT_VALUE = 10000000; diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 9bef7bbd1..a989349ce 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -325,7 +325,7 @@ public HashMap processMonthlyExpenditure(HashMap m * @return A string containing the comment related to the spending habit for the month. */ public String getSpendingHabitComment(int income, int savings) { - if (income >= MIN_AMOUNT_VALUE) { + if (income > MIN_AMOUNT_VALUE) { int savingsPercentage = HUNDRED_PERCENT * savings / income; if (savingsPercentage >= HUNDRED_PERCENT) { diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 195745dc9..de6f11b10 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -293,15 +293,15 @@ ____________________________________________________________ ____________________________________________________________ Here are your requested statistics for the monthly_expenditure type: [Sep 2022] -Income: $2001 -Expense: $21 +Income: $2000 +Expense: $20 Savings: $1980 Spending Habit: Excellent! You saved quite a lot this month. [Oct 2022] -Income: $1 +Income: $0 Expense: $1 -Savings: $0 +Savings: $-1 Spending Habit: Oops, you spent too much. Do manage your expenses within your constraints. @@ -316,8 +316,8 @@ Year: 2022 [food] $20 -----EXPENDITURE----- -Income: $2001 -Expense: $22 +Income: $2000 +Expense: $21 Savings: $1979 ____________________________________________________________ ____________________________________________________________ @@ -330,8 +330,8 @@ Year: 2022, Month: 10 [food] $20 -----EXPENDITURE----- -Income: $1 -Expense: $2 +Income: $0 +Expense: $1 Savings: $-1 ____________________________________________________________ ____________________________________________________________ @@ -347,8 +347,8 @@ The past 3 weeks: [food] $20 -----EXPENDITURE----- -Income: $1 -Expense: $1 +Income: $0 +Expense: $0 Savings: $0 ____________________________________________________________ ____________________________________________________________ @@ -361,8 +361,8 @@ The past 4 months: [food] $20 -----EXPENDITURE----- -Income: $2001 -Expense: $21 +Income: $2000 +Expense: $20 Savings: $1980 ____________________________________________________________ ____________________________________________________________ From acb069d18fcd15056bb4a1c1fcd8b080ef801d77 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 21:27:27 +0800 Subject: [PATCH 269/416] Update parser check on minimum amount for Transactions and add test cases --- src/main/java/seedu/duke/common/Constants.java | 2 +- src/main/java/seedu/duke/parser/ParameterParser.java | 2 +- text-ui-test/EXPECTED.TXT | 9 +++++++++ text-ui-test/input.txt | 3 +++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index e524a4c74..f0e8a6212 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -18,7 +18,7 @@ public class Constants { // One million transactions is the capacity allowed public static int MAX_TRANSACTIONS_COUNT = 1000000; - // The amount of one transaction is allowed to be in the range or 0 <= x <= 10000000 + // The amount of one transaction is allowed to be in the range or 0 < x <= 10000000 public static int MIN_AMOUNT_VALUE = 0; public static int MAX_AMOUNT_VALUE = 10000000; diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index d7b14611d..fc48d2710 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -425,7 +425,7 @@ private static int parseAmountTag(String parameter) throws InputTransactionInval throw new InputTransactionInvalidAmountException(); } int amount = Integer.parseInt(parameter); - if (amount < MIN_AMOUNT_VALUE || amount > MAX_AMOUNT_VALUE) { + if (amount <= MIN_AMOUNT_VALUE || amount > MAX_AMOUNT_VALUE) { parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + parameter); throw new InputTransactionInvalidAmountException(); diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index de6f11b10..4a1e9a912 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -217,6 +217,15 @@ I have added the following Expense transaction: Budget remained for Sep 2022: $9980. Keep it up! ____________________________________________________________ ____________________________________________________________ +Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +____________________________________________________________ +____________________________________________________________ +Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +____________________________________________________________ +____________________________________________________________ +Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +____________________________________________________________ +____________________________________________________________ I have added the following Income transaction: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary Budget remained for Sep 2022: $9980. Keep it up! diff --git a/text-ui-test/input.txt b/text-ui-test/input.txt index 2d9085e64..aeef527a2 100644 --- a/text-ui-test/input.txt +++ b/text-ui-test/input.txt @@ -24,6 +24,9 @@ stats s/categorical_savings s/categorical_savings stats s/categorical_savings list add t/expense c/food a/20 d/13092022 i/NIL +add t/expense c/food a/0 d/13092022 i/NIL +add t/expense c/food a/00000 d/13092022 i/NIL +add t/expense c/food a/9999999999999999999999999999999999999999999999999999999999999999999999999 d/13092022 i/NIL add t/income c/salary a/2000 d/30092022 i/jan_salary add t/expense c/transport a/1 d/02102022 i/bus_fare add t/ c/transport a/1 d/02102022 i/bus_fare From 23fbf53354f377fc6fdb94005d2929fccb80593c Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:18:43 +0800 Subject: [PATCH 270/416] Update parameter naming in command summary --- docs/UserGuide.md | 68 +++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index bf3541583..58c28b81f 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -114,46 +114,40 @@ _Written by: Brian Wong Yun Long_ ## Command Summary -| Command | Command Syntax | Example | -|--------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| help | help | help | -| help (detailed) | help o/detailed | help o/detailed | -| add | add t/TRANSACTION_TYPE c/CATEGORY_TYPE a/TRANSACTION_AMOUNT d/TRANSACTION_DATE i/ADDITIONAL_INFO | add t/expense c/transport a/1 d/02102022 i/bus_fare
add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss | -| list | list | list | -| list ( with filters ) | list c/OPTIONAL_CATEGORY d/OPTIONAL_DATE t/OPTIONAL_TRANSACTION_TYPE m/OPTIONAL_MONTH y/OPTIONAL_YEAR p/OPTIONAL_PERIODS n/OPTIONAL_NUMBER_OF_PERIODS | list c/food d/13092022
list t/income d/30092022
list p/months n/1
list t/income d/30092022 y/2022
list t/income d/30092022 y/2022 m/9 | -| statistics for categorical savings | stats s/categorical_savings | stats s/categorical_savings | -| statistics for monthly expenditure | stats s/monthly_expenditure | stats s/monthly_expenditure | -| statistics for time insight of a specific year OR month of specific year | stats s/time_insights y/YEAR_NUMBER m/OPTIONAL_MONTH | stats s/time_insights y/2022
stats s/time_insights y/2002 m/10 | -| statistics for time insight for the PAST periods from current date | stats s/time_insights p/PERIODS n/NUMBER_OF_PERIODS | stats s/time_insights p/weeks n/3
stats s/time_insights p/months n/4 | -| budget | budget b/MONTHLY_BUDGET | budget b/9999999999999
budget b/1 | -| delete | delete e/TASK_NUMBER | delete e/3 | -| purge | purge | purge | -| find | find KEYWORD(s) | find bus_fare
find transport
find Sep 13 | -| bye | bye | bye | - -Mandatory Tags - -* The `TRANSACTION_TYPE` is either `"expense"` or `"income"`. -* The `CATEGORY_TYPE` is a one-word parameter flexibly defined by the user. [ No numerals, symbols or spacings are allowed ] -* The `TRANSACTION_AMOUNT` is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] -* The `TRANSACTION_DATE` MUST be in ddMMyyyy format. -* The `ADDITIONAL_INFO` is a single limitless parameter defined by the user. [ Spacings are not allowed ] -* The `TASK_NUMBER` is the entry value which is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] -* The `KEYWORD` are parameter values within Search-fields that would be searched. [ Available Search-fields: date, transaction type, category , amount, information. Cross-search across different Search-fields NOT supported ] -* The `MONTHLY_BUDGET` is a positive numeral that is above 0 and below a Trillion (1000000000000) . [ No alphabets, symbols or spacings allowed ] -* The `YEAR_NUMBER` is the year in yyyy format. -* The `PERIODS` is the selection of either periods in `"weeks"` or `"months"`. [Used with "n/NUMBER_OF_PERIODS" ONLY] -* The `NUMBER_OF_PERIODS` is the number of periods to view. [Used with "p/PERIODS" ONLY] +| Command | Command Syntax | Example | +|--------------------------------------------------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| help | help | help | +| help (detailed) | help o/detailed | help o/detailed | +| add | add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION | add t/expense c/transport a/1 d/02102022 i/bus_fare
add t/income c/bonus a/10000000 d/03102022 i/thank_you_boss | +| list | list | list | +| list ( with filters ) | list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER] | list c/food d/13092022
list t/income d/30092022
list p/months n/1
list t/income d/30092022 y/2022
list t/income d/30092022 y/2022 m/9 | +| statistics for categorical savings | stats s/categorical_savings | stats s/categorical_savings | +| statistics for monthly expenditure | stats s/monthly_expenditure | stats s/monthly_expenditure | +| statistics for time insight of a specific year OR month of specific year | stats s/time_insights y/YEAR [m/MONTH] | stats s/time_insights y/2022
stats s/time_insights y/2002 m/10 | +| statistics for time insight for the PAST periods from current date | stats s/time_insights p/PERIOD n/NUMBER | stats s/time_insights p/weeks n/3
stats s/time_insights p/months n/12 | +| budget | budget b/BUDGET | budget b/9999999999999
budget b/1 | +| delete | delete e/ENTRY | delete e/3 | +| purge | purge | purge | +| find | find KEYWORDS | find bus_fare
find transport
find Sep 13 | +| bye | bye | bye | + +Tags used: +* The `TYPE` is either `"expense"` or `"income"`. +* The `CATEGORY` is a one-word parameter flexibly defined by the user. [ No numerals, symbols or spacings are allowed ] +* The `AMOUNT` is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] +* The `DATE` MUST be in ddMMyyyy format. +* The `DESCRIPTION` is a single limitless parameter defined by the user. [ Spacings are not allowed ] +* The `ENTRY` is the entry value which is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] +* The `KEYWORDS` are parameter value(s) within Search-fields that would be searched. [ Available Search-fields: date, type, category , amount, description. Cross-search across different Search-fields NOT supported ] +* The `BUDGET` is a positive numeral that is above 0 and below a Trillion (1000000000000) . [ No alphabets, symbols or spacings allowed ] +* The `YEAR` is the year in yyyy format. +* The `MONTH` is the month in numerical form. [ From 1 -12 , where 1 represents January ] +* The `PERIOD` is the selection of either periods in `"weeks"` or `"months"`. [ Used with "n/NUMBER" ONLY ] +* The `NUMBER` is the number of periods to view. [ Used with "p/PERIODS" ONLY ] Optional tags -* The `OPTIONAL_MONTH` is the month value in numerical form where January is represented by '1'. [ from 1 - 12 . Must be used with y/OPTIONAL_YEAR or y/YEAR_NUMBER ] -* The `OPTIONAL_CATEGORY` is the category label for the transactions under the same transaction category -* The `OPTIONAL_DATE` is the date in ddMMyyyy format. -* The `OPTIONAL_YEAR` is the year in yyyy format.[ Not recommended to use with d/ tag , since if the year is different from the year in the date d/ tag, nothing would show.] -* The `OPTIONAL_PERIODS` is the selection of either periods in `"weeks"` or `"months"`. [Used with "n/OPTIONAL_NUMBER_OF_PERIODS" ] -* The `OPTIONAL_NUMBER_OF_PERIODS`is the number of periods to view. [Used with "p/OPTIONAL_PERIODS"] -* The `OPTIONAL_TRANSACTION_TYPE` is either `"expense"` or `"income"`. +These tags are enclosed with "[" and "]" in the **command syntax** in the table above. _Written by: Yong Chin Han_ From 16ee27c91214b0e80d9ac345920a8f9e956235a1 Mon Sep 17 00:00:00 2001 From: chinhan99 <62207919+chinhan99@users.noreply.github.com> Date: Wed, 26 Oct 2022 22:26:03 +0800 Subject: [PATCH 271/416] Update Optional tag description for readability --- docs/UserGuide.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 58c28b81f..17e598b65 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -131,7 +131,7 @@ _Written by: Brian Wong Yun Long_ | find | find KEYWORDS | find bus_fare
find transport
find Sep 13 | | bye | bye | bye | -Tags used: +Tags used ( **OPTIONAL** tags are enclosed with SQUARE BRACKETS "[" and "]" in the **command syntax in the table** above): * The `TYPE` is either `"expense"` or `"income"`. * The `CATEGORY` is a one-word parameter flexibly defined by the user. [ No numerals, symbols or spacings are allowed ] * The `AMOUNT` is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] @@ -146,8 +146,6 @@ Tags used: * The `NUMBER` is the number of periods to view. [ Used with "p/PERIODS" ONLY ] -Optional tags -These tags are enclosed with "[" and "]" in the **command syntax** in the table above. _Written by: Yong Chin Han_ From 7257f30e47bd59fcac1b210f599a13ea94336f63 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 26 Oct 2022 23:27:27 +0800 Subject: [PATCH 272/416] Fix Stats Param Parser --- src/main/java/seedu/duke/common/ErrorMessages.java | 2 +- src/main/java/seedu/duke/parser/ParameterParser.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index a1b003caf..0520e9699 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -14,7 +14,7 @@ public enum ErrorMessages { ERROR_GLOBAL_EMPTY_PARAMETER("Parameter behind tag(s) is found to be empty, please check your input!"), ERROR_GLOBAL_INVALID_INDEX("Invalid index, please ensure your index is correct!"), ERROR_GLOBAL_INVALID_MONTH("Invalid month, please check your input! Note: Month should be between 1 to 12."), - ERROR_GLOBAL_INVALID_YEAR("Invalid year, please check your input! Note: Year should be 1000 onwards."), + ERROR_GLOBAL_INVALID_YEAR("Invalid year, please check your input! Note: Year should be 1000 - 9999."), ERROR_GLOBAL_INVALID_PERIOD("Type of period given is invalid, please check your input!"), ERROR_GLOBAL_INVALID_NUMBER("Number for period stats given is invalid, please check your input!"), ERROR_GLOBAL_NUMBER_NOT_NUMERIC("Non-integer detected, please enter a numerical integer!"), diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index d7b14611d..6940a615c 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -68,6 +68,7 @@ public class ParameterParser { private static final int SPLIT_POSITION = 2; private static final int MINIMUM_TAG_LENGTH = 2; private static final int MINIMUM_YEAR = 1000; + private static final int MAXIMUM_YEAR = 9999; private static final int SMALLEST_POSITIVE_INTEGER = 0; private static final int JANUARY = 1; private static final int DECEMBER = 12; @@ -597,7 +598,7 @@ public static int parseYearTag(String parameter) throws GlobalInvalidYearExcepti + parameter); throw new GlobalNumberNotNumericException(); } - if (year < MINIMUM_YEAR) { + if (year < MINIMUM_YEAR || year > MAXIMUM_YEAR) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); throw new GlobalInvalidYearException(); From e638d2c91d83cb0ffb215605047c4ca808e0b9bd Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 26 Oct 2022 23:58:47 +0800 Subject: [PATCH 273/416] Fix Stats Bugs --- .../java/seedu/duke/common/ErrorMessages.java | 1 + .../java/seedu/duke/data/TransactionList.java | 49 ++++++++++++++++++- .../StatsInvalidNumberException.java | 20 ++++++++ .../seedu/duke/parser/ParameterParser.java | 24 ++++----- text-ui-test/EXPECTED.TXT | 6 --- 5 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 src/main/java/seedu/duke/exception/StatsInvalidNumberException.java diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 0520e9699..84cc3505d 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -25,6 +25,7 @@ public enum ErrorMessages { ERROR_TRANSACTION_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), ERROR_FIND_COMMAND_MISSING_KEYWORDS("Keyword(s) for your search expression missing, please check your input!"), ERROR_STATS_COMMAND_INVALID_TYPE("Type of statistics given is invalid, please check your input!"), + ERROR_STATS_INVALID_NUMBER("Invalid number, please check your input! Note: Stats number must between 1 - 100"), ERROR_UNKNOWN_HELP_OPTION("The parameter used for help option is unknown, please check your input!"), ERROR_STORAGE_TRANSACTION_CORRUPTED("Transaction values corrupted." + " To preserve data, please STOP the program and edit your data file correctly."), diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 4e3b93b33..de9589c55 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -262,8 +262,31 @@ public HashMap processCategoricalSavings(HashMap processCategoricalSavings(ArrayList timeTransactions, + HashMap categoricalSavings) { + for (Transaction transaction : timeTransactions) { + String category = transaction.getCategory(); + int amount = transaction.getAmount(); + // Creates a new category with starter amount if category not exists in hashmap + if (!categoricalSavings.containsKey(category)) { + categoricalSavings.put(category, amount); + continue; + } + categoricalSavings.put(category, categoricalSavings.get(category) + amount); + } + + return categoricalSavings; + } + + /** + * Formats the hashmap of categorical savings into a categorical savings list, using default transactions. * * @return A string that represents the formatted categorical savings list. */ @@ -282,6 +305,28 @@ public String listCategoricalSavings() { return categoricalSavingsList; } + //@@chydarren + /** + * Formats the hashmap of categorical savings into a categorical savings list, using timeTransactions. + * + * @param timeTransactions An arraylist of transactions. + * @return A string that represents the formatted categorical savings list. + */ + public String listCategoricalSavings(ArrayList timeTransactions) { + String categoricalSavingsList = ""; + HashMap categoricalSavings = new HashMap<>(); + // Adds each amount from transactions list to the categories in categorical savings hashmap + categoricalSavings = processCategoricalSavings(timeTransactions, categoricalSavings); + + // Formats every entry in the hashmap into a categorical savings list + for (HashMap.Entry entry : categoricalSavings.entrySet()) { + categoricalSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getKey(), + POSTFIX_CATEGORY, DOLLAR_SIGN, entry.getValue(), LINE_SEPARATOR); + } + + return categoricalSavingsList; + } + /** * Reads the transactions list and adds each amount to the month and year in monthly savings hashmap. * @@ -395,7 +440,7 @@ public String listTimeStats(ArrayList timeTransactions, int year, i + LINE_SEPARATOR + LINE_SEPARATOR + INFO_STATS_CATEGORIES_HEADER + LINE_SEPARATOR; } - String categoricalList = listCategoricalSavings(); + String categoricalList = listCategoricalSavings(timeTransactions); timeInsightsList += categoricalList; // Formats every entry in the hashmap into a time insights list //for (Transaction entry : timeTransactions) { diff --git a/src/main/java/seedu/duke/exception/StatsInvalidNumberException.java b/src/main/java/seedu/duke/exception/StatsInvalidNumberException.java new file mode 100644 index 000000000..5ba788acc --- /dev/null +++ b/src/main/java/seedu/duke/exception/StatsInvalidNumberException.java @@ -0,0 +1,20 @@ +package seedu.duke.exception; + +//@@author paullowse +import seedu.duke.common.ErrorMessages; + +/** + * Represents the exception where the type given for requesting statistics is not valid. + */ +public class StatsInvalidNumberException extends MoolahException { + + /** + * Returns the error message of the exception to alert user of the exception. + * + * @return A string containing the error message + */ + @Override + public String getMessage() { + return ErrorMessages.ERROR_STATS_INVALID_NUMBER.toString(); + } +} diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 6940a615c..d8f6f9997 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -7,24 +7,24 @@ import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; +import seedu.duke.exception.GlobalDuplicateTagException; import seedu.duke.exception.GlobalEmptyParameterException; import seedu.duke.exception.GlobalInvalidMonthException; -import seedu.duke.exception.GlobalInvalidNumberException; import seedu.duke.exception.GlobalInvalidPeriodException; import seedu.duke.exception.GlobalInvalidYearException; -import seedu.duke.exception.GlobalNumberNotNumericException; -import seedu.duke.exception.GlobalDuplicateTagException; import seedu.duke.exception.GlobalMissingTagException; +import seedu.duke.exception.GlobalNumberNotNumericException; +import seedu.duke.exception.GlobalUnsupportedTagException; +import seedu.duke.exception.HelpUnknownOptionException; +import seedu.duke.exception.InputBudgetDuplicateException; +import seedu.duke.exception.InputBudgetInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; import seedu.duke.exception.InputTransactionInvalidTypeException; -import seedu.duke.exception.GlobalUnsupportedTagException; -import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; -import seedu.duke.exception.HelpUnknownOptionException; -import seedu.duke.exception.InputBudgetInvalidAmountException; -import seedu.duke.exception.InputBudgetDuplicateException; +import seedu.duke.exception.StatsInvalidNumberException; +import seedu.duke.exception.StatsInvalidTypeException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -69,6 +69,7 @@ public class ParameterParser { private static final int MINIMUM_TAG_LENGTH = 2; private static final int MINIMUM_YEAR = 1000; private static final int MAXIMUM_YEAR = 9999; + private static final int MAXIMUM_STATS_NUMBER = 100; private static final int SMALLEST_POSITIVE_INTEGER = 0; private static final int JANUARY = 1; private static final int DECEMBER = 12; @@ -620,8 +621,7 @@ public static String parsePeriodTag(String parameter) throws GlobalInvalidPeriod } } - public static int parseNumberTag(String parameter) throws GlobalNumberNotNumericException, - GlobalInvalidNumberException { + public static int parseNumberTag(String parameter) throws MoolahException { int statsNumber; try { statsNumber = Integer.parseInt(parameter); @@ -630,10 +630,10 @@ public static int parseNumberTag(String parameter) throws GlobalNumberNotNumeric + parameter); throw new GlobalNumberNotNumericException(); } - if (statsNumber < SMALLEST_POSITIVE_INTEGER) { + if (statsNumber <= SMALLEST_POSITIVE_INTEGER || statsNumber > MAXIMUM_STATS_NUMBER) { parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + parameter); - throw new GlobalInvalidNumberException(); + throw new StatsInvalidNumberException(); } return statsNumber; } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 75a81536d..2f83194a4 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -326,8 +326,6 @@ Year: 2022, Month: 10 -----CATEGORIES----- [transport] $1 -[salary] $2000 -[food] $20 -----EXPENDITURE----- Income: $0 @@ -342,9 +340,6 @@ Here are the total savings and expenses for The past 3 weeks: -----CATEGORIES----- -[transport] $1 -[salary] $2000 -[food] $20 -----EXPENDITURE----- Income: $0 @@ -356,7 +351,6 @@ Here are the total savings and expenses for The past 4 months: -----CATEGORIES----- -[transport] $1 [salary] $2000 [food] $20 From 3a1b74550ceef7afc4f4f930b472a3ba89012fe1 Mon Sep 17 00:00:00 2001 From: Chia Thin Hong <37980219+wcwy@users.noreply.github.com> Date: Thu, 27 Oct 2022 02:08:36 +0800 Subject: [PATCH 274/416] Update UserGuide.md Added guides for command format, help command and budget command --- docs/UserGuide.md | 101 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 17e598b65..dad61f31d 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -6,23 +6,25 @@ * [Command Format](#command-format) - [Getting Started](#getting-started) * [Setting Up](#setting-up) - * [Viewing Help: `help`](#viewing-help---help-) + * [Viewing Help: `help`](#viewing-help-help) - [Managing Transactions](#managing-transactions) - * [Adding a Transaction: `add`](#adding-a-transaction---add-) - * [Editing a Transaction: `edit`](#editing-a-transaction---edit-) - * [Listing the Transactions: `list`](#listing-the-transactions---list-) - * [Searching for Transactions: `find`](#searching-for-transactions---find-) - * [Deleting a Transaction: `delete`](#deleting-a-transaction---delete-) - * [Purging all Transactions: `purge`](#purging-all-transactions---purge-) + * [Adding a Transaction: `add`](#adding-a-transaction-add) + * [Editing a Transaction: `edit`](#editing-a-transaction-edit) + * [Listing the Transactions: `list`](#listing-the-transactions-list) + * [Searching for Transactions: `find`](#searching-for-transactions-find) + * [Deleting a Transaction: `delete`](#deleting-a-transaction-delete) + * [Purging all Transactions: `purge`](#purging-all-transactions-purge) - [Budgeting and Financial Insights](#budgeting-and-financial-insights) - * [Viewing the Statistics: `stats`](#viewing-the-statistics---stats-) - * [Managing the Budget: `budget`](#managing-the-budget---budget-) + * [Viewing the Statistics: `stats`](#viewing-the-statistics-stats) + * [Managing the Budget: `budget`](#managing-the-budget-budget) - [General](#general) * [Persistent Data](#persistent-data) - * [Exiting the Program: `exit`](#exiting-the-program---exit-) + * [Exiting the Program: `exit`](#exiting-the-program-exit) - [Command Summary](#command-summary) - [FAQ](#faq) + + ## Introduction _Written by: Chua Han Yong Darren_ @@ -35,6 +37,20 @@ _Written by: Paul Low_ ### Command Format +General illustrations about the text format used for the commands in this guide can be found below. + +1. The **first word** in the command refers to the command word to be supplied by the user. The command words are case-insensitive. + 1. e.g. `exit`, `EXIT` and `eXiT` will all both interpreted as the same. + +2. Words in `UPPER CASE` refer to the parameters in each command. Each parameter must be prepended with a tag. + 1. e.g. in `delete e/ENTRY`, `delete` is the command word, while `ENTRY` is a parameter expected after the tag `e/`. + +3. Parameters in square brackets are optional. + 1. e.g. `help [o/detailed]` can be used as `help o/detailed` or `help. + 2. Note that the parameter `detailed` is written in lower case here, which means, only the exact wording is accepted. + +4. Each parameter is separated by a space. Users are not allowed to use spaces in their parameter. + _Written by: Chia Thin Hong_ ## Getting Started @@ -50,6 +66,38 @@ _Written by: Chua Han Yong Darren_ ### Viewing Help: `help` +Display basic or detailed help message explaining the command available in the application. + +Format: `help [o/detailed]` + +* To view detailed help, the exact `o/detailed` must be added in the command. + +Examples of usage: + + - `help` + + - `help o/detailed` + +Expected output: +``` +help +____________________________________________________________ +Gotcha! Here are the commands that you may use: +Command Word: HELP +To display a list of available commands with their respective expected parameters. +Type "help o/detailed" for a detailed version of all parameters used. +Usage: help [o/detailed] + +Command Word: BUDGET +To set the amount of monthly budget. +Usage: budget b/BUDGET + +Command Word: ADD +To add a new transaction entry, which could be either an "income" or an "expense" into the transaction list. +Usage: add t/TYPE c/CATEGORY a/AMOUNT d/DATE i/DESCRIPTION +... (Similar output for other commands truncated) +``` + _Written by: Chia Thin Hong_ ## Managing Transactions @@ -63,12 +111,17 @@ Format: `todo n/TODO_NAME d/DEADLINE` * The `DEADLINE` can be in a natural language format. * The `TODO_NAME` cannot contain punctuation. -Example of usage: +Examples of usage: `todo n/Write the rest of the User Guide d/next week` `todo n/Refactor the User Guide to remove passive voice d/13/04/2020` +Expected output: +``` + +``` + _Written by: Yong Chin Han_ ### Editing a Transaction: `edit` @@ -98,6 +151,28 @@ _Written by: Brian Wong Yun Long_ _Written by: Paul Low_ ### Managing the Budget: `budget` +To set the amount of monthly budget, with a value between 1 - 1013. + +Format: `budget b/BUDGET` + +* The `BUDGET` must be a whole number between 1 and 1013. +* The monthly budget is set to $1000 by default if the user has never run this command. + +Examples of usage: + +`budget b/1000` + +`budget b/52013149999` + +Expected output: +``` +budget b/52013149999 +____________________________________________________________ +You have successfully updated the budget. +Monthly budget set as: $52013149999 +____________________________________________________________ +``` + _Written by: Chia Thin Hong_ @@ -139,7 +214,7 @@ Tags used ( **OPTIONAL** tags are enclosed with SQUARE BRACKETS "[" and "]" in t * The `DESCRIPTION` is a single limitless parameter defined by the user. [ Spacings are not allowed ] * The `ENTRY` is the entry value which is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] * The `KEYWORDS` are parameter value(s) within Search-fields that would be searched. [ Available Search-fields: date, type, category , amount, description. Cross-search across different Search-fields NOT supported ] -* The `BUDGET` is a positive numeral that is above 0 and below a Trillion (1000000000000) . [ No alphabets, symbols or spacings allowed ] +* The `BUDGET` is a positive numeral that is above 0 and below Ten Trillion (10000000000000) . [ No alphabets, symbols or spacings allowed ] * The `YEAR` is the year in yyyy format. * The `MONTH` is the month in numerical form. [ From 1 -12 , where 1 represents January ] * The `PERIOD` is the selection of either periods in `"weeks"` or `"months"`. [ Used with "n/NUMBER" ONLY ] @@ -155,4 +230,4 @@ _Written by: Yong Chin Han_ **A**: {your answer here} -_Written by: Paul Low_ \ No newline at end of file +_Written by: Paul Low_ From 40ebbeeb857cd39c3b75cc3e655e69090b0c6e03 Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 27 Oct 2022 02:54:59 +0800 Subject: [PATCH 275/416] Change the value of MIN_AMOUNT_VALUE and update logic accordingly MIN_AMOUNT_VALUE is now correctly representing the minimum amount that a user can set for a transaction. --- src/main/java/seedu/duke/common/Constants.java | 6 +++--- src/main/java/seedu/duke/data/TransactionList.java | 11 ++++++----- src/main/java/seedu/duke/parser/ParameterParser.java | 2 +- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index f0e8a6212..b22234d5d 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -18,12 +18,12 @@ public class Constants { // One million transactions is the capacity allowed public static int MAX_TRANSACTIONS_COUNT = 1000000; - // The amount of one transaction is allowed to be in the range or 0 < x <= 10000000 - public static int MIN_AMOUNT_VALUE = 0; + // The amount of one transaction is allowed to be in the range of 1 <= x <= 10000000 + public static int MIN_AMOUNT_VALUE = 1; public static int MAX_AMOUNT_VALUE = 10000000; - // The amount of transaction is allowed to be in the range or 1 <= x <= MAX_AMOUNT * MAX_TRANSACTION + // The amount of transaction is allowed to be in the range or 1 <= x <= MAX_AMOUNT * MAX_TRANSACTION (10^13) public static int MIN_BUDGET_VALUE = 1; public static long MAX_BUDGET_VALUE = Long.valueOf(MAX_TRANSACTIONS_COUNT) * Long.valueOf(MAX_AMOUNT_VALUE); diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index 63840eada..ce515b571 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -44,6 +44,7 @@ public class TransactionList { private static final String POSTFIX_CATEGORY = "]"; private static final String INCOME = "income"; private static final String EXPENSE = "expense"; + private static final int INITIAL_AMOUNT = 0; private static final int HUNDRED_PERCENT = 100; private static final int SEVENTY_FIVE_PERCENT = 75; private static final int FIFTY_PERCENT = 50; @@ -336,8 +337,8 @@ public String listCategoricalSavings(ArrayList timeTransactions) { public HashMap processMonthlyExpenditure(HashMap monthlyExpenditure) { for (Transaction transaction : transactions) { String date = transaction.getDate().format(DateTimeFormatter.ofPattern(DATE_MONTH_PATTERN.toString())); - int income = MIN_AMOUNT_VALUE; - int expense = MIN_AMOUNT_VALUE; + int income = INITIAL_AMOUNT; + int expense = INITIAL_AMOUNT; // Checks whether transaction is Income or Expense and places in respective amount if (transaction instanceof Income) { @@ -370,7 +371,7 @@ public HashMap processMonthlyExpenditure(HashMap m * @return A string containing the comment related to the spending habit for the month. */ public String getSpendingHabitComment(int income, int savings) { - if (income > MIN_AMOUNT_VALUE) { + if (income >= MIN_AMOUNT_VALUE) { int savingsPercentage = HUNDRED_PERCENT * savings / income; if (savingsPercentage >= HUNDRED_PERCENT) { @@ -459,8 +460,8 @@ public String listTimeStats(ArrayList timeTransactions, int year, i * @return An amount arraylist of Expense and Income. */ public ArrayList processTimeSummaryStats(ArrayList timeTransactions) { - int timeExpense = MIN_AMOUNT_VALUE; - int timeIncome = MIN_AMOUNT_VALUE; + int timeExpense = INITIAL_AMOUNT; + int timeIncome = INITIAL_AMOUNT; for (Transaction entry : timeTransactions) { String category = entry.getType(); if (category.equals(EXPENSE)) { diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 005521ec2..d8f6f9997 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -427,7 +427,7 @@ private static int parseAmountTag(String parameter) throws InputTransactionInval throw new InputTransactionInvalidAmountException(); } int amount = Integer.parseInt(parameter); - if (amount <= MIN_AMOUNT_VALUE || amount > MAX_AMOUNT_VALUE) { + if (amount < MIN_AMOUNT_VALUE || amount > MAX_AMOUNT_VALUE) { parserLogger.log(Level.WARNING, "An invalid amount error is caught for the given parameter: " + parameter); throw new InputTransactionInvalidAmountException(); From 164d89cdddd155e0a8de2370df89d7cefe66026c Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 27 Oct 2022 02:57:34 +0800 Subject: [PATCH 276/416] Modify the wordings used in UG for clearer constraint description --- docs/UserGuide.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index dad61f31d..0fb34871b 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -208,15 +208,16 @@ _Written by: Brian Wong Yun Long_ Tags used ( **OPTIONAL** tags are enclosed with SQUARE BRACKETS "[" and "]" in the **command syntax in the table** above): * The `TYPE` is either `"expense"` or `"income"`. -* The `CATEGORY` is a one-word parameter flexibly defined by the user. [ No numerals, symbols or spacings are allowed ] -* The `AMOUNT` is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] -* The `DATE` MUST be in ddMMyyyy format. -* The `DESCRIPTION` is a single limitless parameter defined by the user. [ Spacings are not allowed ] -* The `ENTRY` is the entry value which is a positive numeral that is above 0 and below 100000001. [ No alphabets, symbols or spacings allowed ] -* The `KEYWORDS` are parameter value(s) within Search-fields that would be searched. [ Available Search-fields: date, type, category , amount, description. Cross-search across different Search-fields NOT supported ] -* The `BUDGET` is a positive numeral that is above 0 and below Ten Trillion (10000000000000) . [ No alphabets, symbols or spacings allowed ] -* The `YEAR` is the year in yyyy format. -* The `MONTH` is the month in numerical form. [ From 1 -12 , where 1 represents January ] +* The `CATEGORY` is a one-word parameter flexibly defined by the user. [ No numeral, symbol or spacing is allowed ] +* The `AMOUNT` is a positive whole number that is between 1 and 10000000. [ No alphabet, symbol or spacing is allowed ] +* The `DATE` MUST be in ddMMyyyy format. [ E.g. 29102022 ] +* The `DESCRIPTION` is a one-word parameter defined by the user. [ Spacing is not allowed ] +* The `ENTRY` is the entry value which is a positive numeral that is between 1 and 1000000. [ No alphabet, symbol or spacing is allowed ] +* The `KEYWORDS` is any string used to find matching transactions. [ Spacing is allowed (Special Case) ] + +* The `BUDGET` is a positive whole number that is between 1 and 1013 (Ten Trillion). [ No alphabet, symbol or spacing is allowed ] +* The `YEAR` is the year in yyyy format. +* The `MONTH` is the month in numerical form. [ From 1 to 12 , where 1 represents January etc. ] * The `PERIOD` is the selection of either periods in `"weeks"` or `"months"`. [ Used with "n/NUMBER" ONLY ] * The `NUMBER` is the number of periods to view. [ Used with "p/PERIODS" ONLY ] From e962f2ca13b54f67a083c5bd87f2456c392bb3f0 Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 27 Oct 2022 03:16:33 +0800 Subject: [PATCH 277/416] Change usage of "numerals" to "whole number" and move min-max constants --- docs/UserGuide.md | 4 ++-- src/main/java/seedu/duke/common/Constants.java | 13 +++++++++++++ .../java/seedu/duke/common/ErrorMessages.java | 2 +- .../java/seedu/duke/parser/ParameterParser.java | 16 ++++++++-------- text-ui-test/EXPECTED.TXT | 12 ++++++------ 5 files changed, 30 insertions(+), 17 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 0fb34871b..d5aa04dad 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -212,14 +212,14 @@ Tags used ( **OPTIONAL** tags are enclosed with SQUARE BRACKETS "[" and "]" in t * The `AMOUNT` is a positive whole number that is between 1 and 10000000. [ No alphabet, symbol or spacing is allowed ] * The `DATE` MUST be in ddMMyyyy format. [ E.g. 29102022 ] * The `DESCRIPTION` is a one-word parameter defined by the user. [ Spacing is not allowed ] -* The `ENTRY` is the entry value which is a positive numeral that is between 1 and 1000000. [ No alphabet, symbol or spacing is allowed ] +* The `ENTRY` is the entry value which is a positive whole number that is between 1 and 1000000. [ No alphabet, symbol or spacing is allowed ] * The `KEYWORDS` is any string used to find matching transactions. [ Spacing is allowed (Special Case) ] * The `BUDGET` is a positive whole number that is between 1 and 1013 (Ten Trillion). [ No alphabet, symbol or spacing is allowed ] * The `YEAR` is the year in yyyy format. * The `MONTH` is the month in numerical form. [ From 1 to 12 , where 1 represents January etc. ] * The `PERIOD` is the selection of either periods in `"weeks"` or `"months"`. [ Used with "n/NUMBER" ONLY ] -* The `NUMBER` is the number of periods to view. [ Used with "p/PERIODS" ONLY ] +* The `NUMBER` is a positive whole number that is between 1 and 100, representing the number of periods to view. [ Used with "p/PERIODS" ONLY ] diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index b22234d5d..e6abcdda0 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -9,6 +9,16 @@ *

Note that altering the values in this folder may result in integer overflow in the program in extreme cases. */ public class Constants { + //@@author paullowse + + public static final int MINIMUM_YEAR = 1000; + public static final int MAXIMUM_YEAR = 9999; + public static final int MAXIMUM_STATS_NUMBER = 100; + public static final int MINIMUM_STATS_NUMBER = 1; + + + //@@author wcwy + /* WARNING: Editing the values below may result in integer overflow in 1. TransactionList.calculateMonthlyTotalExpense() @@ -18,10 +28,13 @@ public class Constants { // One million transactions is the capacity allowed public static int MAX_TRANSACTIONS_COUNT = 1000000; + //@@author chinhan99 + // The amount of one transaction is allowed to be in the range of 1 <= x <= 10000000 public static int MIN_AMOUNT_VALUE = 1; public static int MAX_AMOUNT_VALUE = 10000000; + //@@author wcwy // The amount of transaction is allowed to be in the range or 1 <= x <= MAX_AMOUNT * MAX_TRANSACTION (10^13) public static int MIN_BUDGET_VALUE = 1; diff --git a/src/main/java/seedu/duke/common/ErrorMessages.java b/src/main/java/seedu/duke/common/ErrorMessages.java index 84cc3505d..c04922259 100644 --- a/src/main/java/seedu/duke/common/ErrorMessages.java +++ b/src/main/java/seedu/duke/common/ErrorMessages.java @@ -19,7 +19,7 @@ public enum ErrorMessages { ERROR_GLOBAL_INVALID_NUMBER("Number for period stats given is invalid, please check your input!"), ERROR_GLOBAL_NUMBER_NOT_NUMERIC("Non-integer detected, please enter a numerical integer!"), ERROR_TRANSACTION_INVALID_AMOUNT("Invalid amount, " - + "please ensure your amount is in positive numerals ($10000000 or less) only!"), + + "please ensure your amount is in positive whole number ($10000000 or less) only!"), ERROR_TRANSACTION_INVALID_CATEGORY("Invalid category, please ensure your category is correct!"), ERROR_TRANSACTION_INVALID_DATE("Invalid date, please ensure your date format is correct!"), ERROR_TRANSACTION_INVALID_TYPE("Type of transaction given is invalid, please check your input!"), diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index d8f6f9997..bc044a271 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -36,8 +36,6 @@ import java.util.regex.Pattern; import static seedu.duke.command.CommandTag.COMMAND_TAG_BUDGET_AMOUNT; -import static seedu.duke.common.Constants.MAX_BUDGET_VALUE; -import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; @@ -51,8 +49,14 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; +import static seedu.duke.common.Constants.MAX_BUDGET_VALUE; +import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; +import static seedu.duke.common.Constants.MINIMUM_YEAR; +import static seedu.duke.common.Constants.MAXIMUM_YEAR; +import static seedu.duke.common.Constants.MAXIMUM_STATS_NUMBER; +import static seedu.duke.common.Constants.MINIMUM_STATS_NUMBER; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; /** @@ -67,10 +71,6 @@ public class ParameterParser { private static final String DELIMITER = " "; private static final int SPLIT_POSITION = 2; private static final int MINIMUM_TAG_LENGTH = 2; - private static final int MINIMUM_YEAR = 1000; - private static final int MAXIMUM_YEAR = 9999; - private static final int MAXIMUM_STATS_NUMBER = 100; - private static final int SMALLEST_POSITIVE_INTEGER = 0; private static final int JANUARY = 1; private static final int DECEMBER = 12; private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; @@ -630,8 +630,8 @@ public static int parseNumberTag(String parameter) throws MoolahException { + parameter); throw new GlobalNumberNotNumericException(); } - if (statsNumber <= SMALLEST_POSITIVE_INTEGER || statsNumber > MAXIMUM_STATS_NUMBER) { - parserLogger.log(Level.WARNING, "An invalid year number error is caught for the given parameter: " + if (statsNumber < MINIMUM_STATS_NUMBER || statsNumber > MAXIMUM_STATS_NUMBER) { + parserLogger.log(Level.WARNING, "An invalid number error is caught for the given parameter: " + parameter); throw new StatsInvalidNumberException(); } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 86f295a80..edf6a2552 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -217,13 +217,13 @@ I have added the following Expense transaction: Budget remained for Sep 2022: $9980. Keep it up! ____________________________________________________________ ____________________________________________________________ -Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +Invalid amount, please ensure your amount is in positive whole number ($10000000 or less) only! ____________________________________________________________ ____________________________________________________________ -Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +Invalid amount, please ensure your amount is in positive whole number ($10000000 or less) only! ____________________________________________________________ ____________________________________________________________ -Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +Invalid amount, please ensure your amount is in positive whole number ($10000000 or less) only! ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: @@ -410,13 +410,13 @@ ____________________________________________________________ Invalid category, please ensure your category is correct! ____________________________________________________________ ____________________________________________________________ -Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +Invalid amount, please ensure your amount is in positive whole number ($10000000 or less) only! ____________________________________________________________ ____________________________________________________________ -Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +Invalid amount, please ensure your amount is in positive whole number ($10000000 or less) only! ____________________________________________________________ ____________________________________________________________ -Invalid amount, please ensure your amount is in positive numerals ($10000000 or less) only! +Invalid amount, please ensure your amount is in positive whole number ($10000000 or less) only! ____________________________________________________________ ____________________________________________________________ Invalid date, please ensure your date format is correct! From ce1f507c1c8fc53cd95d92dbf1c80bfac71239e7 Mon Sep 17 00:00:00 2001 From: wcwy Date: Thu, 27 Oct 2022 03:22:49 +0800 Subject: [PATCH 278/416] Changing the usage of "between ... and" as per @chinhan99 suggestion --- docs/UserGuide.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index d5aa04dad..7a430dec4 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -151,11 +151,11 @@ _Written by: Brian Wong Yun Long_ _Written by: Paul Low_ ### Managing the Budget: `budget` -To set the amount of monthly budget, with a value between 1 - 1013. +To set the amount of monthly budget, with a value from 1 to 1013. Format: `budget b/BUDGET` -* The `BUDGET` must be a whole number between 1 and 1013. +* The `BUDGET` must be a whole number from 1 to 1013. * The monthly budget is set to $1000 by default if the user has never run this command. Examples of usage: @@ -209,17 +209,17 @@ _Written by: Brian Wong Yun Long_ Tags used ( **OPTIONAL** tags are enclosed with SQUARE BRACKETS "[" and "]" in the **command syntax in the table** above): * The `TYPE` is either `"expense"` or `"income"`. * The `CATEGORY` is a one-word parameter flexibly defined by the user. [ No numeral, symbol or spacing is allowed ] -* The `AMOUNT` is a positive whole number that is between 1 and 10000000. [ No alphabet, symbol or spacing is allowed ] +* The `AMOUNT` is a positive whole number that is from 1 to 10000000. [ No alphabet, symbol or spacing is allowed ] * The `DATE` MUST be in ddMMyyyy format. [ E.g. 29102022 ] * The `DESCRIPTION` is a one-word parameter defined by the user. [ Spacing is not allowed ] -* The `ENTRY` is the entry value which is a positive whole number that is between 1 and 1000000. [ No alphabet, symbol or spacing is allowed ] +* The `ENTRY` is the entry value which is a positive whole number that is from 1 to 1000000. [ No alphabet, symbol or spacing is allowed ] * The `KEYWORDS` is any string used to find matching transactions. [ Spacing is allowed (Special Case) ] -* The `BUDGET` is a positive whole number that is between 1 and 1013 (Ten Trillion). [ No alphabet, symbol or spacing is allowed ] +* The `BUDGET` is a positive whole number that is from 1 to 1013 (Ten Trillion). [ No alphabet, symbol or spacing is allowed ] * The `YEAR` is the year in yyyy format. * The `MONTH` is the month in numerical form. [ From 1 to 12 , where 1 represents January etc. ] * The `PERIOD` is the selection of either periods in `"weeks"` or `"months"`. [ Used with "n/NUMBER" ONLY ] -* The `NUMBER` is a positive whole number that is between 1 and 100, representing the number of periods to view. [ Used with "p/PERIODS" ONLY ] +* The `NUMBER` is a positive whole number that is from 1 to 100, representing the number of periods to view. [ Used with "p/PERIODS" ONLY ] From d948865f571f33ab31c080edfb76d345360308ce Mon Sep 17 00:00:00 2001 From: chydarren Date: Thu, 27 Oct 2022 06:23:06 +0800 Subject: [PATCH 279/416] Add content for Introduction, Setting Up, List and Find Commands --- docs/UserGuide.md | 85 ++++++++++++++++++- .../duke/data/transaction/Transaction.java | 4 +- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 7a430dec4..7b84985b1 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -27,6 +27,19 @@ ## Introduction +Financial bookkeeping via traditional mobile applications has always been a hassle due to the repetitive clicks needed to +manage monetary transactions. With Moolah Manager, you will be encouraged to take ownership of managing your finances via +a time-saving and efficient command-line (CLI) interface. + +The main features of Moolah Manager include: + +- Managing records of monetary transactions +- Gathering financial insights such as categorical savings and periodic expenditure +- General budgeting and reminders about spending + +The application is optimised for use with a keyboard and all you need is to just type in your commands into a terminal. +Moreover, if you are a fast-typist, the recording and querying of transactions can be performed efficiently. + _Written by: Chua Han Yong Darren_ ## About This Guide @@ -57,10 +70,13 @@ _Written by: Chia Thin Hong_ ### Setting Up -{Give steps to get started quickly} - -1. Ensure that you have Java 11 or above installed. -1. Down the latest version of `Duke` from [here](http://link.to/duke). +1. Ensure that you have Java 11 or above installed. If not, kindly install Java's [latest version](https://www.oracle.com/java/technologies/downloads/). +2. Download the latest version of `Moolah Manager` from [here](https://github.com/AY2223S1-CS2113-W12-2/tp/releases). + As shown in Figure 1 below, click on the `duke.jar` file from the latest version available. +3. Copy the file to the directory that you wish to use as a home directory for Moolah Manager. The data saved during the + application will reside in the same directory. +4. Launch a command prompt or terminal and run the command `java -jar duke.jar` to start the application. +5. Moolah Manager will display a greeting message and a remaining budget for the current month. _Written by: Chua Han Yong Darren_ @@ -130,10 +146,71 @@ _Written by: Brian Wong Yun Long_ ### Listing the Transactions: `list` + +Lists all or some transactions based on selection. If tag filters are used, the transactions retrieved from the records must +match all the filter tags that have been specified in order to be recognized as a valid record. + +**Format:** `list [t/TYPE] [c/CATEGORY] [d/DATE] [m/MONTH] [y/YEAR] [p/PERIOD] [n/NUMBER]` + +| Field | Description | +|------------|------------------------------------------------------------------------------------------------------------------------------------| +| `TYPE` | The type of transaction. It should either be `expense` or `income`. | +| `CATEGORY` | A category for the transaction. It is a one-word parameter flexibly defined by the user. No numeral, symbol or spacing is allowed. | +| `DATE` | The date when the transaction took place on. It must be in ddMMyyyy format, e.g. 29102022. | +| `MONTH` | The month which the transaction falls on. It is in numerical form, i.e. from 1 to 12, where 1 represents January. | +| `YEAR` | The year which the transaction falls on. It must be in yyyy format and only year 1000 and onwards are accepted. | +| `PERIOD` | The period which the transaction falls on. It should either be `weeks` or `months` | +| `NUMBER` | The last N number of weeks or months. It is a positive whole number that is from 1 to 100. | + +**Important Information:** + +- In order to view transactions for a specific month via `m/MONTH` tag, it is mandatory to include a `y/YEAR` tag. +- The `p/PERIOD` and `n/NUMBER` tags must be used as a pair for viewing transactions for the + last N number of weeks or months. +- The `m/MONTH` and `y/YEAR` tags should not be used together with `p/PERIOD` and `n/NUMBER` tags. + +**Example(s):** + +- `list` +- `list y/2022` +- `list t/income c/transport d/27102022` +- `list c/food m/1 y/2022` +- `list t/expense c/leisure p/weeks n/5` + +**Expected output:** + +``` +list c/food m/1 y/2022 +____________________________________________________________ +Here are your transaction records: +[-][food] $80 on Jan 13 2022 | Description: toilet_cake +[+][food] $20 on Jan 30 2022 | Description: banana_pudding +____________________________________________________________ +``` + _Written by: Chua Han Yong Darren_ ### Searching for Transactions: `find` +Find a specific or multiple transactions based on any keywords that have been specified. + +**Format:** `find KEYWORDS` + +**Example(s):** + +- `find Jan 30` +- `find banana_pudding` + +**Expected output:** + +``` +find Jan 30 +____________________________________________________________ +Here are the transaction records that match your search expression: +[+][food] $20 at Jan 30 2022 | Description: banana_pudding +____________________________________________________________ +``` + _Written by: Chua Han Yong Darren_ ### Deleting a Transaction: `delete` diff --git a/src/main/java/seedu/duke/data/transaction/Transaction.java b/src/main/java/seedu/duke/data/transaction/Transaction.java index 92d786443..5c23cbd70 100644 --- a/src/main/java/seedu/duke/data/transaction/Transaction.java +++ b/src/main/java/seedu/duke/data/transaction/Transaction.java @@ -14,7 +14,7 @@ public abstract class Transaction { private static final String PREFIX_CATEGORY = "["; private static final String POSTFIX_CATEGORY = "]"; private static final String SYMBOL_PIPE = "|"; - private static final String TEXT_AT = "at"; + private static final String TEXT_ON = "on"; private static final String TEXT_DESCRIPTION = "Description:"; //@@author chinhan99 @@ -87,7 +87,7 @@ public String printFormattedCategory() { @Override public String toString() { return String.format("%s %s%d %s %s %s %s %s", printFormattedCategory(), DOLLAR_SIGN, - amount, TEXT_AT, printFormattedDate(), SYMBOL_PIPE, TEXT_DESCRIPTION, description); + amount, TEXT_ON, printFormattedDate(), SYMBOL_PIPE, TEXT_DESCRIPTION, description); } public void setType(String type) { From dbe9f9f02fcebf763ceb5ba8701736610b4b98ca Mon Sep 17 00:00:00 2001 From: chydarren Date: Thu, 27 Oct 2022 07:52:36 +0800 Subject: [PATCH 280/416] Update EXPECTED.TXT to reflect the change for "at" to "on" for transactions --- text-ui-test/EXPECTED.TXT | 50 +++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index edf6a2552..17e0ce721 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -213,7 +213,7 @@ There are no transaction records found. ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: -[-][food] $20 at Sep 13 2022 | Description: NIL +[-][food] $20 on Sep 13 2022 | Description: NIL Budget remained for Sep 2022: $9980. Keep it up! ____________________________________________________________ ____________________________________________________________ @@ -227,12 +227,12 @@ Invalid amount, please ensure your amount is in positive whole number ($10000000 ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: -[+][salary] $2000 at Sep 30 2022 | Description: jan_salary +[+][salary] $2000 on Sep 30 2022 | Description: jan_salary Budget remained for Sep 2022: $9980. Keep it up! ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: -[-][transport] $1 at Oct 02 2022 | Description: bus_fare +[-][transport] $1 on Oct 02 2022 | Description: bus_fare Budget remained for Oct 2022: $9999. Keep it up! ____________________________________________________________ ____________________________________________________________ @@ -267,13 +267,13 @@ Type of transaction given is invalid, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][food] $20 at Sep 13 2022 | Description: NIL -[-][transport] $1 at Oct 02 2022 | Description: bus_fare +[-][food] $20 on Sep 13 2022 | Description: NIL +[-][transport] $1 on Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][food] $20 at Sep 13 2022 | Description: NIL +[-][food] $20 on Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ @@ -281,7 +281,7 @@ There are no transaction records found. ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][food] $20 at Sep 13 2022 | Description: NIL +[-][food] $20 on Sep 13 2022 | Description: NIL ____________________________________________________________ ____________________________________________________________ @@ -289,7 +289,7 @@ Parameter behind tag(s) is found to be empty, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[+][salary] $2000 at Sep 30 2022 | Description: jan_salary +[+][salary] $2000 on Sep 30 2022 | Description: jan_salary ____________________________________________________________ ____________________________________________________________ @@ -382,7 +382,7 @@ Keyword(s) for your search expression missing, please check your input! ____________________________________________________________ ____________________________________________________________ Here are the transaction records that match your search expression: -[-][transport] $1 at Oct 02 2022 | Description: bus_fare +[-][transport] $1 on Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ @@ -390,9 +390,9 @@ There are no transaction records that match your search expression. ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][food] $20 at Sep 13 2022 | Description: NIL -[+][salary] $2000 at Sep 30 2022 | Description: jan_salary -[-][transport] $1 at Oct 02 2022 | Description: bus_fare +[-][food] $20 on Sep 13 2022 | Description: NIL +[+][salary] $2000 on Sep 30 2022 | Description: jan_salary +[-][transport] $1 on Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ @@ -400,7 +400,7 @@ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: -[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +[+][bonus] $10000000 on Oct 03 2022 | Description: thank_you_boss Budget remained for Oct 2022: $9999. Keep it up! ____________________________________________________________ ____________________________________________________________ @@ -429,10 +429,10 @@ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[-][food] $20 at Sep 13 2022 | Description: NIL -[+][salary] $2000 at Sep 30 2022 | Description: jan_salary -[-][transport] $1 at Oct 02 2022 | Description: bus_fare -[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +[-][food] $20 on Sep 13 2022 | Description: NIL +[+][salary] $2000 on Sep 30 2022 | Description: jan_salary +[-][transport] $1 on Oct 02 2022 | Description: bus_fare +[+][bonus] $10000000 on Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ @@ -440,14 +440,14 @@ Mandatory tag(s) missing, please check your input! ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: -[-][food] $20 at Sep 13 2022 | Description: NIL +[-][food] $20 on Sep 13 2022 | Description: NIL Budget remained for Sep 2022: $10000. Keep it up! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[+][salary] $2000 at Sep 30 2022 | Description: jan_salary -[-][transport] $1 at Oct 02 2022 | Description: bus_fare -[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +[+][salary] $2000 on Sep 30 2022 | Description: jan_salary +[-][transport] $1 on Oct 02 2022 | Description: bus_fare +[+][bonus] $10000000 on Oct 03 2022 | Description: thank_you_boss ____________________________________________________________ ____________________________________________________________ @@ -461,13 +461,13 @@ Invalid index, please ensure your index is correct! ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: -[+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +[+][bonus] $10000000 on Oct 03 2022 | Description: thank_you_boss Budget remained for Oct 2022: $9999. Keep it up! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: -[+][salary] $2000 at Sep 30 2022 | Description: jan_salary -[-][transport] $1 at Oct 02 2022 | Description: bus_fare +[+][salary] $2000 on Sep 30 2022 | Description: jan_salary +[-][transport] $1 on Oct 02 2022 | Description: bus_fare ____________________________________________________________ ____________________________________________________________ @@ -517,7 +517,7 @@ There are no transaction records found. ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: -[-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ +[-][transport] $1 on Oct 02 2022 | Description: i/i/i/i/ Budget remained for Oct 2022: $9999. Keep it up! ____________________________________________________________ ____________________________________________________________ From d8fd309a6f19bf1c42b697ba5a1fbac30ecbcc32 Mon Sep 17 00:00:00 2001 From: chydarren Date: Thu, 27 Oct 2022 09:47:49 +0800 Subject: [PATCH 281/416] Remove duplicated code in TransactionList class, amend existing code to fit time_insights --- .../java/seedu/duke/command/StatsCommand.java | 2 +- .../java/seedu/duke/data/TransactionList.java | 43 ------------------- 2 files changed, 1 insertion(+), 44 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index 963f3493e..237bc56a4 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -178,7 +178,7 @@ public void statsTypeCategoricalSavingsOrMonthlyExpenditure(TransactionList tran if (statsType.equals(CATEGORICAL_SAVINGS)) { // Replaces stats list with categorical savings if stats type is categorical_savings - genericStatsList = transactions.listCategoricalSavings(); + genericStatsList = transactions.listCategoricalSavings(transactions.getTransactions()); } if (genericStatsList.isEmpty()) { diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index ce515b571..591814906 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -242,28 +242,6 @@ public String findTransactions(String keywords) { return transactionsList; } - /** - * Reads the transactions list and adds each amount to the categories in categorical savings hashmap. - * - * @param categoricalSavings A hashmap containing all category-amount pair for total savings. - * @return A hashmap containing all category-amount pair for total savings. - */ - public HashMap processCategoricalSavings(HashMap categoricalSavings) { - for (Transaction transaction : transactions) { - String category = transaction.getCategory(); - int amount = transaction.getAmount(); - // Creates a new category with starter amount if category not exists in hashmap - if (!categoricalSavings.containsKey(category)) { - categoricalSavings.put(category, amount); - continue; - } - categoricalSavings.put(category, categoricalSavings.get(category) + amount); - } - - return categoricalSavings; - } - - //@@paullowse /** * Reads the transactions list and adds each amount to the categories in categorical savings hashmap. * @@ -286,27 +264,6 @@ public HashMap processCategoricalSavings(ArrayList return categoricalSavings; } - /** - * Formats the hashmap of categorical savings into a categorical savings list, using default transactions. - * - * @return A string that represents the formatted categorical savings list. - */ - public String listCategoricalSavings() { - String categoricalSavingsList = ""; - HashMap categoricalSavings = new HashMap<>(); - // Adds each amount from transactions list to the categories in categorical savings hashmap - categoricalSavings = processCategoricalSavings(categoricalSavings); - - // Formats every entry in the hashmap into a categorical savings list - for (HashMap.Entry entry : categoricalSavings.entrySet()) { - categoricalSavingsList += String.format("%s%s%s %s%s%s", PREFIX_CATEGORY, entry.getKey(), - POSTFIX_CATEGORY, DOLLAR_SIGN, entry.getValue(), LINE_SEPARATOR); - } - - return categoricalSavingsList; - } - - //@@chydarren /** * Formats the hashmap of categorical savings into a categorical savings list, using timeTransactions. * From b1d34f529748fc427f6a58062b5125eb874ac1c3 Mon Sep 17 00:00:00 2001 From: chydarren Date: Thu, 27 Oct 2022 11:12:48 +0800 Subject: [PATCH 282/416] Add figure to detail how to download the latest JAR file --- docs/UserGuide.md | 7 +++++++ docs/images/MoolahManagerDownload.png | Bin 0 -> 304903 bytes 2 files changed, 7 insertions(+) create mode 100644 docs/images/MoolahManagerDownload.png diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 7b84985b1..af6317da4 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -73,6 +73,13 @@ _Written by: Chia Thin Hong_ 1. Ensure that you have Java 11 or above installed. If not, kindly install Java's [latest version](https://www.oracle.com/java/technologies/downloads/). 2. Download the latest version of `Moolah Manager` from [here](https://github.com/AY2223S1-CS2113-W12-2/tp/releases). As shown in Figure 1 below, click on the `duke.jar` file from the latest version available. + +

+ +
+ Figure 1: How to Download Moolah Manager +

+ 3. Copy the file to the directory that you wish to use as a home directory for Moolah Manager. The data saved during the application will reside in the same directory. 4. Launch a command prompt or terminal and run the command `java -jar duke.jar` to start the application. diff --git a/docs/images/MoolahManagerDownload.png b/docs/images/MoolahManagerDownload.png new file mode 100644 index 0000000000000000000000000000000000000000..706a87646742b17f7497adcfbb6231144a9f07af GIT binary patch literal 304903 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]qy_3e5RHKzC?}QnI3|@Xg&Ko8&N?4q{_e+ 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 Fe*yHb;0yo& literal 0 HcmV?d00001 From d7db48a892b5b59e800a8e2e68a43113e57a2ec0 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 25 Oct 2022 00:59:25 +0800 Subject: [PATCH 238/416] Update test cases and fix violations to java coding convention --- src/main/java/seedu/duke/common/DateFormats.java | 2 +- text-ui-test/EXPECTED.TXT | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/main/java/seedu/duke/common/DateFormats.java b/src/main/java/seedu/duke/common/DateFormats.java index 9760f2c74..8670520de 100644 --- a/src/main/java/seedu/duke/common/DateFormats.java +++ b/src/main/java/seedu/duke/common/DateFormats.java @@ -34,7 +34,7 @@ public String toString() { } /** - * Retrieves a formatted string containing the month and year of a date + * Retrieves a formatted string containing the month and year of a date. * * @param date Date of the transaction to be considered for the budget. * @return A string containing the formatted output for month and year. diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index c03998379..8940f13fe 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -12,7 +12,7 @@ ____________________________________________________________ ____________________________________________________________ ____________________________________________________________ Hello! I'm Moo and I will help you to manage your finances. -Monthly budget set as: $1000 +Budget remained for current month: $1000 Enter if you need the list of commands. ____________________________________________________________ ____________________________________________________________ @@ -212,17 +212,17 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][food] $20 at Sep 13 2022 | Description: NIL -Budget remained for the month of transaction: $9980 +Budget remained for Sep 2022: $9980. Keep it up! ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: [+][salary] $2000 at Sep 30 2022 | Description: jan_salary -Budget remained for the month of transaction: $7980 +Budget remained for Sep 2022: $9980. Keep it up! ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: bus_fare -Budget remained for the month of transaction: $9999 +Budget remained for Oct 2022: $9999. Keep it up! ____________________________________________________________ ____________________________________________________________ Parameter behind tag(s) is found to be empty, please check your input! @@ -312,7 +312,7 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Income transaction: [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss -Budget remained for the month of transaction: $-9990001 +Budget remained for Oct 2022: $9999. Keep it up! ____________________________________________________________ ____________________________________________________________ Type of transaction given is invalid, please check your input! @@ -352,6 +352,7 @@ ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: [-][food] $20 at Sep 13 2022 | Description: NIL +Budget remained for Sep 2022: $10000. Keep it up! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: @@ -372,6 +373,7 @@ ____________________________________________________________ ____________________________________________________________ I have deleted the following transaction: [+][bonus] $10000000 at Oct 03 2022 | Description: thank_you_boss +Budget remained for Oct 2022: $9999. Keep it up! ____________________________________________________________ ____________________________________________________________ Here are your transaction records: @@ -427,7 +429,7 @@ ____________________________________________________________ ____________________________________________________________ I have added the following Expense transaction: [-][transport] $1 at Oct 02 2022 | Description: i/i/i/i/ -Budget remained for the month of transaction: $9999 +Budget remained for Oct 2022: $9999. Keep it up! ____________________________________________________________ ____________________________________________________________ Not supported tag(s) detected, please check your input! From c45554b23ff91f59f2ce90d15f550e3f8cb4b510 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 25 Oct 2022 01:20:15 +0800 Subject: [PATCH 239/416] Add Junit test and Javadocs for methods in Budget.java --- src/main/java/seedu/duke/data/Budget.java | 5 ++ src/test/java/seedu/duke/data/BudgetTest.java | 54 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 src/test/java/seedu/duke/data/BudgetTest.java diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index 15dce06aa..11bae2c8f 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -68,6 +68,11 @@ public static String generateBudgetRemainingMessage(long totalMonthlyExpense, bo return message; } + /** + * Generates a budget remaining message for the current month. + * + * @return A string containing the remaining budget to be displayed to the user. + */ public static String generateCurrentMonthBudgetRemainingMessage() { LocalDate todayDate = LocalDate.now(); String monthYear = "current month"; diff --git a/src/test/java/seedu/duke/data/BudgetTest.java b/src/test/java/seedu/duke/data/BudgetTest.java new file mode 100644 index 000000000..e6eb29203 --- /dev/null +++ b/src/test/java/seedu/duke/data/BudgetTest.java @@ -0,0 +1,54 @@ +package seedu.duke.data; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.duke.common.InfoMessages.INFO_EXCEEDING_BUDGET; +import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; +import static seedu.duke.common.InfoMessages.INFO_BUDGET_EXCEEDED_TIPS; +import static seedu.duke.common.InfoMessages.INFO_BUDGET_NOT_EXCEEDED_TIPS; + +public class BudgetTest { + @Test + public void generateBudgetRemainingMessage_budgetExceeded_expectExceededStringInMessage() { + assertTrue(Budget.generateBudgetRemainingMessage(10000, false, "") + .startsWith(INFO_EXCEEDING_BUDGET.toString())); + assertTrue(Budget.generateBudgetRemainingMessage(69696969, true, "") + .startsWith(INFO_EXCEEDING_BUDGET.toString())); + } + + @Test + public void generateBudgetRemainingMessage_budgetNotExceeded_expectRemainedStringInMessage() { + assertTrue(Budget.generateBudgetRemainingMessage(0, false, "") + .startsWith(INFO_REMAINING_BUDGET.toString())); + assertTrue(Budget.generateBudgetRemainingMessage(69, true, "") + .startsWith(INFO_REMAINING_BUDGET.toString())); + } + + @Test + public void generateBudgetRemainingMessage_withTips_expectTipsInMessage() { + assertTrue(Budget.generateBudgetRemainingMessage(69, true, "") + .contains(INFO_BUDGET_NOT_EXCEEDED_TIPS.toString())); + assertTrue(Budget.generateBudgetRemainingMessage(69696969, true, "") + .contains(INFO_BUDGET_EXCEEDED_TIPS.toString())); + } + + @Test + public void generateBudgetRemainingMessage_withoutTips_expectNoTipInMessage() { + assertFalse(Budget.generateBudgetRemainingMessage(69, false, "") + .contains(INFO_BUDGET_EXCEEDED_TIPS.toString())); + assertFalse(Budget.generateBudgetRemainingMessage(69696969, false, "") + .contains(INFO_BUDGET_NOT_EXCEEDED_TIPS.toString())); + } + + @Test + public void generateCurrentMonthBudgetRemainingMessage_usualRun_expectCurrentMonthBudget() { + TransactionList transactions = new TransactionList(); + long currentMonthTotalExpense = TransactionList.calculateMonthlyTotalExpense(LocalDate.now()); + assertTrue(Budget.generateCurrentMonthBudgetRemainingMessage() + .contains(Long.toString(currentMonthTotalExpense))); + } +} From 25c96498f64168bc04d47df746a0a40296b75577 Mon Sep 17 00:00:00 2001 From: wcwy Date: Tue, 25 Oct 2022 01:44:47 +0800 Subject: [PATCH 240/416] Implement reminder feature on program start To remind users of whether their budget has been exceeded or not. Test cases have been updated accordingly. --- src/main/java/seedu/duke/Ui.java | 1 - src/main/java/seedu/duke/common/InfoMessages.java | 4 +++- src/main/java/seedu/duke/data/Budget.java | 15 +++++++++++++++ text-ui-test/EXPECTED.TXT | 1 + 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index a599d59a8..37d8c2825 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -9,7 +9,6 @@ import static seedu.duke.common.InfoMessages.INFO_GREET; import static seedu.duke.common.InfoMessages.INFO_HELP_GREET; import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; -import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; import static seedu.duke.common.InfoMessages.INFO_BUDGET_SET_SUCCESSFUL; public class Ui { diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 0dcd5ec13..120116b80 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -47,7 +47,9 @@ public enum InfoMessages { INFO_REMAINING_BUDGET("Budget remained for "), INFO_EXCEEDING_BUDGET("Budget exceeded for "), INFO_BUDGET_EXCEEDED_TIPS("Consider spending less!"), - INFO_BUDGET_NOT_EXCEEDED_TIPS("Keep it up!"); + INFO_BUDGET_NOT_EXCEEDED_TIPS("Keep it up!"), + INFO_BUDGET_EXCEEDED_REMINDER("REMINDER: You have already exceeded the budget set for current month!"), + INFO_BUDGET_NOT_EXCEEDED_REMINDER("REMINDER: Continue to stay within your budget for this month! Good fortune!"); //@@author chydarren public final String message; diff --git a/src/main/java/seedu/duke/data/Budget.java b/src/main/java/seedu/duke/data/Budget.java index 11bae2c8f..bcc61bf73 100644 --- a/src/main/java/seedu/duke/data/Budget.java +++ b/src/main/java/seedu/duke/data/Budget.java @@ -10,9 +10,12 @@ import static seedu.duke.common.InfoMessages.INFO_REMAINING_BUDGET; import static seedu.duke.common.InfoMessages.INFO_BUDGET_EXCEEDED_TIPS; import static seedu.duke.common.InfoMessages.INFO_BUDGET_NOT_EXCEEDED_TIPS; +import static seedu.duke.common.InfoMessages.INFO_BUDGET_NOT_EXCEEDED_REMINDER; +import static seedu.duke.common.InfoMessages.INFO_BUDGET_EXCEEDED_REMINDER; import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; import static seedu.duke.common.InfoMessages.COLON_SPACE; import static seedu.duke.common.InfoMessages.FULL_STOP_SPACE; +import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; //@@author wcwy @@ -79,6 +82,9 @@ public static String generateCurrentMonthBudgetRemainingMessage() { long currentMonthTotalExpense = TransactionList.calculateMonthlyTotalExpense(todayDate); String message = generateBudgetRemainingMessage(currentMonthTotalExpense, false, monthYear); + boolean currentMonthHasExceededBudget = hasExceededBudget(calculateBudgetLeft(currentMonthTotalExpense)); + message += LINE_SEPARATOR + generateBudgetReminder(currentMonthHasExceededBudget); + return message; } @@ -155,5 +161,14 @@ private static String getMoneyManagingTips(boolean hasExceededBudget) { } } + private static String generateBudgetReminder(boolean hasExceededBudget) { + if (hasExceededBudget) { + return INFO_BUDGET_EXCEEDED_REMINDER.toString(); + } else { + return INFO_BUDGET_NOT_EXCEEDED_REMINDER.toString(); + } + + } + } diff --git a/text-ui-test/EXPECTED.TXT b/text-ui-test/EXPECTED.TXT index 8940f13fe..50d861d04 100644 --- a/text-ui-test/EXPECTED.TXT +++ b/text-ui-test/EXPECTED.TXT @@ -13,6 +13,7 @@ ____________________________________________________________ ____________________________________________________________ Hello! I'm Moo and I will help you to manage your finances. Budget remained for current month: $1000 +REMINDER: Continue to stay within your budget for this month! Good fortune! Enter if you need the list of commands. ____________________________________________________________ ____________________________________________________________ From ecef4a13de8a78c8f4ab7b747c836ad3ad38e992 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 08:41:58 +0800 Subject: [PATCH 241/416] Remove category class as no longer needed, earlier replaced by Hashmap --- .../seedu/duke/data/transaction/Category.java | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 src/main/java/seedu/duke/data/transaction/Category.java diff --git a/src/main/java/seedu/duke/data/transaction/Category.java b/src/main/java/seedu/duke/data/transaction/Category.java deleted file mode 100644 index 15a772fdc..000000000 --- a/src/main/java/seedu/duke/data/transaction/Category.java +++ /dev/null @@ -1,39 +0,0 @@ -package seedu.duke.data.transaction; - - -import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; - -public class Category { - //@@author chydarren - private static final String PREFIX_CATEGORY = "["; - private static final String POSTFIX_CATEGORY = "]"; - - private String category; - private int amount; - - public Category(String category) { - this.category = category; - } - - public String getCategory() { - return category; - } - - public void setCategory(String category) { - this.category = category; - } - - public int getAmount() { - return amount; - } - - public void setAmount(int amount) { - this.amount = amount; - } - - @Override - public String toString() { - return String.format("%s%s%s %s%d", PREFIX_CATEGORY, category, POSTFIX_CATEGORY, - DOLLAR_SIGN, amount); - } -} \ No newline at end of file From f5d9693f5238967e3ad40b44d11878cccfe4f81b Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 08:44:00 +0800 Subject: [PATCH 242/416] Improve JavaDoc comments and remove import authorship for non-Command classes only to avoid redundancy --- src/main/java/seedu/duke/Ui.java | 22 +++++++++++-------- .../seedu/duke/command/BudgetCommand.java | 2 -- .../java/seedu/duke/command/ByeCommand.java | 1 - .../seedu/duke/command/DeleteCommand.java | 1 - .../java/seedu/duke/command/EditCommand.java | 1 - .../java/seedu/duke/common/Constants.java | 1 + .../java/seedu/duke/common/DateFormats.java | 3 ++- .../seedu/duke/parser/ParameterParser.java | 14 +++--------- 8 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/main/java/seedu/duke/Ui.java b/src/main/java/seedu/duke/Ui.java index 37d8c2825..c3529e816 100644 --- a/src/main/java/seedu/duke/Ui.java +++ b/src/main/java/seedu/duke/Ui.java @@ -11,6 +11,10 @@ import static seedu.duke.common.InfoMessages.INFO_HELP_PROMPT; import static seedu.duke.common.InfoMessages.INFO_BUDGET_SET_SUCCESSFUL; +/** + * Displays information and error messages based on the user input and the behavior of the + * application. + */ public class Ui { //@@author chydarren private String input; @@ -19,7 +23,7 @@ public class Ui { /** * Prints each message from a variable messages string line by line into the output stream. * - * @param messages A string of variable arguments. + * @param messages A string of variable length arguments. */ public static void printMessages(String... messages) { System.out.println(INFO_DIVIDER); @@ -92,10 +96,10 @@ public static void showHelp(String message) { //@@author chydarren /** - * Prepares the transaction list messages to be displayed to the user. + * Prepares the list messages to be displayed to the user. * - * @param list A string containing the formatted transaction list. - * @param message A message that complements with the transactions list. + * @param list A string containing the formatted list. + * @param message A message that complements with the list. */ public static void showList(String list, String message) { printMessages(message, list); @@ -104,13 +108,13 @@ public static void showList(String list, String message) { //@@author paullowse /** - * Prepares the stats list messages to be displayed to the user. + * Prepares the time insights list messages to be displayed to the user. * - * @param list A string containing the formatted transaction list. - * @param message A message that complements with the transactions list. + * @param list A string containing the formatted time insights list. + * @param message A message that complements with the time insights list. */ - public static void showStatsList(String list, String message, String incomeMessage, - String expenseMessage, String savingsMessage) { + public static void showTimeInsightsList(String list, String message, String incomeMessage, + String expenseMessage, String savingsMessage) { printMessages(message, list, incomeMessage, expenseMessage, savingsMessage); } diff --git a/src/main/java/seedu/duke/command/BudgetCommand.java b/src/main/java/seedu/duke/command/BudgetCommand.java index 1a4e6f2df..c8f33755e 100644 --- a/src/main/java/seedu/duke/command/BudgetCommand.java +++ b/src/main/java/seedu/duke/command/BudgetCommand.java @@ -1,7 +1,6 @@ package seedu.duke.command; //@@author wcwy - import seedu.duke.Storage; import seedu.duke.Ui; import seedu.duke.data.Budget; @@ -36,7 +35,6 @@ public class BudgetCommand extends Command { public static final String COMMAND_DETAILED_HELP = COMMAND_HELP + COMMAND_PARAMETERS_INFO + LINE_SEPARATOR; - //@@author wcwy private long budgetAmount; diff --git a/src/main/java/seedu/duke/command/ByeCommand.java b/src/main/java/seedu/duke/command/ByeCommand.java index e09e67705..e235dbcce 100644 --- a/src/main/java/seedu/duke/command/ByeCommand.java +++ b/src/main/java/seedu/duke/command/ByeCommand.java @@ -8,7 +8,6 @@ import static seedu.duke.common.InfoMessages.INFO_EXIT; import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; - /** * Represents a bye command object that will execute the operations for Bye command. */ diff --git a/src/main/java/seedu/duke/command/DeleteCommand.java b/src/main/java/seedu/duke/command/DeleteCommand.java index 588c15624..c2d303f96 100644 --- a/src/main/java/seedu/duke/command/DeleteCommand.java +++ b/src/main/java/seedu/duke/command/DeleteCommand.java @@ -19,7 +19,6 @@ import static seedu.duke.common.InfoMessages.INFO_DELETE; import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; - /** * Represents a delete command object that will execute the operations for Delete command. */ diff --git a/src/main/java/seedu/duke/command/EditCommand.java b/src/main/java/seedu/duke/command/EditCommand.java index e2888e91c..12f546238 100644 --- a/src/main/java/seedu/duke/command/EditCommand.java +++ b/src/main/java/seedu/duke/command/EditCommand.java @@ -30,7 +30,6 @@ import static seedu.duke.common.InfoMessages.INFO_EDIT_INCOME; import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; - /** * Represents an edit command object that will execute the operations for Edit command. */ diff --git a/src/main/java/seedu/duke/common/Constants.java b/src/main/java/seedu/duke/common/Constants.java index ac6518a0b..fd6f9600d 100644 --- a/src/main/java/seedu/duke/common/Constants.java +++ b/src/main/java/seedu/duke/common/Constants.java @@ -1,6 +1,7 @@ package seedu.duke.common; //@@author wcwy + /** * Represents all the constant settings of the application. * Developers should update the constant in this file to allow for different limits for the application. diff --git a/src/main/java/seedu/duke/common/DateFormats.java b/src/main/java/seedu/duke/common/DateFormats.java index 8670520de..b05dc3efb 100644 --- a/src/main/java/seedu/duke/common/DateFormats.java +++ b/src/main/java/seedu/duke/common/DateFormats.java @@ -3,11 +3,12 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; +//@@author wcwy + /** * Provides enum variables for the approved date formats for input and output. */ public enum DateFormats { - //@@author wcwy DATE_INPUT_PATTERN("ddMMyyyy"), DATE_OUTPUT_PATTERN("MMM dd yyyy"), DATE_MONTH_PATTERN("MMM yyyy"), diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index a8275dee0..6c248eae5 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -1,13 +1,12 @@ package seedu.duke.parser; -//@@author wcwy import seedu.duke.command.Command; import seedu.duke.command.ListCommand; + import seedu.duke.data.Budget; import seedu.duke.data.transaction.Expense; import seedu.duke.data.transaction.Income; -//@@author chydarren import seedu.duke.exception.GlobalEmptyParameterException; import seedu.duke.exception.GlobalInvalidMonthException; import seedu.duke.exception.GlobalInvalidNumberException; @@ -16,8 +15,6 @@ import seedu.duke.exception.GlobalNumberNotNumericException; import seedu.duke.exception.GlobalDuplicateTagException; import seedu.duke.exception.GlobalMissingTagException; - -//@@author brian-vb import seedu.duke.exception.InputTransactionInvalidAmountException; import seedu.duke.exception.InputTransactionInvalidCategoryException; import seedu.duke.exception.InputTransactionInvalidDateException; @@ -26,10 +23,9 @@ import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; import seedu.duke.exception.HelpUnknownOptionException; - -//@@author wcwy import seedu.duke.exception.InputBudgetInvalidAmountException; import seedu.duke.exception.InputBudgetDuplicateException; + import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -38,11 +34,10 @@ import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; + import static seedu.duke.command.CommandTag.COMMAND_TAG_BUDGET_AMOUNT; import static seedu.duke.common.Constants.MAX_BUDGET_VALUE; import static seedu.duke.common.Constants.MIN_BUDGET_VALUE; - -//@@author paullowse import static seedu.duke.command.CommandTag.COMMAND_TAG_HELP_OPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_ENTRY_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_STATS_TYPE; @@ -50,15 +45,12 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; - -//@@author chinhan99 import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_AMOUNT; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_CATEGORY; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DATE; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_DESCRIPTION; import static seedu.duke.command.CommandTag.COMMAND_TAG_TRANSACTION_TYPE; -//@@author chinhan99 import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; import static seedu.duke.common.DateFormats.DATE_INPUT_PATTERN; From f1816ac7112a9355b07850d48af5df559a07c63c Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 08:50:03 +0800 Subject: [PATCH 243/416] Improve naming of variables for Stats command based on previous PR feedback --- .../duke/command/ListAndStatsCommand.java | 49 ++++++++++--------- .../java/seedu/duke/command/ListCommand.java | 8 +-- .../java/seedu/duke/common/InfoMessages.java | 25 +++++----- .../seedu/duke/parser/ParameterParser.java | 19 ++++--- 4 files changed, 49 insertions(+), 52 deletions(-) diff --git a/src/main/java/seedu/duke/command/ListAndStatsCommand.java b/src/main/java/seedu/duke/command/ListAndStatsCommand.java index 5ffe9ba75..93271a5c1 100644 --- a/src/main/java/seedu/duke/command/ListAndStatsCommand.java +++ b/src/main/java/seedu/duke/command/ListAndStatsCommand.java @@ -19,13 +19,13 @@ public abstract class ListAndStatsCommand extends Command { //@@author chydarren private static final int UNDEFINED_PARAMETER = -1; - private static final int TRUE_AND = 1; - private static final int TRUE_OR = 2; - private static final int TRUE_INVALID_OR = 3; + private static final int CONTAIN_BOTH = 1; + private static final int CONTAIN_EITHER = 2; + private static final int CONTAIN_EITHER_INVALID = 3; private static final int FALSE = 0; private static final String WEEKS = "weeks"; private static final String MONTHS = "months"; - private static Logger datedTransactionsLogger = Logger.getLogger(ListAndStatsCommand.class.getName()); + private static Logger listStatsLogger = Logger.getLogger(ListAndStatsCommand.class.getName()); //@@author paullowse public int month; @@ -40,9 +40,10 @@ public ListAndStatsCommand() { this.number = UNDEFINED_PARAMETER; //@@author chydarren - datedTransactionsLogger.setLevel(Level.SEVERE); + listStatsLogger.setLevel(Level.SEVERE); } + //@@author paullowse @Override public void setGlobalMonth(int month) { this.month = month; @@ -73,11 +74,12 @@ public void setGlobalPeriod(String period) { */ public int containMonthYear() { if (month != UNDEFINED_PARAMETER && year != UNDEFINED_PARAMETER) { - return TRUE_AND; + return CONTAIN_BOTH; } else if (year != UNDEFINED_PARAMETER) { - return TRUE_OR; + return CONTAIN_EITHER; } else if (month != UNDEFINED_PARAMETER) { - return TRUE_INVALID_OR; + // Returns CONTAIN_EITHER_INVALID as it is not allowed for month to not have a year + return CONTAIN_EITHER_INVALID; } return FALSE; } @@ -89,9 +91,9 @@ public int containMonthYear() { */ public int containPeriodNumber() { if (period != null && number != UNDEFINED_PARAMETER) { - return TRUE_AND; + return CONTAIN_BOTH; } else if (period != null || number != UNDEFINED_PARAMETER) { - return TRUE_OR; + return CONTAIN_EITHER; } return FALSE; } @@ -104,22 +106,24 @@ public int containPeriodNumber() { public void parseDateIntervalsTags() throws MoolahException { if (containMonthYear() != FALSE && containPeriodNumber() != FALSE) { // Throws an unsupported tag exception if tags are not supposed to be used together - datedTransactionsLogger.log(Level.WARNING, "An exception has been caught " - + "as an invalid combination of tags has been given."); + listStatsLogger.log(Level.WARNING, "Exception occurred as an invalid combination " + + "of tags has been given."); throw new GlobalUnsupportedTagException(); - } else if (containMonthYear() == TRUE_INVALID_OR) { + } else if (containMonthYear() == CONTAIN_EITHER_INVALID) { // Throws a missing tag exception if number and period was not given together - datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " - + "a month was given without a year."); + listStatsLogger.log(Level.WARNING, "Exception occurred as a month was given without " + + "a year."); throw new GlobalMissingYearTagException(); - } else if (containPeriodNumber() == TRUE_OR) { + } else if (containPeriodNumber() == CONTAIN_EITHER) { // Throws a missing tag exception if number and period was not given together - datedTransactionsLogger.log(Level.WARNING, "An exception has been caught as " - + "number and period needs to be given together."); + listStatsLogger.log(Level.WARNING, "Exception occurred as number and period needs " + + "to be given together."); throw new GlobalMissingPeriodNumberTagException(); } } + //@@author paullowse + /** * Gets the complete transactions list by date intervals. * @@ -128,15 +132,14 @@ public void parseDateIntervalsTags() throws MoolahException { */ public ArrayList getTimeTransactions(TransactionList transactions) { ArrayList timeTransactions = transactions.getTransactions(); - - if (containMonthYear() == TRUE_AND) { + if (containMonthYear() == CONTAIN_BOTH) { timeTransactions = transactions.getTransactionsByMonth(year, month); - } else if (containMonthYear() == TRUE_OR) { + } else if (containMonthYear() == CONTAIN_EITHER) { assert month == UNDEFINED_PARAMETER; timeTransactions = transactions.getTransactionsByYear(year); - } else if (containPeriodNumber() == TRUE_AND && period == MONTHS) { + } else if (containPeriodNumber() == CONTAIN_BOTH && period == MONTHS) { timeTransactions = transactions.getTransactionsByMonthRange(LocalDate.now(), number); - } else if (containPeriodNumber() == TRUE_AND && period == WEEKS) { + } else if (containPeriodNumber() == CONTAIN_BOTH && period == WEEKS) { timeTransactions = transactions.getTransactionsByWeekRange(LocalDate.now(), number); } diff --git a/src/main/java/seedu/duke/command/ListCommand.java b/src/main/java/seedu/duke/command/ListCommand.java index 91f779726..44666a63e 100644 --- a/src/main/java/seedu/duke/command/ListCommand.java +++ b/src/main/java/seedu/duke/command/ListCommand.java @@ -135,20 +135,16 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws // Checks if there are any error in the tag combinations related to DateIntervals parseDateIntervalsTags(); - listTransactions(transactions, type, category, date); + listTransactions(transactions); } /** * List all or some transactions based on selection. * * @param transactions An instance of the TransactionList class. - * @param type The type of transaction. - * @param category A category for the transaction. - * @param date Date of the transaction with format in "yyyyMMdd". * @throws MoolahException If any type of exception has been caught within the function calls. */ - private void listTransactions(TransactionList transactions, String type, String category, LocalDate date) - throws MoolahException { + private void listTransactions(TransactionList transactions) throws MoolahException { ArrayList timeTransactions = getTimeTransactions(transactions); String transactionsList = transactions.listTransactions(timeTransactions, type, category, date); diff --git a/src/main/java/seedu/duke/common/InfoMessages.java b/src/main/java/seedu/duke/common/InfoMessages.java index 120116b80..086e9962a 100644 --- a/src/main/java/seedu/duke/common/InfoMessages.java +++ b/src/main/java/seedu/duke/common/InfoMessages.java @@ -10,6 +10,9 @@ public enum InfoMessages { FULL_STOP_SPACE(". "), COLON_SPACE(": "), DOLLAR_SIGN("$"), + INFO_INCOME("Income"), + INFO_EXPENSE("Expense"), + INFO_SAVINGS("Savings"), INFO_DIVIDER("____________________________________________________________"), INFO_ADD_EXPENSE("I have added the following Expense transaction:"), INFO_ADD_INCOME("I have added the following Income transaction:"), @@ -25,19 +28,15 @@ public enum InfoMessages { INFO_LIST_FILTERED("Here are the transaction records that match your search expression:"), INFO_LIST_UNFILTERED("There are no transaction records that match your search expression."), INFO_STATS_EMPTY("There are no statistics available yet for the given statistics type."), - INFO_STATS_CATEGORIES("Here are the total savings for each category:"), - INFO_STATS_MONTHS("Here is the expenditure summary for each month:"), - INFO_STATS_MONTHS_COMMENT_ONE("Excellent! You saved quite a lot this month."), - INFO_STATS_MONTHS_COMMENT_TWO("Wow, keep up the good work. You saved at least 80% of your income."), - INFO_STATS_MONTHS_COMMENT_THREE("Good effort, you saved at least half of your income."), - INFO_STATS_MONTHS_COMMENT_FOUR("Hmm, it spent quite a sum. Try saving more in future."), - INFO_STATS_MONTHS_COMMENT_FIVE("You spent too much. Try to manage your expense within your budget."), - INFO_STATS_TIME("Here are the total savings and expenses for"), - INFO_STATS_INCOME("Total Income = "), - INFO_STATS_EXPENSES("Total Expenses = "), - INFO_STATS_SAVINGS("Total Savings = "), - INFO_STATS_CATEGORIES_HEADER("-----CATGORIES-----"), - INFO_STATS_SUMMARY_HEADER("-----SUMMARY-----"), + INFO_STATS_GENERIC("Here are your requested statistics for the %s type:"), + INFO_STATS_HABIT_VERY_HIGH_SAVINGS("Excellent! You saved quite a lot this month."), + INFO_STATS_HABIT_HIGH_SAVINGS("Wow, keep up the good work. You saved at least two-third of your income."), + INFO_STATS_HABIT_MEDIUM_SAVINGS("Good effort, you saved at least half of your income."), + INFO_STATS_HABIT_LOW_SAVINGS("Hmm, you spent quite a sum. Try saving more in future."), + INFO_STATS_HABIT_VERY_LOW_SAVINGS("Oops, you spent too much. Do manage your expenses within your constraints."), + INFO_STATS_TIME_INSIGHTS("Here are the total savings and expenses for"), + INFO_STATS_CATEGORIES_HEADER("-----CATEGORIES-----"), + INFO_STATS_EXPENDITURE_HEADER("-----EXPENDITURE-----"), INFO_PURGE("All your transactions have been purged."), INFO_PURGE_ABORT("Purging has been aborted. All transactions records are retained."), INFO_PURGE_EMPTY("The command is aborted as the transactions list is empty."), diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index 6c248eae5..d7b14611d 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -71,12 +71,11 @@ public class ParameterParser { private static final int SMALLEST_POSITIVE_INTEGER = 0; private static final int JANUARY = 1; private static final int DECEMBER = 12; - private static final String CLASS_TYPE_EXPENSE = "seedu.duke.data.transaction.Expense"; private static final String CLASS_TYPE_INCOME = "seedu.duke.data.transaction.Income"; - private static final String CATEGORIES = "categories"; - private static final String TIME = "time"; - + private static final String CATEGORICAL_SAVINGS = "categorical_savings"; + private static final String MONTHLY_EXPENDITURE = "monthly_expenditure"; + private static final String TIME_INSIGHTS = "time_insights"; private static final String WEEKS = "weeks"; private static final String MONTHS = "months"; @@ -554,12 +553,12 @@ public static long parseBudgetTag(String parameter) throws MoolahException { */ public static String parseStatsTypeTag(String parameter) throws StatsInvalidTypeException { switch (parameter) { - case CATEGORIES: - return CATEGORIES; - case MONTHS: - return MONTHS; - case TIME: - return TIME; + case CATEGORICAL_SAVINGS: + return CATEGORICAL_SAVINGS; + case MONTHLY_EXPENDITURE: + return MONTHLY_EXPENDITURE; + case TIME_INSIGHTS: + return TIME_INSIGHTS; default: parserLogger.log(Level.WARNING, "An invalid statistic type error is caught for the given parameter: " + parameter); From 276dd8b22bcc04ccb551b7f8e052f2228b4b4608 Mon Sep 17 00:00:00 2001 From: chydarren Date: Tue, 25 Oct 2022 08:50:56 +0800 Subject: [PATCH 244/416] Remove redundant code from Stats command and fix bugs in earlier PR --- .../java/seedu/duke/command/StatsCommand.java | 133 ++++++++---------- .../java/seedu/duke/data/TransactionList.java | 128 +++++++++-------- 2 files changed, 121 insertions(+), 140 deletions(-) diff --git a/src/main/java/seedu/duke/command/StatsCommand.java b/src/main/java/seedu/duke/command/StatsCommand.java index d4968d589..ab142fcf5 100644 --- a/src/main/java/seedu/duke/command/StatsCommand.java +++ b/src/main/java/seedu/duke/command/StatsCommand.java @@ -5,7 +5,7 @@ import seedu.duke.Ui; import seedu.duke.data.TransactionList; import seedu.duke.data.transaction.Transaction; -import seedu.duke.exception.InputTransactionInvalidTypeException; +import seedu.duke.exception.GlobalUnsupportedTagException; import seedu.duke.exception.StatsInvalidTypeException; import seedu.duke.exception.MoolahException; @@ -18,14 +18,15 @@ import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_NUMBER; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_PERIOD; import static seedu.duke.command.CommandTag.COMMAND_TAG_GLOBAL_YEAR; -import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES; -import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS; +import static seedu.duke.common.InfoMessages.COLON_SPACE; +import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; +import static seedu.duke.common.InfoMessages.INFO_EXPENSE; +import static seedu.duke.common.InfoMessages.INFO_INCOME; +import static seedu.duke.common.InfoMessages.INFO_SAVINGS; import static seedu.duke.common.InfoMessages.INFO_STATS_EMPTY; -import static seedu.duke.common.InfoMessages.INFO_STATS_EXPENSES; -import static seedu.duke.common.InfoMessages.INFO_STATS_INCOME; -import static seedu.duke.common.InfoMessages.INFO_STATS_SAVINGS; -import static seedu.duke.common.InfoMessages.INFO_STATS_SUMMARY_HEADER; -import static seedu.duke.common.InfoMessages.INFO_STATS_TIME; +import static seedu.duke.common.InfoMessages.INFO_STATS_EXPENDITURE_HEADER; +import static seedu.duke.common.InfoMessages.INFO_STATS_GENERIC; +import static seedu.duke.common.InfoMessages.INFO_STATS_TIME_INSIGHTS; import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; /** @@ -43,8 +44,9 @@ public class StatsCommand extends ListAndStatsCommand { // The formatting information for the parameters used by the command public static final String COMMAND_PARAMETERS_INFO = "Parameters information:" + LINE_SEPARATOR - + "STATISTICS_TYPE: The type of statistics to be generated. Only \"categories\", \"months\" or \"time\" " - + "is accepted." + + "STATISTICS_TYPE: The type of statistics to be generated. Only \"categorical_savings\", " + + "\"monthly_expenditure\" or \"time_insights\" is accepted." + + LINE_SEPARATOR + "(Optional) MONTH: Month of the transaction. Only integers within 1 to 12 are accepted. Note that " + "month must be accompanied by a year. This tag cannot be used together with [p/PERIOD] or [n/NUMBER] " + "tags." @@ -68,12 +70,10 @@ public class StatsCommand extends ListAndStatsCommand { + LINE_SEPARATOR; //@@author chydarren - private static final int TRUE_AND = 1; - private static final int TRUE_OR = 2; + private static final String CATEGORICAL_SAVINGS = "categorical_savings"; + private static final String MONTHLY_EXPENDITURE = "monthly_expenditure"; + private static final String TIME_INSIGHTS = "time_insights"; private static final int FALSE = 0; - private static final String CATEGORIES = "categories"; - private static final String TIME = "time"; - private static final String MONTHS = "months"; private static Logger statsLogger = Logger.getLogger(StatsCommand.class.getName()); private String statsType; @@ -125,46 +125,35 @@ public void execute(TransactionList transactions, Ui ui, Storage storage) throws statsLogger.setLevel(Level.SEVERE); statsLogger.log(Level.INFO, "Entering execution of the Stats command."); + // Throws an unsupported tag exception if non-time_insights tag is using date intervals tags + if (!statsType.equals(TIME_INSIGHTS) && (containMonthYear() != FALSE + || containPeriodNumber() != FALSE)) { + throw new GlobalUnsupportedTagException(); + } + // Checks if there are any error in the tag combinations related to DateIntervals parseDateIntervalsTags(); - listStatsByStatsType(statsType, transactions); + listStatsByStatsType(transactions); } /** * Lists the statistics depending on the type of statistics requested. * - * @param statsType The type of statistics that is needed. * @param transactions An instance of the TransactionList class. * @throws MoolahException If the type of statistics is not recognised. */ - private void listStatsByStatsType(String statsType, TransactionList transactions) - throws MoolahException { + private void listStatsByStatsType(TransactionList transactions) throws MoolahException { switch (statsType) { - case MONTHS: - statsTypeMonthlyExpenditure(transactions); - break; - case CATEGORIES: - statsLogger.log(Level.INFO, "Stats type has been detected for categorical savings."); - statsTypeCategoricalSavings(transactions); - statsLogger.log(Level.INFO, "End of Stats command."); + case MONTHLY_EXPENDITURE: + case CATEGORICAL_SAVINGS: + statsLogger.log(Level.INFO, "Stats type has been detected for categorical savings " + + "or monthly expenditure."); + statsTypeCategoricalSavingsOrMonthlyExpenditure(transactions); break; //@@author paullowse - case TIME: - statsLogger.log(Level.INFO, "Stats type has been detected for monthly savings."); - // Stats command uses last N months or years - if (containPeriodNumber() == TRUE_AND && containMonthYear() == FALSE) { - statsLogger.log(Level.INFO, "Stats command uses last N months or years."); - statsTypeTimeSavings(transactions); - // Stats command uses either monthly or yearly - } else if (containPeriodNumber() == FALSE && (containMonthYear() == TRUE_OR - || containMonthYear() == TRUE_AND)) { - statsLogger.log(Level.INFO, "Stats command uses either monthly or yearly."); - statsTypeTimeSavings(transactions); - // Throws a missing tag if s/time was called without any relevant tags - } else { - statsLogger.log(Level.WARNING, "An exception has been caught due to a missing tag"); - throw new StatsInvalidTypeException(); - } + case TIME_INSIGHTS: + statsLogger.log(Level.INFO, "Stats type has been detected for time insights."); + statsTypeTimeInsights(transactions); break; //@@author chydarren default: @@ -176,41 +165,28 @@ private void listStatsByStatsType(String statsType, TransactionList transactions //@@author chydarren /** - * Display the statistics requested for current amount of savings in each category. + * Display the statistics requested for current amount of savings in each category or the monthly expenditure. * * @param transactions An instance of the TransactionList class. */ - public void statsTypeCategoricalSavings(TransactionList transactions) { - String categoricalSavingsList = transactions.listCategoricalSavings(); + public void statsTypeCategoricalSavingsOrMonthlyExpenditure(TransactionList transactions) { + String genericStatsList = transactions.listMonthlyExpenditure(); - if (categoricalSavingsList.isEmpty()) { - statsLogger.log(Level.INFO, "Categorical savings list is empty as there are no transactions available."); - Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); - return; + if (statsType.equals(CATEGORICAL_SAVINGS)) { + // Replaces stats list with categorical savings if stats type is categorical_savings + genericStatsList = transactions.listCategoricalSavings(); } - assert !categoricalSavingsList.isEmpty(); - statsLogger.log(Level.INFO, "Categorical savings list is found to contain categories-amount pairs."); - Ui.showList(categoricalSavingsList, INFO_STATS_CATEGORIES.toString()); - } - - /** - * Display the statistics requested for the amount of expenditure and savings accumulated over different months. - * - * @param transactions An instance of the TransactionList class. - */ - public void statsTypeMonthlyExpenditure(TransactionList transactions) throws InputTransactionInvalidTypeException { - String monthlyExpenditureList = transactions.listMonthlyExpenditure(); - - if (monthlyExpenditureList.isEmpty()) { - statsLogger.log(Level.INFO, "Monthly expenditure list is empty as there are no transactions available."); + if (genericStatsList.isEmpty()) { + statsLogger.log(Level.INFO, "Generic stats list is empty as there are no transactions available."); Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); return; } - assert !monthlyExpenditureList.isEmpty(); - statsLogger.log(Level.INFO, "Monthly expenditure list is found to contain month-expenditure pairs."); - Ui.showList(monthlyExpenditureList, INFO_STATS_MONTHS.toString()); + assert !genericStatsList.isEmpty(); + statsLogger.log(Level.INFO, "Generic stats list has info available for categorical savings or " + + "monthly expenditure."); + Ui.showList(genericStatsList, String.format(INFO_STATS_GENERIC.toString(), statsType)); } //@@author paullowse @@ -221,12 +197,12 @@ public void statsTypeMonthlyExpenditure(TransactionList transactions) throws Inp * * @param transactions An instance of the TransactionList class. */ - public void statsTypeTimeSavings(TransactionList transactions) { + public void statsTypeTimeInsights(TransactionList transactions) { ArrayList timeTransactions = getTimeTransactions(transactions); - String timeSavingsList = transactions.listTimeStats(timeTransactions, year, month, period, number); + String timeInsightsList = transactions.listTimeStats(timeTransactions, year, month, period, number); - if (timeSavingsList.isEmpty()) { - statsLogger.log(Level.INFO, "Categorical savings list is empty as there are no transactions available."); + if (timeInsightsList.isEmpty()) { + statsLogger.log(Level.INFO, "Time insights list is empty as there are no transactions available."); Ui.showInfoMessage(INFO_STATS_EMPTY.toString()); return; } @@ -235,14 +211,15 @@ public void statsTypeTimeSavings(TransactionList transactions) { ArrayList amounts; amounts = transactions.processTimeSummaryStats(timeTransactions); - String incomeMessage = INFO_STATS_SUMMARY_HEADER + LINE_SEPARATOR.toString() - + INFO_STATS_INCOME + amounts.get(0); - String expensesMessage = INFO_STATS_EXPENSES + amounts.get(1); - String savingsMessage = INFO_STATS_SAVINGS + amounts.get(2); + String incomeMessage = INFO_STATS_EXPENDITURE_HEADER + LINE_SEPARATOR.toString() + + String.format("%s%s%s", INFO_INCOME, COLON_SPACE, DOLLAR_SIGN) + amounts.get(0); + String expensesMessage = String.format("%s%s%s", INFO_EXPENSE, COLON_SPACE, DOLLAR_SIGN) + amounts.get(1); + String savingsMessage = String.format("%s%s%s", INFO_SAVINGS, COLON_SPACE, DOLLAR_SIGN) + amounts.get(2); - assert !timeSavingsList.isEmpty(); - statsLogger.log(Level.INFO, "Monthly savings list is found to contain categories-amount pairs."); - Ui.showStatsList(timeSavingsList, INFO_STATS_TIME.toString(), incomeMessage, expensesMessage, savingsMessage); + assert !timeInsightsList.isEmpty(); + statsLogger.log(Level.INFO, "Time insights list has info available for the specified time period."); + Ui.showTimeInsightsList(timeInsightsList, INFO_STATS_TIME_INSIGHTS.toString(), incomeMessage, expensesMessage, + savingsMessage); } /** diff --git a/src/main/java/seedu/duke/data/TransactionList.java b/src/main/java/seedu/duke/data/TransactionList.java index f6a91936e..0e70c7ec4 100644 --- a/src/main/java/seedu/duke/data/TransactionList.java +++ b/src/main/java/seedu/duke/data/TransactionList.java @@ -14,30 +14,40 @@ import java.util.stream.Collectors; import static seedu.duke.common.Constants.MAX_AMOUNT_VALUE; +import static seedu.duke.common.Constants.MIN_AMOUNT_VALUE; import static seedu.duke.common.Constants.MAX_TRANSACTIONS_COUNT; import static seedu.duke.common.DateFormats.DATE_MONTH_PATTERN; -import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES_HEADER; import static seedu.duke.common.InfoMessages.LINE_SEPARATOR; import static seedu.duke.common.InfoMessages.DOLLAR_SIGN; -import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_ONE; -import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_TWO; -import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_THREE; -import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_FOUR; -import static seedu.duke.common.InfoMessages.INFO_STATS_MONTHS_COMMENT_FIVE; +import static seedu.duke.common.InfoMessages.COLON_SPACE; +import static seedu.duke.common.InfoMessages.INFO_INCOME; +import static seedu.duke.common.InfoMessages.INFO_EXPENSE; +import static seedu.duke.common.InfoMessages.INFO_SAVINGS; +import static seedu.duke.common.InfoMessages.INFO_STATS_CATEGORIES_HEADER; +import static seedu.duke.common.InfoMessages.INFO_STATS_HABIT_VERY_LOW_SAVINGS; +import static seedu.duke.common.InfoMessages.INFO_STATS_HABIT_LOW_SAVINGS; +import static seedu.duke.common.InfoMessages.INFO_STATS_HABIT_MEDIUM_SAVINGS; +import static seedu.duke.common.InfoMessages.INFO_STATS_HABIT_HIGH_SAVINGS; +import static seedu.duke.common.InfoMessages.INFO_STATS_HABIT_VERY_HIGH_SAVINGS; + + +//@@author chydarren /** * Represents a list of transactions added by the user into the application. - * Operations related to modifying the list of transactions are defined under this class. + * + *

+#mZOIY|Let1Rnt90KMq)yJ_()$EKG~#+AWshr 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%0DC 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�NrqGh!{Jd^@dE>5I!2rW*l{AdDLrC#bqk_rHcWm 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 Pk*ir^?aWFrKS%!y=(CZ4 literal 0 HcmV?d00001 diff --git a/src/main/java/seedu/duke/parser/ParameterParser.java b/src/main/java/seedu/duke/parser/ParameterParser.java index e26429d6f..ec37e7071 100644 --- a/src/main/java/seedu/duke/parser/ParameterParser.java +++ b/src/main/java/seedu/duke/parser/ParameterParser.java @@ -446,7 +446,7 @@ public static String parseStatsTypeTag(String parameter) throws ListStatisticsIn statsType = "categories"; break; default: - parserLogger.log(Level.WARNING, "An invalid statstic type error is caught for the given parameter: " + parserLogger.log(Level.WARNING, "An invalid statistic type error is caught for the given parameter: " + parameter); throw new ListStatisticsInvalidStatsTypeException(); } From 6c9143983a41af6b87ff9f195c48b158e98400b6 Mon Sep 17 00:00:00 2001 From: brian-vb Date: Tue, 18 Oct 2022 23:11:23 +0800 Subject: [PATCH 167/416] Add Documentation --- docs/DeveloperGuide.md | 117 +++++++++++++++++++--- docs/diagram/DeleteCommand.puml | 36 +++++++ docs/diagram/PurgeCommand.puml | 35 +++++++ docs/images/DeleteCommandClassDiagram.png | Bin 0 -> 18893 bytes docs/images/PurgeCommandClassDiagram.png | Bin 0 -> 16394 bytes 5 files changed, 175 insertions(+), 13 deletions(-) create mode 100644 docs/diagram/DeleteCommand.puml create mode 100644 docs/diagram/PurgeCommand.puml create mode 100644 docs/images/DeleteCommandClassDiagram.png create mode 100644 docs/images/PurgeCommandClassDiagram.png diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 33a4c551d..b24f4e101 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -106,7 +106,7 @@ displayed. -A more detailed explaination on the implementation on the transactions can be viewed under Section +A more detailed explanation on the implementation on the transactions can be viewed under Section [Implementation for Transaction](#implementation-for-transaction). #### How the data component interacts @@ -120,7 +120,7 @@ Based on the whether the initialization is successful, the corresponding constru ![Sequence Diagram on Creation of TransactionList](images/TransactionListSequenceDiagram.png) - A transaction (either an income or expense) is created by an `addCommand` class, can be modified by an `editCommand` -class and can be deleted by a `deleteCommand` or `purgeCommand` class. These interactions is described in further detail +class and can be deleted by a `deleteCommand` or `purgeCommand` class. These interactions are described in further detail under each command section below. @@ -228,7 +228,7 @@ _Written by: Chia Thin Hong_ **This feature allows the local and external (handled by Storage class) storage of transaction entries by the user.** -The `AddCommand` inherits properties from the abstract `Command` class.he inheritance of `Command` from `AddCommand` is +The `AddCommand` inherits properties from the abstract `Command` class. The inheritance of `Command` from `AddCommand` is shown below.