By: W09-4      Since: Jan 2019      Licence: MIT

1. Introduction

1.1. What is QuickDocs

QuickDocs is an all in one solution where doctors can have greater control in facilitating patient consultations, organizing appointments, and monitoring financial and inventory records in private clinics.

1.2. Purpose

The developer guide covers the software architecture, design decisions and implementation details of the features in QuickDocs.

1.3. Audience

This document aims to provide guidance for both software developers and software testers working on QuickDocs.

1.4. How to use the Developer Guide

Here are the notations used in the document:

This is a tip. Useful information pertaining to the features will be written here.
This is a note. Additional information that further explains a feature will be written here.
This is a warning. Special information that requires additional attention to be paid will be written here to prevent operational issues from happening.

filepath, command and method are all formatted with a grey background. Example: help, getCurrentConsultation() and logic\LogicManager

2. Setting up

2.1. Prerequisites

  1. JDK 9 or later

    JDK 10 on Windows will fail to run tests in headless mode due to a JavaFX bug. Windows developers are highly recommended to use JDK 9.
  2. IntelliJ IDE

    IntelliJ by default has Gradle and JavaFx plugins installed.
    Do not disable them. If you have disabled them, go to File > Settings > Plugins to re-enable them.

2.2. Setting up the project in your computer

  1. Fork this repo, and clone the fork to your computer

  2. Open IntelliJ (if you are not in the welcome screen, click File > Close Project to close the existing project dialog first)

  3. Set up the correct JDK version for Gradle

    1. Click Configure > Project Defaults > Project Structure

    2. Click New…​ and find the directory of the JDK

  4. Click Import Project

  5. Locate the build.gradle file and select it. Click OK

  6. Click Open as Project

  7. Click OK to accept the default settings

  8. Open a console and run the command gradlew processResources (Mac/Linux: ./gradlew processResources). It should finish with the BUILD SUCCESSFUL message.
    This will generate all resources required by the application and tests.

2.3. Verifying the setup

  1. Run the w09.quickdocs.MainApp and try a few commands

  2. Run the tests to ensure they all pass.

2.4. Configurations to do before writing code

2.4.1. Configuring the coding style

This project follows oss-generic coding standards. IntelliJ’s default style is mostly compliant with ours but it uses a different import order from ours. To rectify,

  1. Go to File > Settings…​ (Windows/Linux), or IntelliJ IDEA > Preferences…​ (macOS)

  2. Select Editor > Code Style > Java

  3. Click on the Imports tab to set the order

    • For Class count to use import with '*' and Names count to use static import with '*': Set to 999 to prevent IntelliJ from contracting the import statements

    • For Import Layout: The order is import static all other imports, import java.*, import javax.*, import org.*, import com.*, import all other imports. Add a <blank line> between each import

Optionally, you can follow the UsingCheckstyle.adoc document to configure Intellij to check style-compliance as you write code.

2.4.2. Updating documentation to match your fork

After forking the repo, the documentation will still have the SE-EDU branding and refer to the se-edu/addressbook-level4 repo.

If you plan to develop this fork as a separate product (i.e. instead of contributing to se-edu/addressbook-level4), you should do the following:

  1. Configure the site-wide documentation settings in build.gradle, such as the site-name, to suit your own project.

  2. Replace the URL in the attribute repoURL in DeveloperGuide.adoc and UserGuide.adoc with the URL of your fork.

2.4.3. Setting up CI

Set up Travis to perform Continuous Integration (CI) for your fork. See UsingTravis.adoc to learn how to set it up.

After setting up Travis, you can optionally set up coverage reporting for your team fork (see UsingCoveralls.adoc).

Coverage reporting could be useful for a team repository that hosts the final version but it is not that useful for your personal fork.

Optionally, you can set up AppVeyor as a second CI (see UsingAppVeyor.adoc).

Having both Travis and AppVeyor ensures your App works on both Unix-based platforms and Windows-based platforms (Travis is Unix-based and AppVeyor is Windows-based)

2.4.4. Getting started with coding

When you are ready to start coding,

  1. Get some sense of the overall design by reading Section 3.1, “Architecture”.

  2. Take a look at [GetStartedProgramming].

3. Design

3.1. Architecture

Architecture2
Figure 1. Architecture Diagram

The Architecture Diagram given above explains the high-level design of the App. Given below is a quick overview of each component.

The .pptx files used to create diagrams in this document can be found in the diagrams folder. To update a diagram, modify the diagram in the pptx file, select the objects of the diagram, and choose Save as picture.

Main has only one class called MainApp. It is responsible for,

  • At app launch: Initializes the components in the correct sequence, and connects them up with each other.

  • At shut down: Shuts down the components and invokes cleanup method where necessary.

Commons represents a collection of classes used by multiple other components. The following class plays an important role at the architecture level:

  • LogsCenter : Used by many classes to write log messages to the App’s log file.

The rest of the App consists of four components.

  • UI: The UI of the App.

  • Logic: The command executor.

  • Model: Holds the data of the App in-memory.

  • Storage: Reads data from, and writes data to, the hard disk.

Each of the four components

  • Defines its API in an interface with the same name as the Component.

  • Exposes its functionality using a {Component Name}Manager class.

For example, the Logic component (see the class diagram given below) defines it’s API in the Logic.java interface and exposes its functionality using the LogicManager.java class.

LogicClassDiagram
Figure 2. Class Diagram of the Logic Component

How the architecture components interact with each other

The Sequence Diagram below shows how the components interact with each other for the scenario where the user issues the command padd …​ (values are not added for brevity).

SDforAddPatient
Figure 3. Component interactions for addpat …​ command

The sections below give more details of each component.

3.2. UI component

QDUiClassDiagram
Figure 4. Structure of the UI Component

The UI aspect of QuickDocs is controlled by a single rootLayoutController that is responsible for handling the user interactions with the interface. It is composed of the resultDisplay, userInputField, inputFeedbackArea, reminderListPanel and the currentSessionLabel controls.

The UI component uses JavaFx UI framework. RootLayout is defined in the matching .fxml file that is in src/main/resources/view folder.

uioverview
Figure 5. Overview of QuickDoc’s user interface
  1. resultDisplay will reflect the results of the command entered

  2. userInputField is where the user can enter their commands

  3. Should the command fail due to erroneous command input, instructions to rectify the command will be displayed on the inputFeedbackArea

  4. Appointments and Reminders are displayed on the ReminderListPanel. Appointments are coloured blue, medicine alarms are coloured red and other reminders are coloured beige.

  5. Current consultation sessions will be indicated on the currentSessionLabel.

Since the commands entered by the user is done through the user interface, the UI component interacts with the Logic specifically for the execution of commands. For details pertaining to the execution of commands, please refer to the section on the logic component

The flow of how the interaction between the two components are as follows:

  1. The user types the command line on the userInputField and presses Enter

  2. The command line is sent to the logic component for execution

  3. Any results or issues encountered during the process of execution will be returned from the Logic component to the UI component

  4. UI will reflect the results or issues faced on the resultDisplay and the inputFeedbackArea respectively

3.3. Logic component

LogicClassDiagram
Figure 6. Structure of the Logic Component

API : Logic.java

  1. Logic uses the QuickDocsParser 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 patient).

  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 displaying help to the user.

Given below is the Sequence Diagram for interactions within the Logic component for the execute("statistics 012019") API call.

StatisticsCommandSDForLogic
Figure 7. Interactions Inside the Logic Component for the statistics 012019 Command

3.4. Model component

The figure below describes the architecture of the model component of this application.

Model diagram
Figure 8. Structure of the Model Component

API : Model.java

The Model,

  • stores a UserPref object that represents the user’s preferences.

  • consists of a QuickDocs object which contains all the data, and lists of managers of sub-modules retrieved from the QuickDocs object.

  • does not depend on any of the other three components.

The QuickDocs,

  • is the ultimate unit storing all sub-models and data for this application.

  • is the class in charge of interacting with the storage component responsible for converting application data to files for storage.

  • see Storage Component for detailed explanation of the role of QuickDocs in Storage component.

The following sections illustrate the design of managers of each sub-module.

3.4.1. Model for Medicine module

QuickDocs supports customized organization of medicine inventory.

The figure below illustrates the implementation of the inventory system for medicine.

MedicineManager diagram
Figure 9. Structure of the MedicineManager

In medicine module, information about a medicine is encapsulated into the Medicine class.

Directory is a container for medicines, and sub-directories as well.

The MedicineManager keeps a list of reference of all unique medicines in the storage, so that no two medicine in the storage could share the same name to avoid confusion.

All occurrences of medicines with the same name across different directories point to the same medicine in the list of unique medicine in MedicineManager.

As the directory-medicine structure resembles the tree data structure, it is possible to support tree-like operations, such as setting the same threshold for the "subtree" of a directory.

3.4.2. Model for Patient Management

The figure below illustrates how is a patient represented and how are patients are stored in QuickDocs.

PatientManager diagram
Figure 10. Structure of PatientManager

A patient in QuickDocs consists of an address, name, NRIC, Contact, Email, Date of Birth, Gender and any number of tags.

Specially, no two patients in QuickDocs can share the same NRIC number. In other words, the patients in QuickDocs are easily identified with their unique NRIC numbers.

The PatientManager keeps a list of patients by chronological order of addition. PatientManager supports searching patients by NRIC, name and tags.

3.4.3. Model for ConsultationManager

The figure below illustrates how consultations with patients are recorded and organized in QuickDocs.

ConsultationManager diagram
Figure 11. Structure of ConsultationManager

A Consultation in QuickDocs is defined to one patient and it consists of an optional Diagnosis and a list of Prescription of medicine.

A diagnosis is then consisting of an assessment, the final conclusion of patient’s illness, and a list of symptoms.

Past consultations are kept as a list in ConsultationManager, and the manager supports listing consultations of the same patient by his/her NRIC.

3.4.4. Model for StatisticsManager

Every monetary transaction happened in the clinic, such as prescriptions to patients, is recorded by QuickDocs, and statistics report could be generated upon user requests.

The figure below illustrates how such records are organized in QuickDocs, and how the statistics reports are generated.

StatisticsManager diagram
Figure 12. Structure of StatisticsManager

Monetary transactions in the clinics are categorized to two forms, i.e purchasing of medicine and revenue from consulting patients.

Both forms have corresponding classes to record such transactions. Every successful execution of purchase medicine command and every successful consultation will create its corresponding record.

MonthStatistics holds records of purchases of medicines and consultations happened in a particular month.

The overall StatisticsManager has a list of MonthStatistics arranged in chronological order.

3.4.5. Model for AppointmentManager

The Appointment module manages time slots for appointment requests from the patients.

The figure below illustrates how AppointmentManager is organized.

AppointmentManager diagram
Figure 13. Structure of AppointmentManager

A Slot is used to represent a time block during clinic’s opening hour available for appointments.

Appointment extends slot and each appointment is assigned exactly one patient.

AppointmentManager holds a list of appointments which can then be operated on upon user commands.

3.4.6. Model for ReminderManager

QuickDocs supports reminding our users about upcoming appointments and warns users about medicines that is low in stock.

QuickDocs also supports customized reminders that could be set up by the users themselves.

The figure below illustrates how reminder module is implemented.

ReminderManager diagram
Figure 14. Structure of ReminderManager

Reminder extends from slot, and has a starting date and end date. Users are free to customize reminders' title and comments for user-initiated reminders.

ReminderManager keeps a list of reminders sorted by the date of reminder. Reminders that expires, i.e passed the end date, will automatically be hidden from the panel list of reminders shown to the user.

Upon every subtraction or addition of medicine quantity in the inventory, the ModelManager calls the ReminderManager to check the sufficiency of medicine against the set threshold and update the reminder panel accordingly, so that the reminders for medicines in low stock is managed automatically upon every change in medicine quantity.

3.5. Storage component

The Storage component of QuickDocs allows data to be saved and read from a json file.

3.5.1. Structure of the Storage Component

StorageClassDiagram
Figure 15. Structure of the Storage Component

API : Storage.java

The Storage component,

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

  • can save all the QuickDocs data in json format into a single json file and read it back

    • this json file contains 8 different lists, with each list consisting of only one of the 8 main JsonAdapted Class objects.

3.5.2. When does QuickDocs read your data?

All data are stored in a json file, with a default filepath data/quickdocs.json. This filepath can be customised in the preferences.json file.

When QuickDocs is launched, all information in the quickdocs.json file will be read. As mentioned in the previous section, the json file contains 8 different lists, and each list will have their information converted to their corresponding model types by their respective toModelType() methods of the 8 different JsonAdapted classes.

These converted objects will then be added into their respective class managers. As mentioned in the Model Component section, the QuickDocs class is responsible for storing all these data as it holds all the different class managers.

Note that there are only 6 managers but there are 8 JsonAdapted classes. This is because converted JsonAdaptedStatistics and JsonAdaptedMonthStatistics objects are both stored in the StatisticsManager. Similarly, both JsonAdaptedMedicine and JsonAdaptedDirectory objects are stored in MedicineManager.

Starting QuickDocs with no data

If the quickdocs.json is not found, or contains any errors such that any information cannot be converted to its corresponding model type, QuickDocs will be launched in a clean slate. If it is the case that the json file is erroneous, it will not be deleted, however it will be overwritten if any saving occurs during the current session.

3.5.3. When does QuickDocs save your data?

QuickDocs saves data whenever there is modification of any information in the current session.

Note that the user mainly interacts with QuickDocs by executing commands, and only some user commands will modify its data. For example, commands such as listmed, to list medicines, or listapp, to list appointments, will not affect the data. However, commands such as editpat, to edit a patient’s particulars, or addapp, to add an appointment, will change the information stored in QuickDocs.

Hence, only methods that modifies data will indicate to the QuickDocs class that a modification occurred. All methods that interact with the various class managers are contained in the ModelManager class, which holds a reference to the main QuickDocs object and references to all class managers. The following are the steps taken when one of these methods, in this case Model#addApp(), is called, which leads to data being saved:

  1. Model#addApp() adds the provided Appointment into AppointmentManager.

  2. Model#addApp() then calls QuickDocs#indicateModification(), providing the boolean true argument. This indicates that a change in data has occurred, which did happened since a new Appointment object had been added.

  3. LogicManager#execute() checks if any modification occurred, through the QuickDocs#isModified() method, which in this case returns true.

  4. LogicManager#execute() then saves the new modified QuickDocs data by calling Storage#saveQuickDocs(), providing it with the QuickDocs object. The new modified data will now overwrite all data stored in the quickdocs.json file.

Design considerations

We chose this implementation to ensure that no data will be lost due to any unforeseen circumstances. For example, if QuickDocs is closed unexpectedly, no data will be lost as any change in information has already been saved when that modification occurred.

3.6. Common classes

Classes used by multiple components are in the quickdocs.commons package.

4. Implementation

This section describes the details on how certain features are implemented.

4.1. Patient management module

The patient management module consists of these commands:

  1. adding a patient record addpat

  2. editing a patient record editpat

  3. deleting a patient record deletepat

  4. listing a patient record (by name, nric, tags or index) listpat

The operations involved in the patient management modules involve the use of the model\Patient\PatientManager.java class. The PatientManager maintains a list of patient records, which are loaded from the quickdocs.json file through the storage component.

4.1.1. Adding a patient

Patient records consist of Name, NRIC, Email, Address, Contact, Gender, Dob (Date of Birth) and tagList fields. The addpat command require users to enter the value of these fields prepended by prefixes. The prefixes are used to separate the parameters and assign the values to these fields.

patientcreation
tagList can contain multiple or no tags at all.

4.1.2. Editing a patient

To edit a patient, a PatientEditedFields is first created. It consist of all the fields of a Patient object but all its values are null initially. This means that only when the user enter a value for a specific field will it be assigned to the PatientEditedFields.

A temporary Patient object is then created with the values of the existing patient record to be edited. The PatientEditedFields will then be checked against this temporary patient object and replace the fields which are non-null.

patientediting

An additional check for NRIC will be done on the list of patient records to ensure that the editing of NRIC does not cause a conflict with existing Patient records. When this additional check is passed, the temporary patient object will replace the existing patient record designated for editing.

4.1.3. Deleting patient records

Each patient have a unique NRIC value. This is how QuickDocs differentiate between the different patient records in the patient list in the PatientManager class.

To delete a patient record, the deletepat and a nric is specified. The patient list will be iterated and the record whose NRIC matches the specified value will be removed.

4.1.4. Listing patient records

Since the patient records are stored in a list, their position in the list (index) can be used to view the details of a specific patient record.

The user can narrow down their patient record searches using the names, nric and tags assigned to each patient, and this results in a sublist of patient records, with their index reflected to be shown on the main display of QuickDocs. The specific session can then be viewed by calling listpat along with the index.

Internally, a ListCommand can be created using four different constructors and each of them have a constructedBy field. The constructedBy field will indicate whether the search is done by indexing, or filtering by name, nric or tags.

  1. If indexing is used, getPatientAtIndex() is called during the execution of the List command to simply retrieve the record in the patient list, at the position specified.

  2. If name is used, findPatientsByName() will be called, and patient records whose names containing the sequence that the user entered will be retrieved.

  3. If the nric is used, findPatientsByNric() is called and all patient records whose NRIC starts with the sequence supplied will be retrieved.

  4. If tag is used, findPatientsByTag is called, retrieving all patient records tied with the tag specified.

Lastly, if listpat is called without any search parameters, QuickDocs will simply list the first 50 patients in the patient list.

4.2. Consultation management module

The consultation module consists of:

  1. The consultation process

  2. Listing of past patient consultation records

The listing of past patient consultation records is similar to the listing of patient records explained earlier, the only difference being it can only be filtered down by NRIC and viewed using indexing.

The consultation process on the other hand, comprises of four stages: starting, diagnosis, prescribing and ending the consultation session. It leverages on the actions done in the patient module and forms the bulk of the processes in the consultation module.

The following section will provide a more in-depth exploration of how the consultation process is implemented. This includes:

  • The explanation of the design and mechanism behind the consultation process

  • The decision making process of selecting the current implementation

4.2.1. Current Implementation for the consultation process

The consultation process comprises of four stages:

  1. starting the consultation with a selected patient

  2. entering the symptoms, assessment of the patient’s current condition

  3. entering the medicine to be prescribed

  4. ending the consultation

The consultation process is facilitated by the ConsultationManager.java class. The ConsultationManager class holds the current consultation session and a list of past consultation records for every patients.

Methods in the ConsultationManager comprises of:

  • createConsultation(Patient) — Starts a consultation session with the current selected patient

  • diagnosePatient(Diagnosis) — Record symptoms patient mentioned and the assessment of the current condition.

  • prescribeMedicine(List of Prescriptions) — Prescribe the medicine and the quantities to be administered.

  • endConsultation() — Ends the consultation session. No further edits can be made to both prescription and diagnosis.

Both diagnosePatient and prescribeMedicine are repeatable. The values entered during the repeated command will simply replace the existing diagnosis / prescription.

QuickDocs only permit one ongoing consultation. During diagnosis and prescription, changes are only made to the current consultation session. The previous consultations should not be edited to prevent falsification of medical records. The current consultation session can only end after both the diagnosis and prescription are finalized.

Given below is an example usage scenario:

Step 1. A previously registered patient arrives and the doctor starts the session by entering the consult command in this manner: consult r/NRIC of the patient. A message to indicate the start of the consultation will be shown in the results display.

  • if the patient is new and his or her details are not recorded in QuickDocs, the command will not be executed and the doctor will be alerted that the consultation cannot continue since no patient records with the entered Nric can be found. An invalid nric entered will also prompt the same response

consultation1

Step 2. The patient will tell the doctor what are his / her ailments. The doctor will record the symptoms down. The doctor will then make the assessment of the illness the patient is having and execute the command by clicking on the Enter on the keyboard.

  • The symptoms and assessment have to be prepended by the s/ and a/ prefix respectively

  • The command entered by the doctor will look something like this: diagnose s/constant coughing s/sore throat a/throat infection

consultation2

Step 3. Should the patient inform the doctor of additional symptoms after the diagnosis is given, the doctor can simply press the up and down key to display the previously entered command on the userInput area. The doctor can then add the new symptom in and press Enter, replacing the previously recorded diagnosis.

consultation3

Step 4. The doctor will then add the medicine to the prescription list, followed by the quantities. Medicine are prepended by the m/ prefix while quantities are prefixed by q/.The order of the quantity entered corresponds with the order the medicine is added in the command:

  • prescribe m/Dextromethorphan m/ibuprofen q/1 q/2 In this case q/1 represents one unit of Dextromethorphan cough syrup is issued while 2 units of ibuprofen (inflammatory tablets) are issued to the patient

  • Alternatively, the doctor can enter the quantity right after the medicine: prescribe m/Dextromethorphan q/1 m/ibuprofen q/2

If any of the medicine issued are insufficient to complete the prescription, or is simply not in the inventory, a message will be displayed in the inputFeedback area. The command will not be executed and remains in the userInput text field. The doctor can then make the changes to the command.

consultation4

Step 5. Just like the diagnosis command, prescription can be replaced by reentering the command.

consultation5

Step 6. After explaining the medicine intake to the patient, the doctor can then end the consultation session on QuickDocs by using the command endconsult. No further changes to the consultation records can be made from this point on.

The following sequence diagrams summarizes what happens when a user perform the entire consultation process, starting with the session initialisation:

consultationSD1

Followed by the adding of the diagnosis:

diagnosisSD

prescribing the medicine to tackle the patient’s condition:

prescriptionSD

finally, saving the consultation record into QuickDocs:

endconsultSD

4.2.2. Design considerations

  1. In a neighbourhood clinic setting, doctors usually tend to only one patient at a time. This is why QuickDocs only allow a single ongoing session in the consultation process.

  2. In Singapore, every person is given a unique NRIC / FIN number regardless of their citizenship statuses. As such the NRIC is used to search for the patient records to start the consultation session.

  3. The prescription and diagnosis commands are made to override their previous states to ease the modification of consultation data. Doctors can simply use the command history to navigate to the previous command entered, make the changes and then execute the command. This allow them to simply add a few words to change consultation data rather than re-entering the entire command line.

  4. Prescription can actually be added before the diagnosis is recorded. The doctor could be expecting a patient for regular checkup and prepare the prescription before the patient enters the room. If the condition remains the same as before, the doctor can simply enter the diagnosis to complete the consultation session, cutting down the time spent on the consultation session.

4.2.3. Alternatives considered

Prior to the current implementation, a few options for the overall consultation process was considered:

Alternative Description Pros Cons

Consultation as one single command

Doctor enter consult followed by all the symptoms, assessment, prescriptions and then execute

Consultation is now restricted to just one class

The consultation creation will truly be one-shot

Input will be verbose, easy for the doctor to make mistakes

Harder to spot and navigate to the erroneous part to make changes

No room for the doctor to make changes as the consultation could have ended with erroneous information recorded

Iterative consultation creation

Doctor enter consult.

Doctor get prompted to enter symptoms and assessment.

Doctor get prompted to enter prescription.

Consultation is ended once prescription is recorded

Less likely to enter erroneous data as consultation is now broken down to different stages

The consultation will take a longer time to be completed

Doctor can only diagnose and prescribe during the session, while other related actions (such as listing past records) can only be done after the consultation

Separate commands for start, diagnose, prescribe and end

(Chosen implementation)

Doctors begin and end session with consult and endconsult.

Prescriptions and diagnosis can be added or replaced using the diagnose and prescribe commands before the session ends.

The editing involve the replacement of the current diagnosis or prescription entry, commands can be reused to perform both add and edit operations.

Editing remain one shot and fast as users can make use of the command history to make changes to a previously entered command to make changes.

Flexibility in recording consultation details, instead of having to go through the start, diagnose, prescribe, end order strictly.

Room for other commands to be executed while a session is ongoing.

Potentially more commands will be called when compared to the other options.

diagnosis and prescription commands entered could be verbose and doctors can make mistakes easily.

Fragment diagnosis and prescription commands even more

Same as the third alternative, but there are commands specific to the adding of symptoms in diagnosis, adding of medicine in prescription.

Separate commands for editing the symptoms added or medicine prescribed

Shorter commands to add symptoms or prescribe medicine. Less mistakes will be made.

Doctors only need to edit specific entries instead of retyping or navigating to previously entered command and make changes.

Way more commands to be entered by users.

Even more commands and methods to be written, there will be a higher possibility of bugs arising from the increase in code volume.

Although the selected option require more input and lengthier commands, it guarantees the flexibility and efficiency QuickDocs aim to deliver for doctors in neighbourhood clinics.

These are some of the considerations taken before the decision was made:

  1. Since QuickDocs aim to provide a single interface for doctors to perform clinical operations more efficiently, the consultation process will require one shot commands to fulfill the efficiency requirement of the overall product.

  2. It is highly possible for doctors to make mistakes with the one-shot commands, especially when there are so many parameters involved in a single command. Therefore the implementation must provide a convenient form of error recovery.

  3. There could be interleaving operations between the modules, such as viewing past consultation records or checking medicine inventory in the midst of the consultation. The implementation must be flexible enough to allow cross module commands during a consultation.

Although the selected option require more user input and involve lengthier commands, a doctor with fast typing speed will be able to circumvent the issues of slightly more verbose command lines easily.

If the doctor enters an erroneous command or simply want to make changes, the command history can be used in conjunction with the one shot commands to make changes quickly.

The selected option also do not restrict doctors to just consultation-related commands. He or she can perform other operations such as checking the inventory or view free appointment slots during the consultation itself.

The selected implementation guarantees the flexibility and efficiency that QuickDocs aim to deliver for doctors in neighbourhood clinics.

4.3. Appointment management module

The Appointment module provides the user with greater control over his/her scheduled appointments by organising them neatly and preventing any clash of appointment timings. Listed below are the commands that the appointment module features:

  1. Adding an appointment, addapp

  2. Listing appointments, listapp

  3. Deleting an appointment, deleteapp

  4. Listing free appointment slots, freeapp

These features are supported by the AppointmentManager class, which stores all created appointments in an ArrayList. On QuickDocs launch, existing appointments are read from the quickdocs.json file through the storage component.

4.3.1. Appointments

An Appointment is a subclass of the Slot class, and has the following 5 compulsory fields:

  • Patient patient - the patient who made this appointment

  • LocalDate date - the date of this appointment

  • LocalTime start - the time this appointment starts

  • LocalTime end - the time this appointment ends

  • String comment - any other details for this appointment

4.3.2. Adding an appointment

The user can add an appointment to his/her schedule to keep track of future meetings, by using the addapp command. All 5 fields of an appointment, as mentioned in the Appointments section, must be specified together with the command.

Input validation

These fields are parsed by the AddAppCommandParser class, creating an AddAppCommand object, which then carries out the following steps before adding the appointment into QuickDocs:

  1. Retrieve the patient, if he/she exists, by calling Model#getPatientByNric(). Note that the user specifies the patient of the appointment by providing the patient’s NRIC, hence the need for this step.

  2. Check if the start and end timings are valid. The appointment timing has to be within office hours (9am to 6pm) and the start time must not be after or equal to the end time.

  3. Create the appointment using the given fields and check if this appointment has any conflict in timing with other existing appointments, by calling Model#hasTimeConflicts().

  4. Finally, add the appointment into AppointmentManager by calling Model#addApp().

If any of the steps 1 to 3 fails, a CommandException will be thrown and the corresponding exception message will be shown to the user.
Adding the appointment into AppointmentManager

Listed below are the steps taken when the Model#addApp() method is called. Note that when an appointment is added, a reminder tailored for this appointment will also be added. This was implemented to ensure that the user does not forget about the appointment in the future.

  1. Add the given appointment into the AppointmentManager by calling its addAppointment() method.

    • AppointmentManager#addAppointment() takes in the appointment to be added and adds it into the ArrayList of appointments in its sorted position. This ensures that this list of appointments is always sorted by date and time, with the earliest appointment at the start of the list.

  2. Create a reminder tailored to this appointment by calling Model#createRemFromApp().

  3. Add the newly created reminder into the ReminderManager by calling Model#addRem().

You can learn more about reminders in the Reminder management module section.

All these steps that are executed when the addapp command is called can be summarized in the Sequence Diagrams shown below:

addapp SD
Figure 16. Sequence diagram when addapp is called


addapp SD ref
Figure 17. Sequence diagram reference: add appointment to AppointmentManager

4.3.3. Listing appointments

The user can list his/her past or future appointments using the listapp command. The user can either provide a range of dates to list out all appointments in those dates, or provide an NRIC to list out all appointments for the patient with the given NRIC.

If the user does not specify a FORMAT and DATE, FORMAT will default to week and DATE will default to the current date, meaning that the current week’s appointments will be displayed.
Listing by dates

The user can specify the range of dates by providing a FORMAT (day, week, or month) and a DATE, which means to list all appointments on the FORMAT (day/month/week) of DATE. The following steps will then be taken:

  1. The ListAppCommandParser class parses these two parameters into LocalDate start and LocalDate end, and creates a ListAppCommand object. start and end represents the start and end dates of the range of dates of appointments to be listed.

  2. ListAppCommand will then be executed, calling Model#listApp(), providing it with the start and end dates.

  3. Model#listApp() then calls AppointmentManager#listAppointments(), with the same 2 arguments, which will return a String of all appointment information within the given range of dates.

Listing by patient’s NRIC

In this case, the user only provides the NRIC of a patient together with the listapp command. The following steps will then be taken:

  1. The ListAppCommandParser class parses the user input into an NRIC object, creating a ListAppCommand object with this NRIC field.

  2. ListAppCommand executes:

    • Firstly, it retrieves the patient with the provided NRIC, if exists, by calling Model#getPatientByNric().

    • Model#listApp() will then be called, providing it with the patient retrieved.

  3. Model#listApp() then calls AppointmentManager#listAppointments(), providing it with the patient, which returns a String of all the given patient’s appointment information.

Model#listApp() and AppointmentManager#listAppointments() are overloaded methods, having different method signatures based on their parameters. One implementation takes in two LocalDate parameters, start and end, while the other implementation takes in a single Patient object. This allows the same method name to be called, and list appointments by either providing a range of dates or a valid patient respectively.

4.3.4. Deleting appointments

The user can delete any appointments created using the deleteapp command. Since there cannot be any clash in timings for appointments, any appointment can be identified uniquely by its date and start time. Hence the user can specify the appointment to be deleted only with those two fields, after which the following steps are taken:

  1. The DeleteAppCommandParser class parses the two parameters into LocalDate date and LocalTime start that specifies the date and start time of the appointment to be deleted respectively. It then creates a DeleteAppCommand object with these two fields.

  2. DeleteAppCommand executes:

    • Firstly, it checks if start is a valid timing, checking if it is within office hours (9am to 6pm).

    • Next, it retrieves the specified appointment, if it exists, by calling Model#getAppointment(), providing it with date and start.

    • Finally, Model#deleteAppointment() is called, providing it with the appointment retrieved.

  3. Model#deleteAppointment() then calls AppointmentManager#deleteAppointment(), providing it with the appointment. AppointmentManager#deleteAppointment() will then remove the appointment from the ArrayList of appointments stored in AppointmentManager.

4.3.5. Free appointment slots

Before deciding on an appointment timing, the user can execute the freeapp command to list out all the timings available for a new appointment booking.

Command format: freeapp f/FORMAT d/DATE

We can see that the freeapp command takes in two parameters:

  1. FORMAT: can be day, week, or month

  2. DATE: a valid date

This command can be roughly translated to:
"Search for free appointment slots on the FORMAT (day/month/week) of DATE."

The FreeAppCommandParser class will parse these two parameters into two dates, LocalDate start and LocalDate end, representing the start and end dates of the search range for free appointment slots. FreeAppCommandParser then constructs a FreeAppCommand object with the start and end fields.

If the user does not specify a FORMAT and DATE, FORMAT will default to month and DATE will default to the next month’s date, meaning that free appointment slots for the whole of the following month will be displayed.
Current Implementation

The search is facilitated by the AppointmentManager class which stores all created Appointments in an ArrayList. AppointmentManager contains the method listFreeSlots() which firstly calls AppointmentManager#getFreeSlots(). getFreeSlots() is the main method that implements the logic behind freeapp.

Given below are the steps taken when listFreeSlots() is called.

Step 1. The method listFreeSlots() takes in the two arguments, start and end, which have been mentioned previously. Firstly, listFreeSlots() calls getFreeSlots(), providing it with the same two arguments, to retrieve a List of free Slots before it can parse them into a String.

freeapp1
Figure 18. Given search range from start to end date


Step 2. In getFreeSlots(), we first retrieve the existing appointments that are within this given search range by using the method AppointmentManager#getAppointments().

freeapp2
Figure 19. Retrieved appointments in the search range


Step 3. Next, we look at all the appointments that are present on the start date, as shown in the diagram below. These appointments are sorted by date and time, with the earliest appointment on the left and the latest on the right.

freeapp3
Figure 20. Selected appointments on start date
Since the appointments are already sorted, we do not need to search through the whole appointment list to find appointments present on the start date. We can simply go through the list from the beginning until we reach an appointment date that is not equals to start.


Step 4. We fill in each empty 'gap' between any two appointments by creating a Slot object.

Each Slot object represents a single time period on a single date. It has three attributes:

  • LocalDate date - the date of this time slot.

  • LocalTime start - the start time of this time slot.

  • LocalTime end - the end time of this time slot.

In this freeapp context, these slots created represents a time period without any scheduled appointments.

freeapp4
Figure 21. Slots created to fill in empty time slots
Slots will only be created for timings during office hours (09:00 to 18:00). This is to prevent any possible inconvenience caused if the user accidentally decides on a timing outside of office hours. (Even though there will be an office hour constraint when the user eventually creates the appointment.)


Step 5. We repeat Steps 3 and 4, replacing the start date with the remaining dates until the end date. All slots created will be added into an ArrayList of free slots, freeSlots.

freeapp5
Figure 22. All empty time slots filled


Step 6. After all the slots are added, we return freeSlots to the caller function listFreeSlots(), to generate a String that represents all the free slots to be appended onto the main display of the UI.


We can summarize the steps taken after the freeapp command is called in the Sequence Diagram below:

freeapp SD
Figure 23. Sequence diagram when freeapp is called
Design Considerations

Listed below are some of the considerations we took when designing the freeapp command.

  1. This feature was implemented for the convenience of the user in choosing a valid appointment slot with his/her patient. It is more intuitive to decide on an appointment slot based on all the empty slots shown, rather than listing out all existing appointments using listapp and then figuring out what timings are available from there.

  2. We require the user to specify the search range by listing the FORMAT and DATE instead of the the start and end dates directly, to make the command more user friendly. The user does not have to be bothered with the exact range of dates to search, and can simply specify a rough date and be provided with information for the neighbouring dates if the FORMAT given is week or month. Moreover, if the user wants to list all free slots for the whole month, they do not have to check what the last date of the month is in order to specify the end date.

Alternatives Considered

Listed below are the methods considered to implement the freeapp command.

Alternative Description Pros Cons

Maintain a permanent list of free slots

Maintain a list of free slots for a pre-determined range (e.g. next three months) instead of creating a new list every time appfree is called.

It will be quicker to search for free slots as the list is already created. We simply need to filter the list with the given search range and print out the resulting filtered slots.

Tedious work needs to be done to maintain this permanent list of free slots, as it has to be modified whenever an appointment is added or deleted.

Also, if the given search range is not within the range of this consistent list of free slots, this list will still have to be created from scratch, defeating the purpose of maintaining this permanent list.

Generate free slots only when required

(Chosen implementation)

We will only generate a list of free slots when the freeapp command is called. This list will be a one-time use only and will not be stored in QuickDocs storage.

The user is given the flexibility to specify the range of dates to list the free slots, as this list is generated on the spot, and is not limited to the dates of a pre-determined list.

Since the generated list of free slots is not stored, extra work will be done in generating the same free slots when the next freeapp is called, that has a range of dates which overlaps the previous freeapp dates.

We decided to implement the second method, as it is more straightforward. Here are the reasons why:

  1. The first implementation is actually just an extension of the chosen implementation as it still requires a way to generate a list of free slots, either when QuickDocs is launched or when the user requests a search range outside of the pre-determined list.

  2. The first implementation additionally requires more effort to maintain this permanent list whenever the list of appointments is modified, which is not straightforward to implement. For example, we need a method to merge two free slots when an appointment is deleted, and another method to split a free slot into two when an appointment is added.

  3. The benefit of a permanent list of free slots is the quicker execution time of freeapp, which will typically only be called a small number of times (around 10) a day, when the user books an appointment slot with his/her patient. The total time saved on executing freeapp a small number of times is therefore negligible.

  4. QuickDocs already has plenty of data to be stored, such as appointments, consultations and medicine records. The minimal benefits that a permanent list of free slots provide does not justify its additional storage cost.

4.4. Reminder management module

The Reminder module provides the user with a way to keep track of future tasks, to-dos, or appointments. The reminders will be displayed on the reminder side bar, and are colour coded as such:

  • Blue: Reminder for a scheduled appointment

  • Red: Reminder to stock up on a medicine

  • Beige: Any other personal reminders

Listed below are the commands that the reminder module features:

  1. Adding a reminder, addrem

  2. Listing reminders, listrem

  3. Deleting a reminder, deleterem

These features are supported by the ReminderManager class, which stores all created reminders in an ArrayList. On QuickDocs launch, existing reminders are read from the quickdocs.json file through the storage component.

4.4.1. Reminders

A Reminder is a subclass of the Slot class, and has the following 5 fields:

  • String title - the title/header for this reminder

  • LocalDate date - the date for this reminder

  • LocalTime start - the start time for this reminder

  • LocalTime end - the end time for this reminder

  • String comment - any other details for this reminder

Only the title, date and start attributes are compulsory fields for a reminder.

4.4.2. Adding a reminder

The user can take note of a task to do by creating a reminder using the addrem command. As mentioned in the Reminders section, only the title, date and start attributes are compulsory fields and must be specified together with the addrem command. Fields end and comment are optional. Below are the steps taken after the user executes the addrem command.

  1. The AddRemCommandParser class parses the user input into 3 to 5 Reminder fields.

  2. The Reminder object is constructed by the parser with the given fields.

  3. An AddRemCommand object is created, providing it with the reminder object to be added.

  4. AddRemCommand executes:

    • Firstly, it checks if there is a duplicate reminder using the Model#duplicateRem() method, which subsequently calls ReminderManager#hasDuplicateReminder().

    • If there are no duplicates, Model#addRem() is called, providing it with the given reminder.

  5. Model#addRem() then calls ReminderManager#addReminder(), providing it with the given reminder. ReminderManager#addReminder() will then add the reminder into the ArrayList of reminders stored in ReminderManager.

The added reminder will only appear on the reminder sidebar if the date of the reminder is within the range of dates that the sidebar is currently displaying. More information can be found in the next section, Listing Reminders.
Automatic generation of reminders

Some reminders will be created and added automatically.

  1. Appointment reminders

    • When an appointment is added, a reminder for this appointment will be generated automatically. More information can be found in the adding an appointment section.

  2. Low medicine alarm reminders

    • The user is able to set a threshold for each medicine in the inventory by using the alarm command. When the quantity of a medicine drops below its threshold, a reminder will be created to alert the user of the low medicine quantity. More information can be found in the medicine implementation section.

4.4.3. Listing reminders

The reminders displayed on the reminder sidebar can be filtered using the listrem command.

Displaying reminders on the sidebar

Similar to the listapp command, the user can specify a range of dates by providing a FORMAT (day, week, or month) and a DATE, which means to display all reminders on the FORMAT (day/month/week) of DATE. The following steps will then be taken:

  1. The ListRemCommandParser class parses these two parameters into LocalDate start and LocalDate end, and creates a ListRemCommand object. start and end represents the start and end dates of the range of dates of reminders to be displayed on the sidebar.

  2. ListRemCommand executes:

    • Firstly, a ReminderWithinDatesPredicate object will be created with the given start and end dates. This predicate is used to update the FilteredList of reminders contained in the ModelManager class.

    • Next, Model#updateFilteredReminderList() is called, providing it with the created predicate.

  3. The FilteredList of reminders will be updated to match the range of dates given and the reminder sidebar is updated.

If the user does not specify a FORMAT and DATE, FORMAT will default to week and DATE will default to the current date, meaning that the current week’s reminders will be displayed.
Display a single reminder

The listrem command can also be used to display the details of a single reminder onto the main display. This is useful when the title or comment of a reminder is too long to be displayed fully on the sidebar. The user can do so by providing the index of the reminder, as shown in the sidebar, together with the listrem command.

  1. The ListRemCommandParser class parses the user input into an Index object, and creates a ListRemCommand object consisting of the Index field.

  2. ListRemCommand executes:

    • Firstly, it retrieves the currently displayed List of reminders by calling Model#getFilteredReminderList().

    • Next, it checks if the given Index is valid, checking if the Index is present in the current List of reminders.

    • If the Index given is valid, the reminder to be displayed is retrieved using the List#get() method on the List of reminders.

  3. The information of the selected reminder is then appended onto the main display of the UI.

Displaying a single reminder can also be done by clicking on the reminder in the sidebar with a mouse. This feature is supported by the ReminderListPanel class which is a UI component for the reminder sidebar. It contains the Node#setOnMouseClicked() method that takes in an EventHandler, which is specified to display the reminder details when the reminder is clicked.

4.4.4. Deleting a reminder

Deleting a reminder is simple, as the user only needs to specify the index of the reminder shown on the sidebar, together with the deleterem command. The process, when deleterem is called, described below is similar to the process when listrem is called to display a single reminder.

  1. The DeleteRemCommandParser class parses the user input into an Index object, and creates a DeleteRemCommand object consisting of the Index field.

  2. DeleteRemCommand executes:

    • Firstly, it retrieves the currently displayed List of reminders by calling Model#getFilteredReminderList().

    • Next, it checks if the given Index is valid, checking if the Index is present in the current List of reminders.

    • If the Index given is valid, the reminder to be deleted is retrieved using the List#get() method on the List of reminders.

    • Model#deleteReminder() is called, providing it with the reminder to be deleted.

  3. Model#deleteReminder() subsequently calls ReminderManager#delete() to delete the given reminder from the ArrayList of reminders stored in ReminderManager.

Reminders are automatically created when an appointment is added, as mentioned here. However, when the reminder of an appointment is deleted, the appointment will NOT be deleted. Use the deleteapp command to delete an appointment.

4.5. Record & Statistics feature

4.5.1. Current implementation

The statistics command is started through the command stats START_MMYY [END_MMYY]. The two MMYY corresponds to a range of dates. The end range is optional,

4.6. Administrative and Statistics Module

The administrative and statistics module currently consists of 2 commands:

  1. setting the consultation fee setconsultfee

  2. querying the statistics statistics

This 2 commands makes use of the classes located in filepath model\record.

4.6.1. Consultation fee

The consultation fee of the clinic is stored as a BigDecimal in the StatisticsManager of QuickDocs, which is loaded from the quickdocs.json file through the storage component. The consultation fee is used for calculating financial statistics for any ConsultationRecord objects.

4.6.2. Querying statistics

The statistics command is started through the command stats START_MMYYYY [END_MMYYYY]. The two MMYYYY corresponds to a range of dates. The end range is optional, and is defaulted to the start range by the StatisticsCommandParser if it does not exist.

The start date is not allowed to be before January 2019, and the end date cannot be before the start date. Hence, QuickDocs currently does not support adding old records before January 2019 due to the implementation of the StatisticsManager. This will be explained in the section below.

MMYYYY is a string, e.g. "012019", which stands for January 2019. It is parsed by StatisticsCommandParser into a YearMonth object.

4.6.3. Statistics and Record - Current Implementation

The statistics class stores 6 types of information:

  1. Number of consultations

  2. Medicines prescribed

  3. Symptoms diagnosed

  4. Revenue

  5. Expenditure

  6. Profit

Number of consultations is stored as an int, while the financial variables are stored using BigDecimals. The number of medicines prescribed and symptoms diagnosed are stored by using a HashMap.

Implementation of additional statistics will be done through adding additional relevant variable fields.

The implementation of Statistics and Record has 3 parts:

  1. Creation of the Record

  2. Adding the Record

  3. Retrieving the Statistics

1. Creation of the Record

In order for the statistics to be keep tracked of, Record objects are used to retrieve information that the StatisticsManager will make use of. The Record class is an abstract class that only has 1 abstract method, toStatistics(StatisticsManager sm), which will generate a Statistics object. Each child class of Record is for a specific operation in QuickDocs, where the implementation toStatistics(StatisticsManager sm) will generate a Statistics object that stores relevant information pertaining to that specific operation. The StatisticsManager is passed in to retrieve the any variable that the Record might require to calculate the statistics, e.g., ConsultationRecord requires the consultationFee variable in StatisticsManager.

Currently, there are only 2 child classes of Record, ConsultationRecord and MedicinePurchaseRecord. ConsultationRecords are created when the a consultation session ends from the EndConsultCommand, while MedicinePurchaseRecord are created when a medicine is purchased via the PurchaseMedicineCommand. The commands will create the Record, and call ModelManager’s addRecord(record, clock) function, which will then result in ModelManager calling StatisticsManager’s record(record, clock) function. The clock used is the system clock, to retrieve the current YearMonth of the Record created. The sequence diagram below illustrates an example ConsultationRecord being created.

RecordCreationSD
Figure 24. Sequence diagram for sample ConsultationRecord creation
2. Adding the record

The StatisticsManager holds an ArrayList of MonthStatistics, where a MonthStatistics object contains the YearMonth, and the Statistics object of that YearMonth. Each MonthStatistics object will be initialised with the zero Statistics object, where all the variables are 0 or contains no elements (not null). The ArrayList starts with a MonthStatistics with the YearMonth 2019 January, and every subsequent index will contain the MonthStatistics with the subsequent month, e.g., the 4th index contains the MonthStatistics with YearMonth 2019 May.

When the StatisticsManager adds a new Record by the record(record, clock) function, it will first retrieve the YearMonth from the clock variable passed in. Next, it will update the size of the ArrayList by calling its own method updateListSize(clock), which is a wrapper for updateListSize(YearMonth). Afterwards, StatisticsManager will find the correct index of the MonthStatistics ArrayList to add the record in. In the current implementation, the record is not actually stored. Instead, the record will be converted to a Statistics object which is then merged with the MonthStatistics’s own Statistics object. The MonthStatistics’s Statistics object will then be reassigned with the newly merged Statistics object. The sequence diagram below illustrates an example ConsultationRecord being added.

SDForAddingRecords
Figure 25. Sequence diagram for adding a sample ConsultationRecord
3. Retrieving the Statistics

When the StatisticsCommand queries for the statistics for a range of months, Logic will call the ModelManager’s getStatistics(FROM_YEARMONTH, TO_YEARMONTH), which then calls StatisticsManager’s getStatistics(FROM_YEARMONTH, TO_YEARMONTH). StatisticsManager will convert the YearMonth objects to their respective indexes with the StatisticsManager’s getYearMonthIndex(YearMonth) function.

StatisticsManager will then obtain the statistics for each of the queried months, and merge them together into a new Statistics object. StatisticsManager will then return the Statistics back to the ModelManager, which would then return it to the StatisticsCommand, which would then return the CommandResult with the statistics converted to a String to the LogicManager.

4.6.4. Statistics and Record - Design considerations

  1. The statistics are stored in months as the design only allows the doctor to query within a minimum timespan of 1 month. Hence, it was decided that the statistics to be stored in months in a chronological order with an ArrayList for ease of retrieval.

  2. Currently, as QuickDOcs is developed in 2019, and there are no plans to allow the doctor to add in past records, the first index in the array of MonthStatistics is allocated to January 2019. Any MMYYYY value before 012019 will not be allowed.

  3. The MonthStatistics objects are stored in an ArrayList as it might be desirable for a MonthStatistics with the zero statistics to exist (all variables 0 or no elements). Such a case might happen when the doctor goes on vacation for the whole month. In addition, it would be easy to retrieve the MonthStatistics object of a specific MMYYYY by indexing.

4.6.5. Statistics and Record - Alternatives Considered

The following table lists out the alternatives designs considered for implementing the storage of the Records and Statistics.

Alternative Description Pros Cons

Storing of individual records for each month (Alternative chosen)

Individual records are stored within the MonthStatistics, along with the Statistics. When the Statistics for a specific month is queried, update the latest statistics and return it.

Individual records are kept, which could potentially be used for other calculations or features.

Storing of individual records is extremely costly in terms of space

Storing the merged statistics of all the records for each month

When a new record is added, it is coverted to a Statistics object which is then merged with the current Statistics object stored.

Only one Statistics object needs to be stored, which saves a lot of storage space.

The individual records are unable to be retrieved. However, the current implementation has no need to retrieve individual records.

4.7. Storing medicines in inventory

One essential aspect of clinic management is about managing medicine storage of the clinic. QuickDocs' medicine management module supports customized medicine organization via a browser-like directory format.

4.7.1. Current implementation

The current implementation takes a similar form as the Windows file browser. The user is free to determine for himself/herself how he/she wants the medicines to be arranged.

To organize the inventory, the following methods in MedicineManager are used:

  • addDirectory(new directory’s name, path of parent directory) — Adds a new directory with the given name to the parent directory corresponding to the path.

  • addMedicine(name, quantity, path of parent directory, price) — Adds a new Medicine with given name, quantity and price to the parent directory corresponding to the path given.

  • addExistingMedicineToDirectory(medicine, path of parent directory) — Assuming the medicine already exists, add a reference of this medicine under the directory corresponding to the path.

The current implementation does not allow multiple medicines with the same to exist simultaneously. However, one medicine could be placed in multiple directories.
Both directories and medicines' names are case-insensitive.

From the initial empty state of the storage, the users could arrange their storage in these following ways:

  1. The initial empty storage consists of an empty directory named as "root". The user can then add directories and medicines into the storage.

  2. The MedicineManager keeps a list of sorted unique medicine in the inventory.

  3. The user could add a new directory via adddirec command by specifying the path of the directory he/she wants to add into and the name of new directory.

  4. The user could add new/existing medicine to a specific directory via the "addMed" command.

    • 1. If there already exists a medicine with the same name in the storage, and the quantity and price is not specified in the command arguments, the existing medicine will be placed in the directory specified.

    • 2. Otherwise, a new medicine with the specified name, quantity and price will be created and added to the specified directory.

 

Given below is an example of organizing medicine from an initial empty QuickDocs.

Step 1: Initially, the storage only consists of an empty directory called root.

The list of unique medicine in MedicineManager is empty.

medicineModule example1

 

Step 2: Via adddirec root Internal, a new directory called "Internal" is added under root.

The list of unique medicine is still empty.

medicineModule example2

 

Step 3: Via a few more adddirec commands, the figure below is an illustration of a sample inventory’s framework.

The list of unique medicine is still empty.

medicineModule example3

 

Step 4: Now the user can add new medicines into the storage via addmed root\Internal\General paracetamol p/40 q/50.

medicineModule example4

The list of unique medicine is also updated.

listOfMedicine example1

 

Step 5: Via a few more addmed commands, some more new medicines are added to the inventory. The following figure shows the result after that

medicineModule example5

The list of unique medicine is also updated.

listOfMedicine example2

 

Step 6: Now, the user found out that aspirin can also be used to treat high blood pressure and decides to put it under "Cardiology" as well.

Via the addmed root\Internal\Cardiology aspirin, a reference to the existing aspirin medicine will be placed under the "Cardiology" directory.

The figure below shows the result of this command.

medicineModule example6

However, the list of unique medicine is not changed, as now new medicine is added.

listOfMedicine example2

This six-step example illustrates the basic implementation of how medicines and directories are organized in QuickDocs.


When typing the directory path in the command box in the ui, QuickDocs supports intelligent suggestions about the next field.

After the user entered at least one \ character to indicate he is inputting a path, the suggestion mode will be turned on.

The user could press Page Up / Page Down bottom to iterate to the previous or the next valid name of sub-directory or medicine in alphabetical order, given that the path given before the previous \ character is valid.

Using the above sample inventory as an example:

medicineModule example7
  • When the user types in addmed root\, the suggestion mode is turned on.

  • The user may not want to type in the full name of the directories, so when he types in addmed root\in, he could then press Page Down to iterate to the next valid name in alphabetical order, which is "Internal".

  • The command box is then automatically filled with addmed root\Internal

  • Similarly, if the user decides to traverse to the previous valid name, he could do so by press Page Up. And the command box will automatically be filled with addmed root\External.

The figure below illustrates how this feature is implemented to make user’s life more convenient.

suggestion diagram
Figure 26. Sequence diagram illustrating the implementation of suggestion mode

QuickDocs also supports setting alarm level for medicines. Every time a medicine’s storage falls below the designated level, a reminder is thrown.

To convenient the users, QuickDocs allow not only threshold setting for individual medicines, but also threshold setting for directories.

Taking the above sample inventory as an example:

medicineModule example7

Setting a threshold for a directory is effectively the same as setting the threshold for every medicine in the "subtree" of that directory. This is down by a tree-like traversal.

For example, alarm root\Internal 400 command sets the alarm level of all medicine in the subtree of "Internal" directory to 400.

medicineModule example8

4.7.2. Design consideration

  1. The current implementation takes into consideration that the users may wish to have some freedom in determining the arrangement of medicine.

  2. When prescribing medicines, a directory system that step by step leads to the desired medicine is to the convenience of the user.

  3. By arranging the medicine by folders, it is then possible to support massive manipulation of medicine by directories.

  4. Additionally, it is impossible to expect the doctor to always remember the full name of medicines correctly. There is a need for an easier way to identify medicines to operate on besides requiring the user to type in full names every time.

  5. Considering that even a small private clinic may have a considerably large set of medicine available in their storage, massive operation on a large set of medicine should be made possible besides operations on single medicine.

4.7.3. Alternatives considered

The table illustrates some of the alternatives I considered during development of this medicine module, the relative advantages they have over the current implementation, and why they are not selected at the end.

Alternative Description Comparative advantages Reasons for not adopting

Store medicines as a simple ArrayList

When users add a new medicine, just append a new medicine to the ArrayList.

During operations on medicines, use name of medicine as a key to select the wanted medicine.

  • Simpler command format: The user could type in less arguments for the same commands.

  • No maintainability issue: There is no constraint to the data structure, therefore there is no need to cross check all modules to ensure the constraints are maintained. Less likely to occur bugs.

  • The user must type in full name of medicines correctly to identify the correct medicine, This contradicts our belief that doctors are unlikely to know the full name of all his/her medicine.

  • There is no freedom for the user to organize his/her medicine. When the user wants to view or operate on all medicine that share some common traits, he/she is not able to do so.

Use a hash map to store the medicines

Use medicine name as the key and the medicine as the value. Search for the key to identify and operate on medicines.

  • Searching and identifying using names as key is more time-efficient compared to the current implementation, especially when data size is large.

  • Simpler command format, less fields to input per command.

  • There is no room for customized arrangement of medicine.

  • The user need to type in the correct full name of medicines to be able to identify medicines. This adds on extra difficulties for users to use the application.

  • Does not support massive operations on similar medicines. Users need to repeat the same operations on many medicines.

Store medicines in a list with tags

When adding medicines, require tags from the users so that searching and identifying medicine is easier.

  • Easier to list all medicines with the same tag.

  • Similar structure with patient module. Less efforts needed on both the developers' side and users' side to familiarise themselves with the data structure.

  • Potential lengthy commands if a medicine is widely applicable to many situations such that it may have a lot tags.

  • Have to either input the full name of medicine or recall the tag correctly to retrieve correct medicine. When there is a large set of medicines and tags, this alternatives provides little extra convenience as compared to the two alternatives above.

Since QuickDocs aims to provide the most convenient experience given a large set of medicine in a clinic inventory, the medicine management module needs to provide a model that makes both typing commands, identifying the correct medicine and massive operation possible.

Combined with the suggestion mode, the current design is the best way to implement all of the three.

4.8. Logging

We are using java.util.logging package for logging. The LogsCenter class is used to manage the logging levels and logging destinations.

  • The logging level can be controlled using the logLevel setting in the configuration file (See Section 4.9, “Configuration”)

  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level

  • Currently log messages are output through: Console and to a .log file.

Logging Levels

  • SEVERE : Critical problem detected which may possibly cause the termination of the application

  • WARNING : Can continue, but with caution

  • INFO : Information showing the noteworthy actions by the App

  • FINE : Details that is not usually noteworthy but may be useful in debugging e.g. print the actual list instead of just its size

4.9. Configuration

Certain properties of the application can be controlled (e.g user prefs file location, logging level) through the configuration file (default: config.json).

5. Documentation

We use asciidoc for writing documentation.

We chose asciidoc over Markdown because asciidoc, although a bit more complex than Markdown, provides more flexibility in formatting.

5.1. Editing Documentation

See UsingGradle.adoc to learn how to render .adoc files locally to preview the end result of your edits. Alternatively, you can download the AsciiDoc plugin for IntelliJ, which allows you to preview the changes you have made to your .adoc files in real-time.

5.2. Publishing Documentation

See UsingTravis.adoc to learn how to deploy GitHub Pages using Travis.

5.3. Converting Documentation to PDF format

We use Google Chrome for converting documentation to PDF format, as Chrome’s PDF engine preserves hyperlinks used in webpages.

Here are the steps to convert the project documentation files to PDF format.

  1. Follow the instructions in UsingGradle.adoc to convert the AsciiDoc files in the docs/ directory to HTML format.

  2. Go to your generated HTML files in the build/docs folder, right click on them and select Open withGoogle Chrome.

  3. Within Chrome, click on the Print option in Chrome’s menu.

  4. Set the destination to Save as PDF, then click Save to save a copy of the file in PDF format. For best results, use the settings indicated in the screenshot below.

chrome save as pdf
Figure 27. Saving documentation as PDF files in Chrome

5.4. Site-wide Documentation Settings

The build.gradle file specifies some project-specific asciidoc attributes which affects how all documentation files within this project are rendered.

Attributes left unset in the build.gradle file will use their default value, if any.
Table 1. List of site-wide attributes
Attribute name Description Default value

site-name

The name of the website. If set, the name will be displayed near the top of the page.

not set

site-githuburl

URL to the site’s repository on GitHub. Setting this will add a "View on GitHub" link in the navigation bar.

not set

site-seedu

Define this attribute if the project is an official SE-EDU project. This will render the SE-EDU navigation bar at the top of the page, and add some SE-EDU-specific navigation items.

not set

5.5. Per-file Documentation Settings

Each .adoc file may also specify some file-specific asciidoc attributes which affects how the file is rendered.

Asciidoctor’s built-in attributes may be specified and used as well.

Attributes left unset in .adoc files will use their default value, if any.
Table 2. List of per-file attributes, excluding Asciidoctor’s built-in attributes
Attribute name Description Default value

site-section

Site section that the document belongs to. This will cause the associated item in the navigation bar to be highlighted. One of: UserGuide, DeveloperGuide, LearningOutcomes*, AboutUs, ContactUs

* Official SE-EDU projects only

not set

no-site-header

Set this attribute to remove the site navigation bar.

not set

5.6. Site Template

The files in docs/stylesheets are the CSS stylesheets of the site. You can modify them to change some properties of the site’s design.

The files in docs/templates controls the rendering of .adoc files into HTML5. These template files are written in a mixture of Ruby and Slim.

Modifying the template files in docs/templates requires some knowledge and experience with Ruby and Asciidoctor’s API. You should only modify them if you need greater control over the site’s layout than what stylesheets can provide. The SE-EDU team does not provide support for modified template files.

6. Testing

6.1. Running Tests

There are three ways to run tests.

The most reliable way to run tests is the 3rd one. The first two methods might fail some GUI tests due to platform/resolution-specific idiosyncrasies.

Method 1: Using IntelliJ JUnit test runner

  • To run all tests, right-click on the src/test/java folder and choose Run 'All Tests'

  • To run a subset of tests, you can right-click on a test package, test class, or a test and choose Run 'ABC'

Method 2: Using Gradle

  • Open a console and run the command gradlew clean allTests (Mac/Linux: ./gradlew clean allTests)

See UsingGradle.adoc for more info on how to run tests using Gradle.

Method 3: Using Gradle (headless)

Thanks to the TestFX library we use, our GUI tests can be run in the headless mode. In the headless mode, GUI tests do not show up on the screen. That means the developer can do other things on the Computer while the tests are running.

To run tests in headless mode, open a console and run the command gradlew clean headless allTests (Mac/Linux: ./gradlew clean headless allTests)

6.2. Types of tests

We have two types of tests:

  1. GUI Tests - These are tests involving the GUI. They include,

    1. System Tests that test the entire App by simulating user actions on the GUI. These are in the systemtests package.

    2. Unit tests that test the individual components. These are in quickdocs.ui package.

  2. Non-GUI Tests - These are tests not involving the GUI. They include,

    1. Unit tests targeting the lowest level methods/classes.
      e.g. quickdocs.model.PatientTest

    2. Integration tests that are checking the integration of multiple code units (those code units are assumed to be working).
      e.g. StorageManagerTest

    3. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
      e.g. LogicManagerTest

7. Dev Ops

7.1. Build Automation

See UsingGradle.adoc to learn how to use Gradle for build automation.

7.2. Continuous Integration

We use Travis CI and AppVeyor to perform Continuous Integration on our projects. See UsingTravis.adoc and UsingAppVeyor.adoc for more details.

7.3. Coverage Reporting

We use Coveralls to track the code coverage of our projects. See UsingCoveralls.adoc for more details.

7.4. Documentation Previews

When a pull request has changes to asciidoc files, you can use Netlify to see a preview of how the HTML version of those asciidoc files will look like when the pull request is merged. See UsingNetlify.adoc for more details.

7.5. Making a Release

Here are the steps to create a new release.

  1. Update the version number in MainApp.java.

  2. Generate a JAR file using Gradle.

  3. Tag the repo with the version number. e.g. v0.1

  4. Create a new release using GitHub and upload the JAR file you created.

Appendix A: Product Scope

Target user profile:

  • doctors operating small neighbourhood clinics in Singapore

  • have minimal assistants or employees to assist with tasks

  • handle the majority of the clinic’s operations themselves

  • prefer desktop apps over other types

  • can type fast

  • prefers typing over mouse input

  • is reasonably comfortable using CLI apps

Value proposition: allow doctors to accomplish greater management of their clinics with minimal manpower more conveniently.

Appendix B: User Stories

Priorities: High (must have) - * * *, Medium (nice to have) - * *, Low (unlikely to have) - *

Priority As a …​ I want to …​ So that I can…​

* * *

doctor

allocate appointments for patients

Prevent clashes in schedules

* * *

doctor

view patient’s contact details

Call and follow up on them

* * *

doctor

record patient particulars

register new walk-in patients to start a consultation session

* * *

doctor

record and view patients past medical records

diagnose them better for current and subsequent consultations

* * *

doctor

view the available time slots quickly

reserve an appointment slot for my patients requiring long term care.

* * *

doctor

view monthly statistics about patients, finances and inventory

make better decisions on how to run my clinic

* * *

forgetful or busy doctor

get reminders of when I am expecting patients

prepare to diagnose them

* *

busy doctor

get reminders whe my medicine is running low

refill my medicine and prevent shortages during prescriptions

* *

doctor

view my patients’ appointment details

send reminders to them

* *

doctor handling patients with chronic illnesses

view detailed information about medicine in my storage

give out prescription that tackles the patients’ symptoms better

* *

doctor

encrypt the patient and medical data

Protect sensitive information like medical history from getting stolen

* *

doctor

find out the most common symptom diagnosed

prepare enough medicine to deal with seasonal illnesses

*

doctor

export patient diagnosis and details

facilitate external providers’ medical care

*

doctor

lock the application

Prevent unauthorised accesses to the application

*

forgetful doctor

keep track of my medical license duration

renew it on time

*

newly trained doctor

search for details of a medical condition

explain to my patients better

Appendix C: Use Cases

(For all use cases below, the System is Quickdocs and the Actor is the doctor, unless specified otherwise)

Use case: Consultation

MSS

  1. User enter consultation command followed by NRIC

  2. Quickdocs show prompt that indicates to user that consultation for that patient started

  3. User enter diagnosis command with symptoms and assessment

  4. Quickdocs indicate to user that the symptoms and assessment are recorded

  5. User enter prescription command with medicine and quantity

  6. Quickdocs indicate to user the medicine and quantity to be administered for current patient

  7. User enter end consultation command

  8. Quickdocs indicate that consultation for current patient ended

    Use case ends.

Extensions

  • 1a. User enter invalid NRIC

    • 1a1. Quickdocs alert user that no patient with entered NRIC exist to start a consultation session with

      Use case ends here.

  • 3a. User left out symptoms or assessment when diagnosis patient

    • 3a1. Quickdocs alert user that some details are left out and prompt user to modify command

      Use case resumes from step 3.

  • 5a. User left out quantities for certain medicine

    • 5a1. Quickdocs alert user that some medicine do not have quantities and prompt them to reenter command

      Use case resumes from step 5.

  • 5b. User left out quantities for certain medicine

    • 5b1. Quickdocs alert user that additional quantities are provided and prompt user to reenter command

      Use case resumes from step 5.

  • 7a. User end consultation when diagnosis is not completed

    • 7a1. Quickdocs alert user that the session is missing a diagnosis

    • 7a2. User will resume perform step 3 and 4 since they were skipped

      Use case ends here.

  • 7b. User end consultation when prescription is not given

    • 7b1. Quickdocs alert user that the session is missing a prescription

    • 7b2. User will resume perform step 5 and 6 since they were skipped

      Use case ends here.

Use case: View patient medical record

MSS

  1. User enter command to view patient history with search criteria

  2. Quickdocs show list of patient’s consultation records

  3. Quickdocs prompt user to enter index

  4. User enter index of record he or she wants to see

  5. Quickdocs show selected patient record

  6. User enters end to stop looking at record

  7. Use case repeat from case 2 until user enters “end” again after step 6

  8. Quickdocs shows message to inform user he or she is no longer looking at patient records

    Use case ends.

Extensions

  • 1a. User enter invalid index

    • 1a1. Quickdocs alert user that the index is invalid

    • 1a2. Quickdocs exit view patients record

      Use case ends here.

  • 1b. No patient record created yet

    • 1b2. Quickdocs alert user that no patient is created yet, suggest to create a new patient record first

      Use case ends.

  • 1c. User enter a patient’s name that is unique in the storage

    Use case resumes from step 1.

  • 1d. User enter a non unique patient’s name

    • 1d1. Quickdocs show list of patients with the same name, and prompt index

    • 1d2. User refine search criteria, either by entering index or full name of the patient

      Use case resumes from step 1.

  • 4a. User enter invalid medical record index

    • 4a1. Quickdocs alert user that index entered was invalid

      Use case resumes from step 3.

  • 6a. User enter command apart from “end”

    • Quickdocs prompt user that command was invalid and inform them that “end” will exit view

      Use case resumes from step 5.

Use case: Allocating an appointment slot

MSS

  1. User enter command to list all free slots, specifying the date and viewing format

  2. User discusses and agrees on an appointment slot with patient

  3. User search for patient’s NRIC by viewing patient records with search criteria

  4. User enter command to add appointment slot, specifying patient’s NRIC, date, start and end time

  5. Quickdocs displays a successful message, showing the details of the newly created appointment slot

    Use case ends

Extensions

  • 1a. User enters invalid keyword when specifying date or format

    • 1a1. Quickdocs displays an error message

      Use case resumes from step 1.

  • 1b. User does not enter any keywords

    • 1b1. Quickdocs displays all free slots for the current week

      Use case resumes from step 2.

  • 4a. User enters invalid NRIC, date or time

    • 4a1. Quickdocs displays an error message

      Use case resumes from step 4.

  • 5b. Quickdocs displays an error message, showing clashes in timing with another appointment slot

    Use case resumes from step 4.

  • 5b. Patient wants to change appointment slot timing

    • 5b1. User enter command to delete appointment slot, specifying date and start time

    • 5b2. Appointment specified deleted

      Use case resumes from step 1.

Use case: Viewing clinic statistics

MSS

  1. User requests to view statistics, specifying the start and end YearMonths

  2. Quickdocs displays the statistics

    Use case ends.

Extensions

  • 1a. User requests to view statistics with valid arguments.

    Use case resumes at step 2.

  • 1b. User requests to view statistics with invalid arguments.

    • 1b1. Quickdocs shows an error message.

      Use case ends.

Use case: Setting alarm threshold for medicines

MSS

  1. User enters "alarm" and the path to the interested medicine and the desired alarm quantity.

  2. QuickDcos set the threshold for the medicine accordingly.

  3. QuickDocs refreshes the reminder sidebar if needed.

    Use case ends.

Extensions

  • 1a. User types in a path leading to a directory.

  • 2a. All medicines under the subtree of that directory is set to the desired quantity.

    Use case resumes at step 3.

Appendix D: Non Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 9 or higher installed.

  2. Should be able to hold up to 1000 patients and their consultation records without a noticeable sluggishness in performance for typical usage.

  3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.

  4. Response time for commands should be below 3 seconds so that clinical operations can be expedited

  5. Commands should be easy to pick up for novice users, they can remember it more quickly and start using them immediately

  6. Commands should be made convenient for expert users as well

  7. Data stored can be easily transferred to another device installed with Quickdocs for operation continuation.

  8. Data stored, especially patient records and particulars, need to be encrypted to prevent unauthorised access and misuse.

Appendix E: Glossary

Mainstream OS

Windows, Linux, Unix, OS-X

Private contact detail

A contact detail that is not meant to be shared with others

Patient Records

A data entry consisting of a patient’s particulars, which includes name, NRIC, email address, home address, gender, contact number, date of birth and the list tags assigned to him or her.

Consultation Records

A data entry consisting of the diagnosis given and medicine prescribed for a single patient during a consultation session with the doctor.

Appendix F: Instructions for Manual Testing

Given below are instructions to test the app manually.

These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.

F.1. Adding patient records

  1. Add a new patient record

    1. Prerequisites: patient to be added must not have the same NRIC as an existing patient’s NRIC, the listpat command can be used to check if there are conflicting NRIC

    2. Test case 1: addpat n/Mohd Hamiru Bin Hamza r/S9876543C a/1 Tampines Street e/mhbh@gmail.com c/92344321 g/M d/1998-07-06
      Expected: Patient with name "Mohd Hamiru Bin Hamza" with NRIC S9876542C added

    3. Test case 2: add n/Nurul Huda Binte Hamza r/S9876543C a/3 Tampines Street e/nhbh@gmail.com c/93124432 g/F d/1998-11-02
      Expected: Patient will not be added, an error message will be shown to indicate that a patient with the same NRIC existed in the records

    4. Other incorrect addpat commands to try: addpat with missing parameters such as gender and date of birth, addpat with name containing of symbols, addpat with invalid NRIC values (8 numbers or invalid last letters).

F.2. Editing patient records

  1. Edit the fields of an existing patient record

    1. Prerequisites: use the listpat command to check for existing patients to be edited, or simply add one using addpat if there isn’t any. You need at least 2 patients to perform this manual test.

    2. Test case1: editPat EXISTING_NRIC n/Peter Tan g/M d/1990-09-01
      Expected: selected patient will have his or her name changed to "Peter Tan", gender changed to "M" and date of birth changed to 1990-09-01.

    3. Test case2: editPat EXISTING_NRIC n/Perry Tan r/ANOTHER_EXISTING_NRIC
      Expected: An error message will be shown to indicate that the edit to the current patient will cause it to have a conflicting NRIC with another patient’s NRIC

    4. Test case 3: editPat EXISTING_NRIC
      Expected: An error message will be shown to indicate that there is nothing to edit for the current selected patient

    5. Other incorrect editpat commands to try: name with numbers, editpat with a non existent NRIC, editPat without an NRIC (without prefix)

F.3. Starting a consultation

  1. Start a consultation for a registered patient

    1. Prerequisites: Existing patients must already be stored in QuickDocs, use listpat to check for both existing and non existing patients' NRIC

    2. Test case 1: consult r/EXISTING_NRIC
      Expected: A message to indicate the start of the consultation session will be shown at the main display. A label will be displayed at the bottom right, displaying the message along with the patient’s NRIC as well.

    3. Test case 2: After test case 1, enter consult r/ANOTHER_EXISTING_NRIC
      Expected: An error message will be shown to alert you that there is already an ongoing consultation session.

    4. Test case 3: exit QuickDocs, and then enter consult r/NON_EXISTING_NRIC
      Expected: since there are no patients with the NRIC, an error message will be shown to indicate that the consultation session cannot start for a non-existing patient

F.4. Ending a consultation

  1. End a consultation session after providing the prescription and diagnosis. A consultation session can only end after both the diagnosis and prescription have been recorded.

    1. Prerequisites: consultation session must already been started for a patient, medicine to be assigned for prescription is already stored in QuickDocs

    2. Test case 1: after starting a consultation, endconsult immediately
      Expected: Error message will be shown to alert you that a diagnosis have not been provided.

    3. Test case 2: add the diagnosis and then endconsult after test case 1
      Expected: Error message will be shown to alert you that a prescription have not been given to end the consultation session.

    4. Test case 3: add the prescription and then endconsult after test case 2
      Expected: A message indicating that the current consultation session have ended. The ongoing session label at the bottom right of QuickDocs is also removed.

F.5. Adding and removing an appointment

  1. Add an appointment for a registered patient

    1. Prerequisites:

      1. Appointment you are adding must not have conflicting timing with other existing appointments. You can use the freeapp command to find an available time slot for any date.

      2. The patient allocated to the appointment must be registered in QuickDocs. You can use the listpat command to search for existing patients to be allocated the new appointment.

    2. Test case 1: addapp r/EXISTING_NRIC d/2019-10-23 s/16:00 e/17:00 c/Weekly checkup
      Expected: A message to indicate that the appointment was successfully added will be shown on the main display, together with the appointment details. A reminder for this appointment will also be created and you can see it in the reminder sidebar on the right, if the date of the appointment is in the current week.

    3. Test case 2: After test case 1, enter addapp r/EXISTING_NRIC d/2019-10-23 s/16:30 e/17:30 c/Weekly checkup
      Expected: since this new appointment a clash in timing with the appointment added in test case 1, an error message will be shown to indicate this conflict and the appointment will not be added.

    4. Test case 3: addapp r/EXISTING_NRIC d/23-10-2019 s/16:00 e/17:00 c/Weekly checkup
      Expected: Error message will be shown to alert you that the date is in the wrong format, as the correct format is YYYY-MM-DD.

    5. Other incorrect addapp commands to try: date without - between month and day, invalid start and end time (start time must be before end time; appointment must be within office hours of 9am to 6pm), any missing prefixes, any missing parameters.

  2. Delete an existing appointment

    1. Prerequisites: appointment to be deleted must already be added in QuickDocs. You can use listapp command to list the existing appointments for a given date. We will assume that the appointment in Test case 1 for adding an appointment has been added.

    2. Test case 1: deleteapp d/2019-10-23 s/16:00
      Expected: A message to indicate that the appointment was successfully deleted will be shown on the main display. The reminder created for this appointment will also be deleted. If this reminder was displayed on the reminder sidebar, it will be removed.

    3. Test case 2: deleteapp d/2030-10-23 s/16:00
      Expected: Assuming that there is no appointment on the given date and time, you will be informed through the error message shown.

    4. Other incorrect deleteapp commands to try: date without - between month and day, invalid start time (start time must be within office hours of 9am to 6pm), any missing prefixes, any missing parameters.

F.6. Querying Statistics

  1. Querying the statistics

    1. Prerequisites: Your system clock should be synchronised with the current internet time.

    2. Test case 1: stats 012019
      Expected: The statistics for the month January 2019 should be listed, regardless if there has been any records added.

    3. Test case 2: stats 122018
      Expected: An error message will be shown, indicating that the command format is wrong.

    4. Test case 3: stats 012019 022019
      Expected: The statistics for the range of month January 2019 to February 2019 should be listed, regardless if there has been any records added.

    5. Test case 4: stats 012999
      Expected: An error message will be shown, indicating that the command format is wrong.

    6. Other incorrect stats commands to try: end date before start date, invalid MMYYYY numbers (e.g., 000000, 132019). All these commands should lead to an error message being shown.

F.7. Adding a new directory for medicine storage

The following tests assume the tester starts with an empty storage.
  1. Add a new directory under the root directory

    1. Test case 1: adddirec root NEW_DIRECTORY
      Expected: A success message confirming the successful addition will be shown in the main display. A new directory called 'NEW_DIRECTORY' will be added to root directory, which is verifiable via listmed root command.

    2. Test case 2:`adddirec root NEW DIRECTORY`
      Expected: As the new directory’s name includes a white space which is not allowed, no new directory will be added and an error message will show up.

    3. Test case 3:`adddirec root\Internal NEW_DIRECTORY`
      Expected: As the tester starts with an empty storage, there is no existing directory at root\Internal. No new directory will be added, and an error message will show up saying "No directory is found at the given path."

    4. Other incorrect adddirec commands to try: commands with missing new directory name

F.8. Adding a medicine into a directory

The following tests assume that root\Internal\General is a valid path, i,e there is already a directory 'General' under directory 'Internal' under root directory.
  1. Add a new medicine into storage

    1. Prerequisites: There is no medicine with the same name as the to-add medicine in the entire storage.

    2. Test case 1: addmed root\Internal\General paracetamol p/2.4 q/40
      Expected: A new medicine called 'paracetamol' with initial quantity 40 and price 2.4 will be added under 'Flu' directory. A success message confirming this addition will show in the main display.

    3. Test case 2: addmed root\Internal\General paracetamol p/2.4 q/40 when there is an existing medicne named 'paracetamol' in the storage.
      Expected: The addition will fails, as there already exists a medicine with same name. No change will occur and an error message will show up.

    4. Test case 3: addmed root\Internal\General paracetamol p/2.4 q/34.5
      Expected: The addition will fail. As the quantity in QuickDocs are in terms of units, no fractional number is allowed in the quantity field. An error message will show up.

    5. Other incorrect addmed commands to try: missing any prefixes, invalid path

  2. Add an existing medicine into a different directory

    1. Prerequisites: There is already an existing medicine called 'paracetamol' somewhere in the storage.

    2. Test case 1: addmed root\Internal\General paracetamol
      Expected: A new reference to the same medicine will be added to root\Internal\General. A success message will show in the main display.

    3. Test case 2: addmed root\Internal\General paracetamol when there is no existing medicine with name 'paracetamol'
      Expected: No change will occur. As there is no such existing medicine, an error message will show up.

    4. Test case 3: addmed root\Internal\General paracetamol when root\Internal\General already contains a reference to paracetamol.
      Expected: As the same medicine cannot be placed into the same directory twice, no change will happen. An error message will show up.

    5. Other incorrect addmed command to try: When there is a subdirectory under the same directory with the same name as the to-add medicine.