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.
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 andEditHealthWorker
command to support undo/redo features
-
-
Community:
-
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
|
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
API :
Logic.java
-
Logic
uses theHealthHubParser
class to parse the user command. -
This results in a
Command
object which is executed by theLogicManager
. -
The command execution can affect the
Model
(e.g. adding a request) or sometimes only affecting the display (eg. listing all requests). -
The result of the command execution is encapsulated as a
CommandResult
object which is passed back to theUi
. -
In addition, the
CommandResult
object can also instruct theUi
to perform certain actions, such as printing out the result message in the command line.
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.
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.
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.
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.
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:
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.
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.
The following activity diagram summarizes what happens when a user executes a new command:
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 |
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 |
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 |
Cons |
Need multiple |
|
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 |
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
-
Undo a command that has been executed
-
Prerequisites: A command that modifies the data has been executed already, for example
delete r 1
. -
Test case:
undo
Expected: The request has been deleted will appear back inside the list in its original position. -
Test case:
undo
without any prerequisites
Expected: The application will show an error message: No more commands to undo!
-
Redoing a command
-
Redo an undone command
-
Prerequisites: A successful undo command have to be executed already.
-
Test case:
redo
Expected: The application will redo the undone command. -
Test case:
redo
without prerequisites
Expected: The application will show an error message: No more commands to redo!
-