Covigent - Developer Guide
- Preface
- Setting Up
-
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
-
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
-
Planned Features
-
Documentation
-
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.
.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 APIinterface
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 abstractUiPart
class.
The MainWindow
is made up of
- A
PatientListPanel
that displays the list of patients. The layout is defined byPatientCard
andPatientDetailsPanel
. - A
RoomListPanel
that displays the list of rooms. The layout is defined byRoomCard
andRoomDetailPanel
. - A
RoomTaskListPanel
that displays the list of tasks. The layout is defined byTaskCard
. - 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 forUi
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
- Once a user input is obtained from the GUI,
Logic
uses theCovigentAppParser
class to parse the users’ commands and return aCommand
object. - The
Command
is executed byLogicManager
. - Depending on the command input by the user, it may mutate the
Model
, such as adding a new patient, room or task. - The result of the command execution is encapsulated as a
CommandResult
that is returned to theUi
. - These
CommandResults
can instruct theUi
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
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 theRoomTaskAssociation
objects, which keep track of the room that a task belongs to. - exposes unmodifiable
ObservableList<Patient>
,ObservableList<Room>
andObservableList<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
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
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
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
andPatientRecords
data in json format - Reads
RoomRecords
andPatientRecords
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:
- Name
- Temperature
- PeriodOfStay
- Age
- Phone
- 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
andComment
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.
Features related to Patient
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 aPatient
toUniquePatientList
. -
UniquePatientList#setPatient(Patient target, Patient editedPatient)
- Edits the attributes ofPatient
. -
UniquePatientList#remove(Patient toRemove)
- Deletes aPatient
fromUniquePatientList
. -
UniquePatientList#asUnmodifiableObservableList()
- Returns an observable list ofPatient
-
UniquePatientList#getPatientWithName(Name name)
- Returns thePatient
with the givenName
if thePatient
object exists inUniquePatientList
. SinceName
is an identifier of aPatient
, this method is used inEditPatientCommand
andDeletePatientCommand
to modify thePatient
or check if thePatient
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#internalList
using 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
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
4.1.5 Search
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:
- roomNumber This gives the room number of the Room object
-
isOccupied
This gives the information of whether the Room is occupied or not. If there is a
Patient
inside theRoom
, then the isOccupied is true, else false -
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. - 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 ofInitRoomCommand
. -
Option 2: Remove
editRoomCommand
ability to change room number and rename it toAllocateRoomCommand
Removing theeditRoomCommand
to change room number is much more time-efficient compared to changing the entire implementation ofInitRoomCommand
. We also decided that there should be no reason that a user would need to change the room number. Renaming the method toAllocateRoomCommand
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
Feature related to Room
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 Integer
defined 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
inRoom
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 inRoom
to retrieve an unmodifiable list of tasks fromRoomTasks
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>
inRoom
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
inRoom
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 aTask
toTaskList
. -
TaskList#setTask(Task target, Task editedTask)
- Replaces aTask
inTaskList
with the editedTask
. -
TaskList#remove(Task toRemove)
- Removes aTask
fromTaskList
. -
TaskList#asUnmodifiableObservableList()
- Returns a read-only list containing allTask
inTaskList
. -
RoomTasks#getTaskWithTaskIndex(Index index)
- Returns theTask
with theIndex
in theTaskList
. This method is currently inRoomTasks
but can be safely refactored intoTaskList
.
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
4.3.5 Search
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 usingLogsCenter.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
- Hotel Staff requests to add a patient into Covigent.
- Covigent adds the patient.
- 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
- Hotel Staff requests to delete a patient from Covigent.
- Covigent deletes the patient and removes the patient from the room he/she is residing in.
- 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
- Hotel Staff inputs the new information about the patient.
- Covigent edits the patient information to the new information.
- 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
- Hotel Staff requests to search patients with a criterion.
- Covigent searches the patients with the input criteria.
- 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
- Hotel Staff adds a patient to Covigent (UC01).
- Hotel Staff requests to allocate the patient to a specified room.
- 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
- Hotel Staff requests to list all the rooms in quarantine facility.
- 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
- Hotel Staff requests to search for an empty room to accommodate patient.
- Covigent finds an empty room.
- 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
- Hotel Staff requests to change number of rooms.
- Hotel Staff inputs the number of rooms to change into.
- Covigent changes the number of rooms in the system.
- 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
- Hotel Staff requests to search for a room with the given patient.
- Hotel Staff inputs the name of the patient to find the corresponding room.
- 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
- Hotel Staff requests to add a task to a room.
- 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
- Hotel Staff requests to delete a task from a room.
- 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
- Hotel Staff inputs the new information about the task.
- Covigent edits the task information to the new information.
- 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
- Hotel Staff requests to search for a task before a specific date.
- Hotel Staff inputs the specific date.
- Covigent searches for tasks before the specific date.
- 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
- Should work on any mainstream OS as long as it has Java
11
or above installed. - Should be able to hold up to 100 patients without a noticeable sluggishness in performance for typical usage.
- 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.
- Should work even without internet connection.
- 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.
Launch and shutdown
-
Initial Launch
-
Download the jar file and copy into an empty folder
-
Double-click the jar file
Expected: Shows the GUI with a set of sample patients.
-
-
Saving Window Preferences
-
Resize the window to an optimum size. Move the window to a different location. Close the window.
-
Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained.
-
-
Storage
-
Launch the application and make a change that changes the state of the program, such as
addpatient
orinitroom
. Close the window. -
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
-
Adding a patient to Covigent
-
Prerequisites: User is viewing the patient tab.
-
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. -
Test case:
addpatient n/John Doe
Expected: No patient is added. Error details shown in the result box. -
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
-
Dealing with missing data files
-
Run the app once and play around with the application. Once a change is made, the program will generate a data files in
./data/
. -
In
./data/
, deletecovigentapp.json
androomInformation.json
. -
Re-launch the app.
Expected: Default Patient information should now be present in the Patient tab. No Rooms present in Room tab.
-
-
Corrupted data files
-
Run the app once and play around with the application. Once a change is made, the program will generate a data files in
./data/
. - In
./data/
, opencovigentapp.json
. On line 2, delete the[
:
1 { 2 "patients" : [ { 3 ...
should become
1 { 2 "patients" : { 3 ...
- Re-launch the app.
Expected: Go to the Patient tab and the tab should not have any data.covigentapp.json
still exists.
-