By: Team AY1920S2-CS2103-T09-3
Since: Feb 2020
Licence: MIT
- 1. Introduction
- 2. Setting up
- 3. Design
- 4. Implementation
- 4.1. Adding transactions (BenjaminFheng)
- 4.2. Editing transactions (BenjaminFheng)
- 4.3. Setting budgets (Hubert Halim)
- 4.4. Command History navigation (Hubert Halim)
- 4.5. Filtering Transactions (Pang Kim Jin)
- 4.6. Chart Analytics (toggleview Command and Bar Graphs by Choi Min Suk + Pie Chart by Pang Kim Jin)
- 4.7. Export (Choi Min Suk)
- 4.8. [Proposed] Data Encryption (Pang Kim Jin)
- 4.9. [Proposed] ExpenseLa Monthly Statement (Pang Kim Jin)
- 4.10. Logging
- 4.11. Configuration
- 5. Documentation
- 6. Testing
- 7. Dev Ops
- Appendix A: Product Scope
- Appendix B: User Stories
- Appendix C: Use Cases (Hubert Halim)
- Appendix D: Non Functional Requirements (Pang Kim Jin)
- Appendix E: Glossary
- Appendix F: Product Survey
- Appendix G: Instructions for Manual Testing (Pang Kim Jin)
- G.1. Launch and Shutdown
- G.2. Setting a monthly budget
- G.3. Resetting total balance
- G.4. Adding a transaction
- G.5. Filtering transactions
- G.6. Deleting a transaction
- G.7. Editing a transaction
- G.8. Analysis of transactions
- G.9. Finding transactions
- G.10. Listing all transactions
- G.11. Clearing all recurring transactions
- G.12. Exporting transaction data to csv
- G.13. Importing transaction data from csv
- G.14. Clearing all data
- G.15. Showing help window
- G.16. Saving data
- Appendix H: Effort
1. Introduction
ExpenseLa is an application for NUS students to be able to track their spending and also gain insights to their monthly spending through data analytics. The data analytics portion of ExpenseLa aims to aid students in viewing and comparing their monthly expenditure on items of different categories such as shopping and groceries, as well as keeping track of their monthly budgets. ExpenseLa records daily incoming and outgoing transactions and constantly keeps track of the user’s budget, income and spending. ExpenseLa is optimised for users who prefer to work with a Command Line Interface (CLI) which works in parallel with a Graphical User Interface (GUI). It is an easy, insight-driven application that hopes to help students keep better track of their expenses.
1.1. Purpose
This developer guide aims to communicate the design and architecture of the software implementation to developers working on ExpenseLa. It also includes future implementations to give developers an idea of the direction that ExpenseLa intends to take on both current and future features. A developer should be able to understand the design, architecture and future goals of ExpenseLa upon reading this guide.
1.2. Scope
This developer guide specifies the technical and non-technical details of ExpenseLa. The technical aspects include the design and architecture of the software and the non-technical aspects include the user stories, use cases and non-functional requirements.
2. Setting up
Pleas refer to the guide here.
3. Design
3.1. Architecture (Pang Kim Jin)
In this section, we will be introducing the individual components that form ExpenseLa using various diagrams.
The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.
The .puml files used to create diagrams in this document can be found in the diagrams folder.
Refer to the Using PlantUML guide to learn how to create and edit diagrams.
|
-
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 method where necessary.
Commons
represents a collection of classes used by multiple other components.
The following class plays an important role at the architecture level:
-
LogsCenter
: Used by many classes to write log messages to the App’s log file.
The rest of the App consists of four components.
Each of the four components
-
Defines its API in an
interface
with the same name as the Component. -
Exposes its functionality using a
{Component Name}Manager
class.
For example, the Logic
component (see the class diagram given below) defines it’s API in the Logic.java
interface and exposes its functionality using the LogicManager.java
class.
Logic
ComponentHow 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
.
delete 1
commandThe sections below give more details of each component.
3.2. UI component (Choi Min Suk)
API : Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, TransactionListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class.
The UI
component uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml
files that are in the src/main/resources/view
folder. For example, the layout of the MainWindow
is specified in MainWindow.fxml
The UI
component does the following actions:
-
Executes user commands using the
Logic
component. -
Listens for changes to
Model
data so that the UI can be updated with the modified data.
3.3. Logic component (Hubert Halim)
API :
Logic.java
Logic is an interface which LogicManager
implements, allowing access to the API. The following items are examples on how the LogicManager class can be interacted with:
-
Logic
uses theExpenseLaParser
class to parse the user command. -
This results in a
Command
object which is executed by theLogicManager
. -
The command execution can affect the
Model
(e.g. adding aTransaction
). -
The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
. -
In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, such as displaying help to the user.
Given below is the Sequence Diagram for interactions within the Logic
component for the execute("delete 1")
API call.
delete 1
Command
The lifeline for DeleteCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
|
clear
Command3.4. Model component (Pang Kim Jin)
API : Model.java
The Model
,
-
stores an ArrayList which contains the user’s command history.
-
stores a
UserPref
object that represents the user’s preferences. -
stores the
ExpenseLa
data. -
stores the
GlobalData
which contains the recurring budget, transactions, total balance, and last updated date. -
stores a
MonthlyData
object which contains budget, expense, and income information set by the user. -
stores a
ToggleView
object that represents the nature of transaction information displayed to the user. -
stores a
Filter
object which represents the filter on the transactions as set by the user -
stores
TransactionList
which contains the list of all transactions -
exposes an unmodifiable
ObservableList<Transaction>
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 three components.
3.5. Storage component (BenjaminFheng)
API : Storage.java
The Storage
component,
-
can save
UserPref
objects in json format and read it back. -
can save the
ExpenseLa
data in json format and read it back. -
can save
GlobalData
data in json format and read it back.
3.6. Common classes
Classes used by multiple components are in the seedu.ExpenseLa.commons
package.
4. Implementation
This section describes some noteworthy details on how certain features are implemented.
4.1. Adding transactions (BenjaminFheng)
We allow users to add Expense/Income transactions into ExpenseLa which denotes a positive or negative inflow of money. This section shows how we handle these requests from the user.
4.1.1. Implementation
We store every single Transaction
added by the user into an ObservableList<Transaction>
, which is a list object in TransactionList
. We used an ObservableList
to easily reflect changes to the list by any other component of ExpenseLa using the list.
These are the ways of implementing either a positive or negative Transaction:
-
Adding an expense (negative transaction): add
-
Adding an income (positive transaction): add i/
These two commands will indicate whether the transaction is positive or negative.
Indicating whether it is a recurring transaction or not will depend if rc/ is present in the input
When inserting a new Expense/Income, the AddCommandParser
will determine which object to initialise depending on whether the "i/" CLI syntax is present. Afterwhich, the AddCommandParser will parse the values of the transaction depending on whether the respective CLI Syntaxes are present.
-
Name
is parsed byAddCommandParser#parseName(ArgumentMultimap)
, CLI Syntax is n/. -
Amount
is parsed byAddCommandParser#parseAmount(ArgumentMultimap)
, CLI Syntax is a/. -
Date
is parsed byAddCommandParser#parseDate(ArgumentMultimap)
, CLI Syntax is d/. -
Category
is parsed byAddCommandParser#parseCategory(ArgumentMultimap)
, CLI Syntax is c/. -
Remark
is parsed byAddCommandParser#parseRemark(ArgumentMultimap)
, CLI Syntax is r/. -
CLI Syntax "rc/" will set the transaction to be a monthly recurring transaction.
ArgumentMultimap is a class that stores all the parsed parameters taken from the user input.
|
Category has a set enum list of values FOOD, SHOPPING, TRANSPORT, GROCERIES, HEALTH, RECREATION, MISC, UTILITIES, INCOME.
|
The following sequence diagram shows how the add transaction operation works:
Figure 9. Sequence diagram of how adding a new Transaction
is processed depending on syntax.
Transaction
are normally instantiated by AddCommandParser#parse(String args)
, which attempts to parse the various parameters supplied in args and return either a positive or negative Transaction
. The following conditions will cause a ParseException
to be thrown by the parser:
-
Missing parameters
-
Incorrect syntax (i.e. missing prefix if required)
-
Illegal values in parameters (i.e. special character and symbols entered for an integer only field)
-
Multiple occurence of parameters which only expects single entry
Incorrect user input will display ParseException message.
|
We will demonstrate how a Transaction
is added into ExpenseLa
below:
Step 1. The user executes the command add n/Pizza a/20.5 d/2020-02-02 to insert a negative transaction with its Name
set to "Pizza", its Amount
set to "20.50" and the Date
set to 02 Feb 2020. The input is now checked and an attempt to parse each parameter occurs:
Category is set to default category MISC
|
Since the user input is valid, the Transaction
is successfully created and inserted into the transaction list. The transaction list now contains 1 Transaction
object.
Step 2. The user executes add i/ n/Salary a/3000 d/2020-02-03 r/Monthly Salary c/income rc/ to indicate his monthly pay, to insert a positive Transaction
.
"rc/" CLI Syntax will set the transaction to be a recurring transaction. |
Again, since the input is valid, the positive Transaction
is successfully added into the transaction list. The transaction list
now contains 2 Transaction
objects.
The following activity diagram summarizes what happens when the user executes a command to add a new Transaction
:
Transaction
into the transaction list.4.1.2. Design considerations
There are many different ways to implement how a transaction is added into ExpenseLa
. In this section, we will justify why we chose to implement it the way we did.
Aspect: Differentiating between positive and negative transactions.
-
Alternative 1: (current choice): Adding a simple "i/" syntax in the input to differentiate between positive and negative transactions
-
Pros: Increases the speed and simplicity of adding a positive or negative transaction. Updating balance in
MonthlyData
information only requires an iteration through all the transaction amounts in transaction list for calculation. -
Cons: Transactions may not be easily distinguishable as positive or negative transactions on first sight, and may only be distinguished when the transaction amount is inspected. This may cause slower processing times when extracting all only positive or only negative transactions.
-
-
Alternative 2: Having separate classes for positive and negative** transactions.
-
Pros: Maintains an intuitive design:
NegativeTransaction
deducts money andPositiveTransaction
increases money. -
Cons: May incur significant overhead since it is likely that both
NegativeTransaction
andPositiveTransaction
will have very similar methods.
-
Alternative 1 was chosen because we want the application to be as simple and quick as possible to indicate positive and negative transactions. We focused on separating the data between months so that the analytics function could calculate data faster.
Aspect: Managing how RecurringTransactions
are handled.
-
Alternative 1: (current choice): Adding a simple "rc/" syntax in the input to differentiate between recurring and non-recurring transactions. "rc/" is quickly parsed by the AddCommandParser and the transaction is labelled as recurring, which is added to RecurringTransactionsList before the command is added to the TransactionsList in Model.
-
Pros: "rc/" is quick and intuitive to indicate during input. Almost no hassle to add the information to RecurringTransactionsList.
-
Cons: Inability for existing transactions to set as recurring transactions.
-
-
Alternative 2: Create a separate command to indicate which transactions are recurring and the range of when it recurs.
-
Pros: Transactions are more customizable for users. Users are able to set recurring transactions to repeat over required months.
-
Cons: Users may easily lose track of the transactions that are recurring which will affect their monthly budgeting.
-
Alternative 1 was chosen because we want to again keep the recurring transactions intuitive and simple. Users can clear their recurring transaction lists whenever they become invalid, and input the new recurring transactions whenever there are changes
4.2. Editing transactions (BenjaminFheng)
The edit functionality modifies details of a specified Transaction
in the existing list and saves
modifications to the external storage file.
4.2.1. Implementation
Edit mechanism utilizes Logic
operations with the EditCommand
class in place of
Command
, and a unique EditCommandParser
class. The following methods are the implementation for
edit operations:
-
EditCommandParser#parse()
- Parses the user’s input via the index of the transaction and creates anEditCommand
to execute the command. -
EditCommand#execute()
- Modifies theTransaction
inModel
with new details and returns aCommandResult
. -
TransactionList#setTransaction(Transaction target, Transaction editedTransaction)
- Sets the modifiedTransaction
to its correct position in the existingTransactionList
.
Command example: edit 1 n/[NAME] a/[AMOUNT] r/[REMARK] will edit the name, amount and remark of transaction of index 1 with the respective inputs.
Below is an example usage scenario for editing a transaction and explanation of how the edit mechanism behaves at each step:
Step 1. The user starts up the application with an initial list loaded from a sample transaction list.
Step 2. The user inputs edit 1 n/Laksa Noodle a/6.00 to edit the transaction (with index 1) name to "Laksa Noodle" and value
to "6". Input is parsed by EditCommandParser#parse()
which creates an EditCommand
.
Step 3. EditCommand#execute()
creates a new transaction that reflects the changes and gets the index of current
transaction to be edited.
Step 4. EditCommand#execute()
replaces original transaction in the list with the eddited transaction.
The following activity diagram gives an overview of what ExpenseLa shows the user when executing edit command:
4.2.2. Design considerations
This subsection explores some alternative designs considered for certain aspects of the feature’s implementation.
Aspect: Modifying details of a transaction
-
Alternative 1 (current choice): Replace the values of the original transaction with new editd values.
-
Pros: Can easily check for inputs that result in no changes.
-
Cons: May incur overhead when creating new instance of
Transaction
.
-
-
Alternative 2: Modify the transaction directly using setter methods.
-
Pros: Easy to implement and efficient.
-
Cons: Modifying transactions violates the immutability principle, possibly resulting in bugs for UI or accessing modified transaction fields.
-
Alternative 1 chosen to maintain better coding practices and maintain immutability of transactions for the entire project.
Overhead of creating new Transaction
is insignificant due to relatively small object size.
Aspect: Edit transaction by getting it’s index or by unique transaction ID
-
Alternative 1 (current choice): Get the index of original transaction in the transactions list and edit its values in the list.
-
Pros: High certainty of obtaining the correct transaction, editing its values and replacing the same index in the transaction list.
-
Cons: If there are too many transactions in the list, it might be tedious to obtain the transaction index by scrolling.
-
-
Alternative 2: Iterate through the transactions list and edit the transaction with the correct transaction ID.
-
Pros: Virtually impossible to not be able to distinguish between similar transactions of different IDs.
-
Cons: Transactions ID would cause significant overhead when looking into each transaction for it’s ID.
-
4.3. Setting budgets (Hubert Halim)
We allow the user to maintain a Budget
for the current month and subsequent months. This section details how ExpenseLa
handles
requests made by the user who is trying to set a budget both for a one time and recurring budget. Budget
is contained inside
MonthlyData
object along with Expense
and Income
and application only has 1 MonthlyData
object for the current month.
object looks like:
If user decides to create a recurring budget, there’ll be additional step of updating the recurringBudget
variable in
GlobalData
. BudgetCommand
in addition to modifying Budget
in MonthlyData
, it will also modify recurringBudget
in GlobalData
.
4.3.1. Implementation
Whenever the user attempts to set a new Budget
, ExpenseLa
will create a new MonthlyData object with the given amount.
The application will then call ModelManager#setMonthlyData(MonthlyData toSet)
. During the creation of the new MonthlyData, the
Budget class will internally check if the budget amount is valid.
We will demonstrate what happens at the back-end whenever the user sets a budget:
Case 1. The user wishes to set their budget to $1500, non-recurring. They execute the command: budget b/1500.
The user’s entry is checked by BudgetCommandParser#parse()
and an attempt to parse each parameter occurs:
-
Budget
is parsed byParseUtil#parseBudget(ArgumentMultimap)
-
rc/
prefix does not exist, so it is not recurring
ArgumentMultimap is a class that stores all the parsed parameters taken from the user input.
|
Since the user input is valid, the Budget
is successfully created and inserted into a newly created MonthlyData
.
Case 2. The user made a typo when setting their budget. They execute the command budget b/1500.
The user’s entry is checked by BudgetCommandParser#parse()
and an attempt to parse each parameter occurs:
-
Budget
is parsed byParseUtil#parseBudget(ArgumentMultimap)
Budget
class then is attempted to be created with the parsed budget amount in the constructor. Internally
Budget will do a validity check using Regex and throw a ParseExection
since amount is not valid.
Case 3. The user wishes to set their budget to $1500, recurring. They execute the command: budget b/1500 rc/.
The user’s entry is checked by BudgetCommandParser#Parse()
and an attempt to parse each parameter occurs:
-
Budget
is parsed byParseUtil#parseBudget(ArgumentMultimap)
-
rc/
prefix exists, so it is recurring
Since the user input is valid, the Budget
is successfully created and inserted into a newly created MonthlyData
.
BudgetCommand will then modify GlobalData
in Model
by calling Logic#setGlobalData
. RecurringBudget
value in GlobalData
is now set to the new Budget
The sequence diagram below depicts what was just elaborated:
Budget
is setBudget
is set4.3.2. Design considerations
We have considered various ways as to how Budget
should be stored in ExpenseLa
. In this section, we will explain the
rationale on our course of actions.
Aspect: Make Budget
a part of a bigger class called MonthlyData
-
Alternative 1 (current choice):
Budget
is a part ofMonthlyData
and anyBudget
operations is throughMonthlyData
-
Pros: Easier to handle
Budget
together with otherMonthlyData
objects and all data inside is synchronised as it is handled by a single object. -
Cons: Overhead when modifying
Budget
as to maintain immutability, a newMonthlyData
object has to be created.
-
-
Alternative 2:
Budget
should be an independent class with a direct reference inExpenseLa
.-
Pros: More freedom and efficiency in doing modifications on
Budget
-
Cons: Need to maintain more references for all different objects.
-
Again, we went with alternative 1 because it is easier to view Budget
along with the other MonthlyData
components
as a collective. And easier to just handle 1 reference in ExpenseLa.
4.4. Command History navigation (Hubert Halim)
Users can navigate to previous commands by pressing the up or down button on the keyboard. Only successful commands are stored in the CommandHistory list and only a maximum of 50 commands can be stored at a time.
4.4.1. Implementation
Every time the user key in a command and press Enter, CommandBox#handleCommandEntered
method will be called.
The method will attempt to execute the command by calling CommandExecutor#execute
method. That method throws an error
if command is invalid. So if the command is valid, the CommandBox#handleCommandEntered
method will call
Logic#deleteCommandFromHistory
to delete the command if it exists in the current command history.
It will then call Logic#addToCommandhistory
to add the command to the command history as its latest entry.
Both commands for add and delete takes in an integer variable called offset
. This variable is maintained by CommandBox
and determines which command the user is currently at in the command history.
Offset starts from -1 indicating CommandBox
is empty and resets to -1 every time a successful command is entered
Command History
is an array list that resides in ModelManager
object. It can be accessed through Logic
by calling
The diagrams below depicts what was just elaborated:
4.4.2. Design considerations
We have considered various ways as to how to implement CommandHistory
to support navigation to previous commands
Since we need to capture keyboard events when user press the keyboard, we decided to implement the event listener and handler
in CommandBox
component as it is more convenient because when a keyboard event is captured, the app can straight away
modify the TextField
in CommandBox
. Since the event when user enter a command is also handled in CommandBox
and we only store successful commands in CommandHistory
, we wait for execution of the Command by CommandExecutor
,
if it is successful, the String for the command is added to, otherwise due to the error thrown and caught somewhere else, the
command is not stored.
4.5. Filtering Transactions (Pang Kim Jin)
The Filter
command allows the user to bring up a list of Transaction
, and filter it by either category, month,
or both at the same time. This is implemented by using a predicate for category and another predicate for month,
both of which inheriting from Predicate<Transaction>
to filter the Transaction
.
4.5.1. Implementation
FilterCommand
is instantiated by FilterCommandParser#parse(String args)
method, which parses the arguments supplied in the user
command to return a FilterCommand
object.
The below sequence diagram depicts the interactions within the Logic
component for the execute("filter c/FOOD m/2020-04") call:
The below scenario shows a typical usage of the filter feature:
Step 1: Upon application launch, the filter for all categories and the current month is automatically applied.
Step 2: User executes the command filter c/food m/2020-02
to bring up transactions in the category "FOOD" for the month
of February 2020. (Note: The command in the command line disappears upon hitting Enter, the command in the command line
is purely for illustration purposes).
Step 3: The FilterCommandParser#parse(String args)
parses the arguments.
Step 4: Since user input is correct and the arguments are parsed, a new FilterCommand
object is created by the
FilterCommandParser
.
Step 5: The FilterCommand
object will use a Predicate<Transaction>
based on the specified category, month, or both, to filter
the list of transactions.
Step 6: The list of filtered transactions is brought up. The filter category and month UI will also update accordingly to show the category and month that the transactions are filtered by.
The below activity diagram gives an overview of the command execution:
4.5.2. Design Considerations
Aspect: Using Predicate
to improve extendability of the Filter
feature in the future.
-
Alternative 1 (current choice): Create a new
Predicate<Transaction>
for each new filter type-
Pros: Greater flexibility can be provided for each filter type since different filter types have different requirements (e.g. Month vs Category)
-
Cons: Tedious to implement a new class for each new type of filter
-
-
Alternative 2: Use a single
Predicate<Transaction>
to filter for all filter types-
Pros: Easy to implement
-
Cons: Prone to being inflexible for extensions
-
We decided to go with Alternative 1 since the current filter feature supports increasing the number of filter types
- on top of the current category and month filters. Despite having a different Predicate
for each filter type, we use
a composed Predicate
comprising of both Predicate
s, making it much easier to support extensions to this feature.
4.5.3. Proposed Extension
We plan to enhance the filter feature to support other arguments in the command to filter by different types such as price range or date range. This allows the user to have greater flexibility and have a better understanding of his/her expenses.
The design consideration mentioned earlier hence facilitates this proposed extension, reducing the difficulty of such a future implementation.
4.6. Chart Analytics (toggleview Command and Bar Graphs by Choi Min Suk + Pie Chart by Pang Kim Jin)
The toggleview command allows the user to switch between viewing the list of 'Transactions' and viewing an analysis of his expenditure.
In the expenditure analysis view we have a bar chart to show expenditure breakdown by date as well as a pie chart to show expenditure breakdown by category.
4.6.1. Implementation
MainWindow
decides whether to show a list of transactions or chart analysis based on ToggleView#isViewList
, by accessing
Logic#getToggleView()
.
Here is a Class Diagram for the implementation of ToggleView
:
The ToggleView mechanism utilizes Logic
operations with the ToggleViewCommand
class in place of Command
. The following
methods are concrete implementations for the toggle operation:
-
ToggleViewCommand#execute()
- Modifies theToggleView
inModel
to view list of transactions or view analytics, and returns aCommandResult
(Step 4 of Logic). -
ToggleView#switchIsViewList()
- Modifies the boolean valueisViewList
inToggleView
to the negation of it’s current value.-
This
ToggleView
is wrapped inExpenseLa
and itsswitchIsViewList()
is called throughExpenseLa#switchToggleView()
. -
ExpenseLa#switchToggleView()
is exposed in theModel
interface asModel#switchToggleView()
.
-
The following sequence diagram illustrates toggleview command execution:
Example of usage
Given next is an example and explanation of how the ToggleView mechanism behaves at each step:
Step 1. The user starts up the application with an initial list loaded from external storage file. The diagram here depicts the example list used throughout this scenario.
Step 2. The user inputs toggleview to change the view from list of transactions to chart analysis.
Step 3. ToggleViewCommand#execute()
switches isViewList
of ToggleView
from true to false.
Step 4. MainWindow#executeCommand()
checks the boolean value of isViewList in ToggleView, which is false,
and displays chart analysis.
Step 5. User inputs toggleview again to change view back to list of transactions.
Step 6. User can set filter to a certain month to view a different kind of bar chart.
The following code snippet from MainWindow#executeCommand()
checking of the boolean value of isViewList in ToggleView,
and deciding whether to show a list of transactions of chart analysis, and what bar graph to show:
// The if else statement checks the value of isViewList from ToggleView
if (logic.getToggleView().getIsViewList()) {
// Creates ui for list of transactions
transactionListPanel = new TransactionListPanel(logic.getFilteredTransactionList());
transactionListAndChartAnalyticsPanelPlaceholder.getChildren().add(transactionListPanel.getRoot());
} else {
// Creates ui for chart analysis
// Calls logic.getIsFilterMonth() to check if the filter is set to a specific month or not, to decide which bar graph to build
chartAnalyticsPanel = new ChartAnalyticsPanel(logic.getFilteredTransactionList(), logic.getIsFilterMonth());
transactionListAndChartAnalyticsPanelPlaceholder.getChildren().add(chartAnalyticsPanel.getRoot());
}
Execution shown to user
The following activity diagram gives an overview of what ExpenseLa shows the user when executing toggleview command:
4.6.2. Design considerations
This section shows some of the design considerations taken when implementing the undo and redo features.
Aspect: Design used to implement toggelview feature
-
Alternative 1 (current choice): Create a ToggleView Class to keep a boolean value of isViewList to keep track of showing list of transactions or chart analysis.
-
Pros: Easily extendable next time to accommodate more different kind of views by changing boolean to possibly enum.
-
Pros: Easy to implement functions to change values in
ToggleView
object, which allows easy extendability next time also. -
Cons: Needs to implement many functions through
Logic
andModel
.
-
-
Alternative 2: Create a boolean value in
Model
to track whether to show list of transactions or chart analysis.-
Pros: Easy implementation and checking of boolean value by
MainWindow
to check which view to show. -
Cons: Not extendable next time when trying to accommodate different kind of views.
-
Alternative 1 was chosen because it is easily extendable, in case we want to improve or develop on the feature in the future. It also follows better OOP principles, making the code much neater and understandable.
4.7. Export (Choi Min Suk)
The export command allows user to export currently filtered transactions to a csv file, in case he would like to use the data for his own analysis.
4.7.1. Implementation
ExportCommand
is instantiated by ExpenseLaParser#parseCommand(String userInput)
, which attempts to split the userInput
into the command word and its parameters. Since ExportCommand
does not require any arguments, it is instantiated directly.
ExportCommand
obtains the filtered list of transaction to export using Model#getFilteredTransactionList()
, which then
attempts to create a csv file in the current directory using the attributes of each transaction.
The sequence diagram below shows how the execution of export is like:
The following conditions will cause a CommandException
to be thrown by the command:
-
Empty filtered transaction list
-
Failure in creating the file
-
Failure in writing to the file (Possibly due to the directory changing while the command is being executed)
The image below shows how the csv file looks when user executes export command successfully:
4.7.2. Proposed extension
In the near future, we plan to enhance the export feature. We want to improve the export command to take in view as a possible argument, thus allowing the user to choose between exporting list of transactions or the chart analysis. The user can use the chart analysis generated for visual presentations, especially if the expense tracker is for a business.
The image below shows how a possible future implementation of this feature could look like:
4.8. [Proposed] Data Encryption (Pang Kim Jin)
Given the sensitive nature of the information provided by users, we would like to safeguard the information provided by our users through encryption. Naturally, the information would be encrypted and decrypted in the back-end without the user requiring to do any of the encryption, decryption, or even any knowledge of how it works.
4.8.1. Proposed Implementation
We thus propose a Keystore
module to contain authorisation certificates or public key certificates
interacting with the Logic
and Storage
modules. With this addition, the following architecture diagram
gives an overview of how it would fit in:
The Keystore
module would have a KeystoreManager
which implements the following methods:
-
KeystoreManager#setCipher(Cipher cipher)
- sets theCipher
for encryption usage. -
KeystoreManager#encryptExpenseLa(ExpenseLa expenseLa)
- encrypts the givenExpenseLa
object with the encryption cipher set with every call toLogicManager#execute()
method. -
KeystoreManager#decryptExpenseLa(ExpenseLa expenseLa)
- Decrypts the encrypted json file upon application launch.
The following class diagram provides a depiction of the above:
KeystoreManager#encryptExpenseLa(ExpenseLa expenseLa) and KeystoreManager#decryptExpenseLa(ExpenseLa expenseLa)
will be using the Advanced Encryption Standard (AES 256) encryption algorithm.
|
4.8.2. Design Considerations
Aspect: Encryption Algorithm
-
Alternative 1: Data Encryption Standard
-
Pros: Simpler to implement encryption and decryption
-
Cons: Weaker security, easy to brute force
-
-
Alternative 2 (current choice) : Advanced Encryption Standard
-
Pros: 256 bit key is exponentially more secure than DES' 56 bit key
-
Cons: Harder to implement
-
4.9. [Proposed] ExpenseLa Monthly Statement (Pang Kim Jin)
Similar to how banks issue a statement of account, we believe that it would be helpful to provide our users with an overview of their expenses. This statement would include the user’s balance, budget, expense, income, and transactions in a user specified time frame.The user can choose to include/exclude certain transactions based on their categories or dates.
4.9.1. Proposed Implementation
To generate the statement, we propose a StatementCommand
that extends Command
and works with ModelManager
just like
all other commands, as depicted in the following diagram:
-
The user uses the
FilterCommand
to filter the list of transactions to show only the transactions with the user’s preferred category and transaction month -
Then
StatementCommand#execute()
will retrieve theFilteredList
of transactions and generate a Portable Document Format (PDF) file with Java’s PDFWrite API.
Below is a truncated example of the PDF ExpenseLa statement:
4.9.2. Design Considerations
Aspect: Time and Nature of Transactions to Export
-
Alternative 1 (current choice): Users get to choose when to generate their statement and which month and categories of transactions to include.
-
Pros: Users get a statement tailored according to their needs.
-
Cons: Users may forget to include certain types of transactions.
-
-
Alternative 2: At the end of every month, a statement of all transactions and user information is exported
-
Pros: Users get a comprehensive view of their expenses
-
Cons: Users may be overloaded with information
-
Ultimately we chose Alternative 1 as we prioritise our user’s freedom of choice and we understand that not all transactions may be relevant for the purposes of exporting the statement.
4.10. Logging
We are using java.util.logging
package for logging. The LogsCenter
class is used to manage the logging levels and logging destinations.
-
The logging level can be controlled using the
logLevel
setting in the configuration file (See Section 4.11, “Configuration”) -
The
Logger
for a class can be obtained usingLogsCenter.getLogger(Class)
which will log messages according to the specified logging level -
Currently log messages are output through:
Console
and to a.log
file.
Logging Levels
-
SEVERE
: Critical problem detected which may possibly cause the termination of the application -
WARNING
: Can continue, but with caution -
INFO
: Information showing the noteworthy actions by the App -
FINE
: Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size
4.11. Configuration
Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json
).
5. Documentation
Refer to the guide here.
6. Testing
Refer to the guide here.
7. Dev Ops
Refer to the guide here.
Appendix A: Product Scope
Target user profile:
-
has a need to keep track of their expenses
-
prefer desktop apps over other types
-
can type fast
-
prefers typing over mouse input
-
is reasonably comfortable using CLI apps
Value proposition: efficient way to keep track of expenses and make decisions based on data and analytics provided
Appendix B: 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 |
|
general user |
add a new expense entry |
keep track of my expenses |
|
employed user |
add a new income entry |
keep track of my income |
|
high-income user |
keep track of all the money I earn |
make decisions on where my most lucrative source of income is |
|
low-income user (like student) |
set budget for current month |
limit my expenditure for the month |
|
low-income user (like student) |
be notified by the application if i am spending too much money |
be wary of overshooting my budget |
|
consistent thrifty user |
set budget for every month(recurrent budget) once |
have no need to constant;y set my unchanging budget |
|
part-time worker with varying income |
be flexible with my budgets |
spend more or less on certain months depending on my financial situation |
|
visual user |
be visually alerted when I spend a certain proportion of my budget |
adjust my spending habit for the rest of the month |
|
careless user |
delete an expense or income entry |
remove entries that I added in by mistake |
|
forgetful user |
find an entry by keyword |
check if I spent money on a particular thing |
|
spendthrift user |
filter expense based on category |
know if I generally spend a lot of money or only on certain months |
|
forward-looking user |
look at my spending trend by week or month |
keep track of my income |
|
couple/student trying to save up |
filter expense based on date or time period |
see how much money I have spent in that time period and make better decisions |
|
couple/student trying to save up |
view amount of budget left to spend |
adjust spending habit for the rest of the month |
|
general user |
view total money I have |
be able to tell how much I can spend |
|
user trying to save money |
view total expense for a particular month |
decide on my future expenditures |
|
visually analytical user |
view pie chart of money spent based on category |
see where I spend the most money on |
|
visually analytical user |
view bar chart of money spent based on time period |
see when I spend the most money |
|
organized user |
organize my expenditure into different categories |
better able to track where I am spending my money |
|
not one-off user |
all my expenditures and income to be saved |
continue on from previous whenever I exit and launch back the application |
|
smart analytical user |
export my expenditure and income |
use the data to make my own analysis |
|
secretive user |
set a password to login tp the application |
prevent unwanted users from viewing my expenses |
|
businessman |
have multiple accounts |
manage my expenses not only for myself but my business |
|
parent |
have multiple accounts |
help manage my children’s expenses |
|
user with many friends |
add friends in the application |
|
help each other in their savings |
|
concerned friend |
look at my friend’s spending habit |
keep a lookout for their expenditure |
|
user who owes people money |
view the people who I owe money to |
keep track of who I owe |
|
user who lends people money |
request payment from people who owe me money |
keep track of my loans |
|
sociable user |
indicate when my expenditure is within the budget |
share the achievement with my friends |
|
lazy user |
have the application make recommendations on my spending habits |
Appendix C: Use Cases (Hubert Halim)
(For all use cases below, the System is the ExpenseLa
and the Actor is the user
, unless specified otherwise)
Use case: Delete expense
MSS
-
User requests to list all transactions
-
System removes all filters and show all expenses
-
User requests to delete a specific transaction in the list
-
System deletes the transaction
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The given index is invalid.
-
3a1. System shows an error message.
Use case resumes at step 2.
-
Use case: filter transactions by category
MSS
-
User requests to list filtered transactions
-
System queries list of transactions
-
Apply filter predicate to update list of filtered transactions
-
System shows filtered list
Use case ends.
Extensions
-
2a. The list is empty.
Use case ends.
-
3a. The category given is not valid.
-
3a1. System shows an error message.
Use case resumes at step 2.
-
Use case: Set recurring monthly budget
-
User requests to set a recurring budget of a specified amount.
-
ExpenseLa
processes the request and sets the specified amount as the budget for the current month. -
ExpenseLa
then update the value of recurringBudget variable in GlobalData to the specified amount.Use case ends.
Extensions
-
1a. The parameters specified by the user are not valid.
-
1a1.
ExpenseLa
displays an invalid parameter error to the user and the monthly budget is not updated.Use case ends.
-
Use Case: Add an expense
MSS
-
User requests to add a new expense into their expensela.
-
The system processes the request and adds the expense transaction into the transactions list.
Use case ends.
Extensions
-
1a. The parameters specified by the user are not valid.
-
1a1. The system displays an invalid parameter error to the user and the transactions list is not updated.
Use case ends.
-
Use Case: Add a recurring income
MSS
-
User requests to add a new recurring income into their expensela.
-
The system processes the request and adds the income transaction into the transactions list.
-
The system then add the income transaction to RecurringTransactionList in GlobalData.
Use case ends.
Extensions
-
1a. The parameters specified by the user are not valid.
-
1a1. The system displays an invalid parameter error to the user and the transactions list is not updated.
Use case ends.
-
Use Case: Show chart analytics view
MSS
-
User requests to toggle to chart analytics view.
-
The system switches the view to charts view.
-
2a. The filter for month is set to all, bar chart displays data of the last 2 years by month.
-
2b. The filter for month is set to a specific month, bar chart displays data by day of the week.
Use case ends.
-
Use Case: Clear transaction list
MSS*
-
User requests to clear all transactions.
-
The system clears transaction list in ExpenseLa.
-
The system resets MonthlyData and GlobalData
Use case ends.
Use Case: Export to CSV file
MSS
-
User requests to export current list being viewed to a CSV file.
-
The system saves the transactions to that csv file named
transactions.csv
by the system.
Use case ends.
Extensions
-
1a. The file does not exists.
-
1a1. The system creates a file with the name
transactions.csv
.Use case resumes at step 2.
-
Use Case: Import from CSV file
MSS
-
User requests to import transaction data from a CSV file.
-
Import the transactions data from the file specified by the user ignoring duplicate and invalid transactions
Use case ends.
-
1a. The file specified by user does not exists.
-
1a1. The system shows an error message prompting user to rectify their command.
Use case ends.
-
Use Case: Find transactions whose name contains certain words
MSS
-
User requests to list transactions whose name contains certain words.
-
The system queries all transactions
-
The system applies predicate to filter only transactions that contain the words specified by user
Use case ends.
-
2a. The list is empty.
Use case ends.
-
3a. The list is empty.
Use case ends.
Appendix D: Non Functional Requirements (Pang Kim Jin)
-
The software should work on any mainstream OS as long as it has Java
11
or above installed. -
The software should be able to hold up to 2000 transactions(expenses and incomes).
-
The software should be able to respond within 5 seconds.
-
A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
-
The software should be able to run irrespective of internet connection.
-
The software should support both manual and automated testing.
-
The source code should be open-source
Appendix E: Glossary
Appendix F: Product Survey
ExpenseLa
Author: Team Halim
Pros:
-
Very easy and quick tracking for people that prefer command line interfaces.
-
Analytics serve as a quick bird’s eye view on monthly spending.
-
Analytics breakdown of spending per category helps me gain insights on the categories which I have spent too much money on.
-
Well made prompts to warn me when I am going to exceed my monthly budget. Helps to better my budgeting practices.
Cons:
-
Takes quite a bit of time to learn all the commands.
-
It can get quite tedious to input every single transaction at any time. Could possibly automate some inputs.
Appendix G: Instructions for Manual Testing (Pang Kim Jin)
Given below are instructions to test the app manually.
These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing. |
G.1. Launch and Shutdown
-
Initial launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-
-
Saving window preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
G.2. Setting a monthly budget
-
Setting a monthly budget to a user decided amount
-
Test Case:
budget b/1000
Expected: The monthly budget for the current month is set to $1000 -
Test Case:
budget
Expected: The monthly budget is not updated. Error is shown in the status message
-
G.3. Resetting total balance
-
Reset
Balance
value to the total from the amount of all transactions stored-
Test Case:
resetBalance
Expected: TheBalance
is reset
-
G.4. Adding a transaction
-
Add either an expense or income transaction
-
Test Case:
add a/ 26.00 n/ Grab Share d/ 2020-02-19 c/ TRANSPORT
Expected: A new expense transaction is added to the transaction list. Depending on the current filter applied this change may or may not be visible. Details of the added transaction is visible in the Command Result -
Test Case:
add a/ 16.00 n/ Pizza r/ Lunch c/ FOOD
Expected: A new expense transaction is added to the transaction list, with the transaction date set to the current date. Details of the added transaction is visible in the Command Result -
Test Case:
add i/ a/ 200.00 n/ pocket money c/INCOME rc/
Expected: A recurring income transaction is added to the transaction list, with the transaction date set to the current date. Details of the added transaction is visible in the Command Result -
Test Case:
add i/ n/ allowance c/INCOME rc/
Expected: No transaction is added. Error details are shown in the Command Result
-
G.5. Filtering transactions
-
Filtering transactions listed by category, month, or both.
-
Test Case:
filter m/2020-04
Expected: A month filter for April 2020 is applied to the transaction list, relevant transactions are listed. Details of the number of transactions found is visible in the Command Result -
Test Case:
filter c/TRANSPORT
Expected: A category filter for "TRANSPORT" is applied to the transaction list, relevant transactions are listed. Details of the number of transactions found is visible in the Command Result -
Test Case:
filter c/FOOD m/2020-02
Expected: A category filter for "FOOD" and month filter for February 2020 is applied to the transaction list, relevant transactions are listed. Details of the number of transactions found is visible in the Command Result -
Test Case:
filter
Expected: No filter is applied and no transactions listed. Error details are shown in the Command Result.
-
G.6. Deleting a transaction
-
Deleting a transaction from the transactions listed
-
Prerequisites: At least one transaction in the list using either
list
orfilter
command. -
Test case:
delete 1
Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. -
Test case:
delete 0
Expected: No transaction is deleted. Error details shown in the status message. -
Test Case:
delete
Expected: No transaction is deleted. Error details are shown in the Command Result.
-
G.7. Editing a transaction
-
Editing a transaction from the transactions listed
-
Prerequisites: At least one transaction in the list using either
list
orfilter
command. -
Test case:
edit 1 a/ 26.00 n/ Grab Share d/ 2020-02-19 c/ TRANSPORT
Expected: First contact is edited from the list. Details of the edited contact shown in the status message. -
Test case:
edit 0 a/ 26.00 n/ Grab Share d/ 2020-02-19 c/ TRANSPORT
Expected: No transaction is edited. Error details shown in the status message. -
Test Case:
edit a/ 26.00 n/ Grab Share d/ 2020-02-19 c/ TRANSPORT
Expected: No transaction is edited. Error details are shown in the Command Result.
-
G.8. Analysis of transactions
-
Toggle between viewing list of transactions and analytics with bar graph and pie chart to show expense trend.
-
Test case:
toggleview
Expected: Transaction view is toggled to analytics view or vice versa. -
Test case:
toggle
Expected: No toggling happens. Error details shown in the status message.
-
G.9. Finding transactions
-
Finding transactions that match any of the supplied keywords in its name field.
-
Test case:
find Airpods
Expected: Transactions with the word "Airpods" in its name will be displayed in the transaction list. -
Test case:
find Airpods Allowance Electricity
Expected: Transactions with the word "Airpods" "Allowance" or "Electricity" in its name will be displayed in the transaction list. -
Test case:
find
Expected: No transactions found. Error details shown in the status message.
-
G.10. Listing all transactions
-
Listing all transactions and resetting all filters to "ALL".
-
Test case:
list
Expected: All transactions will be displayed in the transaction list. Filter for Category and Month are now "ALL".
-
G.11. Clearing all recurring transactions
-
Clearing all recurring transactions stored.
-
Test case:
clearrecurring
Expected: All recurring transactions in the transaction list will be cleared.
-
G.12. Exporting transaction data to csv
-
Exporting the filtered transaction list to a csv file.
-
Prerequisite: There must be at least one transaction in the transaction list
-
Test case:
export
Expected: All transactions in the transaction list after applying filters will be exported.
-
G.13. Importing transaction data from csv
-
Importing the filtered transaction list to a csv file.
-
Prerequisite: There must be at least one transaction in the csv file
-
Test case:
import transactions.csv
Expected: All transactions that are in the correct format and not duplicated entries in the transactions.csv file will be imported.
-
G.14. Clearing all data
-
Clearing all data in ExpenseLa including monthly data and global data
-
Test case:
clear
Expected: All transactions are deleted, balance is set to 0 and monthly data is also set to 0. All recurring data such as budget and transactions are also cleared.
-
G.15. Showing help window
-
View the help window for help with commands
-
Test Case:
help
Expected: A popup with a link to the User Guide pops up.
-
G.16. Saving data
-
Dealing with missing/corrupted data files
-
Delete the data files at
.\data\expenseLa.json
and.\data\globalData.json
-
Appendix H: Effort
In this section, we highlight the amount of effort taken for us to develop ExpenseLa, the challenges faced in this development and our eventual achievement.
H.1. Effort by team
Our application, ExpenseLa, is considerably different from what Address Book 3 (AB3) had implemented. We wanted to take some address book features that were available in AB3, but wanted to build an expense tracker for our project. As such, to build ExpenseLa, we took and modified some features previously available in AB3, but had to create many of our own models and implementations for the new features.
In short, AB3 stores details related to multiple people known to the user. ExpenseLa stores data related to multiple transactions by the user and so much more. ExpenseLa uses that data to track the monthly expenditure, income as well as budget, analyses their expenditure by graphing it out, and provide many other features like filtering transactions as well as new ways of adding transactions to make the application easier to use.
While building ExpenseLa, we spent a considerable amount of time not just implementing our features, but also making sure that new features were linked to ExpenseLa and could cohesively work with other features to make sure the application provided and smooth and consistent user experience. All these discussions were held at least once a week with a minimum 2 hours each round to provide feedback for each other’s implementations and that our implementations could work together.
H.2. Challenges faced
The team encountered some challenges while developing ExpenseLa. The most notable ones are:
-
Initial brainstorming of ideas
At the start of the project, we wanted to do a meeting scheduler for NUS computing students. However after multiple discussions, we deemed it unfit to implement as there were too many components to think about and difficult to implement given our limited time to complete the application. Thus we decided to make an expense tracker instead. We had many different ideas for our expense tracker and ways to implement them. We needed a few meetings to discuss and iron out our direction for the project to ensure everyone was on the same page before starting the development process. -
Storage
We needed a way to store our data so when the application starts we can load previously stored transactions and other data. We decided to adopt and modify slightly AB3’s storage system and implement it in ExpenseLa. -
Commands
An expense tracker requires many different commands to carry out many different actions, especially since it uses command line interface. We adapted AB3’s parser. For similar commands to AB3 like add, we had to change the command to accept different predicates, thus parsing it differently. We also had to think about how to parse new commands, such as filter function and parsing recurring budget and transactions to be stored as Global data (talked about below), which was a big change from AB3. -
Global data
We set ExpenseLa to be timeframe specific, with Monthly Data such as budget, income and expenditure for the month to reset at the beginning of every month. However we also had some data that were not timeframe specific, such as transactions and budget that were set to be recurring every month. We could not store and reference it the same way as how the normal transactions were referenced from the json data. After discussion, we decided to create aGlobalData
class inModelManager
where it will be referenced separately fromExpenseLa
. -
User interface
We decided to keep a ExpenseLa’s UI generally similar to AB3, but with addition of new UI features. We created a new placeholder forMonthly Data
for easy constant viewing by the user. We created a chart analysis for the expenditures, which was in the form of a bar chart and pie chart. The charts took up a considerable amount of space, and putting a separate placeholder for it would be unfeasible for small screens. Thus we implemented atoggleview
command which allows the user to toggle between viewing the list of transactions and the chart analytics. This required us to learn both the chart libraries as well as think about how we were going to change the views, while updating the charts when new commands were added.
Conclusion
From planning to documentation to coding of the project, the team believes that every member has put in equal and huge amount of effort on all parts of the project. We believe that
our product is significantly different from AB3, with many new features which were carefully planned in implementation, and we are proud of ExpenseLa we have built.