Hello there

My name is Jin Jing and I am currently a second-year Computer Science student from the National University of Singapore. I enjoy doing math and I like to solve logic problems in my spare time.

Project: Health Hub

Overview

Health Hub is a desktop application made for hospital administrative staff. With the growing ageing population in Singapore, we see more elderly needing Intermediate and Long-Term Care (ILTC) services. With the increasing number of home healthcare requests, the current system in hospitals requires a lot of paper work from the admin’s side.

Health Hub aims to reduce the workload of the admin by reducing the amount of paperwork involved, hence improving the workflow of the system.

The user interacts with it using a command line interface (CLI), and it has a graphics user interface(GUI). It is written in Java and is a modification of the AddressBook originally made by seedu to help computer science students learn software engineering.

Summary of contributions

  • Enhancement 1: Reimplemented undo/redo 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: The undo/redo feature provides a huge upgrade to the app, as it now allows the user to rectify the mistakes he/she made easily. For example, if the user deleted a request by accident, he/she does not have to add back the request manually as the command can be undone with the undo command.

    • Highlights: After the transformation from AddressBook to HealthHub, the old implementation of the undo/redo command was unable to support our new features. Hence, in order to maintain the functionality as well as to improve the scalability of our app, it have to be reimplemented. It is complicated to implement due to the various design considerations (See below for contributions in Developer’s Guide ).

    • Credits: AddressBook for the original implementation of the undo/redo feature.

  • Enhancement 2: Reimplemented storage component

    • What is does: Stores all requests and health workers data into json text files whenever any of the data is edited or when app is closed.

    • Justification: Storage is a necessary key feature in our app, having a storage system containing information of health workers and requests saves a lot of time when the user wants to view the information of a specific request or health worker. He can simply filter for the conditions he want using the app instead of searching the documents manually.

    • Credits: AddressBook for the original implementation of the storage component.

  • Code contributed: [ModelManager] [ModifyCommandHistory] [HealthWorkerBookStorage] [SerializableHealthWorkerBook] [SerializableRequestBook] [RequestBookStorage]

  • Other contributions:

    • Enhancements to existing features:

      • Modified Complete command and EditHealthWorker command to support undo/redo features

    • Community:

      • Consolidated a list of broken tests in the Issue tracker: #130

      • Bug reports: #160, #159

      • PRs reviewed (with non-trivial review comments): #70, #47,

      • Reported bugs and suggestions for other teams in the class (examples: 156, 142, 148)

Contributions to the User Guide

The following sections of the user guide were written by me. Do take a look.

Undoing Commands : undo

If you wish to undo a mistake that you had made while entering commands, you can restore the lists to the state before the command was called using the undo command.

Format: undo

  • The undo command can only be used to undo commands that modify the lists such as add, delete and edit.

  • For commands that affects the display such as filter, you can use list command to get back the original list.

Redoing undone commands : redo

If you wish to redo a command that you have previously undone, you can use the redo command.

Format: redo

v1.4 Release Notes

With the release of version 1.4, we decided to improve our current features by making them more dynamic and automated to reduce the manual work of changing the data.

  • The health worker field inside requests now shows the NRIC of the health worker instead of the name in previous versions.

  • Editing the NRIC of a health worker will dynamically change the health staff field of all requests assigned to the health worker.

  • Users can assign multiple requests to a health worker only if the requests are at least 2 hours apart.

Contributions to the Developer Guide

The following sections of the developer’s guide were written by me. Please take a look.

Logic component

logicclassdiag
Figure 1. Structure of the Logic Component

API : Logic.java

  1. Logic uses the HealthHubParser class to parse the user command.

  2. This results in a Command object which is executed by the LogicManager.

  3. The command execution can affect the Model (e.g. adding a request) or sometimes only affecting the display (eg. listing all requests).

  4. The result of the command execution is encapsulated as a CommandResult object which is passed back to the Ui.

  5. In addition, the CommandResult object can also instruct the Ui to perform certain actions, such as printing out the result message in the command line.

Storage component

StorageClassDiagram
Figure 2. Structure of the Storage Component

API : Storage.java

The Storage component,

  • can save UserPref objects in json format and read it back.

  • can save the Request, HealthWorker Book data in json format and read it back.

The storage class converts the object data of Request and HealthWorker by converting the objects into a json object of strings which will be stored in the .json file. When reading the file, the json library passes the respective strings into their java object constructors to recreate the objects. === Undo/Redo feature

Current Implementation

The undo/redo mechanism is facilitated by VersionedBook. There are two extensions of it. VersionedHealthWorkerBook extends HealthWorkerBook and VersionedRequestBook extends RequestBook. Both contain an undo/redo history, stored internally as an healthWorkerBookStateList or requestBookStateList and currentStatePointer. Additionally, it implements the following operations:

  • VersionedBook#commit() — Saves the current request/healthworker book state in its history.

  • VersionedBook#undo() — Restores the previous request/healthworker book state from its history.

  • VersionedBook#redo() — Restores a previously undone request/healthworker book state from its history.

These operations are exposed in the Model interface as Model#commit(), Model#undo() and Model#redo() respectively.

The feature also makes use ModifyCommandHistory to keep track of the commands that modified the books. It contains currentStatePointer and a list of CommandType enums to differenciate the type of command to undo or redo. Similar to CommandMode, the various CommandType enums are:

  • CommandType.HEALTHWORKER_COMMAND

  • CommandType.REQUEST_COMMAND

  • CommandType.HEALTHWORKER_AND_REQUEST_COMMAND

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 VersionedRequestBook and VersionedHealthWorkerBook will be initialized with the initial state, and the currentStatePointer for each VersionedBook pointing to that single book state. Since no modify command has been called, the initial list of commands in ModifyCommandHistory is empty and the initial currentStatePointer of ModifyCommandHistory is initialized to -1.

initrb
inithb

Step 2. The user executes delete r 5 command to delete the 5th request in request book. The delete r command calls Model#commit(REQUEST_COMMAND), causing the modified state of the request book after the delete r 5 command executes to be saved in the requestBookStateList, and the currentStatePointer is shifted to the newly inserted request book state. Since the request book is modified, the enum REQUEST_COMMAND is added to the modifyCommandHistory list within the ModifyCommandHistory class and the currentStatePointer is now pointing at the most recent command. The VersionedHealthWorkerBook is unaffected.

rbafterdelete
mchafterdelete

Step 3. The user executes add h n/David …​ to add a new healthworker. The add h command also calls Model#commit(HEALTHWORKER_COMMAND), causing a modified health worker book state to be saved into the healthWorkerBookStateList and the currentStatePointer is shifted to the new health worker book state. The enum HEALTHWORKER_COMMAND is added into the modifyCommandHistory list of the ModifyCommandHistory class. The VersionedRequestBook is unaffected.

hbafteradd
mchafteradd
If a command fails its execution, it will not call Model#commit(), so the book state will not be saved into the healthWorkerBookStateList or requestBookStateList.

Step 4. The user now decides that adding the health worker was a mistake, and decides to undo that action by executing the undo command. The undo command will call Model#undo(), which will first obtain the type of command that needs to be undone by caling ModifyCommandHistory#getUndoCommand(). In this case HEALTHWORKER_COMMAND is returned, and hence will call undo on VersionedHealthWorkerBook. It will shift the currentStatePointer once to the left, pointing it to the previous health worker book state, and restores the health worker book to that state. The currentStatePointer of the ModifyCommandHistory also gets shifted once to the left, pointing it to the previous command.

hbafterundo
mchafterundo
If the currentStatePointer of both VersionedHealthWorkerBook and VersionedRequestBook is at index 0, pointing to the initial book state for both books, then there are no previous 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.
As of version 1.4, the only command that has modifies both the health worker book and the request book is EditHealthWorkerCommand. It is the only command that has CommandType.HEALTHWORKER_AND_REQUEST_COMMAND.

The user first enters the undo command in the command line interface. The logic manager processes the command as a string and checks using the AddressBookParser is the string is a valid command. In this case, the parser sees that the command string matches the undo command string and hence returns an undo command to the LogicManager which then calls execute() to execute the command. The execute() method calls Model#undo() in which the model checks the ModifyCommandHistory for the correct VersionedBook that needs to be undone and then calls VersionedBook#undo(). Upon a successful undo, the UndoCommand will return a successful result to the LogicManager which will then the "Undo Success!" message will be displayed on the command line interface.

The following sequence diagram shows how the undo operation works:

UndoRedoSeqDiagram

The redo command does the opposite — it calls Model#redo(), which first obtains the command type by calling ModifyCommandHistory#getRedoCommand() and based on the returned command type it shifts the currentStatePointer once to the right, pointing to the previously undone state, and restores the respective book to that state.

If the currentStatePointer of both VersionedBook s are pointing to the latest state, then there are no undone book 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 execute the command list r. Commands that do not modify the books, such as list r, will not call Model#commit(), Model#undo() or Model#redo(). Thus, the VersionedBook s and ModyfiCommandHistory do not change.

rbafterlist

Step 6. The user executes delete h 1, which calls Model#commit(HEALTHWORKER_COMMAND). Since the currentStatePointer is not pointing at the end of the healthWorkerBookStateList, all health worker book states after the currentStatePointer will be purged. We designed it this way because it no longer makes sense to redo the add n/David …​ command. This is the behavior that most modern desktop applications follow.

hbafterdel
mchafteroverride

The following activity diagram summarizes what happens when a user executes a new command:

UndoRedoActDiagram

Design Considerations

Aspect: How undo & redo executes
Implementation Saves the entire book (Current implementation) Individual command knows how to undo/redo by itself.

Pros

Less prone to bugs since the we are switching between different versions of the books.

Will use less memory (e.g. for delete, just save the person being deleted).

Cons

May have performance issues in terms of memory usage especially for large numbers of health workers and requests.

Every command will have their own implementation of undo and some of them are slow hence it causes performance issues. (e.g. for clr, all requests that were deleted have to be added back into the list, which will be quite slow if there is a large number of requests).

Aspect: Data structure to support the undo/redo commands
Implementation Use a list to store the history of book states.(Current implementation) Use HistoryManager for undo/redo

Pros

Undo and redo commands runs faster since it only involves the switching of the state pointer.

Supports multiple books with the HistoryManager alone since we are undoing from the command history.

Cons

Need multiple VersionedBook s. One or each type of book.

HistoryManager keeps a record of all commands, even if they are invalid or commands that does not call Model#commit(). Traversing these irrelevant commands to find the next undoable command will take a long time especially if size of history is large.

Aspect: Data structure to handle multiple VersionedBooks
Implementation Use a command history to keep track of the type of book that was modified It is represented as a list of CommandType.(Current implementation) Use a list of pairs. Each state is represented as a pair which stores the currentStatePointer of each book.

Pros

Easily scalable to include more than two books. As the developer can simply add an extra CommandType and extra cases for switch statements.

Supports commands that change multiple books at once since it keeps track of all states.

Cons

Unable to scale as well if there are many commands that modifies multiple books at once.

Keeping multiple integers takes up more memory as compared to keeping a single enum.

Undoing a command

  1. Undo a command that has been executed

    1. Prerequisites: A command that modifies the data has been executed already, for example delete r 1.

    2. Test case: undo
      Expected: The request has been deleted will appear back inside the list in its original position.

    3. Test case: undo without any prerequisites
      Expected: The application will show an error message: No more commands to undo!

Redoing a command

  1. Redo an undone command

    1. Prerequisites: A successful undo command have to be executed already.

    2. Test case: redo
      Expected: The application will redo the undone command.

    3. Test case: redo without prerequisites
      Expected: The application will show an error message: No more commands to redo!