PROJECT: Mortago
1. Introduction
This project portfolio page showcases my contributions to Mortago - A Software Engineering project developed in my second year of undergraduate study at the National University of Singapore.
About the Team
My team consisted of 5 Year 2 Computer Science undergraduates taking the module CS2103T, a compulsory Software Engineering module.
About the Project
We were given the code for Address Book Level 3 (AB3), a contact management software developed over the years by previous students of this module. We were challenged to delve into Brownfield software development by either developing more features for the given application or morph it to a completely new product. We were required to use the given code as the foundation and maintain its original Command Line Interface (CLI) nature. My team chose to morph it to suit a mortuary management system called Mortago.
Mortago is a one-stop platform which automates fridge management, reminds its users of necessary updates, and even allows them to conveniently generate reports! It completely removes the need to use a register. It consolidates all the information about bodies and fridges in one beautiful dashboard. It is a desktop application supporting CLI and is targeted towards mortuary managers who prefer typing while still enjoy the benefits of Graphical User Interface (GUI). It is developed using Java over a short span of 6 weeks and has approximately 20,000 lines of code.
I worked on the notification feature and the delete command. Moreover, I was responsible for automating fridge management in Mortago. The sections below summarize my contributions to the code base, user guide, developer guide, as well as other team project tasks.
The following are the frequently used symbols in this document:
-
delete
: A dark-red text (also known as a mark-up) indicates that this text is either a full or partial command that can be entered into the CLI. It can also mean a component, class, function, or object used in the code base. -
Tips : An annotation indicates important information which may be useful to a user.
This is an example of a tip. |
2. Summary of contributions
This section summarizes my coding, documentation, and other contributions to the team project.
Enhancements
Major Enhancement: I added the notification feature.
-
What it does: This enhancement reminds the mortuary manager to contact the police, after 10 seconds from the point of adding a
Body
to Mortago, by automatically changing its status and showing a pop-up notification. It also maintains a list of all the notifications (Notif
) that can be viewed by either using theshowNotif
command or clicking on the bell icon beside the command box. -
Justification: If the next-of-kin of a body has been uncontactable for more than 24 hours, police must be informed to start a more thorough investigation. It can be tedious for the mortuary manager to manually keep checking which bodies have crosses the 24 hour period. This feature serves the dual purpose of automatically changing the status of the body as well as reminding the manager. For the purpose of testing, the time period is set to 10 seconds instead of 24 hours.
-
Highlights: This command makes use of threading to update the body status after 10 seconds. Reflecting changes in the UI was initially challenging because JavaFX works on a separate thread from the main Logic commands.
-
Credits: @bjhoohaha/duke’s AlertWindow.java was reused to create
NotifWindow
.
Major Enhancement: I morphed the delete
command in AB3.
-
What it does: The
delete
command allows the user to delete aBody
,Worker
, orFridge
in Mortago. It uses flagging to differentiate them. -
Justification: If the user adds a
Body
by mistake, thedelete
command offers a convenient way to remove it. Flagging is used because it is more convenient for the user instead of using 3 different commands for deletion. -
Highlights: Since Mortago aims to automate mortuary management, deleting a
Body
makes additional changes as follows:-
If the
Body
was assigned aFridge
, this command will remove theBody
and reset the status of theFridge
toUNOCCUPIED
. -
If the
Body
has an associatedNotif
, this command will also delete theNotif
.
-
Minor Enhancement: I automated fridge management.
-
What it does: If a
Body
is assigned aFridge
and eitherdelete
orupdate
commands are used on thisBody
, Mortago will automatically make relevant changes to theFridge
. -
Justification: Manually managing bodies and fridges in a mortuary is very tedious as any change to a body may mean that the status of the fridge has changed as well. For instance, when a body is claimed, its fridge is no longer occupied. If a manager is handling several bodies and fridges simultaneously, he may make errors.
-
Highlights: How Mortago automates fridge management for the
delete
command was explained earlier. In addition to that, the following features are automated:-
If the status of a
Body
is updated toCLAIMED
orDONATED
, its associatedFRIDGE
's status is reset toUNOCCUPIED
and theBody
is no longer assigned to thisFridge
. -
A user cannot assign a
Fridge
to aBody
with statusCLAIMED
orDONATED
. -
An
OCCUPIED
Fridge
cannot be deleted. The user needs to either assign theBody
to anotherFridge
or delete theBody
. -
A
Fridge
cannot be assigned multipleBody
(s).
-
Code Contributed
You can view my code -[via Reposense]
Other Contributions
Other contributions which I made to the team are discussed in this section.
-
Project Management:
-
Set-up team repository on Github.
-
Set up Travis CI for continuous integration to build and test the software.
-
Set-up Coveralls to check code coverage.
-
Maintained the issue tracker on GitHub.
-
-
Documentation:
-
Community
3. Contributions to the User Guide
The following are the excerpts I wrote in the user guide to teach a user how the notification feature works and
how they can use the delete
command.
{start of extract 1: notification feature}
View notifs panel: showNotifs
This command allows you to view all Notif
(s).
A Notif
is a notification associated with a Body
. In Singapore, if the next-of-kin is not contactable for more
than 24 hours from the time of addition of a Body
in Mortago,
police must informed. Mortago removes the hassle of manually keeping track of the status of bodies! For the purpose of
testing, instead of 24 hours, Mortago currently
uses 10 seconds.
When you add a Body
or manually set its status to ARRIVED
, Mortago
automatically
changes its status to CONTACT_POLICE
if its status is still ARRIVED
after 10 seconds. It then shows a pop-up
to
remind you to
contact the police. It creates a Notif
for this Body
and you can view all Notif
(s) by either clicking on the
notification bell or typing the showNotif
command.
Format: showNotifs
Once you change the status of the Body
from CONTACT_POLICE
to any other possible status as described in
Section 8.1,
its associated Notif
is deleted.
If you change the status of the Body
before 10 seconds, no pop-up and Notif
are created.
Example:
Imagine that a new Body
(John Doe) has just arrived at your mortuary.
1) Type the add
command as specified in the example in Section 3.1.1. Currently the status of this Body
is ARRIVED
2) Wait for 10 seconds. Mortago automatically changes the status of the Body
to CONTACT_POLICE
and shows
you a
pop-up
notification.
3) Type showNotif
and press ENTER
to execute it.
4) The notification bell opens up a panel and lists all the bodies for which you need to contact the police. You can see John Doe’s ID number B00000020 is in the list.
5) Suppose you have contacted the police, change the status of John Doe using the command update -b /id 20 /status
pending police report
. John Doe’s ID is no longer listed in the notifs panel.
{end of extract 1}
{start of extract 2: delete command}
Deleting an entry : delete
You can delete a body, worker or fridge entry, using its Identification Number by entering a delete command with the
given format below.
Format: delete -FLAG id
You only need to enter the numeric value of the Identification Number while ignoring the prefixed 0s. For example, if
you want to delete a Fridge
with id F01
, you need to only enter delete -f 1
.
When you delete a Body
:
-
Mortago automatically deletes its associated
Notifs
. You can learn more aboutNotifs
in Section 3.2.1. -
If you had assigned a
Fridge
to thisBody
, Mortago automatically sets the status of theFridge
toUNOCCUPIED
.
You cannot delete a Fridge
with status OCCUPIED
. To still proceed with deletion, you need to either delete the
Body
or assign it to another Fridge
.
Example:
Imagine that you added someone (Jim Kerr) by mistake and you want to remove his details from Mortago. You see that his ID number is W00003.
To delete his record:
1) Type delete -w 3
and press ENTER
to execute it.
2) The result box displays the message as shown below.
3) And you can check that Jim Kerr is no longer in the list of workers.
{end of extract 2}
4. Contributions to the Developer Guide
The following is the excerpt I wrote in the Mortago developer guide. It is targeted at potential future developers who may be interested to further develop the application.
{start of extract: notification feature}
Notification Feature
This feature in Mortago reminds a mortuary manager to contact the police when the next-of-kin of a body
has not been contactable for a given period of time from the point of admission of the Body
. He / She then needs
to
contact
the police
to
proceed with a
more thorough investigation. In Singapore, this period is 24 hours. For testing purposes, it has been set to
10 seconds in Mortago.
If the status of a Body
is ARRIVED
after 10 seconds, it is updated to CONTACT_POLICE
and a
pop-up alert is displayed to remind the user.
If you want to change the time period, you can do so by modifying NOTIF_PERIOD and NOTIF_TIME_UNIT variables in
AddCommand.java.
|
Implementation
This command is supported by the model component Notif
and the logic component NotifCommand
.
In Notif
command, the following are the key private variables:
-
body
: Refers to theBody
for which theNotif
is created. This is passed in as a parameter when a new instance of the class is instantiated. -
alert
: Refers to aRunnable
function which checks if the current status of the body isARRIVED
and if so, changes it toCONTACT_POLICE
. -
notifCreationTime
: Refers to aDate
object which stores the date and time at the point of addition of the body in Mortago.
The constructor of a NotifCommand
must be provided with the following parameters:
-
notif
: Refers to the instance of theNotif
which is handled by theNotifCommand
. -
period
: Refers to along
value for which the NotifCommand needs to wait before executing thealert
function of thenotif
.long
is used becausenotifCreationTime.getTime()
returns along
which is useful in storage. It will be explained in further detail later. Currently, this value is set to10
. -
timeUnit
: Refers to aTimeUnit
associated with theperiod
. Currently, this value is set toTimeUnit.SECONDS
.
The following class diagram (Figure 15) models the relationships and dependencies among classes in this feature:
The following sequence diagrams (Figure 16 and 17) illustrate the execution of the notification feature:
The following activity diagram summarizes what happens when a user adds a new body and a NotifCommand
is
instantiated:
|
||
No! If the status of the |
Storing executed and pending Notifs
NotifCommand
supports storage where each Notif
with its associated Body
and the long
equivalent of the
notifCreationTime
is stored in a JSON file along with the other Entities
. When the MainApp
is initialized, the
following happens:
-
All the
Notif
(s) are fetched from the storage. -
For each
Notif
, the difference of the current system time andnotifCreationTime
is calculated. -
If the difference is more than the
period
, then the status of the associatedBody
is changed toCONTACT_POLICE
. Otherwise, theNotifCommand
is scheduled to be executed after the calculated time difference. -
The
Notif
is added to the model.
|
||
|
Defensive programming
The NotifCommand
heavily makes use of the ScheduledExecutorService
and Platform.runLater(Runnable runnable)
to
make changes to the Body
status and Notif
. They use threading to allow tasks to be handled concurrently. For instance,
even after scheduling a Runnable
function, the user is still able to carry on with other commands of Mortago such
as updating the status of the Body
, adding a new Fridge
etc.
As per the official Java Documentation, Platform.runLater(Runnable runnable)
runs the specified runnable function
on a thread dedicated to JavaFX application at some unspecified time in the future. So,
if some updates to the model are wrapped inside it as a Runnable
while others are not, it can result in a
mismatch of model. For instance, you may want to delete a Notif
which already exists in the model. The usual
process will be to first check whether it exists and if it does, then proceed with deletion. However, due to
threading, you may end up in a situation when the app finds the Notif
at the point of checking but throws a
NullPointerException
when proceeding with the deletion.
To prevent this, in Mortago, any addition or deletion of Notif
in the model during the execution of the
NotifCommand
and parts of UpdateCommand
are wrapped inside Platform.runLater(Runnable runnable)
. This ensures
that updates to the model happen sequentially and not concurrently and the relevant changes can be reflected on the
UI.
Moreover, all these operations are wrapped inside a try-catch block. Either the NullPointerException
or
DuplicateNotifException
is directly thrown in the form of CommandException
or it is logged in Logger
. A
code snippet of NotifCommand
to illustrate this is show below.
if (model.hasNotif(notif)) {
try {
model.deleteNotif(notif);
} catch (NullPointerException exp) {
logger.info(MESSSAGE_NOTIF_DOES_NOT_EXIST);
}
}
Platform.runLater(() -> {
if (!model.hasNotif(notif)) {
try {
model.addNotif(notif);
} catch (DuplicateNotifException exp) {
logger.info(MESSAGE_DUPLICATE_NOTIF);
}
}
});
Design Considerations
Aspect: How to delay change in status of the Body
-
Alternative 1 (current choice): Use
ScheduledExecutorService
.-
Pros: Does not depend on thread synchronization and avoids the need to deal with threads directly.
-
Cons: May cause memory leaks if cache is not cleared.
-
-
Alternative 2: Use
Thread.sleep
-
Pros: Straightforward way to delay a thread.
-
Cons: May quickly run into OutOfMemory error.
-
Alternative 1 is the current choice because of its simplicity and robustness as it abstracts away the need to manually deal with threads.
Aspect: How the behaviour of NotifCommand
differs when the app is initialized
-
Alternative 1 (current choice): Do not show a pop-up if the difference between system time and
notifCreationTime
exceedsperiod
.-
Pros: Prevents the situation of multiple pop-ups on app initialization.
-
Cons: Does not prompt the user who may in turn forget to contact the police.
-
-
Alternative 2: Show pop-up on app initialization for
Notif
(s) in storage for which the difference between system time andnotifCreationTime
exceedsperiod
.-
Pros: Ensures that the user does not forget about contacting the police.
-
Cons: May slow down the computer and lag the app if there are too many pop-up notifications at the same time.
-
Alternative 1 is the current choice because we want the app to be responsive and scalable in the long term. The notification bell is placed beside the command box to prevent instances of user forgetting to contact the police.
{end of extract}