1. Overview
This Project Portfolio documents my contribution to the software project PatientBook, a group project collectively done by five Computer Science Undergraduate students in National University of Singapore.
PatientBook is a Command Line Interface desktop application targeted at medical professionals in Singapore. It aims to provide convenience and assurance for doctors, by allowing efficient management of patients and appointments. Doctors can also retrieve information from the built-in disease-symptom database and drug database.
My main contribution to the project includes implementing the feature to add appointments in natural expressions.
2. Summary of Contributions
This section summarises my contributions to the project.
-
Code contributed: [Code]
-
Main feature implemented: implemented the ability to add appointments into the schedule system using natural expressions and in a conversational process.
-
What it does: This feature allows the user to add an appointment in a command format that uses natural expressions such as "next week" to indicate time. It also provides the user with a list of available time slots within the time range for the user to choose from.
-
Justification: This feature improves the user-friendliness of the product, as it allows the user to issue commands in intuitive and flexible ways that simulate our natural language and natural thinking process.
-
Highlights: This feature involves several challenging algorithms such as the keyword search algorithm to find time-related keywords and the algorithm to find available time periods within a time range. Manipulating
java.util.Calendar
also requires in-depth analysis as there are a number of edge cases to consider.
-
-
Other contributions:
-
Minor enhancements:
-
Wrote utility classes such as a generic class
Pair
and aScheduleEventBuilder
class.
-
-
Project management:
-
Enhancements to existing features:
-
Added constraints for
Tag
(pull request: #180)
-
-
Community:
-
3. Contributions to the User Guide
Given below are sections I contributed to the User Guide. They showcase my ability to write easy-to-follow documentation targeting end-users. |
Add an Appointment : add appointment
Format: add appointment for NAME TIME
Alternative Format: add appointment for PATIENT_ID TIME
Creates a new appointment and places it in the schedule. If necessary, switches the application to appointment management mode.
Unlike other commands, this command does not require any prefixing. |
For TIME
parameter, user may choose to use natural expressions to indicate the time or period where the appointment is to be scheduled. The list of natural expressions for TIME
accepted by the programme include:
-
tomorrow
ortmr
-
the day after tomorrow
orthe day after tmr
-
in * day
orin * days
-
in * week
orin * weeks
-
in * month
orin * months
-
this week
ornext week
-
this month
ornext month
-
this Monday
this Tuesday
this Wednesday
this Thursday
this Friday
this Saturday
orthis Sunday
-
next Monday
next Tuesday
next Wednesday
next Thursday
next Friday
next Saturday
ornext Sunday
-
soon
-
recently
-
in a few days
-
in
DD/MM/YYYY
formatAsterisk (*) denotes any positive integer number. Adding an appointment with a time that is already past is allowed. However, this functionality should only be used when user wants to record some critical information about a past appointment that is for some reason unscheduled beforehand.
After the initial input is processed by the programme, user may encounter a maximum of four prompt windows for further input, including:
-
A prompt window where user is required to choose the correct patient to schedule for, if the name provided in the initial input can be matched to multiple patients in the record. The definition of a successful match is the same as that of the command
find patient
, as explained above.Patient_ID
of the intended patient is to be entered. -
A prompt window where user is required to provide a specific time slot for the appointment, from a list of available time periods, in
DD/MM/YYYY hh:mm - hh:mm
format. -
A prompt window where user may choose to provide any tags for the appointment.
-
A prompt window where user may choose to provide any additional notes for the appointment.
Example:
Command entered: add appointment for Xinze tomorrow
1) There are multiple patients named Xinze
in the patient record. Programme requests for a specific patient ID, in a prompt window similar to the following:
2) Once Xinze
is matched to a unique patient in the patient record, programme displays a list of available time periods during tomorrow
, based on the current time at which the command is executed, in another prompt window similar to the following:
3) User further inputs: 31/10/2018 9:30 - 10:30
. Programme proceeds to request for tags to be attached to the appointment, in another prompt window similar to the following:
4) User may choose to provide or not provide any tags. In this case, the user does not wish to attach any tags. After that, programme proceeds to request for additional notes for the appointment, in another prompt window similar to the following:
5) User may choose to provide or not provide any additional notes. In this case, the user does not wish to add any additional notes. Upon completion, programme creates the appointment and displays the following message to the user:
New appointment added: Appointment ID: e3 scheduled for patient ID: p7 during: 31/10/2018 09:30 to 31/10/2018 10:30
Details:
Tags:
4. 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. |
Natural Language Processing for Adding an Appointment
This feature facilitates scheduling, which enables the user to add appointments into the schedule system with user input phrased in natural expressions, and does so in a conversational process enabled by several prompt windows.
Current Implementation
This feature currently mainly implements a ScheduleEvent
parser which parses natural language user input, and creates the intended ScheduleEvent
object to be stored.
There are five steps involved in the processing of this feature:
Step 1. Breaking Down: User input is broken down into sub-fields, namely, patient and time.
Step 2. Patient Parsing: User input for patient is parsed into the corresponding PersonId
object.
Step 3. Time Parsing: User input for time is parsed into a Pair<Calendar>
object.
Step 4. Further Prompting: User is prompted with two prompt windows where tags and additional notes can be added.
Step 5. Generating Appointment: The resulting ScheduleEvent
object corresponding to all user input is created.
The following is an example of a use case, and how the mechanism behaves:
User Input: add appointment for David Lee next week
.
Step 1. Breaking Down: The user input string, starting with for
, is passed into a ScheduleEventParser
object, and broken down into meaningful substrings for patient and time respectively:
-
Programme starts with assuming that the substring for identifying the patient is only one word long, and the remaining string following that one word all the way to the end of the string is the input for time. In this case,
David
is the assumed patient substring andLee next week
is the assumed time substring. -
Programme takes the assumed time substring,
Lee next week
, and checks if it is a valid time expression. -
As a match cannot be found, it means that the assumed demarcation between patient and time inputs is incorrect. Programme makes another attempt by assuming the patient substring is longer by one word (i.e.
David Lee
) and the time substring is shorter by one word (i.e.next week
). It takes the new assumed time substring and checks its validity again. -
As a match is found this time, it indicates that the assumption is correct.
David Lee
will be carried forward to the Patient Parsing step whilenext week
will be carried forward to the Time Parsing step. -
In other cases where a match cannot be found after all assumptions have been tested, an exception will be thrown indicating that the user has not used an accepted expression.
The activity diagram below illustrates this process:
Step 2. Patient Parsing: The string David Lee
is parsed and converted into the corresponding PersonId
object:
-
Programme uses this string as the search string to create a new
MatchPersonPredicate
object which is then used to filter the list of patients. -
If only one patient can be matched, the
PersonId
of the patient is immediately returned. -
If multiple patients can be matched, programme passes the list of matched patients as a
String
into aPrompt
object, where the list is displayed to the user in aPromptWindow
. User is expected to enter the ID of the intended patient. ThePersonID
of the final intended patient is returned.
Step 3. Time Parsing: The string next week
is passed to a DateTimeParser
object where it is parsed and converted into a Pair<Calendar>
object to represent the user’s chosen time slot for the appointment:
-
Programme executes a keyword search and invokes the method
getWeekDates(currentTime, 1)
, where it convertsnext week
into a datetime range, by doing relevant calculations on theCalendar
object which represents the current time. For instance, if the command is executed on 16/10/2018,next week
becomes a datetime range from 22/10/2018 09:00 - 28/10/2018 18:00. This range takes into consideration the doctor’s working hours. -
Programme searches the list of already scheduled appointments within the time range obtained. It finds a list of available time periods by taking the complement within that range (taking into consideration the doctor’s working hours), and passes the list as a
String
into aPrompt
object, where the list is displayed to the user in aPromptWindow
. -
User inputs a specific time slot from the list of available time periods. For instance, user inputs
22/10/2018 09:00 - 10:00
. This string is then passed back toDateTimeParser
and converted into aPair<Calendar>
object that represents this time slot, by invoking the methodparseTimeSlot(timeSlotString)
.
Step 4. Further Prompting: The user is presented with two more PromptWindow
, where they can provide further inputs for tags and additional notes for the appointment. This is done through a simple I/O mechanism.
Step 5. Generating Appointment: Results from the previous steps are used to fill the attributes of a newly created ScheduleEvent
object which is then returned.
The sequence diagram below summarises this feature. Note that step 4 and 5 are omitted in the diagram as they are relatively trivial:
Design Considerations
This section discusses design considerations for current and alternative implementations for this feature.
Aspect: Abstraction over time slot
-
Alternative 1 (current choice): Use a
Pair<Calendar>
where thekey
andvalue
represent the start time and end time of a time slot respectively.-
Pros: It is easy to implement.
-
Cons:
key
andvalue
are not intuitive in this context, hence it is difficult for other developers to understand.
-
-
Alternative 2: Define a
Duration
class which has thePair<Calendar>
as an attribute, providing an additional layer of abstraction.-
Pros: It is easy for new developers to understand the context by defining methods such as
getStartTime()
, at the same time not exposing the internal implementation. -
Cons: Defining this class may not be worth the effort as it has only one use case (as an attribute in
ScheduleEvent
) in the application.
-
Aspect: Algorithm to find available time slots given a list of already scheduled appointments in an interval
-
Alternative 1 (current choice): Loop through the list of appointments twice. The first time is to find available time slots in days where there are scheduled appointments. The second time is to find completely free days. Refer to the code snippets below for illustration.
-
Pros: It is easy to implement.
-
Cons: Performance is adversely affected because the list has to be searched through twice.
private List<Pair<Calendar>> getAvailableSlotList(List<ScheduleEvent> scheduledAppts, Pair<Calendar> dateInterval) { // ... for (int i = 0; i < scheduledAppts.size() - 1; i++) { // ... findAvailableSlotsBetweenTwoAppts(availableSlots, currentEnd, nextStart); // ... } // ... findCompletelyAvailableDays(scheduledAppts, dateInterval, availableSlots); // ... }
private void findCompletelyAvailableDays(List<ScheduleEvent> scheduledAppts, Pair<Calendar> dateInterval, List<Pair<Calendar>> availableSlots) { // ... for (ScheduleEvent appt: scheduledAppts) { // ... } // ... }
-
-
Alternative 2: Keep a day pointer and loop through the list of appointments once to find all available time slots.
-
Pros: It enhances performance because the list is searched through only once.
-
Cons: It is harder to implement due to the difficulties in manipulating
java.util.Calendar
as a day pointer. Edge cases such as crossing the year boundaries are difficult to handle.
-