Regular-Expressions.info was our source for learning regex.
The format of User Guide and Developer Guide was taken from AddressBook Level-3 with some modifications.
Refer to the guide Setting up and getting started.
The Architecture Diagram given above explains the high-level design of the App.
Given below is a quick overview of main components and how they interact with each other.
Main components of the architecture
Main
(consisting of classes Main
and MainApp
) is in charge of the app launch and shut down.
The bulk of the app's work is done by the following 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.Commons
represents a collection of classes used by multiple other components.
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 delete 1
.
Each of the four main components (also shown in the diagram above),
interface
with the same name as the Component.{Component Name}Manager
class (which follows the corresponding API interface
mentioned in the previous point.For example, the Logic
component defines its API in the Logic.java
interface and implements its functionality using the LogicManager.java
class which follows the Logic
interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
The sections below give more details of each component.
The API of this component is specified in Ui.java
The UI consists of a MainWindow
that is made up of parts e.g.CommandBox
, ResultDisplay
, PatientListPanel
, StatusBarFooter
etc. All these, including the MainWindow
, inherit from the abstract UiPart
class which captures the commonalities between classes that represent parts of the visible GUI.
The UI
component uses the 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,
Logic
component.Model
data so that the UI can be updated with the modified data.Logic
component, because the UI
relies on the Logic
to execute commands.Model
component, as it displays Patient
object residing in the Model
.API : Logic.java
Here's a (partial) class diagram of the Logic
component:
The sequence diagram below illustrates the interactions within the Logic
component, taking execute("find n/Bob")
API call as an example.
Note: The lifeline for FindCommandParser
should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
How the Logic
component works:
Logic
is called upon to execute a command, it is passed to an InputParser
object which in turn creates a parser that matches the command (e.g., FindCommandParser
) and uses it to parse the command.Command
object (more precisely, an object of one of its subclasses e.g., FindCommand
) which is executed by the LogicManager
.Model
when it is executed (e.g. to delete a patient).Model
) to achieve.CommandResult
object which is returned back from Logic
.Here are the other classes in Logic
(omitted from the class diagram above) that are used for parsing a user command:
How the parsing works:
InputParser
class creates an XYZCommandParser
(XYZ
is a placeholder for the specific command name e.g., AddCommandParser
) which uses the other classes shown above to parse the user command and create a XYZCommand
object (e.g., AddCommand
) which the InputParser
returns back as a Command
object.XYZCommandParser
classes (e.g., AddCommandParser
, DeleteByIndexCommandParser
, ...) inherit from the Parser
interface so that they can be treated similarly where possible e.g, during testing.API : Model.java
Note: The diagram omits details of UniqueVisitList
as that will be shown in Visit feature.
The Model
component,
Patient
objects (which are contained in a UniquePatientList
object).Patient
objects (e.g., results of a search query) as a separate filtered list which is exposed to outsiders as an unmodifiable ObservableList<Patient>
that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.UserPref
object that represents the user’s preferences. This is exposed to the outside as a ReadOnlyUserPref
objects.Model
represents data entities of the domain, they should make sense on their own without depending on other components).API : Storage.java
The Storage
component,
PatientListStorage
and UserPrefStorage
, which means it can be treated as either one (if only the functionality of only one is needed).Model
component (because the Storage
component's job is to save/retrieve objects that belong to the Model
).Classes used by multiple components are in the seedu.address.commons
package.
This section describes some noteworthy details on how certain features are implemented.
The find
feature is implemented by using the ArgumentTokenizer
, ArgumentMultimap
and Prefix
classes. Prefix
represents argument prefixes such as n/
for names. In combination with the below methods, the parser can access different parts
of the argument based on the prefixes:
ArgumentTokenizer#tokenize(String argsString, Prefix... prefixes)
— Generates an ArgumentMultimap
object which contains
information on string argument mapped to each prefix.ArgumentMultimap#getPreamble()
— Returns the part of the argument before any prefixes.ArgumentMultimap#getValue(Prefix prefix)
— Returns the part of the argument belonging to the given prefix.The following activity diagram describes the operation of FindCommandParser
:
Each valid prefix is checked, and if found, the value proceeding it in the argument string is saved in the form of a subclass
of Predicate
. Otherwise, a Predicate
that always returns true is used instead. When FindCommand
is created, both of the
Predicate
are passed into it, which will be chained into a singular Predicate
such that the Model
component can use to update
the PatientList
.
The operation returns an error if:
p/
prefix contains letters.The current design was chosen to allow for addition of more find
conditions in later iterations. With the current implementation, only
the FindCommandParser
class needs to be changed, as well as the creation of a new Predicate
subclass.
An alternative was to use a flag to denote the condition to filter the list by. For example, if a user wishes to find a patient with
the name Bob
, the command would be find-n Bob
. This was rejected as it only allows for finding with a single condition, leading
to a less flexible feature.
In later iterations, we plan to add appointment date and the 3 visit fields as optional conditions. The implementation will be identical to the above, as we can exploit the chaining of multiple Predicate
objects in Java.
More complex enhancements can be made using the Flag
subclass of Prefix
, which is integrated into ArgumentMultimap
as a zero-argument Prefix
. This allows us to reuse the same command word find
, but implement a different set of behaviour that is triggered when one or more optional Flag
is detected. For example, the command find-d n/Bob
could be implemented to find and delete all patients with the keyword Bob
in their name.
The list in alphabetical order feature is implemented by using the ArrayList
and Comparator
classes. ArrayList
is used to manage the list of Patients
while Comparator
is used to compare the fullName
of each Patient
.
The implementation of the list
command works as follows.
Upon the user's entering the list
command, after checking that the list of patients is not empty, a list of all Patients
is retrieved from the Model
object and added to a separate ArrayList
. From there, a Comparator
is created that sorts the list of Patients
using their fullName
. Then, each element from the ArrayList
is removed then added in the correct alphabetical order. Once this is completed, the CommandResult
returns successfully and displays the correct output, a list of all patients in alphabetical order.
The following sequence diagram shows the sequence of events when the list
command is typed by a user.
The current design was chosen as the existing list of Patients
in the Model
object, getFilteredPatientList()
, is an immutable object that necessitated the creation of an ArrayList
object, though it may be inefficient.
The feature of deleting all entries is implemented via two separate commands (DeleteAllCommand and ForceDeleteAllCommand). When the user enters 'delete-all' command, InputParser updates its isPreviousCommandDeleteAll variable as true and parses the command into a DeleteAllCommand. The LogicManager then executes the DeleteAllCommand. This would return a CommandResult with a confirmation message, which asks if the user wants to truly delete all entries and prompts the user to give either a 'yes' or 'no' command. If the user enters 'yes', the InputParser checks that the previous command was a DeleteAllCommand and parses the 'yes' command into a YesCommand that is set to delete all entries when its execute method is called. The LogicManager executes the YesCommand to delete all entries. If the user enters 'no' instead of 'yes', this would parse the 'no' command into a NoCommand that does not make any changes to the entries. The LogicManager executes the NoCommand, leading to no effective change.
When the 'delete-all-f' command is entered instead of 'delete-all', InputParser would parse the command into a ForceDeleteAllCommand. The LogicManager would execute the ForceDeleteAllCommand. This would set the current model of the patient list to clear out all existing entries by calling setPatientList method with an empty PatientList object used as its argument. A CommandResult object would be returned with a success message that states that all data has been successfully deleted.
The following sequence diagram describes the sequence of logic when the user inputs 'delete-all-f' command:
The current design was chosen so that there is a safety check mechanism that asks for confirmation from the user if the user truly wants to delete all entries when the 'delete-all' command is given. If the user wishes to bypass the safety check and is certain of the intent to delete all entries, the user can enter 'delete-all-f' command to forcefully delete all entries.
The Visit
- Patient
relationship in the Logic
component is identical to that of Patient
- PatientList
relationship. In our implementation, the Patient
class has the added responsibility of acting as a container for all Visit
objects associated with it. This is achieved by the UniqueVisitList
created upon construction of a Patient
instance.
The current design is convenient as all methods relating to the Visit
object will be going through the Patient
object, which means the Visit
does not need to hold a reference to the Patient
it belongs to. Furthermore, Command
instances that uses Visit
will not need a reference to it, ensuring immutability.
The initial design was to have the 3 Visit
fields be part of the Patient
object to keep track of the latest instance of visit. This was primarily for ease of implementation, as fewer new classes would need to be created. However, this design fell apart as we needed to keep track of past patient visits as well.
Target user profile:
Value proposition: MediTrack can manage patient details faster than a typical mouse/GUI driven app.
Priorities: High (must have) - * * *
, Medium (nice to have) - * *
, Low (unlikely to have) - *
Priority | As a … | I want to … | So that I can… |
---|---|---|---|
* * * | receptionist | see usage instructions | refer to instructions when I forget how to use the App |
* * * | receptionist | see the list of patients | check the index of all patients |
* * * | receptionist | add a new patient | |
* * * | receptionist | add a new visit | specify a patient and record their visit |
* * * | receptionist | delete a patient | remove entries that are outdated |
* * * | receptionist | delete a visit | remove details of a patient's latest visit |
* * * | receptionist | find a patient by name | locate details of a patient without having to go through the entire list |
* * | receptionist | find a patient by contact number | look for a specific patient without worrying about duplicate names |
* * | receptionist | delete all patients | easily reset the list to a blank state |
* * | receptionist | exit with a command | close the application with keyboard inputs only |
(For all use cases below, the System is the MediTrack
and the Actor is the receptionist
, unless specified otherwise)
Use case: UC01 - Add a patient
MSS
Receptionist requests to add a patient’s data in the list.
MediTrack adds the patient’s data into the list.
Use case ends.
Extensions
1a. There is already a patient with the same phone number.
Use case ends.
Use case: UC02 - Delete a patient
MSS
Receptionist requests to list patients.
MediTrack shows a list of patients.
Receptionist requests to delete a specific patient in the list.
MediTrack deletes the patient.
Use case ends.
Extensions
2a. The list is empty.
Use case ends.
3a. The given index is invalid.
3a1. MediTrack shows an error message.
Use case resumes at step 2.
Use case: UC03 - Find a patient
MSS
Receptionist requests for a patient's information.
MediTrack returns the patient's information.
Use case ends.
Extensions
1a. The patient's information is not found in the list.
Use case ends.
Use case: UC04 - Exit
MSS
Receptionist requests to exit the program.
Use case ends.
Use case: UC05 - Delete all patients
MSS
Receptionist requests to delete all patients.
MediTrack asks for confirmation.
Receptionist confirms.
MediTrack deletes all patient information.
Use case ends.
Extensions
1a. There are no patient information.
Use case ends.
3a. Receptionist cancels.
Use case ends.
Use case: UC06 - Start the system
MSS
Receptionist starts the program
MediTrack shows a list of patients
Use case ends.
{More to be added}
11
or above installed.Given below are instructions to test the app manually.
Note: These instructions only provide a starting point for testers to work on; testers are expected to do more exploratory testing.
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 contacts. The window size may not be optimum.
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.
Shutting down
exit
yes
delete-all-f
.add n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 b/25/2/2024 s/Male
Deleting a patient while all patients are being shown
Prerequisites: List all patients using the list
command. Multiple patients in the list.
Test case: delete 1
Expected: First patient is deleted from the list. Details of the deleted patient shown in the status message.
Test case: delete 0
Expected: No patient is deleted. Error details shown in the status message.
Other incorrect delete commands to try: delete
, delete x
, ...
(where x is larger than the list size)
Expected: Similar to previous.