This portfolio details the contributions I made to the development of a desktop application as part of an NUS Software Engineering module, CS2103T. Over the span of 8 weeks in AY18/19 Semester 1, my team and I were given the freedom to morph an existing desktop application made up of 10KLoC in Java.
Throughout the 8-week period, we were challenged to incorporate the new Software Engineering concepts taught in class via a series of milestones which were part of the project management process. On top of that, we planned out weekly goals and consistently reassessed our employed programming methodologies and practices, developing our software HealthBase incrementally and iteratively. Figure 1 shows the transformations we made in this brownfield project.
HealthBase is a lightweight and free desktop application which automates the tedious data-handling processes of hospital staff. By complementing the roles of receptionists, nurses and doctors and providing convenient features, the app allows its users to efficiently manage patients’ information, thus easing their workload and allowing them to focus on caring for their patients.
The app’s features can be easily accessed by typing short commands into an input field. These features include (among others):
-
Patient medication data entry and retrieval
-
Patient medical history data entry and retrieval
-
Patient dietary restriction(s) data entry and retrieval
-
Patient visitor history data entry and retrieval
-
Patient appointment management for doctors
-
Real-time management of the check-in/out status of patients
-
Real-time management of the number of visitors any given patient has
-
Major enhancement: Added the functionality to record and view patients' past medical diagnoses
-
What it does: Records a diagnosis entry into the medical history of a given patient. Each entry includes the diagnosis description, the name of the examining doctor and a timestamp.
-
Justification: This feature serves as the primary storage of important patient data, as adjudged by the examining doctor’s assessment. Past diagnoses are easily viewed on the desktop application to provide quick access for the examining doctor.
-
Highlights: This enhancement required a detailed understanding of the Java Architecture for XML Binding software framework (JAXB) for data storage as XML files. Also, the integration of the UI in the feature needed JavaFX knowledge.
-
-
Code contributed:
-
Other contributions:
-
Project management:
-
Spearheaded project planning and managed project scope as project leader
-
-
Documentation:
-
Refactored existing contents of documentation affected by renaming of "HMS2K18" to "HealthBase": (Pull Request: #132)
-
Wrote the
addmh
section in User Guide and Developer Guide.
-
-
Community:
-
Tools:
-
Integrated Github Plugins (Coveralls, Travis CI)
-
Automated team documentation build on GitHub Pages
-
-
The sections below detail my contributions to the User Guide. They demonstrate my ability to write documentation targeting end-users. |
Add a non-blank diagnosis entry with the name of the doctor-in-charge to an existing patient’s medical history. The patient must be registered within the system and the doctor’s name should contain his title, which is followed by his full name. For all words in the doctor’s name, the starting letter must be capitalised.
Format: addmh ic/NRIC mh/DIAGNOSIS doc/DOCTOR-IN-CHARGE
Example(s):
-
addmh ic/S1234567A mh/Patient shows symptoms of flu. Prescribed 2 weeks of panadol, advised patient to rest and rehydrate. doc/Dr. Zhang De
-
addmh ic/T9876543Z mh/Patient appears to have chronic cough. Referred to specialist. doc/Dr.Timothy
To try out the addmh
command:
-
Type out a valid
addmh
command which follows the stated format into the command box. Such an example can be seen in Figure 2. -
Submit the input into HealthBase by pressing kbd:[Enter]. The result display panel will show a successful
addmh
command message, and should show the same results in Figure 3.
Warning
|
The following invalid inputs will result in a command failure, and the display of an appropriate error message. |
-
Invalid NRIC
-
The patient NRIC does not match to an existing patient in the system. The person will first need to be registered.
-
-
Incorrect format of the doctor’s name.
-
Doctor’s title must be included.
-
The first letter of all words in doctor’s name must be captalised.
-
-
Blank diagnosis
-
An empty diagnosis will not be accepted as a valid diagnosis.
-
-
Missing prefixes
-
Not all the prefixes in the stated command format have been included.
-
Tip
|
If you want to view the newly added diagnoses to a particular patient, simply enter |
The sections below detail my contributions to the Developer Guide. They showcase my ability to write technical documentation and the technical depth of my contributions to the project. |
The function of the addmh
command is to allow the user to add a diagnosis to a patient’s medical history.
Each patient’s information is stored within a Person
object. The execution of the addmh
command results in the retrieval of
a particular Person
object, and the consequent updating of the patient’s MedicalHistory
.
Stated below is an example usage scenario and an explanation of the interactions that occurs as a result of the code execution.
The user executes the following input:
addmh ic/S1234567A mh/Hypertension, diagnosed “years ago”, well contracted with Metoponol doc/Dr. Amos
The purpose of the entered input is to record a diagnosis issued by Dr.Amos
, "Hypertension, diagnosed “years ago”, well contracted with Metoponol"
,
into the medical history of the patient with the NRIC S1234567A
.
The sequence diagram in Figure 4 below shows the execution of the given scenario:
addmh ic/S1234567A mh/Hypertension, diagnosed “years ago”, well contracted with Metoponol doc/Dr. Amos
-
Firstly, the
String
user input is passed into theLogicManager::execute
method of theLogicManager
instance as the only parameter. -
Then, the
LogicManager::execute
method callsHealthBaseParser::parseCommand
which receives the user input as a parameter.-
The user input is formatted: the first
String
token is taken as the command word, while the remainingString
is grouped as arguments to be used later by aAddmhCommandParser
. -
From the command word, the HealthBaseParser instance identifies the user input as an
addmh
command and constructs anAddmhCommandParser
instance.
-
-
Next, the
HealthBaseParser
calls theAddmhCommandParser::parse
method. TheAddmhCommandParser
takes in the remaining string,ic/S1234567A mh/Hypertension, diagnosed “years ago”, well contracted with Metoponol doc/Dr. Amos
.-
The string is tokenised to arguments according to their prefixes.
ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NRIC, PREFIX_MED_HISTORY, PREFIX_DOCTOR);
-
A check on the presence of the relevant prefixes
ic/
,mh/
anddoc/
is done. -
If not all prefixes are present, a
ParseException
will be thrown with an error message on the proper usage of theaddmh
command.if (!arePrefixesPresent(argMultimap, PREFIX_NRIC, PREFIX_MED_HISTORY, PREFIX_DOCTOR) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException((String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddmhCommand.MESSAGE_USAGE))); }
-
Otherwise,
Diagnosis
andNric
objects are constructed and used as fields in the creation of anAddmhCommand
object.
-
-
Subsequently, the newly created
AddmhCommand
is returned to back to theLogicManager
instance through theAddmhCommandParser
andHealthBaseParser
objects. -
Afterwards, when control is returned to the
LogicManager
object, it calls theAddmhCommand::execute
method.-
The method takes in a
Model
object to access the application’s data context, the stored data of all persons. -
Its execution sequence may be broken down into the numbered steps in the code snippet below.
public CommandResult execute(Model model, CommandHistory history) throws CommandException { requireNonNull(model); Person patientToUpdate = CommandUtil.getPatient(patientNric, model); // (6) Person updatedPatient = addMedicalHistoryForPatient(patientToUpdate, this.newRecord); // (7) model.updatePerson(patientToUpdate, updatedPatient); // (8) return new CommandResult(String.format(MESSAGE_SUCCESS, patientNric)); // (9) }
-
-
Next, the stored persons data is accessed in the
CommandUtil::getPatient
class method.-
Model::getFilteredPersonList
is called to search for a person with aNric
that matches theNric
field in theAddmhCommand
-
If a match is found, the
Person
is returned to theAddmhCommand::execute
method.public static Person getPatient(Nric nric, Model model) throws CommandException { ObservableList<Person> matchedCheckedOutPatients = model.getFilteredCheckedOutPersonList() .filtered(p -> nric.equals(p.getNric())); if (matchedCheckedOutPatients.size() > 0) { throw new CommandException(MESSAGE_PATIENT_CHECKED_OUT); } ObservableList<Person> matchedCheckedInPatients = model.getFilteredPersonList() .filtered(p -> nric.equals(p.getNric())); if (matchedCheckedInPatients.size() < 1) { throw new CommandException(MESSAGE_NO_SUCH_PATIENT); } if (matchedCheckedInPatients.size() > 1) { throw new CommandException(MESSAGE_MULTIPLE_PATIENTS); } return matchedCheckedInPatients.get(0); }
-
-
Following that, the
Person
's medical history is to be updated.-
The person’s current
medicalHistory
is retrieved, and theDiagnosis
field in theAddmhCommand
is added to it. -
Then, a new
Person
is created with the updated fields, as part of the immutability of thePerson
class.private static Person addMedicalHistoryForPatient(Person patientToEdit, Diagnosis diagnosis) { requireAllNonNull(patientToEdit, diagnosis); MedicalHistory updatedMedicalHistory = patientToEdit.getMedicalHistory(); updatedMedicalHistory.add(diagnosis); return patientToEdit.withMedicalHistory(updatedMedicalHistory); }
-
-
Then, the old
Person
's data will be replaced with the updatedPerson
's data.-
Here the
Model::updatePerson
method is called, and it subsequently calls theHealthBase::updatePerson
method. -
It replaces the person’s existing data in the storage with the person’s updated data.
// ModelManager.java public void updatePerson(Person target, Person editedPerson) { requireAllNonNull(target, editedPerson); internalHealthBase.updatePerson(target, editedPerson); indicateHealthBaseChanged(); } // HealthBase.java public void updatePerson(Person target, Person editedPerson) { requireNonNull(editedPerson); persons.setPerson(target, editedPerson); }
-
-
The
AddmhCommand::execute
execution completes by returning a newCommandResult
that contains a success message to its calling method,LogicManager::execute
. -
Finally the
CommandResult
is returned to the caller ofLogicManager::execute
, and the execution sequence ends.
The activity diagram below summarises what happens when a user executes the addmh
command.
Note
|
If multiple patients with the entered NRIC exist, then the AddmhCommand::execute will throw a CommandException
with an appropriate error message before the use case ends.
|
-
Alternative 1 (current implementation): Use a POJO class to represent the timestamp data in the
Diagnosis
class.-
Pros: Results in improved readability and modularity of code, due to a stronger adherence to the Object-Oriented Programming paradigm.
-
Cons: Increases in modularity can make it difficult to find information, if code becomes over-modularised.
-
-
Alternative 2 (alternative implementation): Use a
String
to represent the timestamp and contain date-time related functions in theDiagnosis
class.-
Pros: Results in more compact code.
-
Cons: Decreases code modularity, and this decreases code readability.
-
-
Alternative 1 (current implementation): Use an ArrayList to store diagnoses in a person.
-
Pros: Allows expandable storage of diagnoses with its dynamic size. As an ArrayList implements the list interface, its contracted functionality suitably represents a list of diagnoses, which makes its usage in developing intuitive.
-
Cons: Slows down performance if the ArrayList capacity is constantly filled due to resizing costs.
-
-
Alternative 2 (alternative implementation): Use an Array to store diagnoses in a person.
-
Pros: Reduces performance losses that may arise from constant resizing of filled ArrayLists.
-
Cons: Decreases flexibility in developing as the Arrays do not support generics in Java.
-