PROJECT: Naggy Joel


Overview

Naggy Joel is a desktop social life manager application for University students to manage their social life and studies. The user interacts with it using a CLI, and it has a GUI created with JavaFX. Naggy Joel allows users to keep track of contacts, restaurants, school deadlines, and events - 4 different but closely related aspects of a University student’s life.

Summary of contributions

Major Enhancements

  • Added the feature to allow users to add/edit/delete restaurants

    • What it does: Allows the user to add/edit/delete a restaurant with the specified name, location, visited status, type of cuisine, price point, and operating hours

    • Justification: Other then keeping contacts, students might want to take note of goods restaurants that they visited and restaurants that open at odd hours (when they need food studying late) and these features allow the student to do that

    • Highlights 1: This set of enhancements comprise a huge amount of code as it required building the Restaurant and RestaurantBook model from scratch, this also required a thorough understanding of restaurants and how they differ from contacts

    • Highlights 2: To maintain consistency of the code, an in-depth understanding of RegEx expressions for validation of input parameters was also required and had to be learnt

    • Credits: {referenced the original AddressBook and Person model}

  • Added the feature to allow users to undo/redo previous commands

    • What it does: Allows the user to undo all previous commands one at a time; preceding undo commands can be reversed by using the redo command

    • Justification: This feature improves the product significantly because a user can make mistakes in commands and this provides a convenient way for users to rectify them

    • Highlights 1: This enhancement affects exiting and future commands, and hence required an in-depth analysis of design alternatives (for example, how do we want to modify ModelManager, how do we want to store the states, what is the impact on memory, what are the commands that changes the state, etc)

    • Highlights 2: The implementation was also challenging as it required a manual inspection and changing of the 20 commands that changes the state of the model (for example, adding an item into an ArrayList had to be done by first creating a new ArrayList and copying all existing items instead of simply adding the new item into the ArrayList, etc)

    • Highlights 3: This enhancement also had to consider what was shown on the GUI as an indication/confirmation to the user that the command was indeed undone/redone

Minor Enhancements

  • Edited the edit/addnote/editnote/deletenote command for AddressBook

    • What it does: Shows the details of the contact edited upon successful command execution

    • Justification: The list of contact shows only the name, tags, and phone number; showing the user the details of these contacts when the user edits the user provides visual confirmation of the successful execution of the commands

    • Highlights: These 4 commands that show the details of a contact changes the state of the data while the (ab)get command does not change the state of the data; hence a careful distinction between the 2 is essential to ensuring undo/redo works properly

  • Reworked the clear command to clear all entries from the entire application

    • What it does: Clears all entries in the entire application instead of just the AddressBook

    • Justification: clear as intuitively understood should be to clear the entire application and not just one portion (AddressBook) of the application

    • Highlights: As the only command that does more than modifies more than 1 area of the model (clears AddressBook, RestaurantBook, SchoolworkTracker, EventTracker), it had to be implemented properly to trigger only 1 state change, and not 4 for undo/redo to work properly

Code Contributed: [Code]

Other Contributions

  • Enhancements to existing features

    • Wrote additional tests for existing features to increase coverage (PR PR #192)

    • Re-organised layout of the help command to group similar commands (PR #77, #181)

    • Edited the GUI of the application (PR #171)

  • Documentation

    • Contributed to both the User Guide and Developer Guide for this project

  • Contribution to team-based tasks

    • Updated the User Guide to match the application for Week 11’s dry run (Commit e909a72)

    • Designed and modelled the initial UI (Commit 463ab1c)

    • Tested all the features in the application for potential bugs

  • Community

    • Contributed to forum discussions (Example: #116)

    • Reported bugs and suggestions for other teams in the class (Repository PED)

    • Reviewed, merged PRs, helped team debug & gave suggestions on features enhancement

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.

Undo and redo commands: undo/redo

Undoes and redoes the last command.
Format: undo/redo

Clearing all entries : clear

Clears all entries from the address book, schoolwork tracker, events book and restaurant book.
Format: clear

Adds a new restaurant : (rt)add

Adds a new restaurant
Format: (rt)add n/NAME l/LOCATION v/VISITED [o/OPERATING_HOURS] [p/PRICE_POINT] [c/CUISINE]

  • VISITED can only be "Yes" or "No"

  • OPERATING_HOURS must be written in HHmm:HHmm format

  • PRICE_POINT contains only dollar signs

    • There are 3 price points, each one distinguished according to the number of dollar signs

      • The lowest price point is $

      • The medium price point is $$

      • The highest price point is $$$

Examples:

  • (rt)add n/rubbish l/bedok o/0900:2300 p/$$ v/No
    Adds a new restaurant called rubbish at bedok with 2 dollar signs price point and opens from 9am to 11pm, and has yet to be visited.

Edits a restaurant: (rt)edit

Edits a restaurant from the list
Format: (rt)edit INDEX [n/RESTAURANT] [l/LOCATION] [v/VISITED] [o/OPERATING_HOURS] [p/PRICE] [c/CUISINE]

  • INDEX must be a positive integer. It corresponds to the INDEX of the restaurant as shown when you list all restaurants in the Restaurant Book using the list function.

  • At least one optional field needs to be specified.

  • Specify nothing after the prefix to delete the field (RESTAURANT, LOCATION, VISITED cannot be deleted)

Example:

  • (rt)edit 1 o/0700:2200
    Edits the 1st restaurant’s operating hours to "0700:2200" in the restaurant book.

  • `(rt)edit 3 p/ `
    Removes the 3rd restaurant’s price point.

Deletes a restaurant: (rt)delete

Deletes a restaurant from the list
Format: (rt)delete INDEX

Example:

  • (rt)delete 1
    Deletes the 1st restaurant in the restaurant book.

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.

(Joel Lim Hee Heng: 3.7, 3.8)

Restaurant Book

Current Implementation

The RestaurantBook helps users keep track of Restaurants, so that users can quickly find an eating place given their constraints (e.g. odd hours, location, food cravings, etc). We will briefly introduce the implementation and logic behind this component as a stepping stone to understand the undo/redo implementation in the next section.

The following depicts the class diagram for the Restaurant component of the application.

rtClassDiagram

Each Restaurant has 9 components Name, Location, Visit, Hours, Price, Cuisine, and 3 lists of Note to represent the recommended, good, and bad food that the user can input. visit represents whether the user has visited the restaurant, and hours represent the restaurant’s opening hours. The other fields are self-explanatory.

We will illustrate how commands to the RestaurantBook works using the simple (rt)add command, which adds a new Restaurant to the RestaurantBook.

rtAddActivityDiagram

The above shows a medium level abstraction of the activity diagram with swim lanes when the user enters a valid add restaurant command. Even though the command is valid, the parameters might not be, or the restaurant entered might be a duplicate (duplicate iff the name and phone matches). Hence, the corresponding units check for these executing the command.

Once these checks pass, before the ModelManager adds the new restaurant in, the ModelManager first does some housekeeping to support the undo/redo operation as seem in the activity diagram. This is the key we want to illustrate here to better understand undo/redo. This is very useful because all commands that change the state will go through a similar process. More details are given below.

Undo/Redo

Current Implementation

This is the Class Diagram for the ModelManager and the ModelState, the 2 integral classes to understand the undo/redo operations. Note that we have omitted methods used to support other commands but are irrelevant to the undo/redo operation and also omitted the getter methods of the ModelState for better readability.

The undo/redo mechanism is facilitated by modifying ModelManager to include 3 additional attributes. undoStack: which stores the lists of ModelState from which the undo operation will draw upon redoStack: which stores the lists of ModelState from which the redo operation will draw upon ** currentModel: which is simply the ModelState that the application is currently in

ModelManagerClassDiagram

We will now go through a very simple example of how the undo/redo operation is implemented. Assume the following is the initial state:

  • Step 1: When the user launches the application for the first time, the undo stack is initialised to only 1 state, which is the current state. The redo stack will always be empty initially.

undo(1)
  • Step 2: Suppose the user executes (rt)add n/KFC l/East v/No to add a new restaurant. This is a command that changes the state of the model. Hence, the application will execute a series of commands that is equivalent to duplicating m0, pushing the duplicate into the undo stack, and then making the currentModel pointer point to the top of the stack. All changes will then be made to this new duplicate, with the previous state m0 intact at the bottom of the undo stack. The redo stack still remains empty.

undo(2)
  • Step 3: Now, suppose the user executes the undo command. The ModelManager will simply push the current state m1 into the redo stack, and make the current state pointer point to the resultant top element in the undo stack m0, which will be the state before the user entered the last command, effectively restoring the previous state. The redo stack simply stores all the undone states.

undo(3)

Note that if the user executes the undo command when the undo stack has only 1 state, an error will be returned.

  • Step 3.1: Now, if the user then executes the redo command, the application will simply push the top state m1 in the redo stack into the undo stack, and make the current state pointer point to the resultant top state in the undo stack again.

undo(3.1)

Note that if the user executes the redo command when the redo stack is empty, an error will be returned.

  • Step 3.2: However, suppose if instead of executing the redo command, the user executes another command that changes the state such as (rt)delete 1, apart from modifying the undo stack as we mentioned earlier when the user executes a command, the redo stack will also be emptied, since there is nothing to redo anymore.

undo(3.2)

This is a brief summary of how the undo/redo operation is implemented. In reality, the type of command that caused the change is also stored in the ModelState. This is to ensure that we can display the appropriate visual confirmation in the UI when the user executes an undo or redo command. For example, when the user redoes a command that added a restaurant, we will want to display the lists of restaurants instead of whatever the user is looking at now.

For greater detail, below is the sequence flow diagram when the user executes an undo command:

undoSequence(1)
undoSequence(2)

redo is simply the opposite of undo . Instead of popping from undo stack and pushing into redo stack, ModelManager pops from the redo stack and pushes the popped state into the undo stack. The current state is still the top of the undo stack.

The update() simply makes the other attributes of the ModelManager point to the corresponding ones in the current state. That is, to update them to point to the current copy. This makes the code cleaner as we don’t have to always access them from the currentModel pointer.

Lastly, do note that there are 2 types of commands: Commands that alters the state (e.g. add, edit): the ModelManager will do the housekeeping to duplicate the state before executing these commands Commands that do not alter the state (e.g. list, get): these commands do not alter the state, and the ModelManager will simply execute these commands without duplicating the state

Design Considerations

Aspect: How undo & redo executes
  • Alternative 1: Creates and saves the entire state of the application from scratch

    • Pros: Easy to implement

    • Cons: Requires huge amount of memory

  • Alternative 2: Stores only the individual command entered

    • Pros: Minimal memory usage

    • Cons: More difficult to ensure correctness as there will be many more alternate paths (scales as the number of commands go up)

  • Alternative 3 (current choice): Partially stores the entire application

    • Pros: Easy to implement

    • Cons: Require significant amount of memory, but less than alternative 1

For alternative 3, the current implementation seems to store the entire application, but in reality, the end objects that the pointers in each ModelState are pointing to are largely the same. For example, suppose we have 1,000 persons in the AddressBook and we edit person 1000. The new ModelState actually points to the same Person object for the 1st 999 objects and only differs in the 1000th object.

Aspect: Which commands will cause a state change
  • Alternative 1: Every command will generate a new state

    • Pros: Easy to implement (need not differentiate); can be more intuitive to some users (e.g. the user accidentally (rt)list and would like to go back to what the user was viewing previously)

    • Cons: Requires more memory; can be less intuitive to some users as the undo for commands such as (rt)list only changes what the user sees but does not perform any real changes to the state

  • Alternative 2 (current choice): Only commands that change the state will cause state transitions

    • Pros: Less memory usage, can be more intuitive to some users; in reality, difficult to tell which is more intuitive to users without more data

    • Cons: More difficult to ensure correctness as we need to ensure the exact set of commands that cause state transitions

Aspect: What to show when the user executes a undo or redo
  • Alternative 1: Simply show the same screen

    • Pros: Easy to implement, need to only take care of the backend without worrying about the UI; user might not want to move away from his/her current screen

    • Cons: User has no confirmation that the undo did happen (e.g. if the user undoes adding a restaurant but we don’t show the updated restaurant list)

  • Alternative 2 (current choice): Displays what the user has undone/redone (e.g. if the user undoes adding a note to a contact, we should show the details of that contact upon the undo operation)

    • Pros: Gives user confirmation of the new data and that the command is indeed undone

    • Cons: More difficult to implement and requires more memory as we will have to store more information to flash to the user the correct screen that corresponds to the command