PROJECT: Splitwiser

Overview

For our Software Engineering project, my team, comprising of 5 students (including myself), was given the codebase for a command line desktop application called 'Address Book', where we were required to morph this given codebase into an application of our choice. We chose to morph this application into a group expense management app called 'SplitWiser'. This enhanced application enables people to simplify expense and debt tracking in group activities, so that people will know exactly how to split the bill at the end of the activity.

Our product has the following main features:

  • Create, update and delete Contacts and Activities

  • Add expenses to activities

  • Add/remove participants from activities

  • Automatic splitting of bills between participants

  • Settle debts within an activity

  • Auto-saving of all data

This is what our project looks like:

Ui
Figure 1. Sample screenshot of SplitWiser

I was responsible for creating the activity class, and the entire infrastructure required to support it. It is the foundational feature of our product, as it stores all information necessary to handle bill splitting. This makes my role particularly crucial as all features related to bill splitting interacts with this class. Furthermore, I created the functionality to create activities, and updated the find functionality to search based on context. The following sections describe my contributions in greater depth, in addition to the documentation that I added pertaining to the aforementioned features.

The table below provides a quick summary of the symbols and formatting used in this portfolio.

code

Command that can be typed into the command box, or indicates a component, class or object in the architecture of the application.

Additional information that is good to know.

Summary of contributions

This section shows a summary of my coding, documentation, and other helpful contributions to the team project.

Major Enhancements

  • Activity Class: I created the Activity class and all its dependencies.

    • What it is: Activity class stores all information for an activity, such as Title, Participants, and Expenses.

    • Justification: This feature is the foundation of our product. Activities contain all necessary information necessary to handle bill splitting.

    • Highlights: Activities stores the following information:

      • Title: Title of the activity. Compulsory field.

      • Participants: Contacts that took part in this activity.

      • Expenses: Expenses incurred by this activity.

  • Storage of Activity: I created the infrastructure that allows local storage of Activities.

    • What it is: Activities can now be locally stored as a ActivityBook.json file. Activities can also be loaded from this file.

    • Justification: Similar to how Contacts are stored locally, Activities should also be saved, so that it would not disappear when the application is closed. Without it, the program is unusable.

    • Highlights: Activities are stored locally, and can be loaded anytime. Validations are executed on every load, preventing corrupted or invalid save files from being loaded.

  • Create Activity: I added the ability to create Activities.

    • What it does: The Activity command allows the user to create new activity.

    • Justification: This is the foundational feature of our product. Activities contain all necessary information needed to handle the splitting of bills, and we need to provide users the ability to create activities to make use of our product.

    • Highlights: Activity command that allows users to create new activities. Users can specify the title of the activity, and can invite contacts into the activity as participants. In order to improve usability, contacts are first searched using exact-matching, and then keyword-based matching if no valid exact match is found.

  • Search functionality with context: I added the ability to search using context.

    • What it does: The find command allows the user to search based on current context.

    • Justification: This feature significantly increase the usability of our product. As time progresses, the application will have many contacts and activities, often similar in nature, therefore having the ability to find the activity you want among many different similar matches is invaluable.

    • Highlights: Find command that searches based on the current context. When viewing a list of activities, find command searches for activity. When viewing a list of contacts, it searches for contacts instead. Uses keyword-based matching.

Other contributions

  • Project management:

    • Improved gradle configuration to show test results (Pull Request #81, #191)

  • Documentations:

    • Added User guide for Activity Command

    • Added section on Activity feature for Developer Guide

  • Community:

    • Reviewed Pull-Requests with non-trivial review comments: (Pull Request #122, #80 )

Contributions to the User Guide

Given below are sections I contributed to the User Guide. They showcase my ability to write documentation targeting end-users.

Create new activity: activity

This command creates a new activity with a title, contacts (optional) and no expenses.

  • Additional contacts can be added to the activity by using p/ prefix.

    • Initially, the application will search for contact with exact matching name.

    • If no exact match is found, keyword matching is used instead. Refer to Finding contacts or activities: find for more details.

    • For a contact to be successfully added, given keywords must have exact 1 matching contact. Otherwise, the activity will be created without adding the contact suggested by the keywords, and warning message will be shown.

  • Changes the current view to this activity (as if view a/ACTIVITY_ID was called).

Format: activity t/ACTIVITY_TITLE [p/PERSON …​]

Example:

Suppose that you went for a breakfast with David and Alexa, and want to create an activity for it.

Step 1: Type in the following command into the command box, and press Enter to execute it.

activity t/Breakfast p/David p/Alexa

CreateActivityStep1

Step 2: Observe the output to check if it was successful, and if there are any warnings. Check the bottom display window for details of the newly created activity. In this case, because there is no contact called "Alexa", it is not added to the activity and a warning message is shown.

CreateActivityStep2
If none of the search terms are valid, an activity will still be created, without any participants.

Finding contacts or activities: find

This command finds contacts or activities whose name or title respectively contain any of the given keywords.

Format: find KEYWORD …​

  • Find command can only be used when viewing list of activities or contacts.

  • Find command will search for activities if used under list activity view. If used under list contact view, the find command will search for contacts.

  • The search is case insensitive. e.g hans will match Hans

  • The order of the keywords does not matter. e.g. Hans Bo will match Bo Hans

  • Only the name of contacts and title of activities are searched.

  • Only full words will be matched e.g. Han will not match Hans

  • Contacts and activities matching at least one keyword will be returned (i.e. OR search). e.g. Hans Bo will return Hans Gruber, Bo Yang

Example:

Suppose that you want to find all the contacts that are called "John".

Step 1: List all contacts in this app by typing in the following command into the command box, and press Enter.

list c/

FindStep1

Step 2: Check that the result box shows the success message, and the bottom display box lists all contacts. This means that you are currently in the "list contact" context.

FindStep2

Step 3: To find all contacts with "John", type in the following command into the command box, and press Enter.

FindStep3

Step 4: Check that the result box shows the success message and the number of matches found, and that the bottom display box shows all matching contacts.

FindStep4
In order to search for activities, replace the command in step 1 with list a/ in order to switch to "list activity" context.

Contributions to the Developer Guide

Given below are sections I contributed to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project.

Activity class

Activity is a class used to store activities and their details. It contains the following information:

  • Primary Key

Each Activity has a unique primary key used by external classes to identify and access them, without unnecessary dependencies.

  • Title

Title of the activity is stored using Title, which runs validations to ensure that the title is valid. Currently, the only restriction for the title is that it can’t be blank.

  • Participants

Participants are saved internally under participantIds, which only stores the primary key of Person involved.

  • Expenses incurred by this activity

Expenses are stored as Expense. Refer to [Expense class] for more details.

  • Outstanding balance for each participant

The balance of each participants are stored internally under participantBalances.

All Activity objects are stored in ActivityBook.

The following class diagram describes the implementation of Activity.

ActivityClassDiagram
Figure 2. Structure of the Activity class

Design Considerations

Aspect: Storage of participants
  • Alternative 1 (current choice): Only storing the primary key of participants

    • Pros: Minimizes dependency and potential bugs.

    • Cons: More difficult for external classes to retrieve participant objects if necessary.

    • Reason for choice: Storing entire object can potentially cause bugs, as there are no mechanisms to ensure the consistency of data between Person object in Activity and AddressBook.

  • Alternative 2: Storing the entire Person object inside the Activity.

    • Pros: Easier to implement. Object can be easily retrieved when necessary.

    • Cons: Creates extra dependency between classes. More prone to error.

Create activity feature

Mechanism to create activity is facilitated by Activity. It extends AddressBook with an ActivityBook, stored internally as an activityList. Additionally, it implements the following operation:

  • Activity() — Constructor used to create a new Activity.

All Activity models are stored inside ActivityBook.

When creating an activity, the title must be specified. It is optional to include participants, as they can be invited separately using the invite command. Refer to [Invite/Disinvite feature] for more information.

Adding of participant uses both exact-match and keyword-based search. First, the search term passed in is used to find an contact with an exact matching name. If no exact match is found, the search term is split into keywords via whitespace. Obtained keywords are then used to search the AddressBook for a matching person. The person is added in as a participant only if there is one exact match. Otherwise, a warning message will be displayed and no participant will be added for this set of keywords.

The aforementioned mechanism was applied to minimize friction for users in using this feature. In early stages of development, it was concluded that making users type in the full name every time was seen as overly tedious. Making use of keyword-based search ensures that the users will only have to type in minimum amount of information to add a person.

Exact matching is always used first to overcome a specific edge case where a contact could not be added, if its name was a substring of another contact’s name. Using exact-matching before applying keyword-based matching helps avoid this problem.

Here is a sample use case in creating an expense:

Step 1. The user launches the application for the first time. The SplitWiser will be initialized with the initial address book and activity book state.

Step 2. The user executes activity t/Breakfast p/David command to create an activity named Breakfast in the activity book, with David as a participant.

Step 3. The user executes activity t/Lunch p/Alex p/David command to create an activity named Lunch in the activity book. In this case, as there are two contacts with the name "Alex", this search term is considered invalid, and only David is added into the Lunch activity as a participant.

The following sequence diagram demonstrates how creating activity works:

CreateActivitySequenceDiagram
Figure 3. Sequence diagram for creating an activity
The lifeline for ActivityCommand should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.

The following activity diagram encapsulates the high level logic of the create activity operation.

CreateActivityActivityDiagram
Figure 4. Activity diagram for creating an activity

The following activity diagram encapsulates the high level logic of finding the correct contact to add as participant, for each keyword supplied.

AddParticipantActivityDiagram
Figure 5. Activity diagram for searching contacts to add as participants

Design Considerations

Aspect: Finding the correct contact to add
  • Alternative 1 (current choice): Users can use keywords to specify the contact to be added.

    • Pros: Improves usability.

    • Cons: Difficult to implement. Necessary to handle multiple edge cases.

    • Reason for choice: Forcing users to specify exact full name was deemed to be too tedious, especially when adding multiple participants.

  • Alternative 2: Users must type in the exact full name of the contact to be added.

    • Pros: Easy to implement. Low possibility of bugs.

    • Cons: Significantly decreases usability of the application.

Find feature

The find mechanism is facilitated by the combination of three classes MainWindow, Model and Context.

Context is an immutable utility class that describes the nature of the current view of the app. Refer to [List feature] for more information about Context.

During execution, the Find command checks the current Context from Model. If the current context is LIST_ACTIVITY, the command will search for Activity using the supplied search terms. If the current context is LIST_CONTACT, the command will search for Contacts using the supplied search terms. Otherwise, the command is used on invalid context and will throw an error. If the command is executed in a valid context, it will update the FilteredList to contain only the matching results. As MainWindow tracks both FilteredList, JavaFX will automatically re-render the contained card entries when the respective list undergoes structural changes.

The Find command uses keyword-based matching to search for the correct results. If any of the keywords in the search term matches the title/name of the activity/contact, it will be included in the search results.

Given below is an example usage scenario of a user intending to find a specific activity.

Step 1. The user launches the SplitWiser application for the first time, which defaults to displaying the list of contacts.

Step 2. The user executes the command string list a/ to prompt the app to display the full list of activities.

Step 3. The user executes the command string find keven to prompt the app to search for all activities that contains the word "keven" in the title.

The following activity diagram encapsulates the high level logic of the find operation.

FindCommandActivityDiagram
Figure 6. Activity diagram for find command

Design Considerations

Aspect: How to specify search type (Activity/Contact)
  • Alternative 1 (current choice): Rely on current context to decide which type of object to search for. Only works when in either of the list context.

    • Pros: More intuitive for user, since the find command searches for the current type of object being viewed. Less typing is needed if user wants to continuously search in the same context.

    • Cons: Harder to implement, extra dependencies with Context class.

    • Reason for choice: Users are more likely to carry out multiple searches, especially when creating an Activity and searching for correct contacts to add.

  • Alternative 2: Pass in an extra argument to specify what type of object one is looking for.

    • Pros: Easier to implement, reduced dependency.

    • Cons: Command gets less user-friendly and longer. Successive find command gets tedious to implement.