1. Introduction

This portfolio serves to showcase my contributions to our team project THRIFT. The project was part of the requirements of a year two software engineering module, CS2103T, that I had taken as an undergraduate in NUS.

1.1. About THRIFT

THRIFT is an application for money-conscious NUS students who wish to track their incomes and expenditures so that they can make informed financial decisions. THRIFT is designed for those who prefer to work quickly with a Command Line Interface (CLI) while still enjoying the benefits of having a Graphical User Interface (GUI). This is how the user interface of THRIFT looks like:

UGUI
Figure 1. Main window of THRIFT which displays a list of user’s transactions along with some useful information

THRIFT is a fork of the original AddressBook-Level3 application which allows users to create a personalised list of contacts. My team and I have since transformed the application to suit our use cases.

1.2. About the THRIFT Team

The THRIFT Team consists of five passionate developers and I was the Software Architect, who is in charge of documentation. The other team members and their respective roles were:

  • Poh Jia Hao (Team Lead)

  • Lee Bo Qiang (User Experience Developer),

  • Lye Jian Wen (Quality Assurance Manager)

  • Kenson Oen (Process Analyst).

Every single member played an important and major role in ensuring the success of THRIFT.

1.3. About this document

In this document, I will use some special text styling as described below to help you understand the document better.

This symbol denotes information you might want to note of.
currencyMappings

Text with grey highlight (called a mark-up) indicates that it is a method/variable name or a command.

ModelManager

Bold text within a mark-up indicates a class/package name.

2. Summary of Contributions

This section provides a summary of the various contributions I have made to THRIFT.

2.1. Code contributed

You can follow these links to view a sample of my code:
[Commits] [Pull requests] [RepoSense Dashboard]

2.2. Functionality enhanced

The following are some of my enhancements:

  • Major enhancement: Added the ability to tag and un-tag entries in the transaction list.

    • What it does: The tag and untag commands allow the user to tag and un-tag their transactions.

    • Justification: It allows users to add and remove specific tags from transactions.

    • Highlights: The original AB3 edit function replaces the entire set of tags related to a transaction entry. My tag command adds to the set instead of overwriting it, with duplicate tags being detected and ignored. My untag command removes specified tags from the set instead of overwriting it, with the user being warned for trying to delete non-existent tags.

  • Major enhancement: Added the ability to convert currency.

    • What it does: The convert command allows the user to convert currency from one to another within the application.

    • Justification: It is possible that user wants to travel overseas or simply purchase something in another currency, they would then want an estimate of the exchange rates they can get.

    • Highlights: The convert command has the ability to use exchange rates stored in a JSON file, which can be obtained by downloading and modifying the JSON output from free online exchange rate services to obtain the latest rates.

    • Credits: The sample rates were obtained from the European Central Bank.

  • Minor enhancements: Modified the THRIFT interface to display tags in different colours.

    • Justification: Tags in THRIFT are meant to categorise the different transactions, it is more meaningful if tags are of different colour if they correspond to different categories.

    • Highlights: The colours are coloured into different colours if they are a keyword, otherwise they are in Purple.

2.3. Other contributions

  • Project management:

    • I managed issues assigned to me from version 1.0 to 1.4 on GitHub.

  • Enhancement to existing features:

    • I added a function to the existing class ArgumentMultimap to enforce the return of no more than one argument value. (Pull request #118)

  • Documentation:

    • I added the sections related to tag, untag and convert commands in the User Guide. (Pull Requests: #69, #178, #201)

    • I added the sections related to the tag command, the untag command and the planned feature on Artificial Intelligence in the Developer Guide. (Pull Requests: #79, #178, #201, #267)

    • I managed the THRIFT documentation and ensured its quality. (Pull Requests: #72, #77, #179, #200, #223, #275, #283)

  • Community:

    • I reviewed and approved pull requests from my teammates. (Pull Requests: #46, #113, #266)

    • I reviewed pull requests of new features from my teammates(with non-trivial comments). (Pull Requests: #113, #119, #206)

    • I gave suggestions for another team in the class (Example)

3. Contributions to the User Guide

The following section showcases my use of proper documentation to guide users through the many features of THRIFT.

{Start of extract from the section on the tag command}

3.1. Tagging a transaction: tag

If you want to categorise your incomes and expenses, you can associate them with a custom meaningful tag using the tag command.

3.1.1. Command syntax

To execute the tag command, you have to use the following syntax:

tag i/INDEX t/TAG_NAME…​

  • Tags the transaction at the specified INDEX with TAG_NAME (can be more than one)

  • INDEX refers to the index number currently shown in the UI (after filtering, if any)

  • INDEX must be a positive integer 1, 2, 3, …​

  • TAG_NAME must be alphanumeric and cannot contain spaces

  • The tag will be coloured accordingly if TAG_NAME is a keyword, else it will be in the default colour

  • If a tag with TAG_NAME in the same case already exists in the entry, it will be ignored

3.1.2. Example usage

Assume you have a few transaction entries already inside THRIFT, and one of them is not categorised yet. You want to tag that entry with a meaningful tag.

Here’s how you can do that:

  1. You want to tag "Uniqlo Jeans" (the entry displayed at index 2) with the tag Shopping since it’s part of your shopping.

    TagStep1
  2. You type tag i/2 t/Shopping into the Command Box and press Enter.

    TagStep2
  3. You will now see that a text representation of both the updated and original entry is displayed in the Result Box and that the tag has appeared at the entry indicating that it has been successfully tagged.

    TagStep3

{Start of extract from the section on the tag command}

{Start of extract from the section on the untag command}

3.2. Untagging a transaction: untag

An entry can be associated with an incorrect tag due to various reasons. Perhaps you made a mistake tagging the entry or perhaps the tag became invalid as it lost its meaning over time, you can fix that by dissociating the tag from the entry using the untag command.

3.2.1. Command syntax

To execute the untag command, you have to use the following syntax:

untag i/INDEX t/TAG_NAME…​

  • Untags the transaction at the specified INDEX with TAG_NAME (can be more than one)

  • INDEX refers to the index number currently shown in the UI (after filtering, if any)

  • INDEX must be a positive integer 1, 2, 3, …​

  • TAG_NAME must be alphanumeric and cannot contain spaces

  • If no tag with TAG_NAME in the same case already exists in the entry, it will be ignored

3.2.2. Example usage

Assume you have a few transaction entries already inside THRIFT, and one of them has an incorrect tag. You want to dissociate the incorrect tag from that entry.

Here’s how you can do that:

  1. You want to dissociate the tag Important from "Humble Bundle" (the entry displayed at index 3) since it is not very important.

    UntagStep1
  2. You type untag i/3 t/Important into the Command Box and press Enter.

    UntagStep2
  3. You will now see that a text representation of both the updated and original entry is displayed in the Result Box and that the tag has disappeared from the entry indicating that it has been successfully removed.

    UntagStep3

{End of extract from the section on the untag command}

{Start of extract from the section on the convert command}

3.3. Converting currency: convert

If you want to travel overseas or go on exchange, you will definitely need foreign currencies. To see how much foreign currency you can get with your local currency, you can use the convert command to get an estimate.

3.3.1. Command syntax

To execute the convert command, you have to use the following syntax:

convert [v/VALUE] c/CURRENCY…​

  • Converts the VALUE in SGD to CURRENCY if one of each is specified.

  • If no VALUE is specified, the value of 1.00 will be used.

  • If more than one CURRENCY is present, the first one will be used as a base and the rest as target currencies

  • VALUE must be positive.

  • CURRENCY must be a supported currency.

3.3.2. Example usage

Assume you want an estimate of how much foreign currency you can get with your local currency and you want to do so without leaving THRIFT.

Here’s how you can do that:

  1. You want an estimate on how much USD you can get with SGD1000.

  2. You type convert v/1000 c/SGD c/USD into the Command Box and press Enter.

    ConvertStep2
  3. You will now see the result of the conversion displayed in the Result Box.

    ConvertStep3

{End of extract from the section on the convert command}

4. Contributions to the Developer Guide

The following section demonstrates my ability to document technical implementations that give developers insight on the application design. My contributions to the technical depth to THRIFT is also shown.

{Start of extract from the section on the tag/untag commands}

4.1. Tag/Untag feature

We allow the user to add and remove custom Tag objects in Transaction entries so that they can categorise the entries to their own liking. The following sections describe how this feature is implemented and the design considerations that led to the solution.

4.1.1. Implementation

The tag/untag command performs modifications on existing Transaction entries that reside in the Model. Implemented using the concept of polymorphism , both Expense and Income objects extend the abstract class Transaction, and are treated the same way in the context of the tag/untag command.

TagPolymorphism
Figure 2. A class diagram illustrating the implementation of Tag inside a Transaction

Due to this polymorphic implementation, many of the driver functions in THRIFT use the Transaction class for both Expense and Income objects. For example, when tagging a new Expense/Income object, the TagCommandParser will treat them as the same object:

TagSequence
Figure 3. A sequence diagram showing how the TagCommandParser is called

When a user enters a tag/untag command, it is parsed by the respective TagCommandParser#parse(String args) and UntagCommandParser#parse(String args) parsers and returns a TagCommand command object or a UntagCommand command object respectively, which will be executed. For both commands, the following conditions will cause a ParseException to be thrown by their respective parsers:

  1. Missing parameters

  2. Incorrect syntax (i.e. missing prefix, if it is required)

  3. Illegal values in parameters (i.e. non-alphanumeric values given for Tag names)

  4. Multiple occurrences of parameters which only expects a single entry

If the user input is incorrect due to any of the reasons above, the corresponding usage syntax will be shown.

The following is an example on how a Tag is added and removed from a Transaction, with details on the processes done in the backend.

Step 1. The user launches the application with data from previous sessions. THRIFT currently contains two Transaction objects and the user is going to perform tagging and un-tagging operations on one of them.

TagUntag1

Step 2. The user executes the command tag i/1 t/Delicious to tag the Transaction at Index 1 with the Tag "Delicious". The input is checked with the TagCommandParser parser and an attempt to parse each parameter occurs:

  • Index is parsed by ParserUtil#parseIndex(String)

  • Tag strings are parsed by ParserUtil#parseTags(Collection<String>) which calls ParserUtil#parseTag(String) iteratively for every string in the collection

ParserUtil is a class that contains useful functions for parsing the inputs from the user.
Tags that already exist inside the specified Transaction will be ignored, and if that results in no tags being added, an error will be shown to the user.

Since the user input is valid, a TagCommand command object is created and executed. As a result, a copy of the Transaction object with the specified Tag added replaces the original in the TransactionList at the same position.

TagUntag2

Step 3. The user realises that the Tag added was not appropriate. The user then executes the command untag i/1 t/Delicious to untag the Tag "Delicious" from the Transaction at Index 1. The input is now checked with the UntagCommandParser parser and similarly, an attempt to parse each parameter occurs.

  • Index is parsed by ParserUtil#parseIndex(String)

  • Tag strings are parsed by ParserUtil#parseTags(Collection<String>) which calls ParserUtil#parseTag(String) iteratively for every string in the collection

Tags that do not exist inside the specified Transaction will be ignored, and if that results in no tags being deleted, an error will be shown to the user.

Again, since the user input is valid, a UntagCommand command object is created and executed. As a result, a copy of the Transaction object with the specified Tag deleted replaces the original in the TransactionList at the same position.

TagUntag1

The following activity diagram summarises what happens when the user executes a command to tag a Transaction.

TagActivity
Figure 4. Activity diagram of the tagging process

The untag command follows the same flow except for the following differences:

  • Continues with the operation only if at least some Tag objects exist in Transaction to be un-tagged

  • Ignores Tag objects that do not already exist in the Transaction as they are not valid for deletion

4.1.2. Design considerations

Aspect: Mutability of Transaction objects

The mutability of the Transaction objects will affect how well the code follows convention, and here are two designs that can be considered:

  • Alternative 1: (current choice): Create a copy of the Transaction object with modified tags and replace the original in the TransactionList

    • Pros: Adheres to the Open-Closed principle of the SOLID design principles, guarantees the resultant Transaction to be as expected and not modified incorrectly during the tag/untag process.

    • Cons: Creates a copy of the Transaction during the tag/untag process and increases the space complexity of the process.

  • Alternative 2: Modify the Transaction object directly to modify tags.

    • Pros: Modifies the Transaction object in place, thus the space complexity of the process is constant.

    • Cons: Violates the Open-Closed principle of the SOLID design principles, the Transaction object can be modified incorrectly during the tag/untag process.

Alternative 1 was chosen as we want to ensure the correctness of the tag/untag process.

Aspect: Uniqueness of Tag objects within a Transaction

The uniqueness of Tag objects within a Transaction will determine how meaningful a tag is, and here are two designs that can be considered:

  • Alternative 1: Do not check if the Tag objects exist(s) within a Transaction when adding Tag objects and implicitly allow duplicates.

    • Pros: Results in a simpler implementation of tag command, as there is no need to check for potential duplicates.

    • Cons: Makes each tag less meaningful as they are no longer unique.

  • Alternative 2: (current choice) Check if the Tag objects exist(s) within a Transaction when adding Tag objects, explicitly prevent duplicates.

    • Pros: Makes each tag more meaningful as they are unique.

    • Cons: Results in a more complex implementation of the tag command, as there is a need to check for potential duplicates.

Alternative 2 was chosen as we want to ensure that tags are meaningful to the user.

{End of extract from the section on the tag/untag commands}

{Start of extract from the section on the proposed AI extension}

4.2. [Proposed] Artificial Intelligence (AI)

Artificial intelligence (AI) is the simulation of human intelligence processes by machines such as computer systems. AI tries to mimic the learning process of humans in order to perform self-correction. We want THRIFT to possess the ability to make calculated decisions and provide the user with appropriate recommendations under the appropriate situations. This way, we can enhance the user experience and make our application more effective.

The following sections will describe how we plan to implement this feature and the possible design considerations.

4.2.1. Proposed implementation

Adhering to the Open-Closed principle, we will not modify the existing architecture but extend it. The following class diagram illustrates how we propose to do it, by adding 2 new classes to the existing architecture:

AIClassDiagram

The new classes will have the following roles:

  • AI: Analyses data from the Data and perform machine learning on it. If the need arises, feedback to the Data class to morph the data inside. Modifies the actions taken by the LogicManager class based on the machine learning model.

  • Data: Collects data from Thrift and formats to it to a form understandable by the AI class. It can be modified by the AI class in the process of machine learning.

4.2.2. Design consideration

Aspect: Portability of the learning model

The portability of the learning model will determine the complexity of the implementation and the following designs can be considered:

  • Alternative 1: Include the machine learning algorithms and models with the application

    • Pros: Allows the application to be portable and self-contained

    • Cons: Results in a larger application size

  • Alternative 2: Develop the machine learning algorithms and models as a separate application, which can be offloaded to a remote location

    • Pros: Allows the main application to be smaller in size

    • Cons: The application is no longer portable and self-contained, and have to depend on the availability of the remote application

We believe that both alternatives can be considered as choosing either option brings a different benefit to THRIFT. A hybrid implementation of both alternatives can also be considered. Regardless of what is eventually chosen, the user experience will be improved by artificial intelligence.

{End of extract from the section on the proposed AI extension}