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; precedingundo
commands can be reversed by using theredo
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 ensuringundo/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
-
Documentation
-
Contributed to both the User Guide and Developer Guide for this project
-
-
Contribution to team-based tasks
-
Community
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]
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]
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.
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
.
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
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.
-
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 duplicatingm0
, pushing the duplicate into the undo stack, and then making thecurrentModel
pointer point to the top of the stack. All changes will then be made to this new duplicate, with the previous statem0
intact at the bottom of the undo stack. The redo stack still remains empty.
-
Step 3: Now, suppose the user executes the
undo
command. TheModelManager
will simply push the current statem1
into the redo stack, and make the current state pointer point to the resultant top element in the undo stackm0
, 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.
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 statem1
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.
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.
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:
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
-