PROJECT: AddressBook - Level 3


About the Project

My team of 4 software engineering students and I were tasked with either enhancing or morphing a basic command line interface desktop address book application for our Software Engineering project. We decided to morph the application, keeping a similar underlying architecture but changing the purpose entirely.

We are living in a fast paced, stressful world where things are constantly changing; and so are our tasks. We thought it would be helpful to have an application to aid us in managing these tasks, so we chose to morph the address book into a student assistant with a personality to liven up our dreary days, and we called her ELISA (Exceptionally Loud and Intelligent Student Assistant). This application enables users to add and track various tasks and events, as well as receive reminders for them. The assistant is in the form of a chat bot, and she has been given a sassy and humorous personality in order to enhance the user experience.

This is how our user interface looks like:

taskpanel annotated

My role was to implement the undo and redo functionality as well as give ELISA more of a personality.

Summary of contributions

  • Code contributed: My contributions can be found here

  • Enhancement added: added the ability to undo/redo previous commands

    • Function: The undo command allows the user to undo a previous command. The user may reverse this undo command with the redo command.

    • Justification: In the event that users have made a mistake or changed their minds about executing a command, the undo command enables them to reverse that command. If they change their minds again and decide to execute the command after all, then the redo command enables them to do so easily.

    • Highlights: The main challenge was to refactor the command from the simpler state method to the inverse function method in order to store only one reference to all other components.

    • Implementation Details:

      • Initially I had implemented this feature by storing the state of the application after each command execution, and the undo command would set the state of the application to a previous one, almost like the application was time travelling. However, this logic interfered with my teammate Bryan’s feature, reminders, resulting in duplicate reminders. Reminders work by storing pending reminders in a list and past reminders in another list. The previous implementation of the undo feature worked by clearing the model and repopulating it after every command execution. However, this method or clearing and repopulating the model did not properly account for the items in the pending and past reminders list. This caused the reminder to be added multiple times and thus there were duplicate reminders.

      • In order to ensure that the undo redo feature worked correctly and did not interfere with any other features, I rewrote the logic of the feature. This time, there was no time travelling backwards. The undo command only made another modification to the application, instead of reversing a previous modification. This was done by ensuring that all commands had a reverse function, which would exactly reverse the effects of that command’s execution. Naturally, redo would simply run the execution again.

  • Enhancement added: gave ELISA a personality and sense of humour

    • Function: For each command, ELISA gives either a confirmation or an error message. I made the messages humorous or sarcastic, instead of plain and emotionless. Additionally, ELISA will provide the user with a randomly selected joke upon request.

    • Justification: ELISA is able to provide a sense of companionship for the user, and the jokes can help relieve stress and provide a welcome break.

  • Other contributions:

    • Enhancements to underlying implementations:

      • Refactored the packages and the parser so that they were no longer named address book, they are now called elisa.

      • Moved and renamed some image and text resource files to make sure that the application works when packaged as a jar file

    • Documentation:

      • Updated the user guide with information on how to use the undo and redo commands

      • Updated the developer guide by giving the implementation of the undo function as well as filling in use cases and user stories

    • Reviewed the following pull requests:

      • PR #116: Made a comment on cleaning up code by deleting it instead of commenting it out

      • PR #132: Suggested to make the command non-case-sensitive, which was adopted.

      • PR #136: Gave a suggestion to shorten the command word, which was adopted as an option along with the original long form

      • PR #156: Proposed to change the colour of the dialog to something less abrasive and gave an alternative colour option. This suggestion was adopted

    • Design:

      • Reviewed and gave suggestions on how to improve the ELISA logo my teammate Chengyi created, which were implemented in the final version of the logo. The logo before, which looks cute and innocent

][

and the logo after, which is more confident and sassy

][
  • Created a chime sound for the reminder function, used in PR #157

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.

Undoing the latest modification(s) : undo

Reverts the latest commands given on the ELISA.
Format: undo

  • undo can only be done if commands have been executed

Examples:

  • undo
    Undoes the last command

Redoing the latest undone command(s) : redo

Re-executes the latest undone commands given on the ELISA.
Format: redo

  • redo can only be done if undo has already been executed

  • After undo, if a new command is executed then redo cannot be executed

Examples:

  • redo

Asking ELISA for a joke : joke

Need to amuse yourself for a bit? Simply use the joke command and ELISA will select a joke from her database for you to enjoy!

  • Note that each joke is randomly selected from the database and may repeat.

Example:

  • joke

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.

[Implemented] Undo/Redo Feature

Current Implementation Logic

The undo function uses the revert command method without using states and history, unlike the proposed method. This is because an issue was encountered with referencing lists and firing reminders multiple times when the state history method was used.

In this implementation, the commands that can be undone; that is, all the commands except UndoCommand, ExitCommand, UpCommand and DownCommand now extend from an abstract class UndoableCommand, which is a subclass of Command. Subclasses of UndoableCommand must implement a method reverse(ItemModel model), which should do the exact opposite of the execute(ItemModel model) in that Command.

The command execution history is stored in a stack, which is maintained in ElisaCommandHistory.

The activity diagram below shows the flow of the undo command.

][

Below is a possible usage scenario and the app behaviour.

Step 1. The user executes task eat. A task with description "eat" is added and then the command is pushed into the commands stack.

][

Step 2. The user realises that adding the task was a mistake, and decides to undo by entering undo into ELISA. The undo command will pop the AddTaskCommand from the stack and reverse the effects of that command, in this case by deleting the task "eat" from the TaskList.

][

Step 3. After successful execution of the UndoCommand a confirmation message is displayed in the chat box.

Of course, the undo feature has its counterpart, the redo command. The commands to be redone are stored in an additional stack within ElisaCommandHistory, and when the redo is done, it executes the command again, which reapplies the most recent change. Every time a new UndoableCommand is executed, the redo stack is cleared. That is, the redo stack is only non empty when the latest executed Command(s) is/are UndoCommand(s).

The activity diagram below shows the flow of the redo command.

][

[Previous] Undo/Redo feature

Previous Implementation [Has since been refactored]

The undo/redo mechanism is facilitated by ElisaStateHistory. It stores a stack of type ElisaState, which keeps track of the app data with a deep copy of the ItemStorage and the current displayed list with a deep copy of VisualizeList. Additionally, it implements the following operations:

  • ElisaStateHistory#push() — Saves the current ELISA state in its history.

  • ElisaStateHistory#pop() — Removes and returns the latest state as long as there is more than 1 item in the stack. This 1 item is the original application state that is saved on startup.

  • ElisaStateHistory#size() — Returns the number of states in the stack.

Given below is an example usage scenario and how the undo mechanism behaves at each step.

Step 1. The user launches the application for the first time. The ElisaStateHistory will be initialized with the initial ELISA state

][

Step 2. The user executes task eat command to add a task with a description "eat". Upon execution of any command except undo and show, the Logic calls ItemModel#updateState() to save the latest state to ElisaStateHistory.

][

Step 3. The user executes task jogging to add another task with description "jogging". This command also calls ItemModel#updateState(), causing another modified ELISA state to be saved into the ElisaStateHistory.

][
If a command fails its execution, it will not call ItemModel#updateState(), so the ELISA state will not be saved.

Step 4. The user now decides that adding the task "jogging" was a mistake, and decides to undo that action by executing the undo command. The undo command will call ElisaStateHistory#popCommand(), which will delete the last added state, and Logic will update the application state to the one at the top of the stack after command execution.

][
If the size of ElisaStateHistory is 1, that is, only containing the initial state, then there are no previous Elisa states to restore. The undo command checks for this case within its execute() method. If so, it will return an error to the user rather than attempting to perform the undo.

The following sequence diagram shows how the undo operation works:

UndoStateSequence

Design Considerations

Aspect: How undo & redo executes
  • Alternative 1 (initial choice): Saves the entire Elisa state.

    • Pros: Easy to implement.

    • Cons: May have performance issues in terms of memory usage, especially when a large number of items are added.

    • Another Con found after implementation: Reminders fire multiple times due to storing multiple references to items

  • Alternative 2 (current choice): Individual command knows how to undo/redo by itself.

    • Pros: Will use less memory (e.g. for delete, just save the item being deleted).

    • Cons: We must ensure that the implementation of each individual command are correct.

Appendix A: User Stories

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

Priority As a …​ I want to …​ So that I can…​

* * *

user

add a new task

record tasks that need to be done 'some day'

* * *

user

mark a task as done

keep track of my remaining tasks

* * *

user

delete a task

remove tasks that I no longer need

* * *

student

add deadline to a task

remember my task deadlines

* * *

user

find upcoming tasks

decide what needs to be done

* * *

user

find a task by description

find only the tasks that are relevant to me at that point in time

* * *

new user

view more information about a command

learn how to use various commands

* * *

forgetful student

be reminded of deadlines

remember to complete them before they are due

* * *

user

type my commands in the text

use the app without needing the mouse

* * *

user

use the undo function

reverse any changes I made by mistake

* *

busy student

see my reminders as notifications

be reminded of them even in other applications

* *

user with many tasks

sort tasks by priority

identify which tasks require my immediate attention

* *

student

turn on priority mode

focus on only one pressing issue at a time

* *

user

have a software that saves after every action

will not lose information even if I close the program by accident

* *

user

look at a summary of all deadlines in the calendar

see when I am free

* *

user

edit the date of a deadline

fix my mistakes if I type the wrong command

*

stressed student

ask ELISA to tell a joke

feel less stressed when my assistant has a sense of humour

*

user

colour code my calendar events

easily categorise and differentiate between them

Use Case 006: Search for a task by its description

MSS

  1. User enters the command to show the task list.

  2. ELISA shows the reminder list.

  3. User enters command to find all matching tasks with the given search term(s)

  4. ELISA shows a list of tasks with descriptions matching the search term(s)

Extensions

  • 4a. There are no matching tasks

    • 4a1. ELISA shows that there are 0 items listed

      Use case ends.

Use Case 007: Undo the last command

MSS

  1. User enters the undo command.

  2. ELISA reverts the last executed command.

  3. ELISA displays a confirmation message.

Extensions

  • 2a. There are no commands to be undone

    • 2a1. ELISA displays an error message.

      Use case ends.

Use Case 008: Using Priority Mode

MSS

  1. User enters the command to enter priority mode.

  2. ELISA hides all tasks except the one with the highest priority.

  3. User enters command to set that task as done once they finish it.

  4. ELISA shows the next highest priority task.

Extensions

  • 1a. User is not viewing the task list

    • 1a1. ELISA displays an error message

      Use case ends.