1. PROJECT: Budget Buddy

This portfolio serves to provide a breakdown of what I have contributed as a student in the software engineering project, Budget Buddy.

2. About Budget Buddy

Budget Buddy is a desktop-based expense tracking application made for students, NUS School of Computing students in particular, to keep track of and manage their finances better. The user interacts with it using a Command Line Interface (CLI), and it has a Graphical User Interface (GUI) created with JavaFX. The application is written in Java, and has about 15 kLoC.

This is a software engineering project in which groups are tasked to enhance or morph a simple CLI-based address book application. The end result is a expense tracking application with focus on programmers, to make customisation that much easier.

Budget Buddy is able to maintain separate accounts, handle a multitude of transactions within an account, and even track any loans that the user might have made. Rules can be added to make repetitive tasks automated, and scripts can even allow the user to create mass changes to data in their own way by themselves.

3. Project Role

My task in this project is the Rule Engine feature, which allows for the automation in the application, executing certain tasks automatically upon the addition of a new transaction. The following sections will elaborate on this feature, which includes the additions I have made to both the user and developer guides.


Take note of the following formatting used in the rest of this portfolio.

command word

Monospace text like this indicates that this is a command word that can be entered as part of a command to be executed by the application.

filename.txt

Bold italicized text like this indicates that this is a filename that can be found within the application.

component

Bold monospace text like this indicates that this is a component that can be found within the application.

4. Summary of contributions

This section provides an overview of my contribution to this application.

4.1. Major enhancement:

Added the ability to add/list/edit/remove rules, as well as executing them.

  • What it does: Allows the user to define a set of rules in the rule engine which will be executed by the rule engine upon adding/editing a transaction. A rule may carry out an action depending on whether the transaction satisfies the rule’s condition, or "predicate".

  • Justification: This feature improves the application significantly because for day-to-day transactions, there may be many repetitions in what the user types. The app can therefore help users save time by automating such repetitive processes, or even more, depending on how the user uses it.

  • Highlights: Setting up rules was a challenge, as it created an additional source of "logic" besides commands, which required a new structure from the bottom up. A method to process rules had to be made, and considering that both expressions and scripts can be used for rules, a unified way to process them was required.

4.2. Minor enhancement:

Set up panel management within the GUI.

  • What it does: Allow easy switching between panels within the same frame.

  • Highlights: Different panels were required based on the currently active tab, hence managing panels properly allowed for switching between them to be much easier.

4.3. Code contributed:

I have contributed over 30 pull requests to the repository as of 11 November 2019.

All of my contributions can be found here: [RepoSense Analysis]

4.4. Other contributions:

  • GUI Enhancements:

    • Updated the GUI layout and colour scheme: Pull requests #60, #95, #185

  • Bug fixes:

    • Fix bugs noted during PE dry run: Pull request #153

  • Documentation:

    • Contributed to the User Guide and Developer Guide. See Section 5 and 6 below for detailed breakdown.

  • Community:

    • Project-wide refactoring on package naming to remove existing references to AB3: Pull request #52

    • Reviewed 50+ pull requests by the team

5. Contributions to the User Guide

This section shows what I have contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

You can refer to the full User Guide here.

5.1. Overview of Contributions

Below is an overview of my contributions to the User Guide.

5.1.1. Improve user experience

I added multiple sections and introductory paragraphs, which include the Introduction, Quick Start, Getting Started, Features, and FAQ, that provide clarification and support in usage for the users who are using the application for the first time, or need help with troubleshooting.

5.1.2. Add main content into the User Guide

I wrote detailed sections about the usage of commands that are in the Budget Buddy application, to guide users in the format and specifications required for those commands. An extract from the User Guide that were written by me is shown in section 5.2 below.

5.2. Automating with rules: rule

Introducing the Rule Engine. A way for you to automate certain actions based on a certain predicate. Spend less time typing repetitive commands, and spend more time keeping track of your expenses!

RuleUG1
Figure 1. Rules in the rule tab

Rules have the following structure: if "predicate" is true, then perform "action". By creating a predicate which defines the condition you set, you can perform any action you create whenever a transaction is added/edited, if the transaction satisfies that condition.

All rules in the Rule Engine will be executed on a transaction, when:

  • It is a new transaction and has been successfully added into an account. OR

  • It is an existing transaction and has been successfully edited.

RuleUG2
Figure 2. Transaction after rules were executed on it

The picture above shows a transaction added with the command:
txn dn/out x/200 d/Treat friends to buffet food!

As you can see, two of the rules from the example list of rules above were applied on the transaction, and added categories to it.

5.2.1. Add a new rule: rule add

Adds a new rule to the rule engine. Both the predicate and action have to be specified. A rule can be formed using either expressions or scripts, or both.

Format: rule add p/<expression|script name> a/<expression|script name>

Expression Formatting Guide:

  • Predicate: In the order of <attribute> <predicate operator> <value>

    • An attribute can be one of the following:

      • inamt : Transaction amount inwards

      • outamt : Transaction amount outwards

      • desc : Transaction description

      • date : Transaction date

    • A predicate operator can be one of the following:

      • = : Equality comparison operator

      • < , <= , >= , > : Inequality comparison operators

      • contains : Substring check operator (cannot be used on dates)

    • A value can be a number or a string of length not more than 180 characters. It can contain the following special characters:
      !#$%&'*=?`+_/[{|}]~^.-

The rest of the formatting guide can be found in the User Guide.

5.2.2. Swap two rules: rule swap

Ths will affect the execution order of the rules. Rules will be executed from the top of the list downwards.

Swaps the position of two specified rules in the Rule Engine.

Format: rule swap <rule 1 ID> <rule 2 ID>

Examples:

  • rule swap 2 4
    Swaps the ordering of the 2nd rule and the 4th rule.

6. Contributions to the Developer Guide

This section shows what I have contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

You can refer to the full Developer Guide here.

6.1. Overview of Contributions

Below is an overview of my contributions to the Developer Guide.

6.1.1. Add main content into the Developer Guide

I wrote detailed sections about the implementations of functionality in the Budget Buddy application, drawn UML diagrams, and documented some use cases for the Developer Guide. A few extracts of the Developer Guide that were written by me are shown in section 6.2 and 6.3 below.

6.2. Rule Management Feature

The Rules feature exists as an integration onto the Transaction system. It makes use of syntax processing, together with scripts to provide an automation solution to repetitive tasks when adding transactions.

Rules are defined with a pair of predicate and action, where an action is performed given that the predicate returns true. This predicate-action split allows us to decouple testing from performing, which helps to increase reusability of individual predicates and actions.

6.2.1. Implementation

Basic attributes and operators are exposed to provide users a way of writing simple tests on transactions without having to manually check and make changes. Storing rules works similarly to LoansManager, where individual rules are stored in a RuleManager which manages all CRUD operations.

All rules are stored in a JSON file when added, formatted to be retrieved and parsed by the application when relaunched.

The following class diagram illustrates the structure of the Rule Model component.

RuleModelClassDiagram
Figure 3. Structure of the Rule Model Component
The rest of the Script and Model components have been omitted to give focus on the Rule model component.

As mentioned above, rules are defined as a pair of predicate and action, which as seen in the above diagram, is divided into the two abstract classes RulePredicate and RuleAction. These two classes are abstract due to two implementation types, either script or expression. Their concrete classes are PredicateExpression and ActionExpression for expressions and PredicateScript and ActionScript for scripts respectively.

6.3. Rule Execution Feature

The structure of rules were separated from the logic of rule execution to maintain the separation of concerns between the Model and Logic components.

Rule execution is hooked into the evaluation of adding or editing a Transaction. This means that for every new transaction, all rules within the Rule Engine will be executed on that transaction. The same can be said for modifying a transaction.

The implementation of the Rule execution is elaborated on below.

6.3.1. Implementation

The RuleEngine is a static class used for interfacing with all the rule processings functionality.

Two executable classes are used in the execution of a rule, Testable and Performable. A Testable represents the executable form of a RulePredicate, which may be either an expression or a script. Correspondingly, a Performable represents the executable form of a RuleAction, which may also be either an expression or a script.

Before executing the existing rules, the index of the transaction and the account that the transaction belongs to are supplied to the RuleEngine through the RuleEngine#executeRules method. This allows for the retrieval of the transaction when a rule is executed against it.

When a rule is executed, this is firstly represented as the execution of the Testable#test method on the given transaction. If the test passes, the predicate is true, and therefore the action is performed. This is represented as the execution of the Performable#perform method on the given transaction.

The following sequence diagram shows the interaction between the RuleEngine and the different objects involved in the execution of the rules on a transaction:

RuleExecutionSequenceDiagram
Figure 4. Sequence diagram representing Rule Execution after adding a new Transaction

Shown above is a sequence diagram which takes place during the execution of the TransactionAddCommand, after the new transaction has already been added. The RuleEngine takes over, and retrieves the relevant handlers from Model.

Thereafter, the list of rules is retrieved from the RuleManager. The RuleEngine iterates through the list, using the RulePredicate and RuleAction of each rule to create the required Testables for testing on the transaction, as well as the Performables for performing the action.

The following activity diagram shows in greater detail the workflow of executing rules.

RuleExecutionActivityDiagram
Figure 5. Activity diagram of the different paths involved in the workflow of executing rules

The activity diagram above has a slightly different context as the sequence diagram, to show a separate use case. In this diagram, instead of a new transaction that is added, we have a transaction that is edited. Both types of commands do not affect the workflow of rule execution.

In this diagram, the generation of a Testable and Performable is shown in greater detail.

Testable is an interface which, like RulePredicate, have its implementations split into expressions and scripts, namely TestableExpression and TestableScript.

Similarly, Performable is an interface which, like RuleAction, have its implementations split into expressions and scripts, namely PerformableExpression and PerformableScript.

Expressions are generated by the RuleEngine when either the RulePredicate or RuleAction are of the expression type. The RuleEngine will retrieve the correct expression constructor from an internal hash map based on the Operator, and create the expression using the given attribute and/or value.

For example, we have an predicate p/desc contains food. One of the classes implementing TestableExpression, ContainsExpression, is created since the predicate has the Operator: contains. The corresponding Attribute: desc and Value: food in the predicate are provided into the ContainsExpression constructor during instantiation.

Scripts, on the other hand, are generated by the RuleEngine when either the RulePredicate or RuleAction are of the script type. The RuleEngine will generate the corresponding Testable or Performable by first retrieving the script from the ScriptLibrary based on its ScriptName. Following that, a TestableScript or PerformableScript is instantiated with a function ScriptEvaluator, which evaluates the script given the transaction and account. This function is then called when Testable#test or Performable#perform is executed.

6.3.2. Design Considerations

Aspect: Duplication of predicates and actions in model and logic
  • Alternative 1 (current choice): Both predicates and actions have their corresponding versions in both model and logic.

    • Pros: Able to split the logic flow and execution code from the data in model.

    • Cons: Seemingly duplicate classes, such as PredicateExpression and TestableExpression, which increases the number of classes.

  • Alternative 2: All execution data and logic is stored in the rule model rather than logic.

    • Pros: Reduce class duplication, less confusion.

    • Cons: Model and Logic will have unnecessary coupling which reduces testability and makes maintenance and integration harder.