1. Preface
  2. Setting Up
  3. Design
    3.1 Architecture: High Level View
    3.2 UI Component
    3.3 Logic Component
    3.4 Model Component
    3.5 Storage Component
    3.6 Commons Component
  4. Implementation
    4.1 Patient Feature
          4.1.1 Overview
          4.1.2 Implementation
          4.1.3 Design Considerations
          4.1.4 Create, Read, Update, Delete
          4.1.5 Search
    4.2 Room Feature
          4.2.1 Overview
          4.2.2 Implementation
          4.2.3 Design Considerations
          4.2.4 Create, Read, Update
    4.3 Task Feature
          4.3.1 Overview
          4.3.2 Implementation
          4.3.3 Design Considerations
          4.3.4 Create, Read, Update, Delete
          4.3.5 Search
    4.4 Logging Feature
    4.5 Configuration Feature
  5. Planned Features
  6. Documentation
  7. Appendix
    A1. Product Scope
    A2. User Stories
    A3. Use Cases
    A4. Non-Functional Requirements
    A5. Glossary
    A6. Instructions for Manual Testing

1. Preface

Covigent is a desktop management application that helps to keep track of the information of quarantined individuals and the tasks to be done by the staff of a small hotel that has been converted to a quarantine facility.

The Developer Guide for Covigent v1.4 is designed to illustrate and identify the high level architecture systems used to design and implement the Covigent application. The document contains an overall view of the system hierarchy, logical views of the system components, and a process view of the system’s communication. We hope that this Developer Guide serves you well in understanding and maintaining the Covigent application.

The link to the GitHub repository can be found here.

Written by: Yun Qing


2. Setting Up

Refer to the guide Setting up and getting started.


3. Design

This section describes some noteworthy details on how Covigent is designed.

3.1 Architecture: High Level View


Figure 1. Architecture diagram of Covigent

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

:bulb: Tip: The .puml files used to create diagrams in this document can be found in the diagrams folder. Refer to the PlantUML Tutorial at se-edu/guides to learn how to create and edit diagrams.

Main has two classes called Main and 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 methods where necessary.

Commons represents a collection of classes used by multiple other components.

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 concrete {Component Name}Manager class (which implements the corresponding API interface mentioned in the previous point.

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


Figure 2. Class diagram of 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 deletepatient Alex Yeoh.


Figure 3. Sequence diagram of deletepatient alex command

The sections below give more details of each component.

3.2 UI Component

The UI component displays information for the users based on user’s input. The GUI to displayed is based on the return from logic. It uses JavaFx UI framework. The layout of these UI parts are defined in matching .fxml files that are in the src/main/resources/view folder. For example, the layout of the MainWindow is specified in MainWindow.fxml

The UI component,

  • Executes user commands using the Logic component.
  • Consists of a MainWindow that is made up of many different parts that inherit from the abstract UiPart class.

The MainWindow is made up of

  • A PatientListPanel that displays the list of patients. The layout is defined by PatientCard and PatientDetailsPanel.
  • A RoomListPanel that displays the list of rooms. The layout is defined by RoomCard and RoomDetailPanel.
  • A RoomTaskListPanel that displays the list of tasks. The layout is defined by TaskCard.
  • A HelpWindow that displays the link to the help page.
  • A CommandBox that displays the area for command input.
  • A ResultDisplay that displays the robot response.
  • A StatusBarFooter that displays the status bar footer.
    Below is a class diagram for Ui


Figure 4. Class diagram of Ui component

API : Ui.java

The UI component listens for changes to Model data so that the UI can be updated with the modified data. Below shows the interaction with Model


Figure 5. Structure of Ui component

Written by: Wai Lok

3.3 Logic Component

The Logic component is the “brains” of Covigent. While the Ui defines the GUI and Model defines in-memory data, the Logic component does most of the heavy-lifting in terms of deciding what to change within the Model and what to return to the Ui.
The diagram below shows the structure of the Logic component and how it interacts with its internal parts.


Figure 6. Structure of the Logic Component

API : Logic.java

  1. Once a user input is obtained from the GUI, Logic uses the CovigentAppParser class to parse the users’ commands and return a Command object.
  2. The Command is executed by LogicManager.
  3. Depending on the command input by the user, it may mutate the Model, such as adding a new patient, room or task.
  4. The result of the command execution is encapsulated as a CommandResult that is returned to the Ui.
  5. These CommandResults can instruct the Ui to perform certain actions, such as displaying help or error messages to the user.

Shown below is the Sequence Diagram within the Logic component for the API call: execute("deletepatient alex").


Figure 7. Interactions inside the Logic Component for the deletepatient alex Command

:information_source: Note: The lifeline for DeletePatientCommandParser should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.


Written by: Ming De

3.4 Model Component

The Model API acts as a facade that handles interaction between different kinds of data in Covigent. These data include user’s preferences, patient records and room list. The Model API exposes the methods that allow the logic component to utilise to perform retrieving and updating of data.

The Model component,

  • stores a UserPref object that represents the user’s preferences.
  • stores a PatientRecords object that stores the data of all the patients.
  • stores a RoomList object that stores the data of all the rooms.
  • stores a RoomTaskRecords object that stores all the RoomTaskAssociation objects, which keep track of the room that a task belongs to.
  • exposes unmodifiable ObservableList<Patient>, ObservableList<Room> and ObservableList<RoomTaskAssociation> which can be observed. This means that the UI can be bound to the lists so that the UI automatically updates when data in the lists changes.
  • does not depend on any of the three components.

The concrete class ModelManager implements Model interface and manages the data for Covigent. ModelManager contains UserPrefs, PatientRecords, RoomList and RoomTaskRecords. These classes manage the data related to their specific features.

Below is a class diagram for ModelManager.


Figure 7. Class Diagram for Model Component

API : Model.java

The breakdown for each type of data in ModelManager, which include PatientRecords, RoomList and RoomTaskRecords, can be found below.

The PatientRecords class is in charge of maintaining the data of the patients and in ensuring the uniqueness of patients according to their names. Below is a class diagram for PatientRecords.


Figure 8. Class Diagram for PatientRecords

:information_source: Note: The PatientRecords class implements the interface ReadOnlyList<Patient> but due to a limitation of PlantUML, the interface is reflected as simply ReadOnlyList.


The RoomList class is in charge of maintaining the data in the rooms and in ensuring the uniqueness of rooms according to the room numbers. As each room stores the data of the patient who resides in the room and the tasks meant for the room, it incorporates data from both Patient and RoomTasks. RoomTasks class is in charge of maintaining the data of the tasks in a room. The full details of Patient can be found in the previous class diagram for PatientRecords so it is no longer reflected in the class diagram for RoomList. The class diagram for RoomList is shown below.


Figure 9. Class Diagram for RoomList

:information_source: Note: The RoomList class implements the interface ReadOnlyList <Room> and the RoomTasks class implements the interface ReadOnlyList<Task>. However, due to a limitation of PlantUML, the interface is reflected as simply ReadOnlyList.


The RoomTaskRecords class is in charge of maintaining the data regarding the association of a task in a room. The RoomTaskAssociation class acts as an association class that ties Task and Room together so that the Task object does not need to know of the details of Room and we are still able to identify the room number that Task belongs to and its index in Room. The class diagram for RoomTaskRecords is shown below.


Figure 10. Class Diagram for RoomTaskRecords

:information_source: Note: The RoomTaskRecords class implements the interface ReadOnlyList <RoomTaskAssociation>. However, due to a limitation of PlantUML, the interface is reflected as simply ReadOnlyList.


Written by: Yun Qing

3.5 Storage Component


Figure 11. Structure of the Storage Component

API : Storage.java

The Storage API handles the reading and writing of data in Json format, enabling Covigent to remember information stored by user even when the application is closed. The Storage API behaves like a façade by handling the storage classes and interfaces.

The Storage interface class diagram is shown below, and as it can be seen it inherits from specific storage interfaces.

The Storage component,

  • Saves UserPref objects in Json format
  • Reads UserPred objects in Json format
  • Saves RoomRecords and PatientRecords data in json format
  • Reads RoomRecords and PatientRecords data in json format

The information of the Patient and Room feature of Covigent is stored locally in the Json format. This is done by adapting the Patient and Room feature into JsonSerializablePatientRecords and JsonSerializableRoomRecords respectively.

The class diagram for StorageManager is shown below


Figure 12. Structure of the StorageManager Component

The information of the Patient and Room feature of Covigent is stored locally in the Json format. This is done by adapting the Patient and Room feature into JsonSerializablePatientRecords and JsonSerializableRoomRecords respectively.

The class diagrams for the above stated adapted classes are shown below


Figure 13. Structure of the JsonSerializableRoomRecords


Figure 14. Structure of the JsonSerializablePatientRecords

Written by: Noorul Azlina

3.6 Commons Component

Classes used by multiple components are in the seedu.addressbook.commons package.


4. Implementation

This section describes some noteworthy details on how Patient, Room and Task features are implemented.

4.1 Patient Feature

4.1.1 Overview

The patient feature in Covigent allows hotel staff to store important information about the individuals that are quarantined in the hotels. Some of these important dynamic information include temperature and comment regarding the patient. To fully understand the patient feature, it is important to learn about the implementation and design considerations of the patient object before looking at the possible commands that can operate on the patient object. A patient is meant to be allocated to a room in the facility, hence tying the patient feature with the room feature together.

4.1.2 Implementation

A Patient object in Covigent contains the following attributes, which is also reflected in Figure 15:

  1. Name
  2. Temperature
  3. PeriodOfStay
  4. Age
  5. Phone
  6. Comment

Each of the attribute is a stand-alone class on its own.


Figure 15. Class Diagram for Patient

4.1.3 Design Considerations

Aspect: Encapsulation of fields for Patient object

  • Option 1: Using primitive data types for Temperature, Age and Comment classes

Classes like Temperature, Age and Comment can be easily treated as primitive data types including double and string. However, this option goes against the spirit of OOP. Furthermore, it does not align to the original design of AddressBook3, which Covigent was morphed from, in which Name and Phone attributes were abstracted out as separate classes.

  • Option 2: Encapsulate constituent patient attributes in their own classes

This option increases OOP and is aligned to the original design of AddressBook3. In addition, this design has proven itself to be extensible in the long run and allows for adjustments to Patient attributes easily. For example, when setting the valid temperature range as input for Temperature, only the Temperature class needs to change, which demonstrates the essence of the single responsibility principle. This allows for better understanding and maintenance of our code base in the future.

Aspect: Decision on the uniqueness of Patient object

  • Option 1: Uniqueness is identified by name, age and phone

Originally, we intended to define two Patient to be equals if they have the same Name, Age and Phone. However, such a design will make it difficult for users of Covigent as they will have to key in all these fields when executing EditPatientCommand and DeletePatientCommand in order for Covigent to uniquely identify the Patient to manipulate.

  • Option 2: Uniqueness is identified by name

Keeping in mind the ease of usage of Covigent for users, we chose to identify Patient uniquely by Name only. As such, when users need to manipulate the data of a Patient, all they need to input is the Name. Furthermore, we believes that since Covigent is used by small hotels, there is a very low chance of two patients having the same names.

Having looked at the design of Patient, we can now explore the possible features related to Patient. In particular, our commands support create, read, update, delete and search.

The features comprise of five commands namely,

  • AddPatientCommand - Adding patients
  • ListPatientCommand - Listing all the patients
  • EditPatientCommand - Editing patients
  • DeletePatientCommand - Deleting patients
  • SearchPatientCommand- Searching for patients

Written by Yun Qing

4.1.4 Create, Read, Update, Delete

In this section, we will cover the implementation of the manipulation of the Patient data. The commands that allow creating, reading, updating and deleting of Patient include AddPatientCommand, ListPatientCommand, EditPatientCommand and DeletePatientCommand.

As the Patient data are stored in UniquePatientList, which ensures the uniqueness of Patient, the actual manipulation of the Patient data is made in UniquePatientList class.

Some of the significant methods within UniquePatientList class that allows the manipulation of the Patient data are shown below:

  • UniquePatientList#add(Patient toAdd) - Adds a Patient to UniquePatientList.
  • UniquePatientList#setPatient(Patient target, Patient editedPatient) - Edits the attributes of Patient.
  • UniquePatientList#remove(Patient toRemove) - Deletes a Patient from UniquePatientList.
  • UniquePatientList#asUnmodifiableObservableList() - Returns an observable list of Patient
  • UniquePatientList#getPatientWithName(Name name) - Returns the Patient with the given Name if the Patient object exists in UniquePatientList. Since Name is an identifier of a Patient, this method is used in EditPatientCommand and DeletePatientCommand to modify the Patient or check if the Patient to be deleted exists.

These methods in UniquePatientList class support the corresponding methods in the facade classes PatientRecords and ModelManager. In particular, the Model interface exposes the methods Model#addPatient(Patient patient), Model#setPatient(Patient target, Patient editedPatient), Model#deletePatient(Patient target), Model#getPatientWithName(Name name) and Model#getFilteredPatientList().

For brevity’s sake, we will only illustrate the implementation of 2 specific commands - AddPatientCommand and EditPatientCommand.

Implementation of AddPatientCommand

The following is a detailed explanation of the operations that AddPatientCommand performs.

Step 1. The user executes addpatient [input all attributes of Patient] command to add a patient to Covigent. An AddPatientCommandParser object is created and the AddPatientCommandParser#parse(String args) method is called, which helps to parse the different attributes of Patient. The parse method returns a new AddPatientCommand object and the AddPatientCommand object stores the Patient to be added.

Step 2. The Patient to be added is then searched through UniquePatientList#internalListusing the Model#hasPatient(Patient patient) method to check if the patient already exists. If the patient already exists, a CommandException object will be thrown with an error message.

Step 3. Through Model#addPatient(Patient toAdd), the Patient will be added to UniquePatientList.

Step 4. A success message with the new patient details will be appended with the AddPatientCommand#MESSAGE_SUCCESS constant. A new CommandResult will be returned with the message.

The sequence diagram for a successful execution of AddPatientCommand can be found below.


Figure 16. Sequence Diagram for AddPatientCommand

:information_source: Note: Due to space limitation in PlantUML diagram, the addpatient command example given in the sequence diagram only consists of two patient attributes, name and age.


Written by Yun Qing

Implementation of EditPatientCommand The following is a detailed explanation of the operations that EditPatientCommand performs.

Step 1. The EditPatientCommand#execute(Model model) method is executed and it checks if the Name defined when instantiating EditPatientCommand(Name patientToBeEdited, EditPatientDescriptor editPatientDescriptor) is valid. The EditPatientDescriptor holds the edited information of the Patient.

Step 2. A new Patient with the updated values will be created and the patient is then searched through UniquePatientList#internalList using the Model#hasPatient(Patient patient) method to check if the patient already exists. If the patient already exists, CommandException will be thrown with an error message.

Step 3. The newly created Patient will replace the existing patient object through the Model#setPatient(Patient target, Patient editedPatient) method.

Step 4. A success message with the edited patient will be appended with the EditPatientCommand#MESSAGE_EDIT_PATIENT_SUCCESS constant. A new CommandResult will be returned with the message.

Written by Ming De

The following is a detailed explanation of the operations that SearchPatientCommand performs.

Step 1. The SearchPatientCommand#execute(Model model) method is executed and checks the criteriaToSearch via confirmCriteria(SearchPatientDescriptor searchPatientDescriptor). The SearchPatientDescriptor holds the name or temperatureRange of the Command.

Step 2. If the criteriaToSearch is SearchCriteria.CRITERIA_NOT_FOUND, CommandException will be thrown with an error message.

Step 3. If the criteriaToSearch is SearchCriteria.CRITERIA_IS_NAME, we update the Model’s FilteredPatientList Predicate via updateNamePredicate(Model model, SearchPatientDescriptor searchPatientDescriptor) Then findPatientWithName(SearchPatientDescriptor searchPatientDescriptor, List<Patient> patientList) will go through Model’s FilteredPatientList, if no patient is found, CommandException will be thrown with an error message. If there is at least one patient found, a new CommandResult will be returned with the message.

Step 4. If the criteriaToSearch is CRITERIA_IS_TEMPERATURE, we update the Model’s FilteredPatientList Predicate via updateTemperaturePredicate(Model model, SearchPatientDescriptor searchPatientDescriptor) Then findPatientWithTemperature(SearchPatientDescriptor searchPatientDescriptor, List<Patient> patientList) will go through Model’s FilteredPatientList, if no patient is found, CommandException will be thrown with an error message. If there is at least one patient found, a new CommandResult will be returned with the message.

Step 5. If the criteriaToSearch is TOO_MANY_CRITERIA A new CommandResult will be returned with the message. method.

Written by Wai Lok

4.2 Room Feature

4.2.1 Overview

The application is able to track the room details. It keep tracks of the whether a room is occupied and the patient inside the room if it is occupied. It also keeps track of the tasks assigned to a specific room. Hence, there is a need to represent the Room List as a list of Rooms on which the application can perform read and update operations.

4.2.2 Implementation

The class diagram for RoomList is shown below.


Figure 17. Class diagram for RoomList

From the diagram above, the RoomList contains of one UniqueRoomList. This UniqueRoomList is a wrapper class around the RoomList which contains an ObservableList of Patient and PriorityQueue of Patient. The RoomList can contain from about 1 to 500 rooms.

Each Room contains the following member attributes, all of which are non-nullable attributes:

  1. roomNumber This gives the room number of the Room object
  2. isOccupied This gives the information of whether the Room is occupied or not. If there is a Patient inside the Room, then the isOccupied is true, else false
  3. patient This gives the patient details and it is stored as an Optional object. If there is no Patient, then the Optional.empty() is assigned.
  4. tasks This gives all the tasks that are assigned to a specific room. The number of tasks assigned can be zero.

The proposed room feature is facilitated by RoomList. It extends ReadOnlyRoomList which reads the Room information Json file, stored internally as an addressBookStateList and currentStatePointer. Additionally, it implements the following operations:

  • RoomList#addRooms(int num) — adds the number of which are said to add together and retains infromation previously stored in each room
  • RoomList#containsRoom(Room toCehck) - checks whether the given room exists
  • RoomList#clearRoom(Name patientName) - removes patient from the room
  • RoomList#setSingleRoom(Room target, Room editedRoom) - sets the editedRoom to the target room

These operations are exposed in the Model interface as Model#addRooms(int num), Model#hasroom(Room room), Model#clearRoom(Name patientName) and Model#setSingleRoom(Room target, Room editedRoom) respectivley.

Written by Noorul Azlina

The GUI for room feature is based on a ListView that updates whenever the RoomList is updated and a scroll pane that displays the details of the room. To ensure that the information displayed in the scroll pane is updated dynamically, we employ the use of Listeners that listens for changes and notify the scroll pane to update.

Written by: Ming De

4.2.3 Design Considerations

Aspect: Decision on allowing editRoomCommand that allows changing of room number to remain

  • Option 1: Do not change the editRoomCommand Allowing the users to change room numbers will give the user more power in customising the rooms. However, this option introduced a bug into the system that could not be easily resolved unless we changed our entire implementation of InitRoomCommand.

  • Option 2: Remove editRoomCommand ability to change room number and rename it to AllocateRoomCommand Removing the editRoomCommand to change room number is much more time-efficient compared to changing the entire implementation of InitRoomCommand. We also decided that there should be no reason that a user would need to change the room number. Renaming the method to AllocateRoomCommand would provide more clarity for the method.

Ultimately, we decided on Option 2. This is because keeping editRoomCommand would lead to a large consumption of time to redesign the system. In order to not stray from our schedule, we have to remove editRoomCommand to ensure that we can develop the other features on time. Furthermore, to solve the bug that was introduced, we would have to store the count of the number of times InitRoomCommand was called. This would cause us to store information in another .json file which is unnecessary. Therefore, we decided that the forgoing a small function like this would be a better choice.

Written by Ming De

The features comprise of five commands namely,

  • InitRoomCommand - Initializes the number of rooms in Covigent app.
  • ListRoomCommand - Lists all the rooms in Covigent app.
  • AllocateRoomCommand - Allocates a patient to a room.
  • SearchRoomCommand - Searches for the room with the specified room number.
  • FindEmptyRoomCommand - Finds an empty room with the lowest room number.

We will illustrate the progress of two of the above commands for simplicity.

4.2.4 Create, Read, Update

Implementation of InitRoomCommand

The following is a detailed explanation of the operations that InitRoomCommand performs.

Step 1. The InitRoomCommand#execute(Model model) method is executed and it check if the Integerdefined when instantiating If it is a positive integer and the number of rooms is more than or equal to the existing number of occupied rooms, InitRoomCommand is valid. The checking of the number of rooms is done by Model#hasSpaceForRooms(). If this is true then Integer is valid, else it is invalid.

Step 2. The stated number of rooms is then set to Integer, if Integer is greater than the existing number of rooms then excess rooms are added to the back of the UniqueRoomList. If the number of rooms is less than the Integer and there are occupied rooms the information of patients in that room is transferred to an empty room in the reduced number of rooms

Step 4. A success message with the Intger appended with the InitRoomCommand#MESSAGE_SUCCESS constant is displayed on the UI. A new CommandResult returns this message.

The activity diagram below illustrates the initRoom.


Figure 18. Activity diagram for initRoom

The Sequence Diagram for initRooms is shown below.


Figure 19. Sequence diagram for initRoom

Written By: Noorul Azlina

Implementation of AllocateRoomCommand

The following is a detailed explanation of the operations that AllocateRoomCommand performs.

Step 1. The AllocateRoomCommand#execute(Model model) method is executed and it checks if the Integer defined when instantiating AllocateRoomCommand(Integer roomNumberToAllocate, AllocateRoomDescriptor AllocateRoomDescriptor) is valid. This is done using the Model#getRoomWithRoomNumber method where it is used to get an Optional<Room>. If Optional<Room> is empty, the Integer is not valid. The AllocateRoomDescriptor holds the information of the Room with the patient allocated.

Step 2. A new Room with the allocated patient will be created and the room is then searched through RoomList#internalList using the Model#hasRoom(Room room) method to check if a room with the same room number exists. If it already exists, CommandException will be thrown with an error message.

Step 3. The newly created Room will replace the existing room object through the Model#setSingleRoom(Room target, Room editedRoom) method.

Step 4. A success message with the allocated room will be appended with the AllocateRoomCommand#MESSAGE_ALLOCATE_ROOM_SUCCESS constant. A new CommandResult will be returned with the message.

The activity diagram below illustrates allocateRoomCommand.


Figure 20. Activity Diagram for AllocateRoomCommand

The sequence diagram for AllocateRoomCommand is shown below.


Figure 21. Sequence Diagram for AllocateRoomCommand

Written by Mingde

4.3 Task Feature

4.3.1 Overview

The task feature in Covigent allows hotel staff to manage and organize time-critical work related to a room in a quarantine facility. Every room can be allocated any number of tasks, with each task keeping track of the description of the work and a due date by which it should be completed.

It is important to note that Covigent handles tasks on a per-room basis. Tasks not specific to any room (e.g. tasks related to the operations of the quarantine facility) are out of scope.

The task feature in Covigent includes the following:

  • Adding a task to a room
  • Displaying all tasks in the user interface
  • Editing the description and due date of a task
  • Removing a task from a room
  • Filtering tasks based on a criterion and displays the filtered tasks in the user interface (currently on supports filtering by due date)

4.3.2 Implementation

At a higher level, tasks share a composition type relationship with rooms. That is, if a room is deleted, all tasks in that room are similarly deleted. We have implemented the task feature based on the class diagram in Figure 22.


Figure 22. Class Diagram for Task

Each Room contains a RoomTasks class, which is a wrapper around TaskList.

Task-related operations that alter the tasks in a Room must be performed through that Room. The API calls for these operations first proceed to Room, which redirects them to RoomTasks. To enforce this constraint, RoomTasks is not publicly exposed in the API for Room (i.e. there is no getter for RoomTasks in Room).

TaskList emulates the other List classes in Covigent such as UniquePatientList and UniqueRoomList, exposing only an unmodifiable ObservableList<Task>. This ObservableList<Task> is subsequently returned by RoomTasks in the getReadOnlyList() method to fulfill its contract with the ReadOnlyList<Task> interface.

4.3.3 Design Considerations

Aspect: Retrieving list of tasks from Room

  • Option 1: Supplying a getter for RoomTasks in Room

Having a getRoomTasks() method to retrieve all the tasks in Room greatly enhances convenience, especially when copying the information from one Room to another. This is because we can use the constructor of Room in this manner: Room copyOfRoom = new Room(..., originalRoom.getRoomTasks())) (... refers to other attributes of Room that need to be passed into constructor). It is a quick way to transfer tasks between Room.

However, this option is not optimal from a defensive programming perspective. Because a client can retrieve RoomTasks from Room, there is potential for abuse. The RoomTasks object may be passed to some other classes that are not Room. Consequently, there is no guarantee that a client will not circumvent Room and call an operation that changes its tasks such as addTask(Task) directly from RoomTasks. This destroys the abstraction barrier of Room as clients can modify the tasks in it via external means.

  • Option 2: Add a getReadOnlyTasks() method in Room to retrieve an unmodifiable list of tasks from RoomTasks

We chose option 2 for Covigent as we thought it was the safer option. Since RoomTasks implements ReadOnlyList<Task>, Room can retrieve and expose it via a getReadOnlyTasks() method for clients to access the tasks. This is more secure from a defensive programming perspective and respects the abstraction barrier. Without the getter for RoomTasks, clients are only allowed to change the tasks in Room via its API. It ensures that the tasks in a Room are not modified without knowledge of Room, while providing the flexibility for clients to access the information in them. An additional benefit of this option is that it obscures the fact that Room depends on RoomTasks for its task-related operations, thus strengthening the composition type relationship between tasks and rooms.

With option 2, the operation for copying tasks from one Room to another can be performed as such: Room copyOfRoom = new Room(..., new RoomTasks(originalRoom.getReadOnlyTasks())). It is probably better for the constructor of Room to take in List<Task> instead of RoomTasks to completely hide the existence of RoomTasks from clients. This can be considered as part of the improvements to be made to Covigent in the future.

A downside of this option is that the implementor (future programmers of Covigent) must be aware of this design decision and avoid exposing RoomTasks through the public API of Room. Moreover, there is a slight increase in complexity now that RoomTasks has to implement ReadOnlyList<Task>.

Aspect: Storing a list of tasks in Room

  • Option 1: Using List<Task> in Room

This option is simple as it does not require any additional classes. However, Room has to handle all task-related operations, which violates the single-responsibility principle. Conceptually, we consider Room to be a container for a patient and tasks. It should not contain implementations of methods related to patients and tasks.

  • Option 2: Using TaskList in Room

While entirely possible to use TaskList to store the tasks in Room (see Figure 23), we cannot be certain that TaskList will not be used elsewhere in Covigent in the future.


Figure 23. Class Diagram for Task using TaskList

For instance, suppose we want to support a list of tasks for specific patients (instead of rooms). It may not make much sense to use TaskList for both patients and rooms as they can have different behaviors. Perhaps a maximum of 5 tasks can be assigned to each patient, while there is no limit to the number of tasks that can be assigned to each room. Taking that into consideration, we decided to add a wrapper class.

  • Option 3: Using an extra class in addition to TaskList

We chose option 3 and added a new class RoomTasks to Room. RoomTasks is a wrapper around TaskList to support room-specific behavior associated with tasks. A simple example is how we can set a maximum number of tasks each Room can hold through RoomTasks without modifying TaskList. In that sense, option 3 respects the open-closed principle.

An alternative is to use a new class that extends from TaskList. This would improve polymorphism as methods that work with TaskList will also work with the new class. However, Liskov substitution principle might be a concern. For instance, if TaskList allows an unlimited number of tasks to be stored but RoomTasks only allows 500, it would be a violation of Liskov substitution principle. As such, we did not choose this alternative.

The disadvantage of using another class is the added complexity as API calls for task-related operations need to be routed from the new class to TaskList.

4.3.4 Create, Read, Update, Delete

In this section, we will cover the implementation of the manipulation of Task data. The commands that allow the task-related operations of creating, reading, updating, and deleting of Task are AddTaskCommand, ListTaskCommand, EditTaskCommand, and DeleteTaskCommand respectively.

The actual manipulation of Task data is performed in the TaskList class. Some significant methods within TaskList that allows the manipulation of Task data are shown below:

  • TaskList#add(Task toAdd) - Adds a Task to TaskList.
  • TaskList#setTask(Task target, Task editedTask) - Replaces a Task in TaskList with the edited Task.
  • TaskList#remove(Task toRemove) - Removes a Task from TaskList.
  • TaskList#asUnmodifiableObservableList() - Returns a read-only list containing all Task in TaskList.
  • RoomTasks#getTaskWithTaskIndex(Index index) - Returns the Task with the Index in the TaskList. This method is currently in RoomTasks but can be safely refactored into TaskList.

These methods in TaskList class support the corresponding methods in RoomTasks. For clients to perform these operations, Room exposes addTask(Task task), setTask(Task task, Task editedTask), and deleteTask(Task task) in its public API. Calls to these APIs are redirected to RoomTasks and eventually TaskList.

Since the commands do not directly interact with Room but rather the Model, the Model interface has to expose addTaskToRoom(Task task, Room room), deleteTaskFromRoom(Task task, Room room) and setTaskToRoom(Task task, Task editedTask, Room room).

For brevity’s sake, we will only illustrate the implementation of the most complex command, EditTaskCommand.

Implementation of EditTaskCommand

The following is an explanation of the operations that EditTaskCommand performs, with the higher level details (LogicManager and CovigentAppParser) omitted for simplicity.

Step 1. The user executes edittask [room number] [task index] [other attributes to edit] command to edit the description of a Task in a Room. An EditTaskCommandParser object is created and the EditTaskCommandParser#parse(String args) method is called, which helps to parse the different attributes of Task. The parse method returns a new EditTaskCommand object with the room number, task index, and a descriptor containing the updated values to replace the original.

Step 2. The Room is retrieved from the list of rooms in Covigent using the Model#getRoomWithRoomNumber(int roomNumber) method. If no such Room exists, a CommandException object will be thrown with an error message.

Step 3. The Task is retrieved from RoomTasks of Room using the Model#getTaskFromRoomWithTaskIndex(Index taskIndex, Room room) method. If no such Task exists, a CommandException object will be thrown with an error message.

Step 4. The Task is edited in EditTaskCommand and replaces the original Task the room with Model#setTaskToRoom(Task target, Task editedTask, Room room).

Step 5. A success message with the new task details will be appended with the EditTaskCommand#MESSAGE_SUCCESS constant. A new CommandResult will be returned with the message.

The sequence diagram for EditTaskCommand can be found below.


Figure 24. Sequence Diagram for EditTaskCommand

Written by Yee Hong

The following is a detailed explanation of the operations that SearchTaskCommand performs.

Step 1. The SearchTaskCommand#execute(Model model) method is executed and it gets information of task from each room in the roomList.

Step 2. If the task has a duedate before the duedate from user command, it is stored in taskListWithDesirableResult.

Step 3. If no task is found, CommandException will be thrown with an error message.

Step 4. If there is at least one task found, the model’s filteredRoomTaskRecords is updated with a dueDatePredicate using updateTasksInFilteredRoomTaskRecords.

Step 5. a new CommandResult will be returned with the message.

Written by Wai Lok

4.4 Logging Feature

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.5, “Configuration Features”)
  • The Logger for a class can be obtained using LogsCenter.getLogger(Class) which will log messages according to the specified logging level
  • 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

Written by Ming De

4.5 Configuration Feature

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


5. Planned Features

This section describes the features planned for the next iteration of Covigent.

5.1 Single Command to Create and Allocate Patient to Room

When the user wants to assign a patient to a room, she must run 2 commands: addpatient and allocateroom. The former creates a new Patient object in Covigent and the latter assigns that Patient to an empty room. To improve the user’s productivity, we can allow the user to specify a room number when executing addpatient. If the room and patient are valid, the patient is added to Covigent and assigned to the room in a single command.

Since these changes will add another layer of complexity, we plan to complete this feature in Covigent v1.5.

5.2 Search for Task by Description

The searchtask command allows searching for tasks only by due dates. A user with many tasks would most likely want to search for tasks by description.

In addition to modifying SearchTaskCommand and SearchTaskCommandParser, a new class DescriptionPredicate might have to be introduced to filter the tasks by description. Considering these additional changes, we plan to complete this feature in Covigent v1.5.

5.3 Scroll to Modified Task in User Interface

Whenever a user adds or edits a Patient or Room, the Patients and Rooms tabs in the user interface scrolls to the entry corresponding to the most recent change. This is useful as the user can immediately review the latest modification on the user interface. To accomplish this, PatientListPanel and RoomListPanel attach a listener to ListView<Patient> and ListView<Room> respectively to update the user interface when changes are detected.

Currently, when the user modifies a Task, the user interface does not scroll to the latest change. The reason is that the current implementation of the Tasks tab retrieves the room and task details from a ListView<RoomTaskAssociation>, as seen in RoomTaskListPanel. The underlying list for ListView<RoomTaskAssociation>, found in RoomTaskRecords is altered each time there are changes to a room, including not only the tasks but also the room itself and the patient in it. Hence, the list is unable to distinguish between modifications to tasks, to rooms, or to patients in rooms. Since we do not want the user interface for Task to scroll when there are changes to Room or Patient, we have disabled the listener for ListView<RoomTaskAssociation>.

Fixing this issue likely requires a significant overhaul of the implementation of the Tasks tab. We plan to complete this feature in Covigent v2.0.

Written by Yee Hong


6. Documentation


7. Appendix

A1. Product scope

Target user profile:

  • Needs to manage a significant number of patients and their tasks
  • Needs to manage a significant number of rooms
  • Wants to keep track of patients and their tasks efficiently
  • Wants to look up patients, rooms and tasks details quickly
  • Prefers desktop apps over other types
  • Prefers typing to mouse interactions
  • Prefers all information to be available at one place
  • Can type fast
  • Is reasonably comfortable using Command Line Interface (CLI) apps

Value proposition:

  • Covigent is a handy tool for quarantine facility managers to manage the rooms and patients in the quarantine facility with increased productivity.
  • Covigent stores and retrieves information faster than a typical mouse/GUI driven app.

A2. User stories

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

Priority As a …​ I want to …​ So that I can…​
* * * staff of a quarantine facility key in new patient information keep track of the information of new patients in the facility
* * * staff of a quarantine facility edit patient information update his/her health status
* * * staff of a quarantine facility delete the records of patients who no longer resides in the facility focus on information of existing patients residing in the facility
* * * staff of a quarantine facility view which rooms are empty allocate patients to them
* * * staff of a quarantine facility allocate patients to room keep track of which room they are living in
* * * staff of a quarantine facility quickly find the room that a patient is staying in locate the patient in the facility easily
* * * staff of a quarantine facility key in new task information keep track of the details of the tasks that I must complete
* * staff of a quarantine facility quickly find the room details of a given room number track the patient and tasks in that room easily
* * staff of the quarantine facility indicate that I have completed the task in the room let other staff know that they no longer have to handle them
* * staff of a quarantine facility find out all the outstanding tasks left in each room serve the quarantined individuals better
* * staff of a quarantine facility quickly search through patient information find the patients that match my criteria
* * staff of a quarantine facility look at all the rooms find out which rooms are occupied and which are not
* * staff of a quarantine facility initialise the number of rooms many times with previous information retained define correct number of rooms if mistake is made I can correct it without having to tediously key in information again
* * staff of a quarantine facility find out an empty room to allocate patient to that empty room easily without having to search through the rooms for an empty room
* * careless staff of a quarantine facility edit a task to allow for me to easily change a mistake that I made while adding tasks
* * forgetful staff of a quarantine facility search a task to allow for me to easily search for a task that end before a certain due date

A3. Use cases

(For all use cases below, the System is Covigent and the Actor is Hotel Staff, unless specified otherwise. In addition, due to limitation in Github markdown, for use cases which include another use case, there will be no underlining of text.)

Use case: UC01 Add a patient

MSS

  1. Hotel Staff requests to add a patient into Covigent.
  2. Covigent adds the patient.
  3. Covigent shows the details of the newly added patient.

Use case ends.

Extensions

  • 2a. Covigent realises that the patient name already exists.
    • 2a1. Covigent displays an error message.

Use case ends.

  • 2b. Covigent realises that the patient information being entered does not conform to the system format.
    • 2b1. Covigent displays an error message.

Use case ends.

Use case: UC02 Delete a patient

MSS

  1. Hotel Staff requests to delete a patient from Covigent.
  2. Covigent deletes the patient and removes the patient from the room he/she is residing in.
  3. Covigent shows the details of the deleted patient.

Use case ends.

Extensions

  • 2a. Covigent realizes the patient does not exist.
    • 2a1. Covigent displays an error message.

Use case ends.

Use case: UC03 Edit a patient

MSS

  1. Hotel Staff inputs the new information about the patient.
  2. Covigent edits the patient information to the new information.
  3. Covigent shows the details of the edited patient.

Use case ends.

Extensions

  • 1a. Covigent realizes that no optional fields are input.
    • 1a1. Covigent displays an error message.

Use case ends.

Use case: UC04 Search for a patient

MSS

  1. Hotel Staff requests to search patients with a criterion.
  2. Covigent searches the patients with the input criteria.
  3. Covigent shows the search results.

Use case ends.

Extensions

  • 2a. Covigent realizes that no such patient is recorded
    • 2a1. Covigent displays an error message.

Use case ends.

Use case: UC05 Allocate a patient to a room

MSS

  1. Hotel Staff adds a patient to Covigent (UC01).
  2. Hotel Staff requests to allocate the patient to a specified room.
  3. Covigent adds the patient to the specified room.

Use case ends.

Extensions

  • 2a. Covigent realizes that the specified room does not exist.
    • 2a1. Covigent displays an error message.

Use case ends.

  • 2b. Covigent realizes that the specified room is not empty.
    • 2b1. Covigent displays an error message.

Use case ends.

Use case: UC06 List all rooms

MSS

  1. Hotel Staff requests to list all the rooms in quarantine facility.
  2. Covigent lists all the rooms in the hotel and whether they are occupied or unoccupied.

Use case ends.

Extensions

  • 1a. Covgient realizes that there are no rooms existing.
    • 1a1. Covigent displays an error message.

Use case ends.

Use case: UC07 Find empty room

MSS

  1. Hotel Staff requests to search for an empty room to accommodate patient.
  2. Covigent finds an empty room.
  3. Covigent shows the room to Hotel Staff.

Use case ends.

Extensions

  • 1a. Covigent realizes that there are no rooms.
    • 1a1. Covigent displays an error message.

Use case ends.

  • 1b. Covigent realizes that there are no empty rooms.
    • 1b1. Covigent displays an error message.

Use case ends.

Use case: UC08 Initialize rooms

MSS

  1. Hotel Staff requests to change number of rooms.
  2. Hotel Staff inputs the number of rooms to change into.
  3. Covigent changes the number of rooms in the system.
  4. Covigent displays all the current rooms.

Use case ends.

Extensions

  • 2a. Covigent realizes that the Hotel Staff inputs an invalid number.
    • 2a1. Covigent displays an error message.

Use case ends.

  • 2b. Covigent realizes that the number of rooms input is less than number of occupied room
    • 2b1. Covigent displays an error message.

Use case ends.

Use case: UC09 Search for a room

MSS

  1. Hotel Staff requests to search for a room with the given patient.
  2. Hotel Staff inputs the name of the patient to find the corresponding room.
  3. Covigent displays the room details that the patient resides in.

Use case ends.

Extensions

  • 2a. Covigent realizes that the patient does not exists in the system.
    • 2a1. Covigent displays an error message.

Use case ends.

  • 2b. Covigent realizes that the patient is not allocated to any room.
    • 2b1. Covigent displays an error message.

Use case ends.

Use case: UC10 Add a task to a room

MSS

  1. Hotel Staff requests to add a task to a room.
  2. Covigent adds the task to the room.

Use case ends.

Extensions

  • 1a. Covigent realizes that the specified room does not exist.
    • 1a1. Covigent displays an error message.

Use case ends.

Use case: UC11 Delete a task from a room

MSS

  1. Hotel Staff requests to delete a task from a room.
  2. Covigent deletes the task from the room.

Use case ends.

Extensions

  • 1a. Covigent realizes that the specified room does not exist.
    • 1a1. Covigent displays an error message.

Use case ends.

  • 1b. Covigent realizes that the specified task does not exist.
    • 1b1. Covigent displays an error message.

Use case ends.

Use case: UC12 Edit a task in a room

MSS

  1. Hotel Staff inputs the new information about the task.
  2. Covigent edits the task information to the new information.
  3. Covigent shows the details of the edited task.

Use case ends.

Extensions

  • 1a. Covigent realizes that no optional fields are input.
    • 1a1. Covigent displays an error message.

Use case ends.

  • 1b. Covigent realizes that the specified room does not exist.
    • 1b1. Covigent displays an error message.

Use case ends.

  • 1c. Covigent realizes that the specified task does not exist.
    • 1c1. Covigent displays an error message.

Use case ends.

  • 1d. Covigent realizes that the new information for the task is the same as the original.
    • 1d1. Covigent displays an error message.

Use case ends.

Use case: UC13 Search Task

MSS

  1. Hotel Staff requests to search for a task before a specific date.
  2. Hotel Staff inputs the specific date.
  3. Covigent searches for tasks before the specific date.
  4. Covigent displays the tasks before the specific date and success message.

Use case ends.

Extensions

  • 2a. Covigent realizes that the format of the date is incorrect.
    • 2a1. Covigent displays an error message.

Use case ends.

  • 3a. Covigent realizes that there is no task matching the criteria.
    • 3a1. Covigent displays an error message.

Use case ends.

A4. Non-Functional Requirements

  1. Should work on any mainstream OS as long as it has Java 11 or above installed.
  2. Should be able to hold up to 100 patients 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. Should work even without internet connection.
  5. Should respond to commands within 3 seconds.

A5. Glossary

  • Mainstream OS: Windows, Linux, Unix, OS-X
  • Patient: An individual residing in the quarantine facility
  • Task: Task is to be completed by staff of the quarantine facility

A6. Instructions for manual testing

Given below are instructions to test the app manually.

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

Launch and shutdown

  1. Initial Launch

    1. Download the jar file and copy into an empty folder

    2. Double-click the jar file

      Expected: Shows the GUI with a set of sample patients.

  2. Saving Window Preferences

    1. Resize the window to an optimum size. Move the window to a different location. Close the window.

    2. Re-launch the app by double-clicking the jar file.

      Expected: The most recent window size and location is retained.

  3. Storage

    1. Launch the application and make a change that changes the state of the program, such as addpatient or initroom. Close the window.

    2. Re-launch the app by double-clicking the jar file.

      Expected: The app should re-launch into the same state as when it was closed.

Adding a patient

  1. Adding a patient to Covigent

    1. Prerequisites: User is viewing the patient tab.

    2. Test case: addpatient n/John Doe t/37.0 d/20200101-20200114 p/91234567 a/22
      Expected: Patient John Doe is added to the list. Details of the newly added patient is shown in the result box and the details panel.

    3. Test case: addpatient n/John Doe
      Expected: No patient is added. Error details shown in the result box.

    4. Other incorrect add patient commands to try: addpatient n/John Doe t/37.0 d/20200101-20190114 p/91234567 a/22
      Expected: Similar to previous.

Saving data

  1. Dealing with missing data files

    1. Run the app once and play around with the application. Once a change is made, the program will generate a data files in ./data/.

    2. In ./data/, delete covigentapp.json and roomInformation.json.

    3. Re-launch the app.

      Expected: Default Patient information should now be present in the Patient tab. No Rooms present in Room tab.

  2. Corrupted data files

    1. Run the app once and play around with the application. Once a change is made, the program will generate a data files in ./data/.

    2. In ./data/, open covigentapp.json. On line 2, delete the [:

       1 {
       2   "patients" : [ {
       3 ...
      

      should become

      1 {
      2   "patients" : {
      3 ...
      
    3. Re-launch the app.

      Expected: Go to the Patient tab and the tab should not have any data. covigentapp.json still exists.