PROJECT: ProManage
ProManage is a professional desktop application for companies that specialises in executing projects that undergoes a long and tedious planning phase. ProManage allow Manager and Employees of project teams to manage their team members and events easily. ProManage is optimized for those who prefer to work with a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI).
Overview
This project portfolio documents my contributions to the development of the CS2113 project, as part of my team T16-2.
Summary of contributions
-
Major enhancement: Extended undo and redo capability of the model via the
undo
andredo
commands to all existing or new commands.-
Justification: With so many commands with different purposes, it can be very easy to make mistakes with unintended effects. For example, if an employee was wrongly renamed, without the undo command, the employee’s original name would be irreversibly lost. Being able to undo or redo commands improves the flexibility of the app, where users do not have to be worried that their wrongly entered commands have irreversible effects.
-
Highlights: This implementation required an overhaul of the existing
Model
to enable intelligent undo and redo capability to both the address book and event list, where the model recognises which of them to undo or redo. As they are intertwined with all other modifying commands, extensive testing was required to harden the app against bugs and ensure that the commands worked properly in all scenarios.
-
-
Minor enhancement:
-
Created EditEvent command which allows the user to edit an event in the event list according to the parameters they entered. Regex was used to ensure that the input parameters were valid.
-
Created DeleteEvent command which allows the user to delete an event specified by its index on the filtered event list.
-
Created SelectEvent command which allows the user to select an event to show its attendees. It filters the person list on the UI to show only the attendees.
-
Implemented
VersionedEventList
class as a subclass ofEventList
but with historical record. -
Implemented
EventModel
interface andPersonModel
interface which are extended by theModel
interface.
-
-
Code contributed: [Functional code]
-
Other contributions:
-
Project management:
-
Helped to manage releases on GitHub
-
Managed the issue tracker on GitHub by creating new issues and closed existing issues that have been resolved.
-
Wrote test cases to increase coverage by 1.2% in total
PR 1: https://github.com/CS2113-AY1819S1-T16-2/main/pull/147
PR 2: https://github.com/CS2113-AY1819S1-T16-2/main/pull/148
PR 3: https://github.com/CS2113-AY1819S1-T16-2/main/pull/150
-
-
Documentation:
-
Updated the User Guide for
deleteEvent
,selectEvent
,undo
, andredo
commands. -
Updated the Developer Guide to include implementation and usage example of
undo
andredo
commands.
-
-
Community:
-
PRs reviewed and merged
-
Helped other teammates to debug their code
-
-
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. |
Deleting an event : deleteEvent
Priority level: default
,manager
Delete the specified event from ProManage.
Format:
deleteEvent EVENT_INDEX
Examples:
-
deleteEvent 2
Deletes the 2nd event in the address book.
Selecting an event : selectEvent
Priority level: all
Selects the specified event by index and displays all employees attending the event.
Format: selectEvent 1
If any of the attendees are edited or removed, the filtered employee list will not update. The |
Examples:
-
selectEvent 2
Selects the 2nd event in the address book and shows its attendees. -
invite 1 to/1
(invites an attendee to an event)
selectEvent 1
(select the event to show the attendee)
edit 1 n/New Name
(edit the name of the attendee)
selectEvent 1
(re-selects the event to show the updated name of the attendee)
Undoing previous command : undo
Priority level: all
Restores the event schedule or address book to the state before the previous undoable command was executed. Only commands that modify the entries in the event schedule or address book are able to be undone. The unfiltered event schedule and address book will be displayed after undoing the previous command.
Format/Prompts:
Enter a command: undo
Undoable commands: those commands that modify the event schedule’s or address book’s content ( |
Examples:
-
delete 1
list all
undo
(reverses thedelete 1
command) -
select 1
list all
undo
Theundo
command fails as there are no undoable commands executed previously. -
delete 1
clear
undo
(reverses theclear
command)
undo
(reverses thedelete 1
command)
Redoing the previously undone command : redo
Priority level: all
Reverses the most recent undo
command. Only commands that modify the entries in the event schedule or address book will be reversed. The unfiltered event schedule and address book will be displayed after redoing the command.
Format/Prompts:
Enter a command: redo
Examples:
-
delete 1
undo
(reverses thedelete 1
command)
redo
(reapplies thedelete 1
command) -
delete 1
redo
Theredo
command fails as there are noundo
commands executed previously. -
delete 1
clear
undo
(reverses theclear
command)
undo
(reverses thedelete 1
command)
redo
(reapplies thedelete 1
command)
redo
(reapplies theclear
command)
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. |
Undo/Redo Feature
Current Implementation
The undo
and redo
feature allows users to undo and redo their previous commands. The commands that can
be undone are only those that modify the contents of the address book or event list, such as adding a person
or event within the app. This is managed within ModelManager
.
The undo/redo mechanism is implemented separately for the address book and event list by the
VersionedAddressBook
and VersionedEventList
. They are extensions of the superclass AddressBook
and
EventList
respectively with a history of states stored internally as an addressBookStateList
for the address book, eventListStateList
for the event list, and a currentStatePointer
in each of them.
To ensure that the address book and event list are undone/redone correctly, a StateHistoryList
(sub-class
of LinkedList
) is kept in the ModelManager
to keep track of the list that was modified.
The following operations were implemented:
-
VersionedAddressBook#commit()
— Saves the current address book state in its internal history. -
VersionedAddressBook#undo()
— Restores the previous address book state from its internal history. -
VersionedAddressBook#redo()
— Restores a previously undone address book state from its internal history. -
VersionedEventList#commit()
— Saves the current event list in its internal history. -
VersionedEventList#undo()
— Restores the previous event list state from its internal history. -
VersionedEventList#redo()
— Restores a previously undone event list state from its internal history.
These operations are exposed in the Model interface as Model#commitAddressBook()
, Model#undoAddressBook()
and Model#redoAddressBook()
for the address book, and Model#commitEventList()
, Model#undoEventList()
and Model#redoEventList()
for the event list. Two wrapper methods, Model#undo()
and Model#redo()
are also implemented. These will be the methods called by the commands undo
and redo
.
The StateHistoryList
implements the following operations for the Model
to decide whether to
undo/redo the address book or event list:
-
StateHistoryList#getCurrentState()
- Retrieves the latest record state. -
StateHistoryList#getNextState()
- Retrieves the next (previously undone) record state. -
StateHistoryList#decrementPointer()
- Shifts record state pointer back. Called duringundo
. -
StateHistoryList#incrementPointer()
- Shifts record state pointer forward. Called duringredo
.
A command can either modify the address book, event list, or both. Within the model, the StateHistoryList
keeps track of all model methods which modify the address book or event list, and stores a history
record value within itself for each method called.
When undoing a command, the ModelManager
requests for the latest record by StateHistoryList#getCurrentState()
and calls the corresponding VersionedAddressBook#undo()
and/or VersionedEventList#undo()
. The ModelManager
also informs the StateHistoryList
that an undo command was issued to shift the pointer back.
When redoing a command,the ModelManager
requests for the next record from StateHistoryList
and
calls the corresponding VersionedAddressBook#redo()
and/or VersionedEventList#redo()
. The ModelManager
also informs the StateHistoryList
that an undo command was issued to shift the pointer forward.
Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
Step 1. The user launches the application for the first time. The VersionedAddressBook and VersionedEventList will be initialized with the initial address book and event list states, and the currentStatePointers will point to their single state respectively. The StateHistoryList is instantiated as an empty list.
Step 2. The user executes add
(with valid input arguments) to add a person to the address book. The add
command calls
Model#addPerson()
, which will add a state record index integer STATE_ADDRESSBOOK
to the StateHistoryList
.
This command is the last entered command, therefore the pointer
in StateHistoryList
shifts forward.
The add
command also calls Model#commitAddressBook()
, causing the modified state of the address book after
the add
command executes to be saved in the addressBookStateList
, and the address book currentStatePointer
is shifted to the newly inserted address book state. The currentStatePointer
for VersionedEventList
remains unchanged
as only the VersionedAddressBook
was modified..
If a command fails its execution, it will not call Model#commitAddressBook() or Model#commitEventList() ,
so the address book or event list state will not be saved into addressBookStateList or eventListStateList .
The StateHistoryList will also not save a record state.
|
Step 3. The user realises that they have accidentally added the wrong person, and decides to undo the action by
executing the undo
command. The undo
command calls Model#undo()
, which gets the latest record state
from StateHistoryList
and recognises that the VersionedAddressBook
was modified in the previous command.
The Model#undoAddressBook()
is then called, which shift the currentStatePointer once to the left, pointing it to
the previous address book state, and restores the address book to that state. Also, the pointer
in StateHistoryList
is shifted to the left by StateHistoryList#decrementPointer
.
If the currentStatePointer is at index 0, pointing to the initial address book state, then there are no previous
address book states to restore. The undo command uses Model#canUndo() to check if this is the case.
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:
Step 4.
The redo command does the opposite — it calls Model#redo()
, which shifts the currentStatePointer
in either
the VersionedAddressBook
and/or VersionedEventList
once to the right, pointing to the previously
undone state, and restores it to that state.
If the pointer is at index stateHistoryList.size() - 1 , pointing to the latest memory state, then
there are no undone states to restore. The redo command uses Model#canRedo() to check if this is the case.
If so, it will return an error to the user rather than attempting to perform the redo.
|
Step 5.
The user then decides to add a new event to the event list, by the addEvent
command. The addEvent
command calls
Model#addEvent()
, which will add a state record index integer STATE_EVENTLIST
to the StateHistoryList
.
This command is the last entered command, therefore the pointer
in StateHistoryList
shifts forward.
The addEvent
command also calls Model#commitEventList()
, causing the modified state of the event list after
the addEvent
command executes to be saved in the eventListStateList
, and the event list currentStatePointer
is shifted to the newly inserted address book state. The currentStatePointer
for VersionedAddressBook
remains unchanged
as only the VersionedEventList
was modified.
Step 6.
The user then decides to execute clear
. Since this command must clear both the address book and event list,
the clear
command calls both Model#commitAddressBook()
and Model#commitEventList()
. It adds a state
record index integer STATE_BOTH
to the StateHistoryList
. The currentStatePointer
in both
VersionedAddressBook
and VersionedEventList
shifts to point to the newly added states respectively.
Future Improvements
Façade Class for VersionedAddressBook and VersionedEventList
To improve abstraction and shift the responsibility of deciding which list to undo or redo away from the
Model
, a façade class can be created, e.g. VersionedProManage
, to hold the StateHistoryList
,
VersionedAddressBook
and VersionedEventList
. The model will then only have to call the VersionedProManage#undo()
or VersionedProManage#redo()
methods.