From 0166af695d829146f8c96982d217f4522cda8d8c Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 20:30:52 +0800
Subject: [PATCH 01/13] Add budget command test
---
fasttrack.log.0 | Bin 169021 -> 172975 bytes
.../logic/commands/SetBudgetCommand.java | 10 ++
src/main/java/fasttrack/model/Budget.java | 5 +
.../logic/commands/SetBudgetCommandTest.java | 72 ++++++++++++++
.../java/fasttrack/ui/UIComponentTest.java | 94 ++++++++++++++++++
5 files changed, 181 insertions(+)
create mode 100644 src/test/java/fasttrack/logic/commands/SetBudgetCommandTest.java
create mode 100644 src/test/java/fasttrack/ui/UIComponentTest.java
diff --git a/fasttrack.log.0 b/fasttrack.log.0
index 509d1c8a6e7313edf9de3856f88f96ef36b93510..fe16bc65d5b9d5dae928722db2d47929ebe1e0e4 100644
GIT binary patch
delta 1019
zcmdnHf@}S8u7(!IEljpkrU&pb@v>Q18Ch5vOc&H{@ngM^5{5=KCy
zrZ@I8YQiG|uNrVf;8y{P3v8aA9yo!?7;jAA(M2#GNYRU)0rBX?OgCW7plD&@ returns true
+ assertTrue(setBudgetCommand.equals(setBudgetCommand));
+
+ // same values -> returns true
+ assertTrue(setBudgetCommand.equals(setBudgetCommandCopy));
+
+ // null -> returns false
+ assertFalse(setBudgetCommand.equals(null));
+
+ // different budget -> returns false
+ assertFalse(setBudgetCommand.equals(setBudgetCommandDifferentBudget));
+ }
+
+ @Test
+ public void testHashCode() {
+ SetBudgetCommand setBudgetCommand = new SetBudgetCommand(budget);
+ SetBudgetCommand setBudgetCommandCopy = new SetBudgetCommand(budget);
+ SetBudgetCommand setBudgetCommandDifferentBudget = new SetBudgetCommand(differentBudget);
+
+ // same object -> returns same hashcode
+ assertEquals(setBudgetCommand.hashCode(), setBudgetCommand.hashCode());
+
+ // same values -> returns same hashcode
+ assertEquals(setBudgetCommand.hashCode(), setBudgetCommandCopy.hashCode());
+
+ // different budget -> returns different hashcode
+ assertNotEquals(setBudgetCommand.hashCode(), setBudgetCommandDifferentBudget.hashCode());
+ }
+
+ @Test
+ public void testToString() {
+ SetBudgetCommand setBudgetCommand = new SetBudgetCommand(budget);
+ assertEquals("SetBudgetCommand{budget=1000.0}", setBudgetCommand.toString());
+ }
+
+ @Test
+ public void execute_validBudget_success() {
+ SetBudgetCommand setBudgetCommand = new SetBudgetCommand(budget);
+ String expectedMessage = SetBudgetCommand.MESSAGE_SUCCESS + budget.toString();
+ Model expectedModel = new ModelManager(model.getExpenseTracker(), new UserPrefs());
+ CommandResult message = setBudgetCommand.execute(expectedModel);
+ assertEquals(expectedMessage, message.getFeedbackToUser());
+ }
+
+}
diff --git a/src/test/java/fasttrack/ui/UIComponentTest.java b/src/test/java/fasttrack/ui/UIComponentTest.java
new file mode 100644
index 00000000000..c274b2d942c
--- /dev/null
+++ b/src/test/java/fasttrack/ui/UIComponentTest.java
@@ -0,0 +1,94 @@
+package fasttrack.ui;
+
+import static fasttrack.testutil.TypicalCategories.FOOD;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+
+import fasttrack.model.category.Category;
+import javafx.application.Platform;
+import javafx.scene.control.Label;
+
+
+public class UIComponentTest {
+
+ private Category category;
+ private int displayedIndex;
+ private int associatedExpenseCount;
+
+ @BeforeEach
+ public void setUp() throws InterruptedException {
+ category = FOOD;
+ displayedIndex = 1;
+ associatedExpenseCount = 3;
+ }
+
+ @BeforeAll
+ static void initJfx() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ Platform.startup(latch::countDown);
+ latch.await();
+ }
+
+ @Test
+ public void testCategoryCard_validData_success() {
+ CategoryCard categoryCard = new CategoryCard(category, displayedIndex, associatedExpenseCount);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ // Test that the category name label is set correctly
+ Label categoryNameLabel = (Label) categoryCard.getRoot().lookup("#categoryName");
+ assertEquals("Food", categoryNameLabel.getText());
+
+ // Test that the index label is set correctly
+ Label indexLabel = (Label) categoryCard.getRoot().lookup("#id");
+ assertEquals("1. ", indexLabel.getText());
+
+ // Test that the expense count label is set correctly
+ Label expenseCountLabel = (Label) categoryCard.getRoot().lookup("#expenseCount");
+ assertEquals("3", expenseCountLabel.getText());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testEquals_validCategoryCard_success() {
+ CategoryCard categoryCard1 = new CategoryCard(category, displayedIndex, associatedExpenseCount);
+ CategoryCard categoryCard2 = new CategoryCard(category, displayedIndex + 1, associatedExpenseCount - 1);
+ CategoryCard categoryCard3 = new CategoryCard(category, displayedIndex + 1, associatedExpenseCount - 1);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ assertEquals(categoryCard1, categoryCard1);
+ assertNotEquals(categoryCard1, categoryCard2);
+ assertEquals(categoryCard2, categoryCard3);
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+}
+
\ No newline at end of file
From 0f55ed83c7398dba2522e568d9b5b13d960dedd4 Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 20:32:44 +0800
Subject: [PATCH 02/13] Fix checkstyle
---
.../fasttrack/logic/commands/SetBudgetCommandTest.java | 2 +-
src/test/java/fasttrack/ui/UIComponentTest.java | 10 ++++------
2 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/test/java/fasttrack/logic/commands/SetBudgetCommandTest.java b/src/test/java/fasttrack/logic/commands/SetBudgetCommandTest.java
index 5c6b593b4e1..d835ed953dc 100644
--- a/src/test/java/fasttrack/logic/commands/SetBudgetCommandTest.java
+++ b/src/test/java/fasttrack/logic/commands/SetBudgetCommandTest.java
@@ -16,7 +16,7 @@
public class SetBudgetCommandTest {
private Budget budget = new Budget(1000);
private Budget differentBudget = new Budget(2000);
- private Model model = new ModelManager(TypicalCategories.getTypicalExpenseTracker(),
+ private Model model = new ModelManager(TypicalCategories.getTypicalExpenseTracker(),
new UserPrefs());
@Test
diff --git a/src/test/java/fasttrack/ui/UIComponentTest.java b/src/test/java/fasttrack/ui/UIComponentTest.java
index c274b2d942c..b345069f846 100644
--- a/src/test/java/fasttrack/ui/UIComponentTest.java
+++ b/src/test/java/fasttrack/ui/UIComponentTest.java
@@ -18,8 +18,7 @@
import javafx.application.Platform;
import javafx.scene.control.Label;
-
-public class UIComponentTest {
+public class UiComponentTest {
private Category category;
private int displayedIndex;
@@ -33,10 +32,10 @@ public void setUp() throws InterruptedException {
}
@BeforeAll
- static void initJfx() throws InterruptedException {
+ static void initJfx() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
- Platform.startup(latch::countDown);
- latch.await();
+ Platform.startup(latch::countDown);
+ latch.await();
}
@Test
@@ -91,4 +90,3 @@ public void testEquals_validCategoryCard_success() {
}
}
}
-
\ No newline at end of file
From 30e2e5c80722bc7554e75b4b3d35ad27223860ae Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 20:41:00 +0800
Subject: [PATCH 03/13] Add component test
---
fasttrack.log.0 | Bin 172975 -> 174952 bytes
.../java/fasttrack/ui/UIComponentTest.java | 55 ++++++++++++++++++
2 files changed, 55 insertions(+)
diff --git a/fasttrack.log.0 b/fasttrack.log.0
index fe16bc65d5b9d5dae928722db2d47929ebe1e0e4..203c89f1db87874592ebd83aeffbd4cd3b05be20 100644
GIT binary patch
delta 207
zcmZ2~oa@Cku7(!IElledPw$__#LsGCU}a)7`Gcb3^!#Z|eoQ6?(oYUU{
y)i9b&-jFLbd4swX$c`)ocRSE*BSr*&I*@NSeg71uw@6|@Eh-=}@#(q?nB)LqB1pXe
delta 13
VcmaEHjcfgJu7(!IElled0{}1N237z7
diff --git a/src/test/java/fasttrack/ui/UIComponentTest.java b/src/test/java/fasttrack/ui/UIComponentTest.java
index b345069f846..bde6fb5718d 100644
--- a/src/test/java/fasttrack/ui/UIComponentTest.java
+++ b/src/test/java/fasttrack/ui/UIComponentTest.java
@@ -1,6 +1,9 @@
package fasttrack.ui;
import static fasttrack.testutil.TypicalCategories.FOOD;
+import static fasttrack.testutil.TypicalCategories.TECH;
+import static fasttrack.testutil.TypicalExpenses.APPLE;
+import static fasttrack.testutil.TypicalExpenses.CHERRY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.fail;
@@ -15,20 +18,29 @@
import org.opentest4j.AssertionFailedError;
import fasttrack.model.category.Category;
+import fasttrack.model.expense.Expense;
import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
public class UiComponentTest {
private Category category;
private int displayedIndex;
private int associatedExpenseCount;
+ private CategoryListPanel categoryListPanel;
+ private ObservableList categories;
+ private ObservableList expenses;
@BeforeEach
public void setUp() throws InterruptedException {
category = FOOD;
displayedIndex = 1;
associatedExpenseCount = 3;
+ categories = FXCollections.observableArrayList(FOOD, TECH);
+ expenses = FXCollections.observableArrayList(APPLE, CHERRY);
}
@BeforeAll
@@ -89,4 +101,47 @@ public void testEquals_validCategoryCard_success() {
fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
}
}
+
+ @Test
+ public void categoryListView_validCategories_countEqual() {
+ CompletableFuture future = new CompletableFuture<>();
+ categoryListPanel = new CategoryListPanel(categories, expenses);
+ Platform.runLater(() -> {
+ try {
+ // Test that the number of categories is correct
+ ListView> categoryListView = (ListView>) categoryListPanel.getRoot().lookup("#categoryListView");
+ assertEquals(categories.size(), categoryListView.getItems().size());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void categoryListView_emptyList_countZero() {
+ categories = FXCollections.observableArrayList();
+ expenses = FXCollections.observableArrayList();
+ CompletableFuture future = new CompletableFuture<>();
+ categoryListPanel = new CategoryListPanel(categories, expenses);
+ Platform.runLater(() -> {
+ try {
+ ListView> categoryListView = (ListView>) categoryListPanel.getRoot().lookup("#categoryListView");
+ assertEquals(0, categoryListView.getItems().size());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
}
From b05f91682930315d1b434d27423242223df01055 Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 20:43:31 +0800
Subject: [PATCH 04/13] Rename component test
---
fasttrack.log.0 | Bin 174952 -> 176929 bytes
...IComponentTest.java => ComponentTest.java} | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
rename src/test/java/fasttrack/ui/{UIComponentTest.java => ComponentTest.java} (99%)
diff --git a/fasttrack.log.0 b/fasttrack.log.0
index 203c89f1db87874592ebd83aeffbd4cd3b05be20..11e8f8b2343e4847f9babd5ff2c6267da83b60cf 100644
GIT binary patch
delta 191
zcmaEHjcegEu7(!IEle4!r>|ekBry3=h|uJJio($qFR=6(mwv&!|0JU_O)jBw4=c-OHGS7!hK&C<>
Date: Mon, 10 Apr 2023 20:50:20 +0800
Subject: [PATCH 05/13] Modify actions for libgtk
---
.github/workflows/gradle.yml | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index 81267b247eb..11db8d3a00f 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -24,7 +24,9 @@ jobs:
- name: Run repository-wide tests
if: runner.os == 'Linux'
working-directory: ${{ github.workspace }}/.github
- run: ./run-checks.sh
+ run: |
+ sudo apt-get install libgtk-3-0
+ ./run-checks.sh
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
From 564821fe5def2aac576f76e5779781a3ec84fc33 Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 21:02:42 +0800
Subject: [PATCH 06/13] Fix linux
---
.github/workflows/gradle.yml | 4 +---
src/test/java/fasttrack/ui/ComponentTest.java | 8 ++++++++
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index 11db8d3a00f..e453a60a51e 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -24,9 +24,7 @@ jobs:
- name: Run repository-wide tests
if: runner.os == 'Linux'
working-directory: ${{ github.workspace }}/.github
- run: |
- sudo apt-get install libgtk-3-0
- ./run-checks.sh
+ run: ./run-tests.sh
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
diff --git a/src/test/java/fasttrack/ui/ComponentTest.java b/src/test/java/fasttrack/ui/ComponentTest.java
index 8817220d442..02129a20b6b 100644
--- a/src/test/java/fasttrack/ui/ComponentTest.java
+++ b/src/test/java/fasttrack/ui/ComponentTest.java
@@ -27,6 +27,7 @@
public class ComponentTest {
+ // Ignore this class on Linux
private Category category;
private int displayedIndex;
private int associatedExpenseCount;
@@ -36,6 +37,10 @@ public class ComponentTest {
@BeforeEach
public void setUp() throws InterruptedException {
+
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
category = FOOD;
displayedIndex = 1;
associatedExpenseCount = 3;
@@ -45,6 +50,9 @@ public void setUp() throws InterruptedException {
@BeforeAll
static void initJfx() throws InterruptedException {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
CountDownLatch latch = new CountDownLatch(1);
Platform.startup(latch::countDown);
latch.await();
From c6cada8ef8583eca56a4f95a2d65ffb021f4dc0a Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 21:03:58 +0800
Subject: [PATCH 07/13] Revert actions
---
.github/workflows/gradle.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index e453a60a51e..81267b247eb 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -24,7 +24,7 @@ jobs:
- name: Run repository-wide tests
if: runner.os == 'Linux'
working-directory: ${{ github.workspace }}/.github
- run: ./run-tests.sh
+ run: ./run-checks.sh
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
From 22cda3f00dc1acc1d11cdab3f3ad0f687bd59a68 Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 21:05:08 +0800
Subject: [PATCH 08/13] Fix trailing whitespace
---
src/test/java/fasttrack/ui/ComponentTest.java | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/test/java/fasttrack/ui/ComponentTest.java b/src/test/java/fasttrack/ui/ComponentTest.java
index 02129a20b6b..5023c45def3 100644
--- a/src/test/java/fasttrack/ui/ComponentTest.java
+++ b/src/test/java/fasttrack/ui/ComponentTest.java
@@ -27,7 +27,7 @@
public class ComponentTest {
- // Ignore this class on Linux
+ // Ignore this class on Linux
private Category category;
private int displayedIndex;
private int associatedExpenseCount;
@@ -37,7 +37,6 @@ public class ComponentTest {
@BeforeEach
public void setUp() throws InterruptedException {
-
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
return;
}
From 1ee5ea05b90e52a874fc077b83665bc99cb32805 Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 21:08:03 +0800
Subject: [PATCH 09/13] Ignore Linux for UI tests
---
src/test/java/fasttrack/ui/ComponentTest.java | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/test/java/fasttrack/ui/ComponentTest.java b/src/test/java/fasttrack/ui/ComponentTest.java
index 5023c45def3..ee1625de9b9 100644
--- a/src/test/java/fasttrack/ui/ComponentTest.java
+++ b/src/test/java/fasttrack/ui/ComponentTest.java
@@ -59,6 +59,9 @@ static void initJfx() throws InterruptedException {
@Test
public void testCategoryCard_validData_success() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
CategoryCard categoryCard = new CategoryCard(category, displayedIndex, associatedExpenseCount);
CompletableFuture future = new CompletableFuture<>();
Platform.runLater(() -> {
@@ -88,6 +91,9 @@ public void testCategoryCard_validData_success() {
@Test
public void testEquals_validCategoryCard_success() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
CategoryCard categoryCard1 = new CategoryCard(category, displayedIndex, associatedExpenseCount);
CategoryCard categoryCard2 = new CategoryCard(category, displayedIndex + 1, associatedExpenseCount - 1);
CategoryCard categoryCard3 = new CategoryCard(category, displayedIndex + 1, associatedExpenseCount - 1);
@@ -111,6 +117,9 @@ public void testEquals_validCategoryCard_success() {
@Test
public void categoryListView_validCategories_countEqual() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
CompletableFuture future = new CompletableFuture<>();
categoryListPanel = new CategoryListPanel(categories, expenses);
Platform.runLater(() -> {
@@ -132,6 +141,9 @@ public void categoryListView_validCategories_countEqual() {
@Test
public void categoryListView_emptyList_countZero() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
categories = FXCollections.observableArrayList();
expenses = FXCollections.observableArrayList();
CompletableFuture future = new CompletableFuture<>();
From c29eefd0caff6560d6d39ade1b904f588d14ae39 Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 21:46:48 +0800
Subject: [PATCH 10/13] Re-add @niceleejy 's UI tests
---
...mponentTest.java => CategoryCardTest.java} | 73 +----------
.../fasttrack/ui/CategoryListPanelTest.java | 101 +++++++++++++++
.../java/fasttrack/ui/CommandBoxTest.java | 120 ++++++++++++++++++
.../java/fasttrack/ui/ExpenseCardTest.java | 111 ++++++++++++++++
.../fasttrack/ui/ExpenseListPanelTest.java | 96 ++++++++++++++
.../java/fasttrack/ui/JavaFxTestHelper.java | 35 +++++
.../ui/RecurringExpenseCardTest.java | 114 +++++++++++++++++
.../ui/RecurringExpensePanelTest.java | 95 ++++++++++++++
8 files changed, 678 insertions(+), 67 deletions(-)
rename src/test/java/fasttrack/ui/{ComponentTest.java => CategoryCardTest.java} (56%)
create mode 100644 src/test/java/fasttrack/ui/CategoryListPanelTest.java
create mode 100644 src/test/java/fasttrack/ui/CommandBoxTest.java
create mode 100644 src/test/java/fasttrack/ui/ExpenseCardTest.java
create mode 100644 src/test/java/fasttrack/ui/ExpenseListPanelTest.java
create mode 100644 src/test/java/fasttrack/ui/JavaFxTestHelper.java
create mode 100644 src/test/java/fasttrack/ui/RecurringExpenseCardTest.java
create mode 100644 src/test/java/fasttrack/ui/RecurringExpensePanelTest.java
diff --git a/src/test/java/fasttrack/ui/ComponentTest.java b/src/test/java/fasttrack/ui/CategoryCardTest.java
similarity index 56%
rename from src/test/java/fasttrack/ui/ComponentTest.java
rename to src/test/java/fasttrack/ui/CategoryCardTest.java
index ee1625de9b9..d550c08cbfa 100644
--- a/src/test/java/fasttrack/ui/ComponentTest.java
+++ b/src/test/java/fasttrack/ui/CategoryCardTest.java
@@ -1,15 +1,13 @@
package fasttrack.ui;
import static fasttrack.testutil.TypicalCategories.FOOD;
-import static fasttrack.testutil.TypicalCategories.TECH;
-import static fasttrack.testutil.TypicalExpenses.APPLE;
-import static fasttrack.testutil.TypicalExpenses.CHERRY;
+import static fasttrack.ui.JavaFxTestHelper.initJavaFxHelper;
+import static fasttrack.ui.JavaFxTestHelper.setUpHeadlessMode;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.fail;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import org.junit.jupiter.api.BeforeAll;
@@ -18,22 +16,15 @@
import org.opentest4j.AssertionFailedError;
import fasttrack.model.category.Category;
-import fasttrack.model.expense.Expense;
import javafx.application.Platform;
-import javafx.collections.FXCollections;
-import javafx.collections.ObservableList;
import javafx.scene.control.Label;
-import javafx.scene.control.ListView;
-public class ComponentTest {
- // Ignore this class on Linux
+public class CategoryCardTest {
+
private Category category;
private int displayedIndex;
private int associatedExpenseCount;
- private CategoryListPanel categoryListPanel;
- private ObservableList categories;
- private ObservableList expenses;
@BeforeEach
public void setUp() throws InterruptedException {
@@ -43,8 +34,6 @@ public void setUp() throws InterruptedException {
category = FOOD;
displayedIndex = 1;
associatedExpenseCount = 3;
- categories = FXCollections.observableArrayList(FOOD, TECH);
- expenses = FXCollections.observableArrayList(APPLE, CHERRY);
}
@BeforeAll
@@ -52,9 +41,8 @@ static void initJfx() throws InterruptedException {
if (System.getProperty("os.name").toLowerCase().contains("linux")) {
return;
}
- CountDownLatch latch = new CountDownLatch(1);
- Platform.startup(latch::countDown);
- latch.await();
+ setUpHeadlessMode();
+ initJavaFxHelper();
}
@Test
@@ -114,53 +102,4 @@ public void testEquals_validCategoryCard_success() {
fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
}
}
-
- @Test
- public void categoryListView_validCategories_countEqual() {
- if (System.getProperty("os.name").toLowerCase().contains("linux")) {
- return;
- }
- CompletableFuture future = new CompletableFuture<>();
- categoryListPanel = new CategoryListPanel(categories, expenses);
- Platform.runLater(() -> {
- try {
- // Test that the number of categories is correct
- ListView> categoryListView = (ListView>) categoryListPanel.getRoot().lookup("#categoryListView");
- assertEquals(categories.size(), categoryListView.getItems().size());
- future.complete(null);
- } catch (AssertionFailedError e) {
- future.completeExceptionally(e);
- }
- });
- try {
- future.get();
- } catch (InterruptedException | ExecutionException e) {
- fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
- }
- }
-
- @Test
- public void categoryListView_emptyList_countZero() {
- if (System.getProperty("os.name").toLowerCase().contains("linux")) {
- return;
- }
- categories = FXCollections.observableArrayList();
- expenses = FXCollections.observableArrayList();
- CompletableFuture future = new CompletableFuture<>();
- categoryListPanel = new CategoryListPanel(categories, expenses);
- Platform.runLater(() -> {
- try {
- ListView> categoryListView = (ListView>) categoryListPanel.getRoot().lookup("#categoryListView");
- assertEquals(0, categoryListView.getItems().size());
- future.complete(null);
- } catch (AssertionFailedError e) {
- future.completeExceptionally(e);
- }
- });
- try {
- future.get();
- } catch (InterruptedException | ExecutionException e) {
- fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
- }
- }
}
diff --git a/src/test/java/fasttrack/ui/CategoryListPanelTest.java b/src/test/java/fasttrack/ui/CategoryListPanelTest.java
new file mode 100644
index 00000000000..a158b96bf87
--- /dev/null
+++ b/src/test/java/fasttrack/ui/CategoryListPanelTest.java
@@ -0,0 +1,101 @@
+package fasttrack.ui;
+
+import static fasttrack.testutil.TypicalCategories.FOOD;
+import static fasttrack.testutil.TypicalCategories.TECH;
+import static fasttrack.testutil.TypicalExpenses.APPLE;
+import static fasttrack.testutil.TypicalExpenses.CHERRY;
+import static fasttrack.ui.JavaFxTestHelper.initJavaFxHelper;
+import static fasttrack.ui.JavaFxTestHelper.setUpHeadlessMode;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+
+import fasttrack.model.category.Category;
+import fasttrack.model.expense.Expense;
+import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.scene.control.ListView;
+
+
+public class CategoryListPanelTest {
+
+ private CategoryListPanel categoryListPanel;
+ private ObservableList categories;
+ private ObservableList expenses;
+
+ @BeforeEach
+ public void setUp() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ categories = FXCollections.observableArrayList(FOOD, TECH);
+ expenses = FXCollections.observableArrayList(APPLE, CHERRY);
+ }
+
+ @BeforeAll
+ static void initJfx() throws InterruptedException {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ setUpHeadlessMode();
+ initJavaFxHelper();
+ }
+
+
+ @Test
+ public void categoryListView_validCategories_countEqual() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ CompletableFuture future = new CompletableFuture<>();
+ categoryListPanel = new CategoryListPanel(categories, expenses);
+ Platform.runLater(() -> {
+ try {
+ // Test that the number of categories is correct
+ ListView> categoryListView = (ListView>) categoryListPanel.getRoot().lookup("#categoryListView");
+ assertEquals(categories.size(), categoryListView.getItems().size());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void categoryListView_emptyList_countZero() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ categories = FXCollections.observableArrayList();
+ expenses = FXCollections.observableArrayList();
+ CompletableFuture future = new CompletableFuture<>();
+ categoryListPanel = new CategoryListPanel(categories, expenses);
+ Platform.runLater(() -> {
+ try {
+ ListView> categoryListView = (ListView>) categoryListPanel.getRoot().lookup("#categoryListView");
+ assertEquals(0, categoryListView.getItems().size());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/fasttrack/ui/CommandBoxTest.java b/src/test/java/fasttrack/ui/CommandBoxTest.java
new file mode 100644
index 00000000000..2e6450fbe5d
--- /dev/null
+++ b/src/test/java/fasttrack/ui/CommandBoxTest.java
@@ -0,0 +1,120 @@
+package fasttrack.ui;
+
+import static fasttrack.ui.JavaFxTestHelper.initJavaFxHelper;
+import static fasttrack.ui.JavaFxTestHelper.setUpHeadlessMode;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Objects;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import fasttrack.logic.commands.exceptions.CommandException;
+import javafx.collections.ObservableList;
+import javafx.scene.Scene;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.HBox;
+
+
+public class CommandBoxTest {
+
+ private static final String VALID_COMMAND = "add n/Milk c/Groceries p/12";
+ private static final String INCOMPLETE_COMMAND = "add n/Milk c/Gro";
+ private static final String ERROR_STYLE_CLASS = "error";
+
+ private CommandBox commandBox;
+ private boolean commandExecuted;
+
+
+ @BeforeEach
+ public void setUp() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ commandExecuted = false;
+ // Dummy command executor function
+ CommandBox.CommandExecutor commandExecutor = commandText -> {
+ if (Objects.equals(commandText, "invalid command")) {
+ throw new CommandException("Invalid command");
+ }
+ commandExecuted = true;
+ return null;
+ };
+ commandBox = new CommandBox(commandExecutor, false);
+ }
+
+ @BeforeAll
+ static void initJfx() throws InterruptedException {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ setUpHeadlessMode();
+ initJavaFxHelper();
+ }
+
+
+ @Test
+ public void handleCommandEntered_emptyCommand_commandNotExecuted() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ TextField textField = (TextField) commandBox.getRoot().lookup("#commandTextField");
+ textField.setText("");
+ commandBox.handleCommandEntered();
+ assertFalse(commandExecuted);
+ }
+
+ @Test
+ public void handleCommandEntered_validCommand_commandExecutedSuccessfully() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ TextField textField = (TextField) commandBox.getRoot().lookup("#commandTextField");
+ textField.setText(VALID_COMMAND);
+ commandBox.handleCommandEntered();
+ assertTrue(commandExecuted);
+ }
+
+ @Test
+ public void handleCommandEntered_invalidCommand_setsStyleToIndicateCommandFailure() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ commandBox.getTextProperty().setValue("invalid command");
+ commandBox.handleCommandEntered();
+ TextField commandTextField = (TextField) commandBox.getRoot().lookup("#commandTextField");
+ ObservableList styleClass = commandTextField.getStyleClass();
+ assertTrue(styleClass.contains(ERROR_STYLE_CLASS));
+ }
+
+ @Test
+ public void setFocus_commandBox_getsFocus() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ TextField textField = (TextField) commandBox.getRoot().lookup("#commandTextField");
+ HBox hbox = new HBox();
+ hbox.getChildren().add(commandBox.getRoot());
+ Scene scene = new Scene(hbox);
+ commandBox.setFocus();
+ TextField focusedTextField = (TextField) scene.getFocusOwner();
+ assertEquals(focusedTextField, textField);
+ }
+
+ @Test
+ public void updateCommandBoxText_validCategoryName_updatesText() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ TextField textField = (TextField) commandBox.getRoot().lookup("#commandTextField");
+ textField.setText(INCOMPLETE_COMMAND);
+ commandBox.updateCommandBoxText("Groceries");
+ String expected = "add n/Milk c/Groceries ";
+ assertEquals(expected, textField.getText());
+ }
+
+}
+
\ No newline at end of file
diff --git a/src/test/java/fasttrack/ui/ExpenseCardTest.java b/src/test/java/fasttrack/ui/ExpenseCardTest.java
new file mode 100644
index 00000000000..8de5f8e7d08
--- /dev/null
+++ b/src/test/java/fasttrack/ui/ExpenseCardTest.java
@@ -0,0 +1,111 @@
+package fasttrack.ui;
+
+import static fasttrack.testutil.TypicalExpenses.APPLE;
+import static fasttrack.ui.JavaFxTestHelper.initJavaFxHelper;
+import static fasttrack.ui.JavaFxTestHelper.setUpHeadlessMode;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+
+import fasttrack.model.expense.Expense;
+import javafx.application.Platform;
+import javafx.scene.control.Label;
+
+class ExpenseCardTest {
+
+ private Expense expense;
+ private int displayedIndex;
+
+ @BeforeEach
+ public void setUp() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ expense = APPLE;
+ displayedIndex = 1;
+ }
+
+ @BeforeAll
+ static void initJfx() throws InterruptedException {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ setUpHeadlessMode();
+ initJavaFxHelper();
+ }
+
+ @Test
+ public void testExpenseCard_validData_success() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ ExpenseCard expenseCard = new ExpenseCard(expense, displayedIndex);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ // Test that the category name label is set correctly
+ Label expenseNameLabel = (Label) expenseCard.getRoot().lookup("#expenseName");
+ assertEquals("Apple", expenseNameLabel.getText());
+
+ // Test that the index label is set correctly
+ Label indexLabel = (Label) expenseCard.getRoot().lookup("#id");
+ assertEquals("1. ", indexLabel.getText());
+
+ // Test that the price label is set correctly
+ Label priceLabel = (Label) expenseCard.getRoot().lookup("#price");
+ assertEquals("$1.50", priceLabel.getText());
+
+ // Test that the category label is set correctly
+ Label categoryLabel = (Label) expenseCard.getRoot().lookup("#category");
+ assertEquals("Food", categoryLabel.getText());
+
+ // Test that the date label is set correctly
+ Label frequencyLabel = (Label) expenseCard.getRoot().lookup("#date");
+ assertEquals("01/03/23", frequencyLabel.getText());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testEquals_validExpenseCard_success() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ ExpenseCard expenseCard1 = new ExpenseCard(expense, displayedIndex);
+ ExpenseCard expenseCard2 = new ExpenseCard(expense, displayedIndex + 1);
+ ExpenseCard expenseCard3 = new ExpenseCard(expense, displayedIndex + 1);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ assertEquals(expenseCard1, expenseCard1);
+ assertNotEquals(expenseCard1, expenseCard2);
+ assertEquals(expenseCard2, expenseCard3);
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+
+}
diff --git a/src/test/java/fasttrack/ui/ExpenseListPanelTest.java b/src/test/java/fasttrack/ui/ExpenseListPanelTest.java
new file mode 100644
index 00000000000..68ce06e28f0
--- /dev/null
+++ b/src/test/java/fasttrack/ui/ExpenseListPanelTest.java
@@ -0,0 +1,96 @@
+package fasttrack.ui;
+
+import static fasttrack.testutil.TypicalExpenses.APPLE;
+import static fasttrack.testutil.TypicalExpenses.BANANA;
+import static fasttrack.testutil.TypicalExpenses.CHERRY;
+import static fasttrack.ui.JavaFxTestHelper.initJavaFxHelper;
+import static fasttrack.ui.JavaFxTestHelper.setUpHeadlessMode;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+
+import fasttrack.model.expense.Expense;
+import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.scene.control.ListView;
+
+
+class ExpenseListPanelTest {
+
+ private ExpenseListPanel expensePanel;
+ private ObservableList expenses;
+
+ @BeforeEach
+ public void setUp() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ expenses = FXCollections.observableArrayList(APPLE, BANANA, CHERRY);
+ }
+
+ @BeforeAll
+ static void initJfx() throws InterruptedException {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ setUpHeadlessMode();
+ initJavaFxHelper();
+ }
+
+
+ @Test
+ public void expenseListView_validExpenses_countEqual() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ expensePanel = new ExpenseListPanel(expenses);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ // Test that the number of recurring expenses is correct
+ ListView> expenseListView = (ListView>) expensePanel.getRoot().lookup("#expenseListView");
+ assertEquals(expenses.size(), expenseListView.getItems().size());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void expenseListView_validExpenses_countZero() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ expenses = FXCollections.observableArrayList();
+ expensePanel = new ExpenseListPanel(expenses);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ ListView> expenseListView = (ListView>) expensePanel.getRoot().lookup("#expenseListView");
+ assertEquals(0, expenseListView.getItems().size());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/fasttrack/ui/JavaFxTestHelper.java b/src/test/java/fasttrack/ui/JavaFxTestHelper.java
new file mode 100644
index 00000000000..86c1f1d6519
--- /dev/null
+++ b/src/test/java/fasttrack/ui/JavaFxTestHelper.java
@@ -0,0 +1,35 @@
+package fasttrack.ui;
+
+import java.util.concurrent.CountDownLatch;
+
+import javafx.application.Platform;
+
+/**
+ * Helper class for JavaFX UI test
+ */
+public class JavaFxTestHelper {
+ private static boolean isInitialized = false;
+
+ /**
+ * Initialises JavaFX platform for UI test
+ */
+ public static void initJavaFxHelper() throws InterruptedException {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ if (!isInitialized) {
+ CountDownLatch latch = new CountDownLatch(1);
+ Platform.startup(latch::countDown);
+ latch.await();
+ isInitialized = true;
+ }
+ }
+
+ public static void setUpHeadlessMode() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ System.setProperty("java.awt.headless", "true");
+ }
+
+}
diff --git a/src/test/java/fasttrack/ui/RecurringExpenseCardTest.java b/src/test/java/fasttrack/ui/RecurringExpenseCardTest.java
new file mode 100644
index 00000000000..ed222d500c1
--- /dev/null
+++ b/src/test/java/fasttrack/ui/RecurringExpenseCardTest.java
@@ -0,0 +1,114 @@
+package fasttrack.ui;
+
+import static fasttrack.testutil.TypicalRecurringExpenses.GYM_MEMBERSHIP;
+import static fasttrack.ui.JavaFxTestHelper.initJavaFxHelper;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+
+import fasttrack.model.expense.RecurringExpenseManager;
+import javafx.application.Platform;
+import javafx.scene.control.Label;
+
+class RecurringExpenseCardTest {
+
+ private RecurringExpenseManager recurringExpenseManager;
+ private int displayedIndex;
+
+ @BeforeEach
+ public void setUp() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ recurringExpenseManager = GYM_MEMBERSHIP;
+ displayedIndex = 1;
+ }
+
+ @BeforeAll
+ static void initJfx() throws InterruptedException {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ initJavaFxHelper();
+ }
+
+
+ @Test
+ public void testRecurringExpenseCard_validData_success() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ RecurringExpenseCard recurringExpenseCard = new RecurringExpenseCard(
+ recurringExpenseManager, displayedIndex);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ // Test that the category name label is set correctly
+ Label expenseNameLabel = (Label) recurringExpenseCard.getRoot().lookup("#expenseName");
+ assertEquals("Gym Membership", expenseNameLabel.getText());
+
+ // Test that the index label is set correctly
+ Label indexLabel = (Label) recurringExpenseCard.getRoot().lookup("#id");
+ assertEquals("1. ", indexLabel.getText());
+
+ // Test that the price label is set correctly
+ Label priceLabel = (Label) recurringExpenseCard.getRoot().lookup("#price");
+ assertEquals("$50.00", priceLabel.getText());
+
+ // Test that the category label is set correctly
+ Label categoryLabel = (Label) recurringExpenseCard.getRoot().lookup("#category");
+ assertEquals("Fitness", categoryLabel.getText());
+
+ // Test that the frequency label is set correctly
+ Label frequencyLabel = (Label) recurringExpenseCard.getRoot().lookup("#frequency");
+ assertEquals("Monthly", frequencyLabel.getText());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void testEquals_validRecurringExpenseCard_success() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ RecurringExpenseCard recurringExpenseCard1 = new RecurringExpenseCard(
+ recurringExpenseManager, displayedIndex);
+ RecurringExpenseCard recurringExpenseCard2 = new RecurringExpenseCard(
+ recurringExpenseManager, displayedIndex + 1);
+ RecurringExpenseCard recurringExpenseCard3 = new RecurringExpenseCard(
+ recurringExpenseManager, displayedIndex + 1);
+ CompletableFuture future = new CompletableFuture<>();
+
+ Platform.runLater(() -> {
+ try {
+ assertEquals(recurringExpenseCard1, recurringExpenseCard1);
+ assertNotEquals(recurringExpenseCard1, recurringExpenseCard2);
+ assertEquals(recurringExpenseCard2, recurringExpenseCard3);
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+}
diff --git a/src/test/java/fasttrack/ui/RecurringExpensePanelTest.java b/src/test/java/fasttrack/ui/RecurringExpensePanelTest.java
new file mode 100644
index 00000000000..b6b487dccee
--- /dev/null
+++ b/src/test/java/fasttrack/ui/RecurringExpensePanelTest.java
@@ -0,0 +1,95 @@
+package fasttrack.ui;
+
+import static fasttrack.testutil.TypicalRecurringExpenses.GYM_MEMBERSHIP;
+import static fasttrack.testutil.TypicalRecurringExpenses.NETFLIX_SUBSCRIPTION;
+import static fasttrack.testutil.TypicalRecurringExpenses.RENT;
+import static fasttrack.ui.JavaFxTestHelper.initJavaFxHelper;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.opentest4j.AssertionFailedError;
+
+import fasttrack.model.expense.RecurringExpenseManager;
+import javafx.application.Platform;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.scene.control.ListView;
+
+
+class RecurringExpensePanelTest {
+
+ private RecurringExpensePanel recurringExpensePanel;
+ private ObservableList recurringExpenses;
+
+ @BeforeEach
+ public void setUp() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ recurringExpenses = FXCollections.observableArrayList(GYM_MEMBERSHIP, NETFLIX_SUBSCRIPTION, RENT);
+ }
+
+ @BeforeAll
+ static void initJfx() throws InterruptedException {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ initJavaFxHelper();
+ }
+
+ @Test
+ public void recurringExpenseListView_validExpenses_countEqual() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ recurringExpensePanel = new RecurringExpensePanel(recurringExpenses);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ // Test that the number of recurring expenses is correct
+ ListView> recurringExpenseListView = (ListView>) recurringExpensePanel
+ .getRoot().lookup("#recurringExpenseListView");
+ assertEquals(recurringExpenses.size(), recurringExpenseListView.getItems().size());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+
+ @Test
+ public void recurringExpenseListView_validExpenses_countZero() {
+ if (System.getProperty("os.name").toLowerCase().contains("linux")) {
+ return;
+ }
+ recurringExpenses = FXCollections.observableArrayList();
+ RecurringExpensePanel recurringExpensePanel = new RecurringExpensePanel(recurringExpenses);
+ CompletableFuture future = new CompletableFuture<>();
+ Platform.runLater(() -> {
+ try {
+ ListView> recurringExpenseListView = (ListView>) recurringExpensePanel
+ .getRoot().lookup("#recurringExpenseListView");
+ assertEquals(0, recurringExpenseListView.getItems().size());
+ future.complete(null);
+ } catch (AssertionFailedError e) {
+ future.completeExceptionally(e);
+ }
+ });
+ try {
+ future.get();
+ } catch (InterruptedException | ExecutionException e) {
+ fail("Assertion error thrown in Platform.runLater thread: " + e.getMessage());
+ }
+ }
+}
From 7fa7dcf502d0810efada89238dceffd23d30450f Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 21:48:12 +0800
Subject: [PATCH 11/13] Fix checkstyle
---
src/test/java/fasttrack/ui/CommandBoxTest.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/test/java/fasttrack/ui/CommandBoxTest.java b/src/test/java/fasttrack/ui/CommandBoxTest.java
index 2e6450fbe5d..d0c46050828 100644
--- a/src/test/java/fasttrack/ui/CommandBoxTest.java
+++ b/src/test/java/fasttrack/ui/CommandBoxTest.java
@@ -117,4 +117,3 @@ public void updateCommandBoxText_validCategoryName_updatesText() {
}
}
-
\ No newline at end of file
From 43738f12edf05db021b1c57661b3980c16d7cccc Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 22:25:12 +0800
Subject: [PATCH 12/13] Write PPP
---
docs/team/shirsho-12.md | 58 ++++++++++++++++++++++++++++++++---------
1 file changed, 45 insertions(+), 13 deletions(-)
diff --git a/docs/team/shirsho-12.md b/docs/team/shirsho-12.md
index 4366ed3a173..d3a4ac7862e 100644
--- a/docs/team/shirsho-12.md
+++ b/docs/team/shirsho-12.md
@@ -5,28 +5,60 @@ title: Shirshajit's Project Portfolio Page
### Project: FastTrack
-FastTrack is a desktop application used for _ _. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about __20__ kLoC.
+FastTrack is an expense tracking app that helps computing students keep track of their expenses by providing a simple and convenient command-line interface. It is optimized for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, FastTrack can get your expense management tasks done faster than traditional GUI apps.
Given below are my contributions to the project.
### Summary of Contributions
-* **New Feature**: MAGIC to be added soon.
- * What it does:
- * Justification:
- * Highlights:
- * Credits:
+* **New Feature**: `Recurring Expenses`: Created the recurring expense functionality
+ * What it does: Creates recurring expenses for the user, i.e., expenses that occur at regular intervals. Theese expenses are automatically generated based on the user's input and current date. The user can also view all recurring expenses, delete recurring expenses, and mark recurring expenses as done.
+ * Justification: This feature improves the product significantly because a large number of users would prefer to track their recurring expenses. This feature also allows the user to save time by not having to manually create recurring expenses.
+ * Highlights: This enhancement affects existing commands and commands to be added in the future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands and the logic to handle the recurring expenses.
-* **New Feature**:
+* **New Feature**: `Category` and `Expense` models: Created the main Category and Expense models.
+ * What it does: The Category model is used to store the different categories of expenses that the user can add. The Expense model is used to store the different expenses that the user can add.
+ * Justification: This feature improves the product significantly because it allows the user to add expenses and categorize them. This feature also allows the user to save time by not having to manually create recurring expenses.
+ * Highlights: This enhancement affects existing commands and commands to be added in the future. It required an in-depth analysis of design alternatives. The implementation was challenging as it required changes to existing commands and the logic to handle the recurring expenses.
-**Enhancements implemented**: to be added soon
+* **Code Contributions**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=shirsho-12&breakdown=true&sort=groupTitle&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
-**Contributions to the UG:** to be added soon
+* **Project Management**:
+ * Managed releases `v1.1` - `v1.4` (3 releases) on GitHub
+ * Managed issue creation and assignment, milestone creation, issue labelling, and issue closing
+ * Enforced coding standards and code quality
+ * Managed the project's [issue tracker](https://github.com/AY2223S2-CS2103T-W09-2/tp/issues)
+ * Managed the project's [pull requests](https://github.com/AY2223S2-CS2103T-W09-2/tp/pulls)
+ * Managed the project [repository](https://github.com/AY2223S2-CS2103T-W09-2/tp) with the help of Isaac,[@gitsac](https://github.com/gitsac/), and Nicholas, [@niceleejy](https://github.com/niceleejy/).
+
+**Enhancements implemented**:
+ * Implemented the recurring expense generation functionality
+ * Developed Expense Type enums for the different frequency types of recurring expenses
+ * Implemented functionality to strip out additional whitespace in user input
-**Contributions to the DG:** to be added soon
+**Contributions to the UG:**
+ * Added documentation for the Category and Expense models
+ * Added documentation for the recurring expense functionality
-**Contributions to team-based tasks:** to be added soon
+**Contributions to the DG:**
+ * Created Activity Diagrams for all the commands
+ * Created UML Diagrams for the Category and Expense models, Commands, and the Parser
+ * Designed the architecture diagrams for the project
-**Review/Mentoring Contributions:** to be added soon
+**Contributions to team-based tasks:**
+ * Managed the setting up of the project repository and test suite
+ * Created test cases for multiple commands, models, and storage
+ * Reviewed and merged multiple pull requests
+ * Redesigned the architecture of the Command classes to make it more extensible
+ * Fixed a number of bugs in the parser and storage classes
+ * Fixed test cases that were failing due to changes in the codebase
+ * Refactored the codebase to improve code quality
-**Contributions beyond team project:** to be added soon
+**Review/Mentoring Contributions:**
+ * Reviewed and provided feedback on multiple pull requests
+ * Reviewed and provided feedback on multiple issues
+ * Reviewed and provided feedback on multiple code quality issues
+
+**Contributions beyond team project:**
+ * Reported bugs and suggestions for improvement for other team projects
+ * Participated in the discussion forum and helped other students with their queries
From 8051b2a6b4b7b0930e8c20f886549616c91bbb2b Mon Sep 17 00:00:00 2001
From: Shirshajit Sen Gupta
Date: Mon, 10 Apr 2023 22:30:15 +0800
Subject: [PATCH 13/13] Fix whitespace
---
docs/DeveloperGuide.md | 578 +++++++++++++++++++++-------------------
docs/UserGuide.md | 341 ++++++++++++------------
docs/team/shirsho-12.md | 110 ++++----
3 files changed, 536 insertions(+), 493 deletions(-)
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index ac13d95c869..6d16070805d 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -2,14 +2,18 @@
layout: page
title: Developer Guide
---
-* Table of Contents
+
+- Table of Contents
{:toc}
+
---
## **FastTrack**
+
FastTrack is an easy-to-use financial management desktop application designed for NUS SoC undergraduate students who are living on a tight budget. With a combination of a Command Line Interface (CLI) and Graphical User Interface (GUI), our app provides a user-friendly and efficient way to track your expenses and manage your finances.
---------------------------------------------------------------------------------------------------------------------
+---
+
## **Purpose of this guide**
The purpose of this guide is to give you a comprehensive insight for developing and maintaining FastTrack.
@@ -18,73 +22,73 @@ If you are a developer, this guide will give you an overview of the high-level [
If you like to know more about the motivation behind FastTrack, checkout the [requirements](#appendix-requirements) section where we cover the [product scope](#product-scope) as well as the [user stories](#user-stories) and [use cases](#use-cases).
---------------------------------------------------------------------------------------------------------------------
+---
## How to use this guide
Here are some notations used in this guide.
### Format
-* `Command` is used to label commands and components.
-* {Placeholder} are used to label placeholders.
-* [Optional], square brackets are used to notate optional fields.
-* :information_source: **Note** is used to provide additional information that you should know.
+
+- `Command` is used to label commands and components.
+- {Placeholder} are used to label placeholders.
+- [Optional], square brackets are used to notate optional fields.
+- :information_source: **Note** is used to provide additional information that you should know.
---
## **Acknowledgements**
-* This project is based on the [AddressBook-Level3](https://github.com/se-edu/addressbook-level3) project created by the [SE-EDU initiative](https://se-education.org/)
-* Libraries used:
- * [JavaFX](https://openjfx.io/)
- * [Jackson](https://github.com/FasterXML/jackson)
- * [JUnit5](https://github.com/junit-team/junit5)
+- This project is based on the [AddressBook-Level3](https://github.com/se-edu/addressbook-level3) project created by the [SE-EDU initiative](https://se-education.org/)
+- Libraries used:
+ - [JavaFX](https://openjfx.io/)
+ - [Jackson](https://github.com/FasterXML/jackson)
+ - [JUnit5](https://github.com/junit-team/junit5)
---------------------------------------------------------------------------------------------------------------------
+---
## **Setting up, getting started**
Refer to the guide [_Setting up and getting started_](SettingUp.md).
---------------------------------------------------------------------------------------------------------------------
+---
## **Design**
-This section gives you an overview of the different components of FastTrack and how they interact with each other.
+This section gives you an overview of the different components of FastTrack and how they interact with each other.
:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
+
### Architecture
-The ***Architecture Diagram*** given below explains the high-level design of the FastTrack and how each component is connected.
+The **_Architecture Diagram_** given below explains the high-level design of the FastTrack and how each component is connected.
-
**Main components of the architecture**
**`Main`** has two classes called [`Main`](https://github.com/AY2223S2-CS2103T-W09-2/tp/blob/master/src/main/java/fasttrack/Main.java) and [`MainApp`](https://github.com/AY2223S2-CS2103T-W09-2/tp/blob/master/src/main/java/fasttrack/MainApp.java). It is responsible for,
-* At app launch: Initializes the components in the correct sequence, and connects them up with each other.
-* At shut down: Shuts down the components and invokes cleanup methods where necessary.
+- At app launch: Initializes the components in the correct sequence, and connects them up with each other.
+- At shut down: Shuts down the components and invokes cleanup methods where necessary.
[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components.
FastTrack also consists of five other components.
-* [**`UI`**](#ui-component): The UI of the App.
-* [**`Logic`**](#logic-component): The command executor.
-* [**`Model`**](#model-component): Holds the data of the App in memory.
-* [**`AnalyticModel`**](#analyticmodel-component): Holds the data and outputs statistics based on the data in memory.
-* [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk.
-
+- [**`UI`**](#ui-component): The UI of the App.
+- [**`Logic`**](#logic-component): The command executor.
+- [**`Model`**](#model-component): Holds the data of the App in memory.
+- [**`AnalyticModel`**](#analyticmodel-component): Holds the data and outputs statistics based on the data in memory.
+- [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk.
**How the architecture components interact with each other**
-The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`.
+The _Sequence Diagram_ below shows how the components interact with each other for the scenario where the user issues the command `delete 1`.
#replace
@@ -92,8 +96,8 @@ The *Sequence Diagram* below shows how the components interact with each other f
Each of the 5 main components (also shown in the diagram above),
-* defines its *API* in an `interface` with the same name as the Component.
-* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point.)
+- defines its _API_ in an `interface` with the same name as the Component.
+- implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point.)
For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
@@ -112,19 +116,20 @@ The **API** of this component is specified in [`Ui.java`](https://github.com/AY2
![Structure of the UI Component](images/UiClassDiagram.png)
The UI consists of a `MainWindow` that is made up of the following parts.
-* `CategoryListPanel`
- * `CategoryCard`
-* `ExpenseListPanel`
- * `ExpenseCard`
-* `RecurringExpensePanel`
- * `RecurringExpenseCard`
-* `StatisticsPanel`
-* `CommandBox`
- * `SuggestionListPanel`
- * `SuggestionCard`
-* `ResultDisplay`
- * `ResultsHeader`
- * `ResultsDetails`
+
+- `CategoryListPanel`
+ - `CategoryCard`
+- `ExpenseListPanel`
+ - `ExpenseCard`
+- `RecurringExpensePanel`
+ - `RecurringExpenseCard`
+- `StatisticsPanel`
+- `CommandBox`
+ - `SuggestionListPanel`
+ - `SuggestionCard`
+- `ResultDisplay`
+ - `ResultsHeader`
+ - `ResultsDetails`
All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
@@ -132,10 +137,10 @@ The `UI` component uses the JavaFx UI framework. The layout of these UI parts ar
The `UI` component,
-* executes user commands using the `Logic` component.
-* listens for changes to `Model` data so that the UI can be updated with the modified data.
-* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
-* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
+- executes user commands using the `Logic` component.
+- listens for changes to `Model` data so that the UI can be updated with the modified data.
+- keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
+- depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
### Logic component
@@ -148,6 +153,7 @@ Here's a (partial) class diagram of the `Logic` component, to help guide you alo
#### **How the `Logic` component works:**
+
1. When `Logic` is called upon to execute a command, it uses the `ExpenseTrackerParser` class to parse the user command.
1. This results in a `Command` object (more precisely, an object of one of its subclasses' subclass e.g., `AddCategoryCommand`, which implements `AddCommand`) which is executed by the `LogicManager`.
1. The command can communicate with the `Model` when it is executed (e.g. to add a category).
@@ -169,28 +175,30 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha
#### **How the parsing works:**
-* When called upon to parse a user command, the `ExpenseTrackerParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCategoryParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCategoryCommand`) which the `ExpenseTrackerParser` returns back as a `Command` object.
-* All `XYZCommandParser` classes (e.g., `AddCategoryParser`, `DeleteCategoryParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing.
+
+- When called upon to parse a user command, the `ExpenseTrackerParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCategoryParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCategoryCommand`) which the `ExpenseTrackerParser` returns back as a `Command` object.
+- All `XYZCommandParser` classes (e.g., `AddCategoryParser`, `DeleteCategoryParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing.
### Model component
+
**API** : [`Model.java`](https://github.com/AY2223S2-CS2103T-W09-2/tp/blob/master/src/main/java/fasttrack/model/Model.java)
#ReplaceUMLHere
-
The `Model` component,
-* contains the expense tracker data i.e., all `Category` objects (which are contained in a `UniqueCategoryList` object), which is pulled from the `ReadOnlyExpenseTracker` instance.
-* contains the currently 'selected' `Category` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be
-'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
-* contains the expense tracker data i.e., all `Expense` objects (which are contained in a `ExpenseList` object), which is pulled from the `ReadOnlyExpenseTracker` instance.
-* contains the currently 'selected' `Expense` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be
-'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
-* does not depend on any of the other four components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
+- contains the expense tracker data i.e., all `Category` objects (which are contained in a `UniqueCategoryList` object), which is pulled from the `ReadOnlyExpenseTracker` instance.
+- contains the currently 'selected' `Category` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be
+ 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
+- contains the expense tracker data i.e., all `Expense` objects (which are contained in a `ExpenseList` object), which is pulled from the `ReadOnlyExpenseTracker` instance.
+- contains the currently 'selected' `Expense` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be
+ 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
+- does not depend on any of the other four components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
#Check if this is needed anymore
+
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Category` list in the `ExpenseTracker`, which `Expense` references. This allows `ExpenseTracker` to only require one `Category` object per unique expense, instead of each `Expense` needing their own `Category` objects.
#ReplaceUMLHere
@@ -207,11 +215,10 @@ The `Model` component,
#This requires brand-new UML diagram of how analyticmodel component works.
The `AnalyticModel` component,
-* contains the expense tracker data (i.e. all `Expense` and `Category` objects, which are respectively contained in `ExpenseList` and `UniqueCategoryList` objects), which are pulled from the `ReadOnlyExpenseTracker` instance.
-* calculates statistics based on the expense tracker data available.
-* listens to any changes made to expense tracker data (by attaching listeners to the `ExpenseList`), thus dynamically updating statistics.
-
+- contains the expense tracker data (i.e. all `Expense` and `Category` objects, which are respectively contained in `ExpenseList` and `UniqueCategoryList` objects), which are pulled from the `ReadOnlyExpenseTracker` instance.
+- calculates statistics based on the expense tracker data available.
+- listens to any changes made to expense tracker data (by attaching listeners to the `ExpenseList`), thus dynamically updating statistics.
### Storage component
@@ -222,15 +229,16 @@ The `AnalyticModel` component,
The `Storage` component,
-* can save both expense tracker data and user preference data in json format, and read them back into corresponding objects.
-* inherits from `ExpenseTrackerStorage`
-* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
+
+- can save both expense tracker data and user preference data in json format, and read them back into corresponding objects.
+- inherits from `ExpenseTrackerStorage`
+- depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
### Common classes
Classes used by multiple components are in the `seedu.addressbook.commons` package.
---------------------------------------------------------------------------------------------------------------------
+---
## **Implementation**
@@ -251,21 +259,18 @@ The `add` command enables users to add an `Expense` to the `ExpenseTracker`.
To aid you in understanding how exactly the `add` command works, here is a list of steps illustrating what occurs when [`LogicManager#execute()` is invoked](#logic-component):
We will be using the user input `add n/Milk p/4.50 c/Groceries` as an example.
-1. The user inputs `add n/Milk p/4.50 c/Groceries` in the command box.
+1. The user inputs `add n/Milk p/4.50 c/Groceries` in the command box.
2. The input is then [parsed](#logic-component) and a `AddExpenseCommand` object is created using the given fields.
-
3. `AddExpenseCommand#execute()` is called, which takes in the currently-running instance of `Model` and adds the `Expense` used to instantiate the `AddExpenseCommand` through the `Model#addExpense()` method, which adds the new `Expense` object to the list of current `Expense` objects. Note that if the newly instantiated `Expense` has a `Category` that does not match any existing `Category` objects, the `Expense` object's `Category` will be added to the `ExpenseTracker` as a new `Category`.
-
4. A `CommandResult` instance is then returned, with feedback that the `Expense` was successfully logged.
:information_source: **Note**:
-- For step 2, if the user does not have any arguments, the `AddExpenseCommand` object will NOT be created!
-
+- For step 2, if the user does not have any arguments, the `AddExpenseCommand` object will NOT be created!
A sequence diagram is provided as follows, which matches the list of steps mentioned above:
@@ -286,21 +291,18 @@ The `delete` command enables users to delete an `Expense` from the `ExpenseTrack
To aid you in understanding how exactly the `delete` command works, here is a list of steps illustrating what occurs when [`LogicManager#execute()` is invoked](#logic-component):
We will be using the user input `delete 1` as an example.
-1. The user inputs `delete 1` in the command box.
+1. The user inputs `delete 1` in the command box.
2. The input is then [parsed](#logic-component) and a `DeleteExpenseCommand` object is created using the given fields.
-
3. `DeleteExpenseCommand#execute()` is called, which takes in the currently-running instance of `Model`, retrieves the `Expense` object at the specified `INDEX`, and deletes it from the underlying list of `Expense` objects of the `Model` instance using `Model#deleteExpense()`.
-
4. A `CommandResult` instance is then returned, with feedback that the `Expense` was successfully deleted.
-
:information_source: **Note**:
-- At step 2, if input is detected as invalid, an error will be shown on the screen and the sequence of action is terminated.
+- At step 2, if input is detected as invalid, an error will be shown on the screen and the sequence of action is terminated.
A sequence diagram is provided as follows, which matches the list of steps mentioned above:
@@ -322,22 +324,19 @@ The `edexp` command enables users to edit an `Expense` in the `ExpenseTracker`.
To aid you in understanding how exactly the `edexp` command works, here is a list of steps illustrating what occurs when [`LogicManager#execute()` is invoked](#logic-component):
We will be using the user input `edexp 1 n/Chicken` as an example, whereby the original `Expense` object has a `EXPENSE_NAME` of `Milk`.
-1. The user inputs `edexp 1 n/Chicken` in the command box.
+1. The user inputs `edexp 1 n/Chicken` in the command box.
2. The input is then [parsed](#logic-component) and a `EditExpenseCommand` object is created using the given fields.
-
3. The `LogicManager#execute()` function causes `EditExpenseCommand#execute()` to be called, which takes in the currently-running instance of `Model`, retrieves the `Expense` object at the specified `INDEX` from the `FilteredList
` of the `Model` instance, and instantiates a new `Expense` object that has the same fields as the retrieved `Expense` object except for the `EXPENSE_NAME`. Note that if the newly instantiated `Expense` has a `Category` that does not match any existing `Category` objects, the `Expense` object's `Category` will NOT be added to the `ExpenseTracker` as a new `Category`, and an error indicating that no such `CATEGORY_NAME` exists will pop up.
-
4. The newly-instantiated `Expense` object with the updated `EXPENSE_NAME`, namely `Chicken`, will then replace the retrieved `Expense` object at the specified `INDEX` in the `ExpenseList` using `Model#setExpense()`.
-
5. A `CommandResult` instance is then returned, with feedback that the `Expense` was successfully edited.
-
:information_source: **Note**:
+
- At step 2, if the input is detected as invalid (either index is invalid or no arguments provided other than index), a matching error will be shown on the screen and the sequence of action is terminated.
- At step 3, if the user provides a category to edit to, and it is found that there is no such category in FastTrack, an error will be shown and the sequence of action is terminated.
@@ -346,15 +345,17 @@ A sequence diagram is provided as follows, which matches the list of steps menti
SEQ#3
### **Design Considerations**
+
**Aspect: How the expense is edited**:
-* **Alternative 1 (Current choice):** Retrieve the specified `Expense`, instantiate a new `Expense` with specified edited fields, and replace the retrieved `Expense` in the `ExpenseList`.
- * Pros: As the UI implementation requires listening in on the `ObservableList`, replacing the previous `Expense` object with a new `Expense` object makes the live-refreshing of our UI much more intuitive.
- * Cons: There is an additional `Expense` object being instantiated to replace the previous `Expense` object.
+- **Alternative 1 (Current choice):** Retrieve the specified `Expense`, instantiate a new `Expense` with specified edited fields, and replace the retrieved `Expense` in the `ExpenseList`.
+
+ - Pros: As the UI implementation requires listening in on the `ObservableList`, replacing the previous `Expense` object with a new `Expense` object makes the live-refreshing of our UI much more intuitive.
+ - Cons: There is an additional `Expense` object being instantiated to replace the previous `Expense` object.
-* **Alternative 2:** Retrieve the specified `Expense` and use setter methods to set the specified fields to be edited.
- * Pros: No additional `Expense` object needs to be instantiated, and it is easier to simply set the fields.
- * Cons: The listeners for the `ObservableList` only detect when there is an actual operation being done on the list, therefore setting fields will not cause the listeners to be updated, making our UI implementation much more complicated.
+- **Alternative 2:** Retrieve the specified `Expense` and use setter methods to set the specified fields to be edited.
+ - Pros: No additional `Expense` object needs to be instantiated, and it is easier to simply set the fields.
+ - Cons: The listeners for the `ObservableList` only detect when there is an actual operation being done on the list, therefore setting fields will not cause the listeners to be updated, making our UI implementation much more complicated.
### \[Implemented\] List feature
@@ -371,19 +372,17 @@ The `list` command enables users to view all expenses within the expense tracker
To aid you in understanding how exactly the `list` command works, here is a list of steps illustrating what occurs when [`LogicManager#execute()` is invoked](#logic-component):
We will be using the user input `list c/Groceries t/week` as an example.
-1. The user inputs `list c/Groceries t/week` in the command box.
+1. The user inputs `list c/Groceries t/week` in the command box.
2. The input is then [parsed](#logic-component) and a `ListExpensesCommand` object is created using the given fields.
-
3. The `LogicManager#execute()` function causes `ListExpensesCommand#execute()` to be called, which takes in the currently-running instance of `Model`, and applies the `ExpenseInCategoryPredicate` and `ExpenseInTimespanPredicate` sequentially onto the `Model` object's `FilteredList` by `Model#updateFilteredExpensesList()`.
-
4. A `CommandResult` instance is then returned, with feedback of how many `Expenses` are currently listed under the current filters.
-
:information_source: **Note**:
+
- At step 2, if an invalid input is detected after `list` (e.g. `list xxxxxx`), an error will be shown and the sequence of action is terminated.
A sequence diagram is provided as follows, which matches the list of steps mentioned above:
@@ -391,16 +390,17 @@ A sequence diagram is provided as follows, which matches the list of steps menti
SEQ#4
### **Design Considerations**
+
**Aspect: Whether to make `ListExpensesCommand` have multiple constructors or to make it take in `ExpenseInCategoryPredicate` and `ExpenseInTimespanPredicate` as `Optional` objects.**
-* **Alternative 1 (Current choice):** `ListExpensesCommand` takes in `Optional` and `Optional` and `Optional` of the `Model` instance, and sets the `EXPENSE_NAME` field of the `RecurringExpenseManager` object found at the given `INDEX` to be `Coconut Milk`.
-
4. A `CommandResult` instance is then returned, with feedback that the `RecurringExpenseManager` object was successfully edited.
-
:information_source: **Note**:
+
- At step 2, if the index provided is detected as invalid or if no arguments other than index are provided, an error will be shown and the sequence of action is terminated.
- At step 3, if the user provides a category to edit to, and it is found that there is no such category in FastTrack, an error will be shown and the sequence of action is terminated.
@@ -536,15 +530,17 @@ A sequence diagram is provided as follows, which matches the list of steps menti
SEQ#7
### **Design Considerations**
+
**Aspect: Whether the `RecurringExpenseManager` object should be replaced instead of having its fields set.**
-* **Alternative 1 (Current choice):** Retrieve the specified `RecurringExpenseManager` and use setter methods to set the specified fields to be edited.
- * Pros: Easy to simply retrieve the `RecurringExpenseManager` object and use setter for the fields.
- * Cons: If any future developments require close listening to the `ObservableList` to be displayed in the UI, some refactoring might need to be done.
+- **Alternative 1 (Current choice):** Retrieve the specified `RecurringExpenseManager` and use setter methods to set the specified fields to be edited.
+
+ - Pros: Easy to simply retrieve the `RecurringExpenseManager` object and use setter for the fields.
+ - Cons: If any future developments require close listening to the `ObservableList` to be displayed in the UI, some refactoring might need to be done.
-* **Alternative 2:** Retrieve the specified `RecurringExpenseManager`, instantiate a new `RecurringExpenseManager` with specified edited fields, and replace the retrieved `RecurringExpenseManager` in the underlying `RecurringExpenseList`.
- * Pros: Future developments which rely on the `ObservableList` and `Optional` and `Optional
:information_source: **Note:**
-* At step 3, if the new category has the same name as an existing category, a `CommandException` will be thrown and the sequence of action will be terminated.
+
+- At step 3, if the new category has the same name as an existing category, a `CommandException` will be thrown and the sequence of action will be terminated.
### \[Implemented\] Delete Category feature
#### **Command format:**
+
`delcat INDEX` where `INDEX` refers to the index of the category to be deleted when `lcat` is called.
#### **What is the feature about:**
+
This command allows user to delete a category of choice, expenses with the deleted category will have its category replaced with the `MiscellaneuosCategory`. The category will be removed from the user's database.
#### **Sequence of actions:**
@@ -626,16 +626,19 @@ This command allows user to delete a category of choice, expenses with the delet
6. A `CommandResult` object is returned.
:information_source: **Note:**
-* At step 2, if an invalid `INDEX` is given, a `CommandException` will be thrown and the sequence of action will be terminated.
-* At step 3, if the `INDEX` given is out of range, a `CommandException` will be thrown and the sequence of action will be terminated.
+
+- At step 2, if an invalid `INDEX` is given, a `CommandException` will be thrown and the sequence of action will be terminated.
+- At step 3, if the `INDEX` given is out of range, a `CommandException` will be thrown and the sequence of action will be terminated.
### \[Implemented\] Edit Category feature
#### **Command format:**
+
`edcat INDEX [c/NAME] [s/SUMMARY]` where `INDEX` refers to the index of the category to be edited when `lcat` is called, `NAME` is the new name of the category, `SUMMARY` is the new summary of the category. Note that either a `NAME` or `SUMMARY` must be present for the command to be executed.
#### **What is the feature about:**
-This command allows user to edit a category of choice. We allow users to edit either the category's name, summary or both in this command.
+
+This command allows user to edit a category of choice. We allow users to edit either the category's name, summary or both in this command.
#### **Sequence of actions:**
@@ -647,21 +650,24 @@ This command allows user to edit a category of choice. We allow users to edit ei
6. A `CommandResult` object is returned.
:information_source: **Note:**
-* At step 2, if an invalid `INDEX` is given or **both** `c/NAME` and `s/SUMMARY` is missing, a `CommandException` will be thrown and the sequence of actions will be terminated.
-* At step 4, if there is a category with the same name as the new name given, a `CommandException` is thrown and the sequence of actions will be terminated.
+
+- At step 2, if an invalid `INDEX` is given or **both** `c/NAME` and `s/SUMMARY` is missing, a `CommandException` will be thrown and the sequence of actions will be terminated.
+- At step 4, if there is a category with the same name as the new name given, a `CommandException` is thrown and the sequence of actions will be terminated.
#### Design considerations:
+
**Aspect: How the category object is edited**:
-* **Alternative 1 (Current choice):** Directly retrieve and edit the currently-specified `Category` object.
- * Pros: No need to re-point all `Expense` objects currently affiliated with the `Category` object that is being edited.
- * Cons: Mutates the state of the `Category` object, which might not be ideal if seeking immutability.
+- **Alternative 1 (Current choice):** Directly retrieve and edit the currently-specified `Category` object.
+
+ - Pros: No need to re-point all `Expense` objects currently affiliated with the `Category` object that is being edited.
+ - Cons: Mutates the state of the `Category` object, which might not be ideal if seeking immutability.
+
+- **Alternative 2 :** Retrieve the specified `Category` object's name and summary, and create a new `Category` object
+ that uses the same name and summary before replacing the required name or summary depending on user's arguments.
+ - Pros: Enforces immutability by replacing the previous `Category` object.
+ - Cons: There is now a need to re-direct all `Expense` objects affiliated with the previous `Category` object of interest.
-* **Alternative 2 :** Retrieve the specified `Category` object's name and summary, and create a new `Category` object
-that uses the same name and summary before replacing the required name or summary depending on user's arguments.
- * Pros: Enforces immutability by replacing the previous `Category` object.
- * Cons: There is now a need to re-direct all `Expense` objects affiliated with the previous `Category` object of interest.
-
### \[Implemented\] List Category feature
#### **Command Format**
@@ -677,44 +683,47 @@ The `lcat` command enables users to view all `Category` objects within the expen
To aid you in understanding how exactly the `lcat` command works, here is a list of steps illustrating what occurs when [`LogicManager#execute()` is invoked](#logic-component):
We will be using the user input `lcat` as an example.
-1. The user inputs `lcat` in the command box.
+1. The user inputs `lcat` in the command box.
2. The input is then [parsed](#logic-component) and a `ListCategoryCommand` object is created using the given fields.
-
3. The `LogicManager#execute()` function causes `ListCategoryCommand#execute()` to be called, which takes in the currently-running instance of `Model`, and calls `Model#updateFilteredExpenseList()` with `PREDICATE_SHOW_ALL_EXPENSES`.
-
4. A `CommandResult` instance is then returned, with feedback of how many `Category` objects are present in the expense tracker, as well as switching the screen to that of a list of the `Category` objects.
-
As the flow of events are almost identical to that of the [`list` command](#implemented-list-feature) with the sole difference being the different method names and the lack of filters, there are no sequence diagrams provided for this feature.
### **\[Implemented\] Recurring Expense feature:**
#### **What is the feature about:**
+
Since recurring expenses are prominent in today's society (e.g. Netflix, Transportation), therefore having a feature to allows FastTrack to regularly add recurring expenses automatically is a must.
To implement this feature, 3 classes were added:
+
1. [`RecurringExpenseManager`](#the-recurringexpensemanager-class)
2. [`RecurringExpenseList`](#the-recurringexpenselist-class)
3. [`RecurringExpenseType`](#the-recurringexpensetype-class)
##### **The `RecurringExpenseManager` class:**
+
The `RecurringExpenseManager` class is used as a generator of recurring expenses. When a user wants to add a recurring expense in FastTrack, the user can use the `addrec` command to create a new `RecurringExpenseManager` object. This object will have the following fields:
-* `expenseName` - Name of the recurring expense.
-* `amount` - Unit price of the recurring expense.
-* `category` - The category of the recurring expense.
-* `startDate` - The date to start adding the recurring expenses.
-* `[endDate]` - An optional ending date of the recurring expense.
-* `nextExpenseDate` - The next date to charge the recurring expense.
-* `recurringExpenseType` - The interval to charge the recurring expense (day, week, month, year).
+
+- `expenseName` - Name of the recurring expense.
+- `amount` - Unit price of the recurring expense.
+- `category` - The category of the recurring expense.
+- `startDate` - The date to start adding the recurring expenses.
+- `[endDate]` - An optional ending date of the recurring expense.
+- `nextExpenseDate` - The next date to charge the recurring expense.
+- `recurringExpenseType` - The interval to charge the recurring expense (day, week, month, year).
##### **The `RecurringExpenseList` class:**
-The `RecurringExpenseList` class works similar to the `ExpenseList` and `UniqueCategoryList` classes where is stores all the `RecurringExpenseManager` object created by the user.
+
+The `RecurringExpenseList` class works similar to the `ExpenseList` and `UniqueCategoryList` classes where is stores all the `RecurringExpenseManager` object created by the user.
##### **The `RecurringExpenseType` class:**
+
This is an enum class that stores the valid intervals (day, week, month, year) for a recurring expense. It also contains the `RecurringExpenseType#getNextExpenseDate()` method that calculates the next date given the interval and target date.
#### **Sequence of actions:**
@@ -731,24 +740,27 @@ Pre-requisite: The user has added some `RecurringExpenseManager` into FastTrack
4. The `ExpenseTracker` will then add each of the expenses in the returned list to FastTrack.
#### **Design considerations:**
+
##### Aspect: Making `RecurringExpenseManager` a class:
-* **Alternative 1:** Have a `RecurringExpenseManager` extend from `Expense`. Consist of another list of recurring expenses generated by the RecurringExpenseManager.
- * Pros: Addition of 1 class only. Works similar to an `Expense` object with just an addition list to store the recurring expenses.
- * Cons: Methods for adding a new recurring expense is longer as we need to traverse the entire expense list to locate the `RecurringExpenseManager` and add to the back of the list. Furthermore, the deletion of the `RecurringExpenseManager` also removes the existing recurring expenses.
-* **Alternative 2 (current choice):** Abstract `RecurringExpenseManager` to its own class, similar to category.
- * Pros: Able to easily locate all `RecurringExpenseManager` objects that the user created, making addition of recurring expenses into FastTrack simpler and quicker. Deletion of the `RecurringExpenseManager` does not delete existing recurring expenses.
- * Cons: Requires the addition of another `RecurringExpenseList` class to store all `RecurringExpenseManager` objects. Tedious to maintain and implement.
+- **Alternative 1:** Have a `RecurringExpenseManager` extend from `Expense`. Consist of another list of recurring expenses generated by the RecurringExpenseManager.
+ - Pros: Addition of 1 class only. Works similar to an `Expense` object with just an addition list to store the recurring expenses.
+ - Cons: Methods for adding a new recurring expense is longer as we need to traverse the entire expense list to locate the `RecurringExpenseManager` and add to the back of the list. Furthermore, the deletion of the `RecurringExpenseManager` also removes the existing recurring expenses.
+- **Alternative 2 (current choice):** Abstract `RecurringExpenseManager` to its own class, similar to category.
+ - Pros: Able to easily locate all `RecurringExpenseManager` objects that the user created, making addition of recurring expenses into FastTrack simpler and quicker. Deletion of the `RecurringExpenseManager` does not delete existing recurring expenses.
+ - Cons: Requires the addition of another `RecurringExpenseList` class to store all `RecurringExpenseManager` objects. Tedious to maintain and implement.
**Alternative 2** was chosen as it would be more ideal to abide by the _Separation of Concerns_ principle. This allows proper separation of the generator class `RecurringExpenseManager` and the `Expense` class.
### **\[Implemented\] Budget feature:**
#### **Command format:**
-`set p/AMOUNT` where `AMOUNT` refers to the monthly budget amount.
+
+`set p/AMOUNT` where `AMOUNT` refers to the monthly budget amount.
#### **What is the feature about:**
-This feature allows users to add a monthly budget to FastTrack. A weekly budget will be calculated for users by taking `AMOUNT` / 4. The `Budget` class is meant to be coupled with `AnalyticModel` to allow users to view helpful [statistics](#implemented-expense-statistics-feature) such as remaining budget etc.
+
+This feature allows users to add a monthly budget to FastTrack. A weekly budget will be calculated for users by taking `AMOUNT` / 4. The `Budget` class is meant to be coupled with `AnalyticModel` to allow users to view helpful [statistics](#implemented-expense-statistics-feature) such as remaining budget etc.
#### **Sequence of actions:**
@@ -762,19 +774,21 @@ Below is the sequence diagram when a user uses the `set` command:
4. A `CommandResult` object is returned.
:information_source: **Note:**
-* At step 2, if an invalid amount is given, a `CommandException` will be thrown and the sequence of action will be terminated.
+
+- At step 2, if an invalid amount is given, a `CommandException` will be thrown and the sequence of action will be terminated.
+
#### **Design considerations:**
##### Aspect: Making `Budget` a class:
-* **Alternative 1:** Make budget a field with `Double` type in `ExpenseTracker` rather than creating a new class.
- * Pros: Easier to implement as there is no need for a creation of a class.
- * Cons: Budget related calculations have to be done within the `ExpenseTracker` class, adding clutter. Modifications to the budget will also be more tedious as we have to locate these methods within `ExpenseTracker`.
-
-* **Alternative 2 (Current choice):** Make a new `Budget` class.
- * Pros: Abstract all budget related operations to a class to maintain clean code. Modifications to budget related operations are also easier.
- * Cons: Less convenient as it requires the creation of a new class.
-**Alternative 2** was chosen as it abides to the separation of concerns principle. This is to achieve better modularity and readability of the code base.
+- **Alternative 1:** Make budget a field with `Double` type in `ExpenseTracker` rather than creating a new class.
+ - Pros: Easier to implement as there is no need for a creation of a class.
+ - Cons: Budget related calculations have to be done within the `ExpenseTracker` class, adding clutter. Modifications to the budget will also be more tedious as we have to locate these methods within `ExpenseTracker`.
+- **Alternative 2 (Current choice):** Make a new `Budget` class.
+ - Pros: Abstract all budget related operations to a class to maintain clean code. Modifications to budget related operations are also easier.
+ - Cons: Less convenient as it requires the creation of a new class.
+
+**Alternative 2** was chosen as it abides to the separation of concerns principle. This is to achieve better modularity and readability of the code base.
### \[Implemented\] Expense Statistics Feature
@@ -795,6 +809,7 @@ The following statistics are calculated and displayed to the user. In FastTrack,
8. `Budget Utilised` - The percentage of monthly budget that the user has utilised this month
#### Implementation Details
+
The current implementation of the expense summary feature requires consistent calculations of the user's expense statistics. As such, an `AnalyticModelManager`, which implements the `AnalyticModel` interface is used to keep track of all of these statistics.
It contains fields which keep track of each individual statistic as mentioned in the above list, as well as specific methods to perform new calculations and updates to each of these fields.
@@ -803,6 +818,7 @@ The following Class Diagram describes the structure of the `AnalyticModelManager
// class diagram for AnalyticModelManager
`AnalyticModelManager` requires an instance of `ExpenseTracker` to read and obtain the following unmodifiable, `ObservableList` objects containing data from FastTrack:
+
1. `allExpenses`: an `ObservableList` of Expense objects representing all expenses in the expense tracker
2. `allCategories`: an `ObservableList` of Category objects representing all expense categories in the expense tracker
3. `simpleBudget`: a `ObjectProperty` object representing the monthly budget of the user.
@@ -819,100 +835,102 @@ This method of implementation closely follows the _Observer Pattern_, which prom
**Aspect: Separation of Analytics and App Data**:
-* **Alternative 1 (Current choice):** Create two separate `ModelManager` components, one for managing analytics and another for managing app data.
- * Pros: As analytics functions are read-only and do not require modifying the internal data of FastTrack, keeping the expense summary statistics in a separate component is ideal as it abides by the _Separation of Concerns_ principle.
- * Cons: Less convenient to implement as a new class would be created.
-* **Alternative 2 :** Store fields and methods for calculating expense summary statistics inside the existing `ModelManager` component.
- * Pros: Easier to implement as the class already exists, there is no need to instantiate another `AnalyticModelManager` class.
- * Cons: Will result in the current `ModelManager` being cluttered due to the presence of many differing getter and setter methods. Violates the _Separation of Concerns_ principle since the process of calculating statistics does not involve modifying the data within FastTrack, i.e. is purely read-only.
+- **Alternative 1 (Current choice):** Create two separate `ModelManager` components, one for managing analytics and another for managing app data.
+ - Pros: As analytics functions are read-only and do not require modifying the internal data of FastTrack, keeping the expense summary statistics in a separate component is ideal as it abides by the _Separation of Concerns_ principle.
+ - Cons: Less convenient to implement as a new class would be created.
+- **Alternative 2 :** Store fields and methods for calculating expense summary statistics inside the existing `ModelManager` component.
+ - Pros: Easier to implement as the class already exists, there is no need to instantiate another `AnalyticModelManager` class.
+ - Cons: Will result in the current `ModelManager` being cluttered due to the presence of many differing getter and setter methods. Violates the _Separation of Concerns_ principle since the process of calculating statistics does not involve modifying the data within FastTrack, i.e. is purely read-only.
**Alternative 1** was chosen as it would be more ideal to abide by the principle of _Separation of Concerns_. Moreover, as many developers were working on features that require direct modification of the `ModelManager` component, separating analytics into another `AnalyticModelManager` eliminates the possibility of merge conflicts.
**Aspect: GUI update when user updates expenses in FastTrack**:
-* **Alternative 1:** Call methods to calculate and refresh the summary statistics after every user command.
- * Pros: More convenient to implement since it is easy to ensure the GUI is always updated whenever the user enters a command
- * Cons: Inefficient, as this would require tearing down and creating a new instance of each `UI` component in order to display the updated data. Redundant calculations would also need to be performed every single time the user enters a command that does not change the underlying expense data.
-* **Alternative 2 (Current choice):** Use the _Observer Pattern_ to allow `UI` to update whenever the underlying data of FastTrack changes
- * Pros: More efficient, since no redundant calculations are performed. The `AnalyticModelManager` also does not need a reference to the `UI` component to perform an update, which reduces the coupling between these classes.
- * Cons: Was more time-consuming to implement, due to the need to learn about mechanisms like bindings and change listeners in JavaFX.
+- **Alternative 1:** Call methods to calculate and refresh the summary statistics after every user command.
+ - Pros: More convenient to implement since it is easy to ensure the GUI is always updated whenever the user enters a command
+ - Cons: Inefficient, as this would require tearing down and creating a new instance of each `UI` component in order to display the updated data. Redundant calculations would also need to be performed every single time the user enters a command that does not change the underlying expense data.
+- **Alternative 2 (Current choice):** Use the _Observer Pattern_ to allow `UI` to update whenever the underlying data of FastTrack changes
+ - Pros: More efficient, since no redundant calculations are performed. The `AnalyticModelManager` also does not need a reference to the `UI` component to perform an update, which reduces the coupling between these classes.
+ - Cons: Was more time-consuming to implement, due to the need to learn about mechanisms like bindings and change listeners in JavaFX.
**Alternative 2** was chosen as it was neater to implement and performs statistic calculations only when absolutely necessary, this preventing unnecessary wastage of computational resources.
### \[Implemented\] Category Autocomplete feature
-The category autocompletion feature in FastTrack is implemented using two `UI` classes, namely the `SuggestionListPanel` and the `CommandBox`.
+The category autocompletion feature in FastTrack is implemented using two `UI` classes, namely the `SuggestionListPanel` and the `CommandBox`.
-The `SuggestionListPanel` is a JavaFX `UI` component responsible for displaying the suggestion list for the category autocomplete feature. It receives two parameters in its constructor, namely an `ObservableList` and a `CommandBox`.
+The `SuggestionListPanel` is a JavaFX `UI` component responsible for displaying the suggestion list for the category autocomplete feature. It receives two parameters in its constructor, namely an `ObservableList` and a `CommandBox`.
The `ObservableList` contains all available categories in FastTrack, and the `CommandBox` is the `UI` component that contains a text field which allows users to enter commands, as well as for setting text to autofill.
-To filter the categories based on the user's input, the `SuggestionListPanel` makes use of a `FilteredList`. This filtered list is used as the data source for the `ListView` `UI` component of the `SuggestionListPanel`.
+To filter the categories based on the user's input, the `SuggestionListPanel` makes use of a `FilteredList`. This filtered list is used as the data source for the `ListView` `UI` component of the `SuggestionListPanel`.
The `FilteredList` displays filtered categories based on whether the category name matches the user's input.
Upon initialization, the `SuggestionListPanel` sets up autocomplete handlers for the suggestion list by calling the `SuggestionListPanel#initialiseAutocompleteHandlers()` method. This method registers listeners for focus changes, key presses, and autocomplete filters, which work together to ensure the autocomplete feature works responsively.
-When the user enters a command, they can trigger the autocomplete feature by typing `c/` followed by a few characters. These characters can then be used to filter the categories, and the most likely suggestions are displayed in the suggestion list.
-To load the filter for autocompletion, the `SuggestionListPanel#loadAutocompleteFilter()` method within `SuggestionListPanel#initialiseAutocompleteHandlers()` sets up a listener for changes in the text field of the `CommandBox`.
+When the user enters a command, they can trigger the autocomplete feature by typing `c/` followed by a few characters. These characters can then be used to filter the categories, and the most likely suggestions are displayed in the suggestion list.
+To load the filter for autocompletion, the `SuggestionListPanel#loadAutocompleteFilter()` method within `SuggestionListPanel#initialiseAutocompleteHandlers()` sets up a listener for changes in the text field of the `CommandBox`.
If the user types `c/` to start entering a category name, the `SuggestionListPanel#getAutocompleteSuggestions()` method is called to retrieve the most likely suggestions based on the user's input so far.
This method filters the categories based on the user's input and updates the filtered categories as the items in the `ListView` `UI` component of the `SuggestionListPanel`.
-When the suggestion list is visible, the user can navigate through the suggestions using the `UP` and `DOWN` arrow keys.
+When the suggestion list is visible, the user can navigate through the suggestions using the `UP` and `DOWN` arrow keys.
The `CommandBox#initialiseAutocompleteHandler()` method adds a key press event filter to the command text field that listens for the `UP` arrow key.
When the user presses the `UP` arrow key, the focus is given to the `SuggestionListPanel` if it is visible (i.e. the user had typed in `c/`), and the `SuggestionListPanel#selectLastItemOnFocus()` method selects the last item in the suggestion list by default. When the user presses the `DOWN` arrow key, the `SuggestionListPanel#handleDownArrowKey()` method is called to check if the user is about to navigate out of the suggestion list and is responsible for returning the focus back to the `CommandBox`.
-To select a category from the suggestion list, the user can either navigate through the list using the arrow keys and press the `ENTER` key or press the `TAB` key to select the bottom-most suggestion in the list.
+To select a category from the suggestion list, the user can either navigate through the list using the arrow keys and press the `ENTER` key or press the `TAB` key to select the bottom-most suggestion in the list.
When the user makes a selection using the `ENTER` key, a callback function within the `SuggestionListPanel#addKeyPressListener()` method is called. This function updates the suggested text in the `CommandBox` by calling the `SuggestionListPanel#updateSuggestedText()` method, which sets the text in the `CommandBox` and subsequently hides the suggestion list.
If the user makes the selection by pressing the `TAB` key, the `CommandBox#initialiseAutocompleteHandler()` method simulates a `UP` arrow key press followed by an `ENTER` key press, which also causes the first suggestion in the list to be selected.
-
#### Design considerations:
**Aspect: How the autocomplete feature should be implemented**:
-* **Alternative 1 (Current choice):** Add the autocomplete logic within the `SuggestionListPanel` and `CommandBox` classes itself
- * Pros: This design is more convenient and allows the methods within each class to focus specifically on their own behaviors, without explicit knowledge of the autocomplete feature.
- * Cons: Currently, the `SuggestionListPanel` is tightly coupled to the `CommandBox` through its constructor parameters. This means that more modifications would need to be made if a new text input component was introduced.
+- **Alternative 1 (Current choice):** Add the autocomplete logic within the `SuggestionListPanel` and `CommandBox` classes itself
+
+ - Pros: This design is more convenient and allows the methods within each class to focus specifically on their own behaviors, without explicit knowledge of the autocomplete feature.
+ - Cons: Currently, the `SuggestionListPanel` is tightly coupled to the `CommandBox` through its constructor parameters. This means that more modifications would need to be made if a new text input component was introduced.
-* **Alternative 2 :** Create a new `AutocompleteLogic` class which uses the _Observer Pattern_ to listen to and propagate changes across the `SuggestionListPanel` and `CommandBox` components.
- * Pros: Enforces loose coupling by the _Observer Pattern_
- * Cons: Tedious to implement, and the added flexibility might be unnecessary since the autocomplete feature is not likely to be further extended upon.
+- **Alternative 2 :** Create a new `AutocompleteLogic` class which uses the _Observer Pattern_ to listen to and propagate changes across the `SuggestionListPanel` and `CommandBox` components.
+ - Pros: Enforces loose coupling by the _Observer Pattern_
+ - Cons: Tedious to implement, and the added flexibility might be unnecessary since the autocomplete feature is not likely to be further extended upon.
---------------------------------------------------------------------------------------------------------------------
+---
## **Documentation, logging, testing, configuration, dev-ops**
-* [Documentation guide](Documentation.md)
-* [Testing guide](Testing.md)
-* [Logging guide](Logging.md)
-* [Configuration guide](Configuration.md)
-* [DevOps guide](DevOps.md)
+- [Documentation guide](Documentation.md)
+- [Testing guide](Testing.md)
+- [Logging guide](Logging.md)
+- [Configuration guide](Configuration.md)
+- [DevOps guide](DevOps.md)
---------------------------------------------------------------------------------------------------------------------
+---
## **Appendix: Requirements**
### Product scope
**Target user profile**:
-* NUS undergraduate students from the School of Computing
-* Tech-savvy and able to type fast
-* Comfortable using CLI apps
-* Has to manage a large number of different general consumption and professional recurring expenses
+
+- NUS undergraduate students from the School of Computing
+- Tech-savvy and able to type fast
+- Comfortable using CLI apps
+- Has to manage a large number of different general consumption and professional recurring expenses
**Value proposition**:
-* Easy-to-use and allows students to log their daily expenses quickly and efficiently via a CLI
-* Students can keep track of their spending habits with informative statistics
-* FastTrack provides visual feedback and suggestions to help NUS students make more informed financial decisions
+
+- Easy-to-use and allows students to log their daily expenses quickly and efficiently via a CLI
+- Students can keep track of their spending habits with informative statistics
+- FastTrack provides visual feedback and suggestions to help NUS students make more informed financial decisions
### User stories
Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
| Priority | As a … | I want to … | So that I can… |
-|----------|-----------------------|-----------------------------------------------------------------------|-------------------------------------------------------------------------------|
+| -------- | --------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `* * *` | New user | See usage instructions | Refer to instructions when I forget how to use the app |
| `* * *` | NUS Computing Student | Add my own expense categories | Categorise my expenses with a high degree of customisation |
| `* * *` | NUS Computing Student | Delete an expense category | Remove categories that I no longer need |
@@ -947,20 +965,23 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
3. FastTrack adds the new category.
4. FastTrack responds with a success message indicating the category has been successfully added.
- Use case ends.
+ Use case ends.
**Extensions**
-* 2a. The user does not enter the required category name.
- * 2a1. FastTrack responds telling the user that a name is required and the command is invalid.
- * 2a2. User enters add command with the category name.
- * 2a3. Steps 2a1-2a2 are repeated until the data entered are correct.
+
+- 2a. The user does not enter the required category name.
+
+ - 2a1. FastTrack responds telling the user that a name is required and the command is invalid.
+ - 2a2. User enters add command with the category name.
+ - 2a3. Steps 2a1-2a2 are repeated until the data entered are correct.
Use case resumes at step 3.
-* 2b. The category name already exists
- * 2b1. FastTrack informs the user that the category name has already been used and prompts the user for a different category name.
- * 2b2. User enters add command with the category name.
- * 2b3. Steps 2b1-2b2 are repeated until the data entered are correct.
+- 2b. The category name already exists
+
+ - 2b1. FastTrack informs the user that the category name has already been used and prompts the user for a different category name.
+ - 2b2. User enters add command with the category name.
+ - 2b3. Steps 2b1-2b2 are repeated until the data entered are correct.
Use case resumes at step 3.
@@ -977,12 +998,14 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
Use case ends.
**Extensions**
-* 3a. The user selects an invalid category index (the index is out of bounds of the list)
- * 3a1. FastTrack displays an error message telling the user to key in a valid index.
- * 3a2. User enters delete command with the category index.
- * 3a3. Steps 3a1-3a2 are repeated until the data entered are correct.
- Use case resumes at step 4.
+- 3a. The user selects an invalid category index (the index is out of bounds of the list)
+
+ - 3a1. FastTrack displays an error message telling the user to key in a valid index.
+ - 3a2. User enters delete command with the category index.
+ - 3a3. Steps 3a1-3a2 are repeated until the data entered are correct.
+
+ Use case resumes at step 4.
### Use case: UC3 - Edit an Expense
@@ -994,12 +1017,14 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
4. FastTrack responds with a success message indicating the expense has been successfully edited.
**Extensions**
-* 2a. The user selects an expense that does not exist.
- * 2a1. FastTrack displays an error message and does not allow the user to edit the expense.
-* 2b. The user tries to save an expense with invalid or missing data.
- * 2b1. FastTrack displays an error message indicating the fields that need to be corrected.
+
+- 2a. The user selects an expense that does not exist.
+ - 2a1. FastTrack displays an error message and does not allow the user to edit the expense.
+- 2b. The user tries to save an expense with invalid or missing data.
+ - 2b1. FastTrack displays an error message indicating the fields that need to be corrected.
**Precondition: The user has created at least one expense in the app**
+
### Use case: UC4 - List all Categories
**MSS**
@@ -1007,12 +1032,12 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
1. User requests to list all categories.
2. FastTrack displays all categories.
- Use case ends.
-
+Use case ends.
**Extensions**
-* 2a. The user does not have any categories.
- * 2a1. FastTrack only displays the Misc category
+
+- 2a. The user does not have any categories.
+ - 2a1. FastTrack only displays the Misc category
### Use case: UC5 - Add an expense
@@ -1024,10 +1049,11 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
**Extensions**
-* 2a. User keys in information in wrong format.
- * 2a1. FastTrack returns an error, requesting that the user inputs information in the correct format.
- * 2a2. User inputs information again.
- * 2a3. Steps 2a1-2a2 are repeated until the information being input is of the correct format.
+- 2a. User keys in information in wrong format.
+
+ - 2a1. FastTrack returns an error, requesting that the user inputs information in the correct format.
+ - 2a2. User inputs information again.
+ - 2a3. Steps 2a1-2a2 are repeated until the information being input is of the correct format.
Use case resumes from step 3.
@@ -1040,17 +1066,17 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
3. User keys in the command to delete the expense.
4. FastTrack responds with a successfully deleted message.
- Use case ends.
+ Use case ends.
**Extensions**
-* 3a. User keys in an invalid expense index.
- * 3a1. FastTrack returns an error, requesting that the user inputs the correct expense index.
- * 3a2. User inputs information again.
- * 3a3. Steps 3a1-3a2 are repeated until the expense index being input by the user is valid.
+- 3a. User keys in an invalid expense index.
- Use case resumes from step 4.
+ - 3a1. FastTrack returns an error, requesting that the user inputs the correct expense index.
+ - 3a2. User inputs information again.
+ - 3a3. Steps 3a1-3a2 are repeated until the expense index being input by the user is valid.
+ Use case resumes from step 4.
**MSS**
@@ -1069,18 +1095,19 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
1. User requests to list all expense in a given category.
2. FastTrack displays all expenses in a given category added by user.
- Use case ends.
+ Use case ends.
-* 1a. User does not enter a category.
- * 1a1. FastTrack displays error message.
+- 1a. User does not enter a category.
- Use case ends.
+ - 1a1. FastTrack displays error message.
-* 1b. The given category is invalid.
+ Use case ends.
- * 1b1. FastTrack displays an error message.
+- 1b. The given category is invalid.
- Use case ends.
+ - 1b1. FastTrack displays an error message.
+
+ Use case ends.
### Use case: UC9 - List all expense in the past week
@@ -1089,7 +1116,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
1. User requests to list all expense in the past week.
2. FastTrack displays all expenses added by user in the past week .
- Use case ends.
+ Use case ends.
### Use case: UC10 - List all expense in a given category in the past week
@@ -1098,7 +1125,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
1. User requests to list all expense in a category in the past week.
2. FastTrack displays all expenses added by user in the category in the past week.
- Use case ends.
+ Use case ends.
### Use case: UC11 - Find an expense
@@ -1116,7 +1143,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
1. User wants to wipe all currently-logged expenses.
2. User keys in the command to clear all logged expenses.
- Use case ends.
+ Use case ends.
### Use case: UC13 - Get Help within the app
@@ -1126,7 +1153,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
2. User keys in the command to get help.
3. FastTrack opens a pop-up window to show help for commands and a link to the User Guide.
- Use case ends.
+ Use case ends.
### Use case: UC13 - Exit from FastTrack
@@ -1136,7 +1163,7 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
2. User keys in the command to exit the application.
3. FastTrack exits and is closed.
- Use case ends.
+ Use case ends.
### Non-Functional Requirements
@@ -1150,9 +1177,9 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
### Glossary
-* **Mainstream OS**: Windows, Linux, Unix, OS-X
+- **Mainstream OS**: Windows, Linux, Unix, OS-X
---------------------------------------------------------------------------------------------------------------------
+---
## **Appendix: Instructions for manual testing**
@@ -1176,28 +1203,27 @@ testers are expected to do more *exploratory* testing.
1. Resize the window to an optimum size. Move the window to a different location. Close the window.
1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+ Expected: The most recent window size and location is retained.
1. _{ more test cases … }_
-
### Saving data
1. Dealing with missing/corrupted data files
1. In the case whereby your data files end up corrupted, FastTrack will wipe all data and allow you to start over
- with a clean slate.
+ with a clean slate.
1. _{ more test cases … }_
-
### Effort
The difficulty of FastTrack as a project was quite high, but one that allowed us to learn a lot. As our group lacked experience in working with
-a properly-structured team as well as a pre-existing codebase that had to be overhauled for our own purposes, we had to start from scratch to learn
+a properly-structured team as well as a pre-existing codebase that had to be overhauled for our own purposes, we had to start from scratch to learn
how exactly commands and user inputs were handled by the pre-existing AB3 codebase.
We faced numerous challenges in our journey to make FastTrack, not limited to:
+
1. Learning how the codebase worked and how to restructure existing code.
2. How to design the structure of FastTrack so that the `Category`, `Expense` and `RecurringExpenseManager` made sense.
3. Abstracting out certain checks into proper methods in the correct classes so as to reduce violations of principles.
@@ -1205,10 +1231,10 @@ We faced numerous challenges in our journey to make FastTrack, not limited to:
Our group reused much of what AB3 had in place, with the main bulk of reusability coming from AB3's parsing set-up and
the model as well as storage systems, which we found to be useful as they worked in quite an intuitive and robust manner after we took some
-time to understand them.
+time to understand them.
However, we faced major difficulties in attempting to modify AB3 to suit FastTrack, as we realized
-that AB3 primarily dealt with `Person` objects, which were only one type of entity, while we had to deal with 3 kinds of entities as
+that AB3 primarily dealt with `Person` objects, which were only one type of entity, while we had to deal with 3 kinds of entities as
mentioned above. This led to multiple problems in our implementations and debugging stages, and we had to race against time
on multiple occasions to thankfully, successfully resolve these issues.
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index ba34b616c42..f042ced4844 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -3,13 +3,15 @@ layout: page
title: FastTrack User Guide
---
+---
---------------------------------------------------------------------------------------------------------------------
# **Table of Contents**
+
{:toc}
+
1. [Introduction to FastTrack](#introduction-to-fasttrack)
2. [Why You Should Use FastTrack](#why-you-should-use-fasttrack)
3. [Purpose of this guide](#purpose-of-this-guide)
@@ -29,8 +31,8 @@ title: FastTrack User Guide
9. [Editing the data file (For Advanced Users)](#editing-the-data-file)
10. [Frequently Asked Questions](#frequently-asked-questions)
+---
---------------------------------------------------------------------------------------------------------------------
# Introduction to FastTrack
FastTrack is an easy-to-use **financial management desktop application** designed for NUS SoC undergraduate students who are living on a tight budget.
@@ -41,10 +43,12 @@ FastTrack prioritizes speed and efficiency to save your precious time and money,
![Ui](images/Ui.png)
---------------------------------------------------------------------------------------------------------------------
+---
+
## Why you should use FastTrack
**FastTrack** is an expense tracking app that helps computing students keep track of their expenses by providing a simple and convenient command-line interface. Here are some reasons why you should consider using FastTrack:
+
1. **Simplicity:** FastTrack provides a _simple_ and _easy-to-use_ command-line interface that allows you to quickly add and track your expenses. This makes it ideal for computing students who prefer to use the command line to work with data.
2. **Speed:** FastTrack prioritizes speed and efficiency. With its command-line interface and all commands being a simple and one-line, it skips the hassle of clicking through screens like other expense tracking apps. The entire interface is shown in one screen.
3. **Convenience:** FastTrack can be used on any platform, including Windows, Mac, and Linux, making it convenient for computing students to track their expenses regardless of the platform they are using.
@@ -52,28 +56,30 @@ FastTrack prioritizes speed and efficiency to save your precious time and money,
5. **Security:** FastTrack is a locally hosted app that allows you to keep your expenses and financial information private. It does not require any personal information or financial details to use, ensuring that your information remains secure.
6. **Free and Open Source:** FastTrack is a free and open-source app, meaning that it is available for download and use by anyone.
---------------------------------------------------------------------------------------------------------------------
+---
# Purpose of this Guide
This User Guide provides information on how to use FastTrack. It includes:
-* [Installation](#quick-start-and-installation) and setup of app
-* Detailing [features](#features) of the app
-* Usage of app and its commands
-* Tips, tricks and warnings on usage of commands
-* Troubleshooting tips
-* Answering [Frequently Asked Questions](#frequently-asked-questions)
---------------------------------------------------------------------------------------------------------------------
+- [Installation](#quick-start-and-installation) and setup of app
+- Detailing [features](#features) of the app
+- Usage of app and its commands
+- Tips, tricks and warnings on usage of commands
+- Troubleshooting tips
+- Answering [Frequently Asked Questions](#frequently-asked-questions)
+
+---
# Understanding this guide
## Icons
+
Throughout FastTrack's user guide, you may encounter unfamiliar symbols. This is a quick overview of what these symbols
mean and what to look out for.
| **Icon** | **Meaning** |
-|----------------------|-------------|
+| -------------------- | ----------- |
| :warning: | Warning |
| :information_source: | Information |
| :bulb: | Tip |
@@ -97,8 +103,8 @@ This provides additional useful information that may help you with using FastTra
This provides some quick and convenient hacks that you can use to optimize your experience with FastTrack.
+---
---------------------------------------------------------------------------------------------------------------------
## Quick start and Installation
1. Ensure you have Java `11` or above installed in your Computer.
@@ -109,7 +115,6 @@ This provides some quick and convenient hacks that you can use to optimize your
4. Double-click the FastTrack JAR file to run the application.
-
A Graphical User Interface (pictured below) will appear. Note how the app contains some sample data.
![Ui](images/Ui_Revised.png)
@@ -117,20 +122,19 @@ This provides some quick and convenient hacks that you can use to optimize your
6. Type a command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
Some example commands you can try:
- * `list` : Lists all expenses
-
- * `clear` : Clears the sample data
+ - `list` : Lists all expenses
- * `add c/groceries n/milk p/4.50 d/14/2/23` : Adds an expense named `milk` to the expenses list with a price of $4.50 and a date of 14/02/2023
+ - `clear` : Clears the sample data
- * `delete 3` : Deletes the 3rd expense shown in the current list
+ - `add c/groceries n/milk p/4.50 d/14/2/23` : Adds an expense named `milk` to the expenses list with a price of $4.50 and a date of 14/02/2023
- * `exit` : Exits the app
+ - `delete 3` : Deletes the 3rd expense shown in the current list
+ - `exit` : Exits the app
7. Refer to the [Features](#features) below for details of each command.
---------------------------------------------------------------------------------------------------------------------
+---
# Graphical User Interface (GUI) Walkthrough
@@ -140,16 +144,15 @@ The following diagrams highlight the different sections of the _Graphical User I
The **main display**. It displays all added expenses on the left, showing each expense's price, category and date added.
![FastTrack GUI](images/demo/intro/fasttrack_labeled_2.png)
-The **Category display**. It shows all currently added Categories for expenses. This display is shown only after using the
+The **Category display**. It shows all currently added Categories for expenses. This display is shown only after using the
command to list categories.
![FastTrack GUI](images/demo/intro/fasttrack_labeled_3.png)
The **Recurring Expense display**. It shows all currently added Recurring Expenses. This display is shown only after using the
command to list recurring expenses.
-
| **FastTrack UI Part** | **Description** |
-|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| One-time Expense Display | Displays the list of saved one-time expenses with filters applied (if any). This display occupies the _Main View_ section. |
| Category Display | Displays the list of saved categories, including the number of expenses associated with each category. This display occupies the _Main View_ section. |
| Recurring Expense Display | Displays the list of saved recurring expenses. This display occupies the _Main View_ section. |
@@ -158,15 +161,16 @@ command to list recurring expenses.
| Expense Summary Display | A visual display containing expense statistics (Refer to the feature [Expense Statistics](#expense-statistics-feature) below for details. |
| Toolbar | Contains buttons which allow you to access the user guide and exit from the application. |
---------------------------------------------------------------------------------------------------------------------
+---
## Features
The features of FastTrack can be divided into 4 groups, **Category Features**, **Expense Features**, **General Features** and **Expense Statistics Feature**. With these 4 groups in mind, remembering the different commands becomes extremely convenient, as each group contains mainly 4 types of operations - add, delete, edit and list!
### Commands
+
| [**Category**](#category-features) | [**One-Time Expenses**](#one-time-expenses) | [**Recurring Expenses**](#recurring-expenses) | [**General**](#general-features) |
-|-----------------------------------------------------------|----------------------------------------------------------------------|--------------------------------------------------------------------|-----------------------------------------------------|
+| --------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------- |
| [Add a category](#adding-a-category-addcat) | [Add an expense](#adding-an-expense-add) | [Add a recurring expense](#adding-a-recurring-expense-addrec) | [Set a budget](#setting-a-budget-set) |
| [Edit a category](#editing-a-category-edcat) | [Edit an expense](#editing-an-expense-edexp) | [Edit a recurring expense](#editing-a-recurring-expense-edrec) | [Category autocompletion](#category-autocompletion) |
| [Delete a category](#deleting-a-category-delcat) | [Delete an expense](#deleting-an-expense-delete) | [Delete a recurring expense](#deleting-a-recurring-expense-delrec) | [Clear all entries](#clearing-all-entries-clear) |
@@ -174,8 +178,9 @@ The features of FastTrack can be divided into 4 groups, **Category Features**, *
| [View category summary](#viewing-category-summary-sumcat) | [List expenses](#listing-expenses-list) | | [View help](#viewing-help-help) |
### Other Notable Features
+
| [**Expense Statistics Feature**](#expense-statistics-feature) |
-|-------------------------------------------------------------------------------------|
+| ----------------------------------------------------------------------------------- |
| [Monthly spending statistic](#monthly-spending-statistic) |
| [Monthly remaining statistic](#monthly-remaining-statistic) |
| [Monthly percentage change statistic](#monthly-percentage-change-statistic) |
@@ -185,7 +190,6 @@ The features of FastTrack can be divided into 4 groups, **Category Features**, *
| [Total spent statistic](#total-spent-statistic) |
| [Budget utilisation percentage statistic](#budget-utilisation-percentage-statistic) |
-
## Command Syntax
First-time users may have difficulty understanding the syntax described in the command instructions.
@@ -202,20 +206,22 @@ command tag1/ PARAMETER_1 tag2/ PARAMETER_2 [tag3/ PARAMETER_3]
```
| Element | Format | Usage |
-|-------------|-----------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| ----------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `command` | Name of command
eg. `add`, `find` | Specifies the command to be executed. |
| `tag/` | Prefix for a field, followed by `/` | Specifies which field given input argument is for |
| `PARAMETER` | Words that are in UPPERCASE | Specifies user input for field specified by `tag/`
e.g.
In `add c/CATEGORY_NAME`, `CATEGORY_NAME` is a parameter, which the user decides is “groceries”. The final command will be entered as add `c/groceries`. |
| `[]` | Square brackets around `tag/` and `PARAMETER` | Indicates that field specified by `tag/` is optional and may be omitted.
If left unspecified, it will be set to a default value. |
For example, the command format for `add`:
+
```
add c/CATEGORY_NAME n/ITEM_NAME p/PRICE [d/DATE]
```
-* `add` is the `command` name.
-* `c/`, `n/`, `p/`, `d/` are `tag/`s to denote fields of _category_, _name_, _price_ and _date_ respectively.
-* `CATEGORY_NAME`, `ITEM_NAME`, `PRICE`, `DATE` are `PARAMETERS` to be supplied to the aforementioned `tag/`s.
-* `[d/DATE]` indicates that the field for the date is optional.
+
+- `add` is the `command` name.
+- `c/`, `n/`, `p/`, `d/` are `tag/`s to denote fields of _category_, _name_, _price_ and _date_ respectively.
+- `CATEGORY_NAME`, `ITEM_NAME`, `PRICE`, `DATE` are `PARAMETERS` to be supplied to the aforementioned `tag/`s.
+- `[d/DATE]` indicates that the field for the date is optional.
Don't worry if it takes a bit of time to get used to the commands. Once you're familiar with the commands, you'll be able to add expenses quickly and easily.
@@ -225,13 +231,13 @@ Don't worry if it takes a bit of time to get used to the commands. Once you're f
Before diving further into the guide, here are some things to take note about the way we formatted commands for FastTrack in this user guide.
-* Parameters can be in any order.
+- Parameters can be in any order.
e.g. if the command specifies `c/CATEGORY_NAME p/PRICE`, `p/PRICE c/CATEGORY_NAME` is also acceptable.
-* If a parameter is expected only once in the command, but you specified it multiple times, only the **last occurrence** of the parameter will be taken.
+- If a parameter is expected only once in the command, but you specified it multiple times, only the **last occurrence** of the parameter will be taken.
e.g. if you specify `p/4.50 p/5.80`, only `p/5.80` will be taken.
-* Extraneous parameters for commands that do not take in parameters (such as `help`, `exit`) will be ignored.
+- Extraneous parameters for commands that do not take in parameters (such as `help`, `exit`) will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`.
@@ -239,17 +245,18 @@ Before diving further into the guide, here are some things to take note about th
**:warning: Warning about Date Formatting:**
-Some commands may include the entry of a `DATE`, like the `add` command or the `addrec` command. A specific date format, `Day/Month/Year` should be used to format the date provided.
+Some commands may include the entry of a `DATE`, like the `add` command or the `addrec` command. A specific date format, `Day/Month/Year` should be used to format the date provided.
However, if the day is **within 1-31** but **not a valid date**, _e.g. 30th February_, FastTrack will smartly correct your date provided to the last date of the month.
i.e, `30/2/2023` will be corrected to `28/2/2023`.
-
+
Back to Top
---------------------------------------------------------------------------------------------------------------------
+---
+
# **Category Features**
FastTrack makes it easy for you to keep track of your spending by organizing expenses into categories. A category is like a folder that holds all your expenses that fall under a specific theme. For example, you might have a category called Groceries where you record all purchases from Fairprice or NTUC.
@@ -262,19 +269,20 @@ FastTrack even has a default Misc category for any expenses that you haven't cat
**:information_source: Info**
Note that category names in FastTrack are case-insensitive. For example, a category named `Groceries` will be treated as the exact same category as `groceries`.
+
-### Command Summary
+### Command Summary
| Feature | Command Format | Examples |
-|----------------------------------------------------------|---------------------------------------------|------------------------------------|
+| -------------------------------------------------------- | ------------------------------------------- | ---------------------------------- |
| [**List Categories**](#listing-categories-lcat) | `lcat` | `lcat` |
| [**Add Category**](#adding-a-category-addcat) | `addcat c/CATEGORY_NAME s/SUMMARY` | `addcat c/Groceries s/for living` |
| [**Delete Category**](#deleting-a-category-delcat) | `delcat INDEX` | `delcat 1` |
| [**Edit Category**](#editing-a-category-edcat) | `edcat INDEX [c/CATEGORY_NAME] [s/SUMMARY]` | `edcat 1 c/New Name s/New Summary` |
| [**Category Summary**](#viewing-category-summary-sumcat) | `sumcat INDEX` | `sumcat 2` |
---------------------------------------------------------------------------------------------------------------------
+---
## **Listing Categories** `lcat`
@@ -283,6 +291,7 @@ Displays the list of categories in FastTrack.
Format: `lcat`
### Demonstration
+
1. Type `lcat` into the command box
2. FastTrack displays the list of categories with the confirmation message `Listed all categories`
@@ -295,16 +304,17 @@ Adds a new category to FastTrack. If a category with the same name already exist
Format: `addcat c/CATEGORY_NAME s/SUMMARY`
| Parameter | Description |
-|-----------------|-----------------------------------------------------|
+| --------------- | --------------------------------------------------- |
| `CATEGORY_NAME` | Title of the category to be added. |
| `SUMMARY` | Short summary of what this category keeps track of. |
-
### Examples
-* `addcat c/Groceries s/for living` creates a new `Groceries` category with the summary of `for living`.
-* `addcat c/Entertainment s/for fun!` creates a new `Entertainment` category with the summary of `for fun!`.
+
+- `addcat c/Groceries s/for living` creates a new `Groceries` category with the summary of `for living`.
+- `addcat c/Entertainment s/for fun!` creates a new `Entertainment` category with the summary of `for fun!`.
### Demonstration
+
1. Enter the command `lcat` to switch to the **Category Display**
2. Enter the command `addcat c/Groceries s/for living` into the command box
3. FastTrack adds the new category to the category list with the confirmation message `New category added: groceries`
@@ -318,10 +328,9 @@ Deletes the category at the specified `INDEX` in the category list.
Format: `delcat INDEX`
| Parameter | Description |
-|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INDEX` | The index number shown in the displayed category list.
It must be a positive integer i.e. 1, 2, 3, ...
Expenses previously categorised under the specified category will be automatically re-categorized under the `Misc` category.
|
-
**:information_source: Info:**
@@ -329,12 +338,13 @@ If you delete a category that has existing expenses associated with it, those ex
-
### Examples
-* `lcat` followed by `delcat 2` deletes the second category in the category list
-* `lcat` followed by `delcat 1` deletes the first category in the category list
+
+- `lcat` followed by `delcat 2` deletes the second category in the category list
+- `lcat` followed by `delcat 1` deletes the first category in the category list
### Demonstration
+
1. Enter the command `lcat` to switch to the **Category Display**
2. Enter the command `delcat 7` into the command box
3. FastTrack deletes the seventh category `Food` from the category list with the confirmation message `Deleted category: food`
@@ -351,16 +361,18 @@ Both `CATEGORY_NAME` and `SUMMARY` are optional by themselves, but **at least**
to `INDEX`, otherwise the command will not be executed.
| Parameter | Description |
-|-----------------|-----------------------------------------------------------------------------------------------------------|
+| --------------- | --------------------------------------------------------------------------------------------------------- |
| `INDEX` | The index of the category to be edited.
It must be a positive integer i.e. 1, 2, 3, ... |
| `CATEGORY_NAME` | The new name of the category being edited at the specified index.
This parameter is optional. |
| `SUMMARY` | The new summary of the category being edited at the specified index.
This parameter is optional. |
### Examples
+
- `edcat 1 c/Drink` changes the name of the first category in the category list to `Drink`
- `edcat 2 c/Food s/Eating out` changes the name and summary of the second category in the category list to `Food` and `Eating out` respectively.
### Demonstration
+
1. Enter the command `lcat` to switch to the **Category Display**
2. Enter the command `edcat 1 c/Drink` into the command box
3. FastTrack edits the name of the first category `Food` from the category list to `Drink` with the confirmation message `Edited category: Drink`
@@ -375,10 +387,11 @@ Displays the category summary for a category.
Format: `sumcat INDEX`
| Parameter | Description |
-|-----------|--------------------------------------------------------------------------------------------------|
+| --------- | ------------------------------------------------------------------------------------------------ |
| `INDEX` | The index of the category to be edited.
It must be a positive integer i.e. 1, 2, 3, ... |
### Demonstration
+
1. Enter the command `lcat` to switch to the **Category Display**
2. Enter the command `sumcat 2` into the command box.
3. FastTrack displays the summary of the Entertainment category in the Results Display.
@@ -392,39 +405,41 @@ _`sumcat 2` shows the summary of the Entertainment category._
Back to Top
---------------------------------------------------------------------------------------------------------------------
+---
# **Expense Features**
+
An **expense** is a single purchase that you want to track. Each expense has a _name_, _price_, _category_, and _date_. With FastTrack, you can easily duplicate an expense if you happen to make the same purchase multiple times, such as buying a coffee from CoolSpot every morning on your way to NUS.
Finally, there are **recurring expenses**. These are expenses that are charged on a regular basis, such as a monthly subscription to Netflix or an annual Heroku subscription. Instead of manually creating an expense every time the payment is due, you can set up a recurring expense in FastTrack.
Simply specify the start date, interval (daily, weekly, monthly, yearly), and end date (if applicable), and FastTrack will automatically generate the expenses for you.
-### Command Summary
-#### One-time Expenses
+### Command Summary
-| Feature | Command Format | Examples |
-|----------------------------------------------------------------------|---------------------------------------------------------------------------------------|----------------------------------------------------------------|
-| [**List Expenses**](#listing-expenses-list) | `list [c/CATEGORY_NAME] [t/TIMEFRAME]` | `list c/Food t/month` |
-| [**Add Expense**](#adding-an-expense-add) | `add c/CATEGORY_NAME n/ITEM_NAME p/PRICE [d/DATE]` | `add c/Food p/20 n/Mac d/14/2/23` |
-| [**Delete Expense**](#deleting-an-expense-delete) | `delete INDEX` | `delete 1` |
-| [**Edit Expense**](#editing-an-expense-edexp) | `edexp INDEX [c/CATEGORY_NAME] [n/EXPENSE_NAME] [d/DATE] [p/PRICE]` | `edexp 1 c/Food n/Mac d/20/4/23 p/10` |
-| [**Find Expense**](#search-for-an-expense-by-keyword-find) | `find KEYWORD [MORE_KEYWORDS]` | `find KFC chicken` |
+#### One-time Expenses
+| Feature | Command Format | Examples |
+| ---------------------------------------------------------- | ------------------------------------------------------------------- | ------------------------------------- |
+| [**List Expenses**](#listing-expenses-list) | `list [c/CATEGORY_NAME] [t/TIMEFRAME]` | `list c/Food t/month` |
+| [**Add Expense**](#adding-an-expense-add) | `add c/CATEGORY_NAME n/ITEM_NAME p/PRICE [d/DATE]` | `add c/Food p/20 n/Mac d/14/2/23` |
+| [**Delete Expense**](#deleting-an-expense-delete) | `delete INDEX` | `delete 1` |
+| [**Edit Expense**](#editing-an-expense-edexp) | `edexp INDEX [c/CATEGORY_NAME] [n/EXPENSE_NAME] [d/DATE] [p/PRICE]` | `edexp 1 c/Food n/Mac d/20/4/23 p/10` |
+| [**Find Expense**](#search-for-an-expense-by-keyword-find) | `find KEYWORD [MORE_KEYWORDS]` | `find KFC chicken` |
#### Recurring Expenses
+
| Feature | Command Format | Example |
-|----------------------------------------------------------------------|---------------------------------------------------------------------------------------|----------------------------------------------------------------|
+| -------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| [**List Recurring Expense**](#listing-recurring-expenses-lrec) | `lrec` | `lrec` |
| [**Add Recurring Expense**](#adding-a-recurring-expense-addrec) | `addrec c/CATEGORY_NAME n/ITEM_NAME p/PRICE t/INTERVAL sd/START_DATE [ed/END_DATE]` | `addrec c/Shows n/Netflix p/10 t/month sd/10/3/23 ed/10/03/24` |
| [**Delete Recurring Expense**](#deleting-a-recurring-expense-delrec) | `delrec INDEX` | `delrec 1` |
| [**Edit Recurring Expense**](#editing-a-recurring-expense-edrec) | `edrec INDEX [c/CATEGORY_NAME] [n/EXPENSE_NAME] [p/PRICE] [t/INTERVAL] [ed/END_DATE]` | `edrec 1 c/Show n/Disney Plus p/2 t/week ed/10/5/24` |
---------------------------------------------------------------------------------------------------------------------
+---
## **One-Time Expenses**
---------------------------------------------------------------------------------------------------------------------
+---
## **Listing expenses** `list`
@@ -439,7 +454,7 @@ If you don't specify any filters, the expense list will show all your expenses b
Format: `list [c/CATEGORY_NAME] [t/TIMEFRAME] [r/RECUR_PERIOD]`
| Parameter | Description |
-|-----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CATEGORY_NAME` | The name of the category of which expenses are classed under.
This parameter is optional. |
| `TIMEFRAME` | The timeframe of which expenses were added.
The timeframes available are:
1. `week` (alias: `w`)
2. `month` (alias: `m`)
3. `year` (alias: `y`)
This parameter is optional. |
@@ -451,22 +466,25 @@ A `TIMEFRAME` allows you to set a specific interval to filter your expenses. `t/
### Examples
-* `list`
-* `list c/Groceries t/week`
-* `list c/Entertainment t/month`
-* `list c/Food`
-* `list t/w`
-* `list c/Entertainment t/year`
+
+- `list`
+- `list c/Groceries t/week`
+- `list c/Entertainment t/month`
+- `list c/Food`
+- `list t/w`
+- `list c/Entertainment t/year`
### Demonstration
### **List Expenses by Category**
+
1. Enter the command `list c/drink` into the command box
2. FastTrack displays the expenses under the category `Drink` with the confirmation message `2 expenses listed`. The number of expenses may differ for every user.
![FastTrack list1](images/demo/expense/list1.png)
### **List All Expenses**
+
1. Enter the command `list` into the command box
2. FastTrack displays all expenses with the confirmation message `5 expenses listed`. The number of expenses may differ for every user.
@@ -482,10 +500,10 @@ A `TIMEFRAME` allows you to set a specific interval to filter your expenses. `t/
**:information_source: Using both `CATEGORY_NAME` and `TIMEFRAME` filters:**
-* Using both the category and timeframe filters will only display expenses that satisfy both the filter conditions.
- e.g. in `list c/food t/week`, only expenses with both the category name "Food" and date falling within the current week will be displayed.
-
+- Using both the category and timeframe filters will only display expenses that satisfy both the filter conditions.
+e.g. in `list c/food t/week`, only expenses with both the category name "Food" and date falling within the current week will be displayed.
+
## **Adding an expense** `add`
@@ -494,18 +512,19 @@ Adds a new one-time expense to FastTrack.
Format: `add c/CATEGORY_NAME n/ITEM_NAME p/PRICE [d/DATE]`
| Parameter | Description |
-|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `CATEGORY_NAME` | The category which the expense should be classified under.
If there is no such category in FastTrack, a new category will be created with the specified category name. |
| `ITEM_NAME` | Name of the expense being added. |
| `PRICE` | The price of the expense being added.
The specified price should be a number, e.g. 4, 4.50. |
| `DATE` | The date of the expense being added.
The date format should be d/m/yyyy.
This is an optional input, and if left unspecified, the date of the expense will be set to the **current date** by default. |
-
### Examples
-* `add c/groceries n/milk p/4.50 `
-* `add c/entertainment p/20 n/movie night d/14/2/23`
+
+- `add c/groceries n/milk p/4.50 `
+- `add c/entertainment p/20 n/movie night d/14/2/23`
### Demonstration
+
1. Enter the command `add c/groceries n/milk p/4.50` into the command box
2. FastTrack adds the new expense under the new category `Groceries` with the confirmation message `New expense added: Name: milk, Amount: $4.5, Date: 2023-04-07, Category: groceries`.
@@ -515,8 +534,6 @@ Format: `add c/CATEGORY_NAME n/ITEM_NAME p/PRICE [d/DATE]`
![FastTrack add2](images/demo/expense/add2.png)
-
-
## **Deleting an expense** `delete`
Deletes an expense at the specified `INDEX` in the expense list.
@@ -524,19 +541,20 @@ Deletes an expense at the specified `INDEX` in the expense list.
Format: `delete INDEX`
| Parameter | Description |
-|-----------|-------------------------------------------------------------------------------------------------------------------|
+| --------- | ----------------------------------------------------------------------------------------------------------------- |
| `INDEX` | The index number shown in the displayed categories list.
It must be a positive integer i.e. 1, 2, 3, ... |
### Examples
-* `list` followed by `delete 2` deletes the second expense in the expense list
-* `find movie` followed by `delete 1` deletes the first expense in the results of the `find` command
+
+- `list` followed by `delete 2` deletes the second expense in the expense list
+- `find movie` followed by `delete 1` deletes the first expense in the results of the `find` command
### Demonstration
+
1. Type the command `list` to switch to the **Expense Display**
2. Enter the command `delete 2` into the command box
3. FastTrack deletes the second expense in the expense list with the confirmation message `Deleted expense: Name: milk, Amount: $4.5, Date: 2023-04-07, Category: groceries`.
-
## **Editing an expense** `edexp`
Edits the expense at the specified `INDEX` in the expense list.
@@ -547,7 +565,7 @@ Every parameter except for `INDEX` is optional by themselves, but **at least** o
to `INDEX`, otherwise the command will not be executed.
| Parameter | Description |
-|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
+| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `INDEX` | The index of the expense to be edited.
It must be a positive integer i.e. 1, 2, 3, ... |
| `CATEGORY_NAME` | The new category name of the expense to be changed to.
This parameter is optional. |
| `EXPENSE_NAME` | The new expense name of the expense to be changed to.
This parameter is optional. |
@@ -555,11 +573,12 @@ to `INDEX`, otherwise the command will not be executed.
| `PRICE` | The new price of the expense to be changed to.
The specified price should be a number, e.g. 4, 4.50.
This parameter is optional. |
### Examples
-* `edexp 1 c/groceries` changes the category of the first expense in the expense tracker
-* `edexp 2 p/20 n/movie night` changes the price and name of the second expense in the expense tracker
+- `edexp 1 c/groceries` changes the category of the first expense in the expense tracker
+- `edexp 2 p/20 n/movie night` changes the price and name of the second expense in the expense tracker
### Demonstration
+
1. Type the command `list` to switch to the **Expense Display**
2. Enter the command `edexp 2 p/20 n/movie night c/entertainment`
3. FastTrack edits the second expense in the expense list with the confirmation message `Edited expense: Name: movie night, Amount: $20.0, Date: 2023-04-03, Category: entertaiment`.
@@ -573,25 +592,28 @@ Find expenses whose names contain any of the given keywords.
Format: `find KEYWORD [MORE_KEYWORDS]`
-* The search is case-insensitive. e.g. `dinner` will match `Dinner`
-* The order of the keywords does not matter. e.g. `ramen Dinner` will match `Dinner ramen`
-* Only the name of the expense is searched
-* Only full words will be matched e.g. `dinn` will not match `dinner`
-* Expenses matching at least one keyword will be returned
+- The search is case-insensitive. e.g. `dinner` will match `Dinner`
+- The order of the keywords does not matter. e.g. `ramen Dinner` will match `Dinner ramen`
+- Only the name of the expense is searched
+- Only full words will be matched e.g. `dinn` will not match `dinner`
+- Expenses matching at least one keyword will be returned
e.g. `movie dinner` will return `dinner with Alex`, `movie with friends`
### Examples
Suppose you have 3 expenses logged:
+
```
Date: 2023-03-02, Category: Food, Name: McDonald's, Price: $7.50
Date: 2023-03-02, Category: Food, Name: KFC, Price: $6.00
Date: 2023-03-03, Category: Groceries, Name: Milk, Price: $4.00
```
-* `find kfc milk` returns `Milk` and `KFC`
-* `find mcdonald's` returns `McDonald's`
+
+- `find kfc milk` returns `Milk` and `KFC`
+- `find mcdonald's` returns `McDonald's`
### Demonstration
+
1. Enter the command `find movie` into the command box to find expenses with the keyword `movie`
2. FastTrack filters the expense list to show only the expenses matching the given keyword, with the confirmation message `Edited expense: Name: movie night, Amount: $20.0, Date: 2023-04-03, Category: entertaiment`.
@@ -601,11 +623,11 @@ Date: 2023-03-03, Category: Groceries, Name: Milk, Price: $4.00
Back to Top
+---
---------------------------------------------------------------------------------------------------------------------
## **Recurring Expenses**
---------------------------------------------------------------------------------------------------------------------
+---
## **Listing Recurring Expenses** `lrec`
@@ -614,6 +636,7 @@ Displays the list of recurring expenses in FastTrack.
Format: `lrec`
### Demonstration
+
1. Type `lrec` into the command box
2. FastTrack displays the list of recurring expenses with the confirmation message `Listed all recurring expenses`
@@ -626,7 +649,7 @@ Adds a recurring expense to FastTrack.
Format: `addrec c/CATEGORY_NAME n/ITEM_NAME p/PRICE t/INTERVAL sd/START_DATE [ed/END_DATE]`
| Parameter | Description |
-|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CATEGORY_NAME` | The category which the recurring expense should be classified under.
If there is no such category, a new category will be created with the specified category name. |
| `ITEM_NAME` | Name of the recurring expense being added. |
| `PRICE` | The price of the recurring expense being added.
The specified price should be a number, e.g. 4, 4.50. |
@@ -639,38 +662,37 @@ Format: `addrec c/CATEGORY_NAME n/ITEM_NAME p/PRICE t/INTERVAL sd/START_DATE [ed
**:information_source: Info**
Note that once a recurring expense is added, it automatically adds a series of expenses to the expense list at the specified interval until the `END_DATE`. If an `END_DATE` is not yet specified, the expenses will be added up to the current date.
-
+
:bulb: Tip:
-FastTrack's recurring expense feature is designed to help you keep track of regular expenses that occur at a fixed interval, such as monthly subscription fees or cloud storage bills.
+FastTrack's recurring expense feature is designed to help you keep track of regular expenses that occur at a fixed interval, such as monthly subscription fees or cloud storage bills.
By setting up a recurring expense, you save precious time and effort by automating the process of adding these expenses to FastTrack.
-
-
**:exclamation: Caution**
-Avoid setting an `END_DATE` that is too far in the future or a `START_DATE` that is too far in the past. Setting a date range that spans a large number of years or generates a large number of expenses may cause FastTrack to become temporarily unresponsive.
+Avoid setting an `END_DATE` that is too far in the future or a `START_DATE` that is too far in the past. Setting a date range that spans a large number of years or generates a large number of expenses may cause FastTrack to become temporarily unresponsive.
For example, if the current date is `3/2/2023` and the `START_DATE` is set to `3/2/2000` with an `INTERVAL` of `day`, this will generate over 8,000 expenses and may cause performance issues.
-
+
### Examples
-* `addrec n/milk c/groceries p/4.50 sd/20/3/2023 t/month`
-* `addrec n/milk c/groceries p/4.50 sd/20/3/2023 ed/15/5/2023 t/w`
+
+- `addrec n/milk c/groceries p/4.50 sd/20/3/2023 t/month`
+- `addrec n/milk c/groceries p/4.50 sd/20/3/2023 ed/15/5/2023 t/w`
### Demonstration
+
1. Enter the command `addrec n/milk c/groceries p/4.50 sd/20/1/2023 t/week` to create a weekly recurring expense starting on 20/1/2023.
2. FastTrack creates the new recurring expense with the confirmation message `New recurring expense added: Recurring Expense: milk, Amount: 4.5, Category: groceries, Start Date: 2023-01-20, End Date: Ongoing, Recurring Expense Type: WEEKLY`
![FastTrack addrec1](images/demo/recurring_expense/addrec1.png)
-
3. Enter the command `list` to switch to the **Expense Display**. Notice that FastTrack has automatically added the weekly expenses in the expense list!
![FastTrack addrec2](images/demo/recurring_expense/addrec2.png)
@@ -682,25 +704,22 @@ Deletes an expense category at the specified `INDEX` in the recurring expense li
Format: `delrec INDEX`
| Parameter | Description |
-|-----------|--------------------------------------------------------------------------------------------------------------------------|
+| --------- | ------------------------------------------------------------------------------------------------------------------------ |
| `INDEX` | The index number shown in the displayed recurring expense list.
It must be a positive integer i.e. 1, 2, 3, ... |
-
-
:information_source: Automatic Deletion of Recurring Expenses:
If a recurring expense's end date has already passed, and the current date too is already past the end date, FastTrack automatically deletes the recurring expense the next time the application is started. This means you do not need to worry about manually deleting recurring expenses which are no longer applicable!
-
-
### Examples
-* `lrec` followed by `delrec 2` deletes the second recurring expense in the recurring expense list
-* `lrec` followed by `delrec 1` deletes the first recurring expense in the recurring expense list
+- `lrec` followed by `delrec 2` deletes the second recurring expense in the recurring expense list
+- `lrec` followed by `delrec 1` deletes the first recurring expense in the recurring expense list
### Demonstration
+
1. Enter the command `lrec` to switch to the **Recurring Expense Display**
2. Enter the command `delrec 2`
3. FastTrack deletes the second recurring expense in the recurring expense list with the confirmation message `Deleted recurring expense: Recurring Expense: Netflix, Amount: 16, Category: entertainment, Start Date: 2023-01-01, End Date: Ongoing, Recurring Expense Type: MONTHLY`.
@@ -717,7 +736,7 @@ Every parameter except for `INDEX` is optional by themselves, but **at least** o
to `INDEX`, otherwise the command will not go through.
| Parameter | Description |
-|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `INDEX` | The index of the recurring expense to be edited.
It must be a positive integer i.e. 1, 2, 3, ... |
| `CATEGORY_NAME` | The new category name of the recurring expense to be changed to.
This parameter is optional. |
| `EXPENSE_NAME` | The new expense name of the recurring expense to be changed to.
This parameter is optional. |
@@ -725,21 +744,23 @@ to `INDEX`, otherwise the command will not go through.
| `INTERVAL` | The new recurrence period of the expense to be changed.
The timeframes available are:
1. `day`
2. `week`
3. `month`
4. `year`
This parameter is optional. |
| `END_DATE` | The new ending date of recurring expense.
The date format should be d/m/yyyy.
This parameter is optional. |
-
**:exclamation: Caution**
-If you want to stop a recurring expense before its intended `END_DATE`, make sure to terminate it before the current date.
+If you want to stop a recurring expense before its intended `END_DATE`, make sure to terminate it before the current date.
If you edit the recurring expense to end before the current date, this only prevents new expenses from being added, but expenses that were previously generated will still exist in FastTrack.
+
### Examples
-* `edrec 1 c/groceries t/week` updates the category and recurrence period first recurring expense in the expense tracker.
-* `edrec 2 p/4.50 ed/15/5/2023` updates the price and ending date of the second recurring expense in the expense tracker.
+
+- `edrec 1 c/groceries t/week` updates the category and recurrence period first recurring expense in the expense tracker.
+- `edrec 2 p/4.50 ed/15/5/2023` updates the price and ending date of the second recurring expense in the expense tracker.
### Demonstration
+
1. Enter the command `lrec` to switch to the **Recurring Expense Display**
-2. Say you have upgraded to a Netflix yearly subscription plan - enter the command `edrec 2 p/200 t/year`
+2. Say you have upgraded to a Netflix yearly subscription plan - enter the command `edrec 2 p/200 t/year`
3. FastTrack edits the second recurring expense in the recurring expense list with the confirmation message `Edited recurring expense generator: Recurring Expense: Netflix, Amount: 200.0, Category: entertainment, Start Date: 2023-01-20, End Date: Ongoing, Recurring Expense Type: YEARLY`.
![FastTrack edrec1](images/demo/recurring_expense/edrec1.png)
@@ -749,28 +770,24 @@ If you edit the recurring expense to end before the current date, this only prev
Back to Top
-
---------------------------------------------------------------------------------------------------------------------
+---
# General Features
-
-### Command Summary
+### Command Summary
| Feature | Command Format | Examples |
-|-----------------------------------------------|----------------|--------------|
+| --------------------------------------------- | -------------- | ------------ |
| [**Set Budget**](#setting-a-budget-set) | `set p/AMOUNT` | `set p/1000` |
| [**Help**](#viewing-help-help) | `help` | `help` |
| [**Exit program**](#exiting-fasttrack-exit) | `exit` | `exit` |
| [**Clear data**](#clearing-all-entries-clear) | `CLEAR` | `CLEAR` |
-
-
---------------------------------------------------------------------------------------------------------------------
+---
## **Setting A Budget** `set`
-Sets a monthly budget for FastTrack. For first-time users of FastTrack, no budget is set and some expense statistics are not updated.
+Sets a monthly budget for FastTrack. For first-time users of FastTrack, no budget is set and some expense statistics are not updated.
In order to view all the expense statistics, you must first set a budget using this command.
@@ -779,10 +796,9 @@ FastTrack derives the weekly budget from this monthly budget by dividing the mon
Format `set p/AMOUNT`
| Parameter | Description |
-|-----------|------------------------------------------------------------------------------------------|
+| --------- | ---------------------------------------------------------------------------------------- |
| `AMOUNT` | The monthly budget amount to set. The specified budget should be a number, e.g. 4, 4.50. |
-
**:exclamation: Caution**
@@ -791,11 +807,12 @@ FastTrack does not allow setting a budget of $0
-
### Examples
-* `set p/500` sets the monthly budget of FastTrack to $500
+
+- `set p/500` sets the monthly budget of FastTrack to $500
### Demonstration
+
1. Enter the command `set p/500` to set the monthly budget of FastTrack to $500
2. FastTrack updates the monthly budget to $500 with the confirmation message `Monthly budget successfully set to $500.0`
@@ -822,20 +839,19 @@ Narrow down the list of suggested categories by typing the first few words of yo
-
**:exclamation: Caution**
-To use category autocompletion, make sure that `c/` is the last text you've entered into the command box.
-If there's any other text in front of `c/`, the autocompletion feature will be disabled.
-
+To use category autocompletion, make sure that `c/` is the last text you've entered into the command box.
+If there's any other text in front of `c/`, the autocompletion feature will be disabled.
+
### Demonstration
1. Enter `list c/` into the command box
2. A list of suggested categories appear in a popup above the command box
-3. Navigate into the suggestion list using the `UP` arrow key and press `ENTER` on the desired category `Transportation`.
+3. Navigate into the suggestion list using the `UP` arrow key and press `ENTER` on the desired category `Transportation`.
4. This autocompletes the category name
5. If you need to navigate out of the suggestion list, press the `DOWN` arrow key until the cursor returns to the command box
@@ -850,7 +866,6 @@ If there's any other text in front of `c/`, the autocompletion feature will be d
![FastTrack autocomplete_b1](images/demo/general/autocomplete_b1.png)
![FastTrack autocomplete_b2](images/demo/general/autocomplete_b2.png)
-
## **Clearing all entries** `CLEAR`
Clears all entries from FastTrack. This command removes all stored expenses, recurring expenses and categories.
@@ -863,6 +878,7 @@ Format: `CLEAR`
This command will delete **all** the data stored in FastTrack apart from the stored monthly budget. To minimise the risk of accidentally using this command, we have made it such that the command only works when the `CLEAR` is fully uppercase.
Exercise caution before using this command.
+
### Demonstration
@@ -879,7 +895,6 @@ This command closes FastTrack and saves the data to the `fastTrack.json` file lo
Format: `exit`
-
## **Viewing help** `help`
Shows a message explaining how to access the help page, as well as a quick rundown of what commands can be used.
@@ -894,15 +909,15 @@ Format: `help`
# Expense Statistics Feature
-FastTrack provides you with real-time statistics on your spending to help you keep track of your monthly budget.
+FastTrack provides you with real-time statistics on your spending to help you keep track of your monthly budget.
Here are the types of statistics displayed and what they mean.
![FastTrack expense_statistic](images/demo/general/summary.png)
-
## Monthly spending statistic
-This statistic represents the total amount of money you have spent in the current month.
-It includes all expenses recorded in the current month.
+
+This statistic represents the total amount of money you have spent in the current month.
+It includes all expenses recorded in the current month.
For example, if the current month is March, this statistic shows the total amount of money spent in March.
@@ -913,68 +928,64 @@ It gives you an idea of how much money you have left to spend for the rest of th
## Monthly percentage change statistic
-This statistic represents the percentage increase or decrease in your monthly spending relative to the previous month.
+This statistic represents the percentage increase or decrease in your monthly spending relative to the previous month.
The indicator colour is red if it is a percentage increase and green if it is a percentage decrease.
-For example, if you spent $500 last month and $750 this month, the monthly percentage change indicator would be `+50.00%` and be displayed in a red colour.
+For example, if you spent $500 last month and $750 this month, the monthly percentage change indicator would be `+50.00%` and be displayed in a red colour.
If you spent $750 last month and $500 this month, the monthly percentage change would be `-33.30%` and be displayed in a green color.
## Weekly spending statistic
-This statistic represents the total amount of money you have spent in the current week, starting from Monday to Sunday.
+This statistic represents the total amount of money you have spent in the current week, starting from Monday to Sunday.
This gives you an idea of how much money you are spending on a weekly basis.
## Weekly remaining statistic
-This statistic represents the amount of money you have left from your weekly budget.
+This statistic represents the amount of money you have left from your weekly budget.
Your weekly budget is the value of your monthly budget divided by four. This gives you an idea of how much money you have left to spend for the rest of the week.
:exclamation: **Caution:**
-Please take note that this value should be treated as a rough guide.
-Even if you have exceeded your previous week's budget, this statistic will show that you have more remaining, as the weekly budget is fixed based on the monthly budget.
+Please take note that this value should be treated as a rough guide.
+Even if you have exceeded your previous week's budget, this statistic will show that you have more remaining, as the weekly budget is fixed based on the monthly budget.
Therefore, it is important to use this value as an estimate and not solely rely on it for your spending decisions!
-
## Weekly percentage change statistic
This statistic represents the percentage increase or decrease in your weekly spending relative to the previous week.
The indicator colour is red if it is a percentage increase and green if it is a percentage decrease.
-For example, if you spent $500 last week and $750 this week, the weekly percentage change indicator would be `+50.00%` and be displayed in a red colour.
+For example, if you spent $500 last week and $750 this week, the weekly percentage change indicator would be `+50.00%` and be displayed in a red colour.
If you spent $750 last week and $500 this week, the weekly percentage change would be `-33.30%`.
## Total spent statistic
-This statistic represents the total amount of money you have spent to date, starting from the first expense you recorded in FastTrack.
+This statistic represents the total amount of money you have spent to date, starting from the first expense you recorded in FastTrack.
This gives you an idea of how much money you have spent over the period of time from when you started tracking your expenses.
-
## Budget utilisation percentage statistic
-This statistic represents the percentage of your monthly budget that you have already utilised in the current month.
+This statistic represents the percentage of your monthly budget that you have already utilised in the current month.
-For example, if your monthly budget is $1000, and you have already spent $500, your budget utilised percentage would be `50%`.
+For example, if your monthly budget is $1000, and you have already spent $500, your budget utilised percentage would be `50%`.
This gives you an idea of how much of your monthly budget you have used up.
-
**:exclamation: Caution**
-Even if you have exceeded your budget, this statistic will reflect that you have fully utilised your budget, and will remain at `100%`.
+Even if you have exceeded your budget, this statistic will reflect that you have fully utilised your budget, and will remain at `100%`.
-
-
Back to Top
---------------------------------------------------------------------------------------------------------------------
+---
+
# Saving the data
All data in FastTrack are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
@@ -987,8 +998,7 @@ FastTrack's data are saved as a JSON file `[JAR file location]/data/fastTrack.js
If your changes to the data file makes its format invalid, FastTrack will discard all data and start with an empty data file at the next run.
-
---------------------------------------------------------------------------------------------------------------------
+---
# Frequently Asked Questions
@@ -1005,7 +1015,7 @@ If your changes to the data file makes its format invalid, FastTrack will discar
**A**: Try resizing the FastTrack window size by increasing its width until the full expense names are within view.
**Q**: Can I set reminders for recurring expenses in FastTrack?
-**A**: No, FastTrack does not currently have a built-in feature for setting reminders for recurring expenses.
+**A**: No, FastTrack does not currently have a built-in feature for setting reminders for recurring expenses.
However, you can use an external calendar or reminder app to keep track of recurring expenses.
**Q**: Does FastTrack integrate with payment systems like credit cards or PayPal?
@@ -1014,4 +1024,3 @@ However, you can use an external calendar or reminder app to keep track of recur
Back to Top
-
diff --git a/docs/team/shirsho-12.md b/docs/team/shirsho-12.md
index d3a4ac7862e..180ef3726f7 100644
--- a/docs/team/shirsho-12.md
+++ b/docs/team/shirsho-12.md
@@ -11,54 +11,62 @@ Given below are my contributions to the project.
### Summary of Contributions
-* **New Feature**: `Recurring Expenses`: Created the recurring expense functionality
- * What it does: Creates recurring expenses for the user, i.e., expenses that occur at regular intervals. Theese expenses are automatically generated based on the user's input and current date. The user can also view all recurring expenses, delete recurring expenses, and mark recurring expenses as done.
- * Justification: This feature improves the product significantly because a large number of users would prefer to track their recurring expenses. This feature also allows the user to save time by not having to manually create recurring expenses.
- * Highlights: This enhancement affects existing commands and commands to be added in the future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands and the logic to handle the recurring expenses.
-
-* **New Feature**: `Category` and `Expense` models: Created the main Category and Expense models.
- * What it does: The Category model is used to store the different categories of expenses that the user can add. The Expense model is used to store the different expenses that the user can add.
- * Justification: This feature improves the product significantly because it allows the user to add expenses and categorize them. This feature also allows the user to save time by not having to manually create recurring expenses.
- * Highlights: This enhancement affects existing commands and commands to be added in the future. It required an in-depth analysis of design alternatives. The implementation was challenging as it required changes to existing commands and the logic to handle the recurring expenses.
-
-* **Code Contributions**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=shirsho-12&breakdown=true&sort=groupTitle&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
-
-* **Project Management**:
- * Managed releases `v1.1` - `v1.4` (3 releases) on GitHub
- * Managed issue creation and assignment, milestone creation, issue labelling, and issue closing
- * Enforced coding standards and code quality
- * Managed the project's [issue tracker](https://github.com/AY2223S2-CS2103T-W09-2/tp/issues)
- * Managed the project's [pull requests](https://github.com/AY2223S2-CS2103T-W09-2/tp/pulls)
- * Managed the project [repository](https://github.com/AY2223S2-CS2103T-W09-2/tp) with the help of Isaac,[@gitsac](https://github.com/gitsac/), and Nicholas, [@niceleejy](https://github.com/niceleejy/).
-
-**Enhancements implemented**:
- * Implemented the recurring expense generation functionality
- * Developed Expense Type enums for the different frequency types of recurring expenses
- * Implemented functionality to strip out additional whitespace in user input
-
-**Contributions to the UG:**
- * Added documentation for the Category and Expense models
- * Added documentation for the recurring expense functionality
-
-**Contributions to the DG:**
- * Created Activity Diagrams for all the commands
- * Created UML Diagrams for the Category and Expense models, Commands, and the Parser
- * Designed the architecture diagrams for the project
-
-**Contributions to team-based tasks:**
- * Managed the setting up of the project repository and test suite
- * Created test cases for multiple commands, models, and storage
- * Reviewed and merged multiple pull requests
- * Redesigned the architecture of the Command classes to make it more extensible
- * Fixed a number of bugs in the parser and storage classes
- * Fixed test cases that were failing due to changes in the codebase
- * Refactored the codebase to improve code quality
-
-**Review/Mentoring Contributions:**
- * Reviewed and provided feedback on multiple pull requests
- * Reviewed and provided feedback on multiple issues
- * Reviewed and provided feedback on multiple code quality issues
-
-**Contributions beyond team project:**
- * Reported bugs and suggestions for improvement for other team projects
- * Participated in the discussion forum and helped other students with their queries
+- **New Feature**: `Recurring Expenses`: Created the recurring expense functionality
+
+ - What it does: Creates recurring expenses for the user, i.e., expenses that occur at regular intervals. Theese expenses are automatically generated based on the user's input and current date. The user can also view all recurring expenses, delete recurring expenses, and mark recurring expenses as done.
+ - Justification: This feature improves the product significantly because a large number of users would prefer to track their recurring expenses. This feature also allows the user to save time by not having to manually create recurring expenses.
+ - Highlights: This enhancement affects existing commands and commands to be added in the future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands and the logic to handle the recurring expenses.
+
+- **New Feature**: `Category` and `Expense` models: Created the main Category and Expense models.
+
+ - What it does: The Category model is used to store the different categories of expenses that the user can add. The Expense model is used to store the different expenses that the user can add.
+ - Justification: This feature improves the product significantly because it allows the user to add expenses and categorize them. This feature also allows the user to save time by not having to manually create recurring expenses.
+ - Highlights: This enhancement affects existing commands and commands to be added in the future. It required an in-depth analysis of design alternatives. The implementation was challenging as it required changes to existing commands and the logic to handle the recurring expenses.
+
+- **Code Contributions**: [RepoSense link](https://nus-cs2103-ay2223s2.github.io/tp-dashboard/?search=shirsho-12&breakdown=true&sort=groupTitle&sortWithin=title&since=2023-02-17&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other)
+
+- **Project Management**:
+ - Managed releases `v1.1` - `v1.4` (3 releases) on GitHub
+ - Managed issue creation and assignment, milestone creation, issue labelling, and issue closing
+ - Enforced coding standards and code quality
+ - Managed the project's [issue tracker](https://github.com/AY2223S2-CS2103T-W09-2/tp/issues)
+ - Managed the project's [pull requests](https://github.com/AY2223S2-CS2103T-W09-2/tp/pulls)
+ - Managed the project [repository](https://github.com/AY2223S2-CS2103T-W09-2/tp) with the help of Isaac,[@gitsac](https://github.com/gitsac/), and Nicholas, [@niceleejy](https://github.com/niceleejy/).
+
+**Enhancements implemented**:
+
+- Implemented the recurring expense generation functionality
+- Developed Expense Type enums for the different frequency types of recurring expenses
+- Implemented functionality to strip out additional whitespace in user input
+
+**Contributions to the UG:**
+
+- Added documentation for the Category and Expense models
+- Added documentation for the recurring expense functionality
+
+**Contributions to the DG:**
+
+- Created Activity Diagrams for all the commands
+- Created UML Diagrams for the Category and Expense models, Commands, and the Parser
+- Designed the architecture diagrams for the project
+
+**Contributions to team-based tasks:**
+
+- Managed the setting up of the project repository and test suite
+- Created test cases for multiple commands, models, and storage
+- Reviewed and merged multiple pull requests
+- Redesigned the architecture of the Command classes to make it more extensible
+- Fixed a number of bugs in the parser and storage classes
+- Fixed test cases that were failing due to changes in the codebase
+- Refactored the codebase to improve code quality
+
+**Review/Mentoring Contributions:**
+
+- Reviewed and provided feedback on multiple pull requests
+- Reviewed and provided feedback on multiple issues
+- Reviewed and provided feedback on multiple code quality issues
+
+**Contributions beyond team project:**
+
+- Reported bugs and suggestions for improvement for other team projects
+- Participated in the discussion forum and helped other students with their queries