diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 5eab1d430e4..70550f45e74 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -196,10 +196,47 @@ The sequence diagram below closely describes the interaction between the various * Pros: Better performance, since this only requires searching through the person list once. * Cons: The order of person list will be lost, since `Name` of a `Person` may be edited. + +### Edit `doctor` or `patient` + +Edits a `doctor` or `patient` entry by indicating their `Index`. +This command is implemented through the `EditCommand` class which extends the `Command` class. + +* Step 1. User enters an `edit` command. +* Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `editCommandParser`. +* Step 3. The `parse` command in `editCommandParser` calls `ParserUtil` to create instances of objects for each of the fields. + * If there are any missing fields, a `CommandException` is thrown. + * If input arguments does not match contraints for the fields, a `IllegalArgumentException` is thrown. + * If the provided `index` is invalid, a `CommandException` is thrown. + +The activity diagram below demonstrates this error handling process in more detail. + + + +* Step 4. The `parse` command in `editCommandParser` return an instance of `editCommand`. +* Step 5. The `LogicManager` calls the `execute` method in `editCommand`. +* Step 6. The `execute` method in `editCommand` executes and creates a new edited person. +* Step 6. The `execute` method in `editCommand` calls the `setPerson` in model to update the details of the respective person. +* Step 7. The `execute` method in `editCommand` also iterates through the `ObservableList` and retrieves all appointments that have the person to be edited, and calls the `setDoctorNric` or `setPatientNric` methods to update all relevant appointments related to the patient or doctor. +* Step 8. Success message gets printed onto the results display to notify user. + +The sequence diagram below closely describes the interaction between the various components during the execution of the `EditCommand`. + + + + +Why is this implemented this way? +1. Making both `Doctor` and `Patient` class extend the `Person` class makes it easier to execute edit operations. +2. `Doctor` and `Patient` all exhibit similar qualities, and thus can inherit from the `Person` superclass. +3. Eliminates the need for separate edit commands for doctor and patient. +4. Since appointments are constructed with unique `Person` `Nric` fields, it does not make sense to have an appointment that does not have valid or outdated doctor or patient entries. +5. As such, the solution that is inbuilt to editing a `Person`, comes with the added functionality on the backend to augment all related `Appointment` entries as well. +6. This results in an updated `Appointments` panel, and saves the user from the hassle of needing to manually edit outdated `Appointment` entries one by one. + ### Delete `doctor` or `patient` Deletes a `doctor` or `patient` entry by indicating their `Index`. -This command is implemented through the `DeleteCommand` class which extend the `Command` class. +This command is implemented through the `DeleteCommand` class which extends the `Command` class. * Step 1. User enters an `delete` command. * Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `deleteCommandParser`. @@ -284,6 +321,44 @@ The sequence diagram below closely describes the interaction between the various * Furthermore, it might get confusing for the user if everything was dumped into the same list of them to sieve through. Perhaps the user was only concerned with looking up patients in which case the appointments would simple be added clutter. * The increased level of integration would also be problems for implementation and testing as existing functionality would have to be adapated exposing the system to more risks and potential for bugs. Eg: the classes would have to change from `Person` to `Entry` in a number of different places. + +### Edit `Appointment` +Edits an `Appointment` entry by indicating their `Index`. +This command is implemented through the `EditAppointmentCommand` class which extends the `Command` class. + +* Step 1. User enters an `editappt` command. +* Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `editAppointmentCommandParser`. +* Step 3. The `parse` command in `editAppointmentCommandParser` calls `ParserUtil` to create instances of objects for each of the fields. + * If there are any missing fields, a `CommandException` is thrown. + * If input arguments does not match contraints for the fields, a `IllegalArgumentException` is thrown. + * If the provided `index` is invalid, a `CommandException` is thrown. + +The activity diagram below demonstrates this error handling process in more detail. + + + +* Step 4. The `parse` command in `editAppointmentCommandParser` returns an instance of `editAppointmentCommand`. +* Step 5. The `LogicManager` calls the `execute` method in `editAppointmentCommand`. +* Step 6. The `execute` method in `editAppointmentCommand` executes and calls `setAppointment` in model to set an updated appointment into the system. +* Step 7. Success message gets printed onto the results display to notify user. + +The sequence diagram below closely describes the interaction between the various components during the execution of the `EditAppointmentCommand`. + + + +Why is this implemented this way? +1. The `Appointment` class has very similar functionalities to that of the `Person` class, in which both classes deal with edit operations. +2. Furthermore on the UI, the `Appointment` column runs parallel to the `Person` column, as such, the behaviours (UX) of operating on the `Person` panel should have a similar feel and experience when dealing with `Appointment` objects. +3. This parallelism is also reflected in the backend code, and hence is very similar to how editing a `Person` is implemented - this is mostly seen through the naming conventions of the classes related to `EditPerson`, such as `EditAppointment` +4. This results in a more familiar experience for both users and developers alike as there is familiarity and some level of consistency when dealing with `Person` and `Appointment` classes. + +Alternative implementation for consideration +1. Since both classes exhibit similarities in both code structure and behaviour, we might consider creating a generic class distinguished between `Person` and `Appointment` via enums to handle edits. +2. This will centralise the behaviours, and reduce the amount of code needed to perform the edit function. +3. A further extension is to do so with all other overlapping functionalities, such as `add` or `delete`, however we leave that possibility for future discussion and refinement. + + + ### Delete `Appointment` Deletes an `Appointment` entry by indicating their `Index`. This command is implemented through the `DeleteAppointmentCommand` class which extend the `Command` class. @@ -319,31 +394,139 @@ Alternative implementation for consideration 2. This will centralise the behaviours, and reduce the amount of code needed to perform the delete function. 3. A further extension is to do so with all other overlapping functionalities, such as `add` or `edit`, however we leave that possibility for future discussion and refinement. +### Find `Person` +Queries a `Person` entry, either `Doctor` or `Patient` by indicating their name or a substring of their name. +This command is implemented through the `FindCommand` class which extends the `Command` class. -### Query `doctor` and `patient` +The `find` command is able to take in multiple parameters (at least one), each of these parameters are read as strings, and act as separate queries. This is a feature of the `find` command, to have the capability of parsing both parameters as a short-hand feature. +Example, the command `find hans doe` is equivalent of returning the logical 'or' result of `find hans` and `find doe`. -Queries a 'doctor' or 'patient' entry by indicating their name or a substring of their name. -This command is implemented through the `QueryDoctor` and `QueryPatient` classes which extend the `Command` class. +* Step 1. User enters a `find` command. +* Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `findCommandParser`. +* Step 3. The `parse` command in `FindCommandParser` checks if at least one parameter is present. + * If the field is missing, a `ParseException` is thrown, and the UI displays an error message. -* Step 1. User enters an `querypatient` or `querydoctor` command. -* Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `queryDoctorCommandParser` or `queryPatientCommandParser`. -* Step 3. The `parse` command in `queryDoctorCommandParser` or `queryPatientCommandParser` calls `ParserUtil` to create instances of objects for each of the fields. - * If there are any missing fields, a `CommandException` is thrown. - * If input arguments does not match contraints for the fields, a `IllegalArgumentException` is thrown. +The activity diagram below demonstrates this error handling process in more detail. + + + +* Step 4. The `parse` command in `FindCommandParser` returns an instance of `FindCommand`. +* Step 5. The `LogicManager` calls the `execute` method in `FindCommand`. +* Step 6. The `execute` method in `FindCommand` executes and calls `updateFilteredPersonList` in model with the `NameContainsKeywordsPredicate` to get a filtered list of person entries of the entered keyword(s), both `patient` and `doctor` entries can be displayed. +* Step 7. A Success message gets printed onto the results display to notify user and the list of matching results is produced. + +The sequence diagram below closely describes the interaction between the various components during the execution of the `FindCommand`. + + + +Alternative implementations considered +1. The following sections describes the behaviour of querying `doctor` and `patient` entries by separate commands by all of the entry's fields, both following a very similar logic to how the `find` command is implemented. We might consider using flags to be more precise with our searches, (e.g a -doctor or -patient flag to indicate we wish to search for only `doctor` and `patient` entries respectively) so as to avoid the need to create additional commands. However, we felt that this approach overloaded the `find` method too much, and overcomplicated the `find` command's usage. +2. Even if the `find` command was to be overloaded with flags, we foresaw that the creation of distinct commands to fit the flags parsed by the `find` command was unavoidable. As such, it was prudent to start with the implementation of the distinct commands first (as described in the following sections, each tied to a specific command), and leave the overloading of the `find` command as a later increment. + + +### Query `doctor` +Queries a `doctor` entry by indicating the exact string, or substring of any of a `doctor`'s fields - name, nric, phone number and date of birth (as displayed on the UI. e.g 30 January 2023). +This command is implemented through the `QueryDoctorCommand` class which extends the `Command` class. + +Similar to the `find` command, the `doctor` query command is able to take in multiple parameters (at least one), each of these parameters are read as strings, and act as separate queries, with the result returned being the logical 'or' result of applying a `doctor` query to each parameter separately. + +* Step 1. User enters a `doctor` command. +* Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `QueryDoctorCommandParser`. +* Step 3. The `parse` command in `QueryDoctorCommandParser` checks if at least one parameter is present. + * If the field is missing, a `ParseException` is thrown, and the UI displays an error message. The activity diagram below demonstrates this error handling process in more detail. - + -* Step 4. The `parse` command in `queryDoctorCommandParser` or `queryDoctorCommandParser` return an instance of `queryPatientCommand` or `queryPatientCommand` respectively. -* Step 5. The `LogicManager` calls the `execute` method in `queryDoctorCommandParser` or `queryDoctorCommandParser`. -* Step 6. The `execute` method in `queryDoctorCommandParser` or `queryDoctorCommandParser` executes and calls `updateFilteredPersonList` in model to get a filtered list of `Doctor` or `Patient`. +* Step 4. The `parse` command in `QueryDoctorCommandParser` returns an instance of `QueryDoctorCommand`. +* Step 5. The `LogicManager` calls the `execute` method in `QueryDoctorCommand`. +* Step 6. The `execute` method in `QueryDoctorCommand` executes and calls `updateFilteredPersonList` in model with the `DoctorContainsKeywordsPredicate` to get a filtered list of only `Doctor` entries of the entered keywords(s). * Step 7. Success message gets printed onto the results display to notify user and the list of matching results is produced. Why is this implemented this way? -1. Making both `Doctor` and `Patient` class extend the `Person` class makes it easier to execute query operations. -2. `Doctor` and `Patient` all exhibit similar qualities, and thus can inherit from the `Person` superclass. +1. The backend execution of the `doctor` command and the `find` command are very similar, however the choice to separate the two query commands is justified due to the expansion of fields in the `doctor` query, conceptually making them distinct commands. +2. Furthermore, there is an additional check to check if an entry is of type `doctor` that is not present in the `find` command. + + +### Query `patient` +Queries a `patient` entry by indicating the exact string, or substring of any of a `patient`'s fields - name, nric, phone number and date of birth (as displayed on the UI. e.g 30 January 2023). +This command is implemented through the `QueryPatientCommand` class which extends the `Command` class. + +Similar to the `find` command, the `patient` query command is able to take in multiple parameters (at least one), each of these parameters are read as strings, and act as separate queries, with the result returned being the logical 'or' result of applying a `patient` query to each parameter separately. + +* Step 1. User enters a `patient` command. +* Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `QueryPatientCommandParser`. +* Step 3. The `parse` command in `QueryPatientCommandParser` checks if at least one parameter is present. + * If the field is missing, a `ParseException` is thrown, and the UI displays an error message. + +The activity diagram below demonstrates this error handling process in more detail. + + + +* Step 4. The `parse` command in `QueryPatientCommandParser` returns an instance of `QueryPatientCommand`. +* Step 5. The `LogicManager` calls the `execute` method in `QueryPatientCommand`. +* Step 6. The `execute` method in `QueryPatientCommand` executes and calls `updateFilteredPersonList` in model with the `PatientContainsKeywordsPredicate` to get a filtered list of only `Patient` entries of the entered keywords(s). +* Step 7. Success message gets printed onto the results display to notify user and the list of matching results is produced. + + +Why is this implemented this way? +1. The backend execution of the `patient` command and the `find` command are very similar, however the choice to separate the two query commands is justified due to the expansion of fields in the `patient` query, conceptually making them distinct commands. +2. Furthermore, there is an additional check to check if an entry is of type `patient` that is not present in the `find` command. + + +### Query `apptfordoctor` +Queries an `appointment` entry that has the associated `doctor`'s `Nric`, by indicating the exact `Nric` of the doctor as the search parameter. +This command is implemented through the `apptfordoctor` class which extends the `Command` class. + +The `apptfordoctor` command takes in multiple parameters (at least one), and each of these parameters are read as strings (not case-sensitive, i.e S1234567A is equivalent to s1234567a), and returns the logical 'or' result of applying the `apptfordoctor` command to each parameter separately. **Note that no errors will not be thrown if the inputted `Nric`(s) are not of the appropriate form** (i.e Begins with one of S, T, G, F, or M, followed by 7 numerical digits, then ended by an alphabetical letter), but rather the expected return result is that no queries will be found. + +* Step 1. User enters an `QueryDoctorAppointment` command. +* Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `QueryDoctorAppointmentCommandParser`. +* Step 3. The `parse` command in `QueryDoctorAppointmentCommandParser` checks if at least one parameter is present. + * If the field is missing, a `ParseException` is thrown, and the UI displays an error message. + +The activity diagram below demonstrates this error handling process in more detail. + + + +* Step 4. The `parse` command in `QueryDoctorAppointmentCommandParser` returns an instance of `QueryDoctorAppointmentCommand`. +* Step 5. The `LogicManager` calls the `execute` method in `QueryDoctorAppointmentCommand`. +* Step 6. The `execute` method in `QueryDoctorAppointmentCommand` executes and calls `updateFilteredAppointmentList` in model with the `AppointmentContainsDoctorPredicate` to get a filtered list of appointment entries with the entered keyword(s), only those `appointment`(s) that have the associated `doctor`'s `Nric` entries are be displayed. +* Step 7. Success message gets printed onto the results display to notify user and the list of matching results is produced. + + +Why is this implemented this way? +1. This command closely resembles the `find` command, but can be seen as a stricter version as the results queried do not include substring searches. Therefore, it is justified to separate this command from the `find` command as two distinct commands with distinct `commandParsers`. +2. The rationale behind excluding substring searches for `appointment`(s) is that if a hospital clerk is searching for a specific `doctor`'s scheduled `appointment`(s), the hospital clerk already has the `doctor`'s unique `Nric` and hence including substring querying is irrelevant. + + +### Query `apptforpatient` + +Queries an `appointment` entry that has the associated `patient`'s `Nric`, by indicating the exact `Nric` of the patient as the search parameter. +This command is implemented through the `apptforpatient` class which extends the `Command` class. + +The `apptforpatient` command takes in multiple parameters (at least one), and each of these parameters are read as strings (not case-sensitive, i.e S1234567A is equivalent to s1234567a), and returns the logical 'or' result of applying the `apptforpatient` command to each parameter separately. **Note that no errors will not be thrown if the inputted `Nric`(s) are not of the appropriate form** (i.e Begins with one of S, T, G, F, or M, followed by 7 numerical digits, then ended by an alphabetical letter), but rather the expected return result is that no queries will be found. + +* Step 1. User enters an `QueryPatientAppointment` command. +* Step 2. The `AddressBookParser` will call `parseCommand` on the user's input string and return an instance of `QueryPatientAppointmentCommandParser`. +* Step 3. The `parse` command in `QueryPatientAppointmentCommandParser` checks if at least one parameter is present. + * If the field is missing, a `ParseException` is thrown, and the UI displays an error message. + +The activity diagram below demonstrates this error handling process in more detail. + + + +* Step 4. The `parse` command in `QueryPatientAppointmentCommandParser` returns an instance of `QueryPatientAppointmentCommand`. +* Step 5. The `LogicManager` calls the `execute` method in `QueryPatientAppointmentCommand`. +* Step 6. The `execute` method in `QueryPatientAppointmentCommand` executes and calls `updateFilteredAppointmentList` in model with the `AppointmentContainsPatientPredicate` to get a filtered list of appointment entries with the entered keyword(s), only those `appointment`(s) that have the associated `patient`'s `Nric` entries are be displayed. +* Step 7. Success message gets printed onto the results display to notify user and the list of matching results is produced. + + +Why is this implemented this way? +1. This command closely resembles the `find` command, but can be seen as a stricter version as the results queried do not include substring searches. Therefore, it is justified to separate this command from the `find` command as two distinct commands with distinct `commandParsers`. +2. The rationale behind excluding substring searches for `appointment`(s) is that if a hospital clerk is searching for a specific `patient`'s scheduled `appointment`(s), the hospital clerk already has the `patient`'s unique `Nric` and hence including substring querying is irrelevant. [//]: # (### \[Proposed\] Undo/redo feature) diff --git a/docs/UserGuide.md b/docs/UserGuide.md index f13b5e7cd39..dac7a4da58c 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,6 +1,6 @@ --- layout: page -title: User Guide +title: MediCLI User Guide --- ## Welcome to MediCLI! @@ -34,11 +34,19 @@ This UG offers a comprehensive overview of the features on offer, and provide yo You may skip to the features section which elaborates on the individual commands that you can run to get the most out of the system. ## How to use this UG --includes information on how users can -effectively navigate the document, clarifies -the meaning of icons and formatting used, -and provides guidance on understanding -features, functions, or command +As you read through this MediCLI User Guide, you will come across a variety of different types of text formats. The table below will explain to you what they mean. + +| Text Format | What it means | +|--------------------|--------------------------------------------------------------------------------------------------------| +| [hyperlink]() | Text in blue are hyperlinks and will take you to a different page. | +| `command` | Text in lowercase with grey background box are MediCLI commands. | +| `FIELD` | Text in uppercase with grey background box are inputs to MediCLI commands | +| `[OPTIONAL_FIELD]` | Text in uppercase with grey background box and square brackets are optional inputs to MediCLI commands | + +Take note of these text-boxes, as they give you important information for using MediCLI. +
:bulb: **TIP**: Tip call-outs give you helpful pointers in MediCLI!
+
:information_source: **INFO**: Info call-outs give you information about MediCLI that you can take note of!
+
:exclamation: **DANGER**: Danger call-outs like this contain dangerous actions you should remember when using MediCLI to avoid errors!
-------------------------------------------------------------------------------------------------------------------- ## Key Product Information @@ -69,63 +77,124 @@ On top of the primary entities and operations highlighted above, MediCLI also pr ## Quick start Guide -[-offers detailed information on how users -can get started, encompassing installation -instructions, compatibility with different -operating systems, elements of the graphical -user interface (GUI), and a tutorial on using -the command-line interface (CLI).] -### Compatibility -[OS] +Ready to step into the world of MediCLI? This section will provide detailed information on how users can get started, +which includes basic system requirements, installation instructions, overview of the main window, +and a tutorial on using the command-line interface (CLI). + +### System Compatibility + +MediCLI is written with the Java programming language on the backend and JavaFX on the front end. +Therefore, a device with Java version 11 or above and JavaFX version 17 or above installed is required to run MediCLI. -### Installation +Compatible Operating Systems: +* Any device running Windows, macOS, or Ubuntu with sufficient Java and JavaFX compatibility. -1. Ensure you have Java `11` or above installed in your Computer. +Recommended Minimum System Requirements: +* 2-core CPU running at 2.40 GHz +* 4GB RAM +* 2GB free disc space + +### Installation Instructions + +1. Please make sure the computer you are using meets the system compatibility specified above. 1. Download the latest `MediCLI.jar` from [here](https://github.com/AY2324S2-CS2103T-T15-1/tp/releases). -1. Copy the file to the folder you want to use as the _home folder_ for your MediCLI. +
:information_source: **INFO**: The MediCLI jar file can be found at the bottom of the release notes
+ +1. We recommend you to copy the file into the folder you want to use as the _home folder_ for MediCLI. This is because running the application will create additional storage and logging files. + +1. Congratulations! You now have MediCLI successfully downloaded on your computer. + +### Starting up MediCLI + +Once you have installed MediCLI onto your computer (refer to the sub-section above), navigate to the instruction specific to your operating system below. + +#### Windows + +1. Open file explorer and navigate to the home folder containing the MediCLI jar file. + +2. Double-click on the MediCLI application and it should start up!
+ + ![Ui](images/WindowsStartup.png) + +#### macOS + +1. Open finder and navigate to the home folder containing the MediCLI jar file. -1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar MediCLI.jar` command to run the application.
+2. Double-click on the MediCLI application and it should start up!
+ + ![Ui](images/macOSStartup.png) + +#### CLI Alternative Solution + +1. Open a command terminal, `cd` into the home folder containing the MediCLI jar file, and use the `java -jar MediCLI.jar` command to run the application.
A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) + + ![Ui](images/InitialState.png) + +### Overview of MediCLI Main Window + +MediCLI has 4 primary components in its main window. Detailed descriptions of each can be found below. + +![Ui](images/GUI.png) + +Command Input - This is where you will type your commands. + +Results Display - MediCLI will respond to you here with either a success message or a detailed description of what went wrong. + +Persons Panel - This is where you will see a list of the filtered patients and patients. + +Appointments Panel - This is where you will see a list of the filtered patients and patients. ### How to use the command line interface (CLI) -### Initial start-up and sample use-case +MediCLI is operated using typed commands to the command line interface (CLI). Do not worry if you do not understand CLI yet; Here we will explain to you the formats of text commands and how to use them. + +![Ui](images/cli_format.png) + +| CLI Format | What it means | +|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Command | The command tells MediCLI what action you want to perform. | +| Index | Certain MediCLI commands have an `INDEX` field, which is a number that is assigned to a particular patient, doctor or appointment. Index must be larger than 1 and can be up to the maximum number of patients/doctors or appointments as listed in the MediCLI GUI. | +| Parameter Prefix | Fields typically have a prefix like `i/` or `n/` followed by the field content. This tells MediCLI what field you are entering. | +| Command Parameter | The command parameter is the parameter prefix followed by field content. For example, the command parameter to enter NRIC would be `i/S1234567A` | + +
:information_source: **INFO**: Not all MediCLI commands have fields! For example, the command to clear all data is simply `clear`.
+ +### Quick Tutorial on a Sample Use Case 1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try: + + Some example commands you can try (Assuming MediCLI is opened for the first time and is in its initial state with the default sample data): * `list` : Lists all contacts. - * `addpatient i/S1234567A n/John Doe d/2003-01-30 p/98765432` : Adds a patient named `John Doe` to the MediCLI system. + * `adddoctor i/S1234567B n/Amy Smith d/2003-01-30 p/98765432` : Adds a doctor named `Amy Smith` to the MediCLI system. - * `delete 3` : Deletes the 3rd contact shown in the current list. + * `addappt ad/2024-06-09 10:15 dn/S1234567B pn/S1234567A` : Schedules an appointment between the doctor `Amy Smith` and the patient `John Doe`. - * `clear` : Deletes all contacts. + * `delete 2` : Deletes the 2nd person currently listed in the MediCLI system (patient named David Li). * `exit` : Exits the app. 1. Refer to the [Features](#features) below for details of each command. --------------------------------------------------------------------------------------------------------------------- - ## Features
**:information_source: Notes about the command format:**
-* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`. +* Words in `UPPER_CASE` are the parameters to be supplied by you.
+ e.g. in `addpatient i/NRIC n/NAME d/DOB p/PHONE`, `NAME` is a parameter which can be used as `n/John Doe`. * Items in square brackets are optional.
- e.g `edit INDEX [n/NAME] [p/PHONE]` can be used as `edit 1 n/John Doe` or as `edit 1 p/91234567`. + e.g `edit INDEX [i/NRIC] [n/NAME] [p/PHONE] [d/DOB]` can be used as `edit 1 n/John Doe` or as `edit 1 i/t1234567s`. * Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. + e.g. if the command specifies `n/NAME p/PHONE`, `p/PHONE n/NAME` is also acceptable. * Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`. @@ -170,7 +239,7 @@ Examples: ![add_patient_result](images/addPatient.png) -### Adding a Doctor: `adddoctor` +### Adding a Doctor : `adddoctor` Adds a doctor into the MediCLI system. @@ -193,19 +262,19 @@ Examples: ![add_doctor_result](images/addDoctor.png) -### Adding an appointment: `addappt` +### Adding an appointment : `addappt` Adds an appointment to MediCLI. Appointments are between a doctor with the specified `DOCTOR_NRIC` and a patient with the `PATIENT_NRIC` on a specific date and time. -Note that while you cannot create a new appointment with the date/time in the past, appointments that were valid when created but are now past their date will be allowed to remain in the system. This is an intended feature to allow the hospital admins to track a patient/doctors past appointments. -Format: `addappt ad/DATE dn/DOCTOR_NRIC pn/PATIENT_NRIC` +Note that while you cannot create a new appointment with the date and time in the past, appointments that were valid when created but are now past their date and time will be allowed to remain in the system. This is an intended feature to allow the hospital admins to track a patient/doctors past appointments. +Format: `addappt ad/DATETIME dn/DOCTOR_NRIC pn/PATIENT_NRIC` Field Constraints: -- `DATE`: Input must be in the format `yyyy-MM-dd HH:MM`. Specified date must be >= current date and time. i.e. appointment cannot be scheduled in the past. -- `DOCTOR_NRIC`: Follows the correct Singapore NRIC format. Begin with one of S, T, G, F, or M, followed by 7 numerical digits, then ended by an alphabetical letter. This field is non-case-sensitive. -- `PATIENT_NRIC`: Follows the correct Singapore NRIC format. Begin with one of S, T, G, F, or M, followed by 7 numerical digits, then ended by an alphabetical letter. This field is non-case-sensitive. +- **DATETIME**: Input must be in the format `yyyy-MM-dd HH:MM`. Specified date and time must be later than the current date and time. i.e. appointment cannot be scheduled in the past. +- **DOCTOR_NRIC**: Follows the correct Singapore NRIC format. Begin with one of S, T, G, F, or M, followed by 7 numerical digits, then ended by an alphabetical letter. This field is non-case-sensitive. +- **PATIENT_NRIC**: Follows the correct Singapore NRIC format. Begin with one of S, T, G, F, or M, followed by 7 numerical digits, then ended by an alphabetical letter. This field is non-case-sensitive. Command Constraints: -- All of the above fields (`DATE`, `DOCTOR_NRIC`, `PATIENT_NRIC`) are compulsory and must be non-empty. +- All of the above fields (`DATETIME`, `DOCTOR_NRIC`, `PATIENT_NRIC`) are compulsory and must be non-empty. - A doctor with the specified `DOCTOR_NRIC` must already exist in the MediCLI System. - A patient with the specified `PATIENT_NRIC` must already exist in the MediCLI System. @@ -215,7 +284,7 @@ Examples: ![add_appointment_result](images/addAppointment.png) -### Editing a person: `edit` +### Editing a person : `edit` Edits an existing person in the MediCLI system. @@ -225,6 +294,7 @@ Format: `edit INDEX [i/NRIC] [n/NAME] [p/PHONE] [d/DOB]` * At least one of the optional fields must be provided. * Existing values will be updated to the input values. * Note that editing a patient or doctor and not changing any of the values of the parameters is allowed and is considered a valid edit by the system. +* Note that editing a patient or doctor will recursively update the relevant details of all appointments related to the patient or doctor. Field Constraints: * **NRIC** : Follows the correct Singapore NRIC format. Begin with one of S, T, G, F, or M, followed by 7 numerical digits, then ended by an alphabetical letter. This field is non-case-sensitive. @@ -241,23 +311,42 @@ Examples: ![add_appointment_result](images/editPerson.png) -### Editing an appointment: `editappt` -Edits an existing person in the MediCLI system. +### Editing an appointment : `editappt` +Edits an existing appointment in the MediCLI system. -Format: `editappt INDEX ad/DATE` +Format: `editappt INDEX ad/DATETIME` * Edits the appointment at the specified `INDEX`. The index refers to the index number shown in the displayed appointment list. The index **must be a positive integer** 1, 2, 3, …​ * Existing values will be updated to the input values. Field Constraints: -* **DATE** : Input must be in the format `yyyy-MM-dd HH:MM`. Specified date must be >= current date and time. i.e. appointment cannot be scheduled in the past. +* **DATETIME** : Input must be in the format `yyyy-MM-dd HH:MM`. Specified date and time must be later than the current date and time. i.e. appointment cannot be scheduled in the past. Examples: -* `editappt 1 ad/2024-04-09 11:00` Edits the appointment date of the first appointment in the appointment list to `2024-04-09 11:00` +* `editappt 1 ad/2025-04-09 11:00` Edits the appointment date and time of the first appointment in the appointment list to `2025-04-09 11:00` ![add_appointment_result](images/editAppointment.png) +### Finding both doctor and patient by name: `find` + +Finds `Patient`(s) or `Doctor`(s) whose details contain any of the given keywords. + +Format for querying patients or doctors: `find KEYWORD [MORE_KEYWORDS]` + +Command Constraints: + +* The search is case-insensitive. e.g `hans` will match `Hans` +* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans` +* Only the name field is searched. +* Both full words and substrings will be matched e.g. `Han` will match `Hans` +* Patients and Doctors matching at least one keyword will be returned (i.e. logical 'OR' search). + e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` + +Examples: +* `find John` returns `john` and `John Doe` +* `find alex david` returns `Alex Yeoh`, `David Li`
+ ### Querying persons by name: `patient` Finds `Patient`(s) whose details contain any of the given keywords. @@ -274,12 +363,13 @@ Command Constraints: e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` Examples: -* `patient John` returns `john` and `John Doe` -* `patient alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'patient alex david'](images/findAlexDavidResultPatient.png) +* `patient John David` returns `patient` with name `John Doe` and `patient` with name `David Li` +* `patient S1234` returns `patient` with `Nric` `S1234567A`, `patient` with `Nric` `S1234765Q` +* `patient 30 Jan` returns `patient` with `DoB` `30 January 1990`, `patient` with `DoB` `30 January 2001`
+ ![result for 'patient alex david'](images/findPatient.png) -### Querying persons by name: `doctor` +### Querying persons by name : `doctor` Finds `Doctors`(s) whose details contain any of the given keywords. @@ -295,12 +385,13 @@ Command Constraints: e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` Examples: -* `doctor John` returns `john` and `John Doe` -* `doctor alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'doctor alex david'](images/findAlexDavidResultDoctor.png) +* `doctor John David` returns `doctor` with name `John Doe` and `doctor` with name `David Li` +* `doctor S1234` returns `doctor` with `Nric` `S1234567A`, `doctor` with `Nric` `S1234765Q` +* `doctor 30 Jan` returns `doctor` with `DoB` `30 January 1990`, `doctor` with `DoB` `30 January 2001`
+ ![result for 'doctor alex david'](images/findDoctor.png) -### Querying appointments by NRIC `apptforpatient` +### Querying appointments by Patient's NRIC : `apptforpatient` Format: `apptforpatient KEYWORD [MORE_KEYWORDS]` @@ -313,16 +404,16 @@ match appointments that involve `S1234562A` and `S1234561A`. * Appointments with `Patient`s whose NRICs match at least one keyword will be returned (i.e. `OR` search). Example: -* `apptforpatient s0123456a` returns all `Appointment` object(s) that `Patient` with NRIC `S0123456A` is involved in. +* `apptforpatient s0123456a` returns all `Appointment` entries that `Patient` with `Nric` `S0123456A` is involved in. -* Initial State (All `Appointment`s listed) +* All `Appointment`s listed ![result for 'list'](images/findAppointmentInitialPatient.png) -* After Querying (Only `Appointment`s with `Patient` of NRIC `S0123456A`) +* Only `Appointment`s with `Patient` of `Nric` `S0123456A` ![result for 'apptforpatient S0123456A'](images/findAppointmentResultPatient.png) -### Querying appointments by NRIC `apptfordoctor` +### Querying appointments by Doctor's NRIC : `apptfordoctor` Format: `apptfordoctor KEYWORD [MORE_KEYWORDS]` @@ -338,16 +429,16 @@ match appointments that involve `S1234562A` and `S1234561A`. Example: * `apptfordoctor s1234561a` returns all `Appointment` object(s) that `Doctor` with NRIC `S1234561A` is involved in. -* Initial State (All `Appointment`s listed) -![result for 'apptfordoctor S1234561A'](images/findAppointmentInitialDoctor.png) +* All `Appointment`s listed +![result for 'list'](images/findAppointmentInitialDoctor.png) -* After Querying (Only `Appointment`s with `Doctor` of NRIC `S1234561A`) +* Only `Appointment`s with `Doctor` of `Nric` `S1234561A` ![result for 'apptfordoctor S1234561A'](images/findAppointmentResultDoctor.png) ### Deleting a doctor or patient : `delete` -Deletes the specified doctor / patient from the mediCLI system. **Note that all associated appointments with this doctor / patient will also be recursively deleted.** Please exercise caution when using the delete command and removing a patient or a doctor from MediCLI, as this action cannot be undone. +Deletes the specified doctor / patient from the MediCLI system. **Note that all associated appointments with this doctor / patient will also be recursively deleted.** Please exercise caution when using the delete command and removing a patient or a doctor from MediCLI, as this action cannot be undone. * Deletes the doctor / patient at the specified `INDEX`. * The index refers to the index number shown in the displayed doctor and patient list. @@ -358,11 +449,11 @@ Examples: * `patient John` followed by `delete 1` deletes the 1st patient in the results of the `patient` search command. * `doctor Steve` followed by `delete 2` deletes the 2nd doctor in the results of the `doctor` search command. -![result for 'delete 1'](images/deletePatient.png) +![result for 'delete 1'](images/deletePerson.png) ### Deleting appointment : `deleteappt` -Deletes the specified appointment from the mediCLI system. +Deletes the specified appointment from the MediCLI system. Format: `deleteappt INDEX` @@ -376,9 +467,9 @@ Examples: * `apptfordoctor S1234567B` followed by `deleteappt 2` deletes the 2nd appointment in the results of the `apptfordoctor` search command. Visual Guide -* Initial State (All appointments listed after running `list`) +* All appointments listed after running `list` ![result for 'list'](images/deleteApptInitialState.png) -* Final State (After running `deleteappt` with `Index` of `1`) +* After running `deleteappt` with `Index` of `1` ![result for 'deleteappt 1'](images/deleteApptFinalState.png) ### Clearing all entries : `clear` @@ -389,6 +480,8 @@ Warning!!! This will wipe the entire data from the system upon being executed. P Format: `clear` +![result for 'clear'](images/clear.png) + ### Exiting the program : `exit` Exits the program. @@ -415,11 +508,51 @@ Furthermore, certain edits can cause the mediCLI to behave in unexpected ways (e **Q**: How do I transfer my data to another Computer?
**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous MediCLI home folder. +**Q**: Can I use MediCLI on different operating systems?
+**A**: Yes, MediCLI is compatible with multiple operating systems as long as you have Java 11 or above installed. You can run it on Windows, macOS, or Linux. + +**Q**: Is there a limit to the number of patients, doctors, or appointments I can add to MediCLI?
+**A**: There is no built-in limit to the number of entries you can add to MediCLI. However, the performance may be affected if you add an extremely large number of entries. + +**Q**: Can I customise the appearance or theme of the interface in MediCLI?
+**A**: Currently, there is no option to customise the appearance or theme of the interface in MediCLI. It has a default interface optimised for efficiency and usability. + +**Q**: Does MediCLI support multi-user access or user authentication?
+**A**: No, MediCLI is designed for single-user access only. It does not have built-in support for multi-user access or user authentication. + +**Q**: Can I export data from MediCLI to other formats like CSV or Excel?
+**A**: Currently, there is no built-in feature to export data from MediCLI to other formats. However, you can manually extract data from the JSON file if needed. + -------------------------------------------------------------------------------------------------------------------- ## Known issues -1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again. +1. **Issue**: When using multiple screens, if the MediCLI application is moved to a secondary screen and later switched to using only the primary screen, the graphical user interface (GUI) may open off-screen upon application launch. + + **Impact**: Users may find it challenging to interact with the application as the GUI is rendered off-screen, making it inaccessible and difficult to use. + + **Workaround**: To resolve this issue, users can delete the preferences.json file generated by the application before launching MediCLI again. This action resets the application preferences, ensuring that the GUI opens within the visible area of the primary screen. +2. **Issue**: MediCLI may experience performance degradation when handling a large number of entries, such as patients, doctors, or appointments. + + **Impact**: Users may notice slower response times or delays when adding, editing, or deleting entries, especially in cases with a large dataset. + + **Workaround**: Users can optimise performance by limiting the number of entries stored in MediCLI or by periodically archiving old data to reduce the dataset size. +3. **Issue**: Editing data directly in the data file may lead to unexpected behavior or data corruption. + + **Impact**: Users who manually edit the JSON data file used by MediCLI may inadvertently introduce errors or inconsistencies, resulting in data loss or application crashes. + + **Workaround**: It's recommended to avoid directly editing the data file unless absolutely necessary. Users should exercise caution and make backups before making any changes to the data file. +4. **Issue**: MediCLI does not provide built-in data export functionality to formats like CSV or Excel. + + **Impact**: Users may face challenges when trying to export data from MediCLI for analysis or reporting purposes, especially if they rely on external tools or software that require specific file formats. + + **Workaround**: Users can manually extract data from the JSON data file used by MediCLI and convert it to the desired format using third-party tools or scripts. Alternatively, they can explore custom export solutions or request this feature from the developers. + +5. **Issue**: When the name entered into the system is too lengthy, MedicCLI truncates the name and adds ellipses. + + **Impact**: Users may face challenges reading or finding long names when reading from MediCLI, especially on smaller displays. + + **Workaround**: Users can reduce the amount of characters typed into the name field or search for longer names based on the first few characters shown instead of the entire name . -------------------------------------------------------------------------------------------------------------------- @@ -429,11 +562,11 @@ Action | Format, Examples --------|------------------ **Add Patient** | `addpatient i/NRIC n/NAME d/DOB p/PHONE_NUMBER`
e.g., `addpatient i/S1234567A n/John Doe d/2003-01-30 p/98765432` **Add Doctor** | `adddoctor i/NRIC n/NAME d/DOB p/PHONE_NUMBER`
e.g., `adddoctor i/S1234567A n/John Doe d/2003-01-30 p/98765432` -**Add Appointment** | `addappt ad/DATE dn/DOCTOR_NRIC pn/PATIENT_NRIC`
e.g., `addappt ad/2024-08-11 23:50 dn/S1234567A pn/S1234567B` +**Add Appointment** | `addappt ad/DATETIME dn/DOCTOR_NRIC pn/PATIENT_NRIC`
e.g., `addappt ad/2024-08-11 23:50 dn/S1234567A pn/S1234567B` **Clear** | `clear` **Delete Person** | `delete INDEX`
e.g., `delete 3` **Delete Appointment** | `deleteappt INDEX`
e.g., `deleteappt 3` -**Edit Appointment** | `editappt INDEX ad/DATE`
e.g.,`editappt 1 ad/2024-04-09` +**Edit Appointment** | `editappt INDEX ad/DATETIME`
e.g.,`editappt 1 ad/2024-04-09 10:10` **Edit Person** | `edit INDEX [n/NAME] [p/PHONE] [i/NRIC] [d/DOB]`
e.g.,`edit 1 p/91234567 n/Betsy Crower` **Exit** | `exit` **Query Patient** | `patient KEYWORD [MORE_KEYWORDS]`
e.g., `patient James Jake` diff --git a/docs/_layouts/page.html b/docs/_layouts/page.html index 01e4b2a93b8..3740360ca5e 100644 --- a/docs/_layouts/page.html +++ b/docs/_layouts/page.html @@ -2,7 +2,7 @@ layout: default ---
- +

{{ page.title | escape }}

diff --git a/docs/assets/css/style.scss b/docs/assets/css/style.scss index b5ec6976efa..0ff03ded082 100644 --- a/docs/assets/css/style.scss +++ b/docs/assets/css/style.scss @@ -10,3 +10,17 @@ height: 21px; width: 21px } + +h2, h3 { + font-weight: bold; + color: #257ec7; +} + +.post-title { + color: #257ec7; + font-weight: bold; +} + +#logo { + margin-bottom: 15px; +} diff --git a/docs/diagrams/DeleteAppointmentActivityDiagram.puml b/docs/diagrams/DeleteAppointmentActivityDiagram.puml index bfb016082ef..ebaaabe7790 100644 --- a/docs/diagrams/DeleteAppointmentActivityDiagram.puml +++ b/docs/diagrams/DeleteAppointmentActivityDiagram.puml @@ -4,7 +4,7 @@ skinparam ActivityFontSize 15 skinparam ArrowFontSize 12 start -:User enters command to appointment; +:User enters command to delete appointment; if () then ([command is invalid]) :Show error message\nfor invalid command; diff --git a/docs/diagrams/EditAppointmentActivityDiagram.puml b/docs/diagrams/EditAppointmentActivityDiagram.puml new file mode 100644 index 00000000000..54066f7aab1 --- /dev/null +++ b/docs/diagrams/EditAppointmentActivityDiagram.puml @@ -0,0 +1,26 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 + +start +:User enters command to edit appointment; + +if () then ([command is invalid]) + :Show error message\nfor invalid command; +else ([else]) + if () then ([missing required fields]) + :Show error message\nfor missing required fields; + else ([else]) + if () then ([Invalid appointment index detected]) + :Show error message\nindicating invalid Appointment index; + else ([else]) + :edit appointment\nin the appointment list; + :Update the 'appointment' panel\nin the GUI; + :Show success message\nwith edited appointment information; + endif; + endif +endif + +stop +@enduml diff --git a/docs/diagrams/EditAppointmentSequenceDiagram.puml b/docs/diagrams/EditAppointmentSequenceDiagram.puml new file mode 100644 index 00000000000..02e383cee56 --- /dev/null +++ b/docs/diagrams/EditAppointmentSequenceDiagram.puml @@ -0,0 +1,65 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":EditAppointmentCommandParser" as EditAppointmentCommandParser LOGIC_COLOR +participant "e:EditAppointmentCommand" as EditAppointmentCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("editappt i/...") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("editappt i/...") +activate AddressBookParser + +create EditAppointmentCommandParser +AddressBookParser -> EditAppointmentCommandParser +activate EditAppointmentCommandParser + +create EditAppointmentCommand +EditAppointmentCommandParser -> EditAppointmentCommand : : parse("editappt i/...") +activate EditAppointmentCommand + +EditAppointmentCommand --> EditAppointmentCommandParser +deactivate EditAppointmentCommand + +EditAppointmentCommandParser --> AddressBookParser +deactivate EditAppointmentCommandParser + +'Hidden arrow to position the destroy marker below the end of the activation bar. +EditAppointmentCommandParser -[hidden]-> AddressBookParser +destroy EditAppointmentCommandParser + +AddressBookParser --> LogicManager +deactivate AddressBookParser + +LogicManager -> EditAppointmentCommand : execute() +activate EditAppointmentCommand + +EditAppointmentCommand -> Model : setAppointment(toEdit) +activate Model + +Model --> EditAppointmentCommand +deactivate Model + +create CommandResult +EditAppointmentCommand -> CommandResult +activate CommandResult + +CommandResult --> EditAppointmentCommand : result +deactivate CommandResult + +EditAppointmentCommand --> LogicManager : result +deactivate EditAppointmentCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditPersonActivityDiagram.puml b/docs/diagrams/EditPersonActivityDiagram.puml new file mode 100644 index 00000000000..5611c189610 --- /dev/null +++ b/docs/diagrams/EditPersonActivityDiagram.puml @@ -0,0 +1,26 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 + +start +:User enters command to edit doctor or patient; + +if () then ([command is invalid]) + :Show error message\nfor invalid command; +else ([else]) + if () then ([missing required fields]) + :Show error message\nfor missing required fields; + else ([else]) + if () then ([Invalid person index detected]) + :Show error message\nindicating invalid Person index; + else ([else]) + :Edit patient/doctor\nfrom the persons list\nalso updates any appointments\nassociated with the edited patient/doctor; + :Update the 'person' panel\nand appointments panel\n in the GUI; + :Show success message\nwith edited doctor/patient information; + endif; + endif +endif + +stop +@enduml diff --git a/docs/diagrams/EditPersonSequenceDiagram.puml b/docs/diagrams/EditPersonSequenceDiagram.puml new file mode 100644 index 00000000000..3febefb3602 --- /dev/null +++ b/docs/diagrams/EditPersonSequenceDiagram.puml @@ -0,0 +1,65 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":EditCommandParser" as EditCommandParser LOGIC_COLOR +participant "e:EditCommand" as EditCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("edit i/...") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("edit i/...") +activate AddressBookParser + +create EditCommandParser +AddressBookParser -> EditCommandParser +activate EditCommandParser + +create EditCommand +EditCommandParser -> EditCommand : : parse("edit i/...") +activate EditCommand + +EditCommand --> EditCommandParser +deactivate EditCommand + +EditCommandParser --> AddressBookParser +deactivate EditCommandParser + +'Hidden arrow to position the destroy marker below the end of the activation bar. +EditCommandParser -[hidden]-> AddressBookParser +destroy EditCommandParser + +AddressBookParser --> LogicManager +deactivate AddressBookParser + +LogicManager -> EditCommand : execute() +activate EditCommand + +EditCommand -> Model : setPerson(toEdit) +activate Model + +Model --> EditCommand +deactivate Model + +create CommandResult +EditCommand -> CommandResult +activate CommandResult + +CommandResult --> EditCommand : result +deactivate CommandResult + +EditCommand --> LogicManager : result +deactivate EditCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FindActivityDiagram.puml b/docs/diagrams/FindActivityDiagram.puml new file mode 100644 index 00000000000..58e61c4c62e --- /dev/null +++ b/docs/diagrams/FindActivityDiagram.puml @@ -0,0 +1,20 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 + +start +:User enters command to query a person,\nthe person can be either a doctor or patient.; + + +if () then ([missing required fields]) + :Show error message\nfor missing required fields; +else ([else]) + :Search the person from person list; + :Update the 'person' panel\nin the GUI to display the list; + :Show success message\nwith found person(s) information; +endif + + +stop +@enduml diff --git a/docs/diagrams/FindPersonSequenceDiagram.puml b/docs/diagrams/FindPersonSequenceDiagram.puml new file mode 100644 index 00000000000..1d1c497b8d6 --- /dev/null +++ b/docs/diagrams/FindPersonSequenceDiagram.puml @@ -0,0 +1,65 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR +participant "e:FindCommand" as FindCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("find ...") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("find ...") +activate AddressBookParser + +create FindCommandParser +AddressBookParser -> FindCommandParser +activate FindCommandParser + +create FindCommand +FindCommandParser -> FindCommand : : parse("find ...") +activate FindCommand + +FindCommand --> FindCommandParser +deactivate FindCommand + +FindCommandParser --> AddressBookParser +deactivate FindCommandParser + +'Hidden arrow to position the destroy marker below the end of the activation bar. +FindCommandParser -[hidden]-> AddressBookParser +destroy FindCommandParser + +AddressBookParser --> LogicManager +deactivate AddressBookParser + +LogicManager -> FindCommand : execute() +activate FindCommand + +FindCommand -> Model : find(person) +activate Model + +Model --> FindCommand +deactivate Model + +create CommandResult +FindCommand -> CommandResult +activate CommandResult + +CommandResult --> FindCommand : result +deactivate CommandResult + +FindCommand --> LogicManager : result +deactivate FindCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/QueryDoctorActivityDiagram.puml b/docs/diagrams/QueryDoctorActivityDiagram.puml new file mode 100644 index 00000000000..d9ab467d880 --- /dev/null +++ b/docs/diagrams/QueryDoctorActivityDiagram.puml @@ -0,0 +1,20 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 + +start +:User enters command to query doctor(s); + + +if () then ([missing required fields]) + :Show error message\nfor missing required fields; +else ([else]) + :Search doctor(s) from person list; + :Update the 'person' panel\nin the GUI to display the list; + :Show success message\nwith queried doctor(s) information; +endif + + +stop +@enduml diff --git a/docs/diagrams/QueryDoctorAppointmentActivityDiagram.puml b/docs/diagrams/QueryDoctorAppointmentActivityDiagram.puml new file mode 100644 index 00000000000..c61fd3323e7 --- /dev/null +++ b/docs/diagrams/QueryDoctorAppointmentActivityDiagram.puml @@ -0,0 +1,20 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 + +start +:User enters command to query appointment with the associated doctor; + + +if () then ([missing required fields]) + :Show error message\nfor missing required fields; +else ([else]) + :Search Appointments(s) from person list; + :Update the 'appointment' panel\nin the GUI to display the list; + :Show success message\nwith queried appointment information; +endif + + +stop +@enduml diff --git a/docs/diagrams/QueryPatientActivityDiagram.puml b/docs/diagrams/QueryPatientActivityDiagram.puml new file mode 100644 index 00000000000..813328b9bc5 --- /dev/null +++ b/docs/diagrams/QueryPatientActivityDiagram.puml @@ -0,0 +1,20 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 + +start +:User enters command to query patient(s); + + +if () then ([missing required fields]) + :Show error message\nfor missing required fields; +else ([else]) + :Search patient(s) from person list; + :Update the 'person' panel\nin the GUI to display the list; + :Show success message\nwith queried patient(s) information; +endif + + +stop +@enduml diff --git a/docs/diagrams/QueryPatientAppointmentActivityDiagram.puml b/docs/diagrams/QueryPatientAppointmentActivityDiagram.puml new file mode 100644 index 00000000000..b57083e3ae4 --- /dev/null +++ b/docs/diagrams/QueryPatientAppointmentActivityDiagram.puml @@ -0,0 +1,20 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 + +start +:User enters command to query appointments with associated patient; + + +if () then ([missing required fields]) + :Show error message\nfor missing required fields; +else ([else]) + :Search Appointment(s) from appointment list; + :Update the 'appointment' panel\nin the GUI to display the list; + :Show success message\nwith queried appointment information; +endif + + +stop +@enduml diff --git a/docs/images/EditAppointmentActivityDiagram.png b/docs/images/EditAppointmentActivityDiagram.png new file mode 100644 index 00000000000..0d614dfee6f Binary files /dev/null and b/docs/images/EditAppointmentActivityDiagram.png differ diff --git a/docs/images/EditAppointmentSequenceDiagram.png b/docs/images/EditAppointmentSequenceDiagram.png new file mode 100644 index 00000000000..b62244b81dc Binary files /dev/null and b/docs/images/EditAppointmentSequenceDiagram.png differ diff --git a/docs/images/EditPersonActivityDiagram.png b/docs/images/EditPersonActivityDiagram.png new file mode 100644 index 00000000000..9b343328aeb Binary files /dev/null and b/docs/images/EditPersonActivityDiagram.png differ diff --git a/docs/images/EditPersonSequenceDiagram.png b/docs/images/EditPersonSequenceDiagram.png new file mode 100644 index 00000000000..d7cc00f6833 Binary files /dev/null and b/docs/images/EditPersonSequenceDiagram.png differ diff --git a/docs/images/FindActivityDiagram.png b/docs/images/FindActivityDiagram.png new file mode 100644 index 00000000000..7bacc37593f Binary files /dev/null and b/docs/images/FindActivityDiagram.png differ diff --git a/docs/images/FindPersonSequenceDiagram.png b/docs/images/FindPersonSequenceDiagram.png new file mode 100644 index 00000000000..f11a348a14d Binary files /dev/null and b/docs/images/FindPersonSequenceDiagram.png differ diff --git a/docs/images/GUI.png b/docs/images/GUI.png new file mode 100644 index 00000000000..94e4a71ff05 Binary files /dev/null and b/docs/images/GUI.png differ diff --git a/docs/images/InitialState.png b/docs/images/InitialState.png new file mode 100644 index 00000000000..5186d193473 Binary files /dev/null and b/docs/images/InitialState.png differ diff --git a/docs/images/QueryDoctorActivityDiagram.png b/docs/images/QueryDoctorActivityDiagram.png new file mode 100644 index 00000000000..627fa0d0c79 Binary files /dev/null and b/docs/images/QueryDoctorActivityDiagram.png differ diff --git a/docs/images/QueryDoctorAppointmentActivityDiagram.png b/docs/images/QueryDoctorAppointmentActivityDiagram.png new file mode 100644 index 00000000000..0e0ad807f9a Binary files /dev/null and b/docs/images/QueryDoctorAppointmentActivityDiagram.png differ diff --git a/docs/images/QueryPatientActivityDiagram.png b/docs/images/QueryPatientActivityDiagram.png new file mode 100644 index 00000000000..11efa4398b2 Binary files /dev/null and b/docs/images/QueryPatientActivityDiagram.png differ diff --git a/docs/images/QueryPatientAppointmentActivityDiagram.png b/docs/images/QueryPatientAppointmentActivityDiagram.png new file mode 100644 index 00000000000..b6687d568ac Binary files /dev/null and b/docs/images/QueryPatientAppointmentActivityDiagram.png differ diff --git a/docs/images/WindowsStartup.png b/docs/images/WindowsStartup.png new file mode 100644 index 00000000000..7d88d37e5fc Binary files /dev/null and b/docs/images/WindowsStartup.png differ diff --git a/docs/images/addAppointment.png b/docs/images/addAppointment.png index c7e71975d8b..87752eeb579 100644 Binary files a/docs/images/addAppointment.png and b/docs/images/addAppointment.png differ diff --git a/docs/images/addDoctor.png b/docs/images/addDoctor.png index 0b647b0a014..a775b0ba894 100644 Binary files a/docs/images/addDoctor.png and b/docs/images/addDoctor.png differ diff --git a/docs/images/addPatient.png b/docs/images/addPatient.png index 99118a35ec5..01c07a2fff4 100644 Binary files a/docs/images/addPatient.png and b/docs/images/addPatient.png differ diff --git a/docs/images/clear.png b/docs/images/clear.png new file mode 100644 index 00000000000..5ae8c9a0066 Binary files /dev/null and b/docs/images/clear.png differ diff --git a/docs/images/cli_format.png b/docs/images/cli_format.png new file mode 100644 index 00000000000..73351fe716e Binary files /dev/null and b/docs/images/cli_format.png differ diff --git a/docs/images/deleteApptFinalState.png b/docs/images/deleteApptFinalState.png index ba87c3837f0..a08b804ff3e 100644 Binary files a/docs/images/deleteApptFinalState.png and b/docs/images/deleteApptFinalState.png differ diff --git a/docs/images/deleteApptInitialState.png b/docs/images/deleteApptInitialState.png index 07fcfa3e313..69699e07a77 100644 Binary files a/docs/images/deleteApptInitialState.png and b/docs/images/deleteApptInitialState.png differ diff --git a/docs/images/deletePatient.png b/docs/images/deletePatient.png deleted file mode 100644 index 100965baca3..00000000000 Binary files a/docs/images/deletePatient.png and /dev/null differ diff --git a/docs/images/deletePerson.png b/docs/images/deletePerson.png new file mode 100644 index 00000000000..b9261670c20 Binary files /dev/null and b/docs/images/deletePerson.png differ diff --git a/docs/images/editAppointment.png b/docs/images/editAppointment.png index ec30d4b353c..f5bcdf51f6c 100644 Binary files a/docs/images/editAppointment.png and b/docs/images/editAppointment.png differ diff --git a/docs/images/editPerson.png b/docs/images/editPerson.png index 226198acea9..189b2db43eb 100644 Binary files a/docs/images/editPerson.png and b/docs/images/editPerson.png differ diff --git a/docs/images/findAppointmentInitialDoctor.png b/docs/images/findAppointmentInitialDoctor.png index e59f1223575..5b641f46782 100644 Binary files a/docs/images/findAppointmentInitialDoctor.png and b/docs/images/findAppointmentInitialDoctor.png differ diff --git a/docs/images/findAppointmentInitialPatient.png b/docs/images/findAppointmentInitialPatient.png index 59ed0e3d5b2..6cd9b331957 100644 Binary files a/docs/images/findAppointmentInitialPatient.png and b/docs/images/findAppointmentInitialPatient.png differ diff --git a/docs/images/findAppointmentResultDoctor.png b/docs/images/findAppointmentResultDoctor.png index 96047f478d4..26aeabd94d4 100644 Binary files a/docs/images/findAppointmentResultDoctor.png and b/docs/images/findAppointmentResultDoctor.png differ diff --git a/docs/images/findAppointmentResultPatient.png b/docs/images/findAppointmentResultPatient.png index c36b0c0752b..62b2f9b43a1 100644 Binary files a/docs/images/findAppointmentResultPatient.png and b/docs/images/findAppointmentResultPatient.png differ diff --git a/docs/images/findDoctor.png b/docs/images/findDoctor.png new file mode 100644 index 00000000000..1f9d194c1ea Binary files /dev/null and b/docs/images/findDoctor.png differ diff --git a/docs/images/findPatient.png b/docs/images/findPatient.png new file mode 100644 index 00000000000..eaaf0fb3118 Binary files /dev/null and b/docs/images/findPatient.png differ diff --git a/docs/images/macOSStartup.png b/docs/images/macOSStartup.png new file mode 100644 index 00000000000..9739e394f30 Binary files /dev/null and b/docs/images/macOSStartup.png differ diff --git a/docs/images/medicli_logo.png b/docs/images/medicli_logo.png new file mode 100644 index 00000000000..ce6be66f767 Binary files /dev/null and b/docs/images/medicli_logo.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..8494ebd01cb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,6 +1,6 @@ --- layout: page -title: AddressBook Level-3 +title: MediCLI --- [![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) @@ -8,10 +8,10 @@ title: AddressBook Level-3 ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**MediCLI is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +* If you are interested in using MediCLI, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing MediCLI, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/address/commons/core/LogsCenter.java index 8cf8e15a0f0..aa8a6405cfc 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/address/commons/core/LogsCenter.java @@ -20,7 +20,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "medicli.log"; private static final Logger logger; // logger for this class private static Logger baseLogger; // to be used as the parent of all other loggers created by this class. private static Level currentLogLevel = Level.INFO; diff --git a/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java b/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java index 6c59a254fee..9b7008c606d 100644 --- a/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddAppointmentCommand.java @@ -23,7 +23,7 @@ public class AddAppointmentCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an appointment to the MediCLI system.\n" + "Parameters: " - + PREFIX_DATE + "DATE " + + PREFIX_DATE + "DATE-TIME " + PREFIX_DOCTORNRIC + "DOCTOR NRIC " + PREFIX_PATIENTNRIC + "PATIENT NRIC\n" + "Example: " + COMMAND_WORD + " " diff --git a/src/main/java/seedu/address/logic/commands/AddDoctorCommand.java b/src/main/java/seedu/address/logic/commands/AddDoctorCommand.java index f9a07c952b0..1561a9e2c0a 100644 --- a/src/main/java/seedu/address/logic/commands/AddDoctorCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddDoctorCommand.java @@ -6,6 +6,11 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_NRIC; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; +import java.util.logging.Level; +import java.util.logging.Logger; + +import seedu.address.Main; +import seedu.address.commons.core.LogsCenter; import seedu.address.commons.util.ToStringBuilder; import seedu.address.logic.Messages; import seedu.address.logic.commands.exceptions.CommandException; @@ -33,6 +38,7 @@ public class AddDoctorCommand extends Command { public static final String MESSAGE_SUCCESS = "New doctor added: %1$s"; public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; + private static Logger logger = LogsCenter.getLogger(Main.class); private final Doctor toAdd; @@ -49,6 +55,7 @@ public CommandResult execute(Model model) throws CommandException { requireNonNull(model); if (model.hasPerson(toAdd)) { + logger.log(Level.INFO, "Duplicate person detected! (when executing command: adddoctor)"); throw new CommandException(MESSAGE_DUPLICATE_PERSON); } diff --git a/src/main/java/seedu/address/logic/commands/DeleteAppointmentCommand.java b/src/main/java/seedu/address/logic/commands/DeleteAppointmentCommand.java index 7f50d2093df..5235d2e550f 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteAppointmentCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteAppointmentCommand.java @@ -3,14 +3,17 @@ import static java.util.Objects.requireNonNull; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import seedu.address.Main; +import seedu.address.commons.core.LogsCenter; import seedu.address.commons.core.index.Index; import seedu.address.commons.util.ToStringBuilder; import seedu.address.logic.Messages; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; import seedu.address.model.appointment.Appointment; - /** * Deletes an appointment identified using it's displayed index from mediCLI. */ @@ -24,6 +27,7 @@ public class DeleteAppointmentCommand extends Command { + "Example: " + COMMAND_WORD + " 1"; public static final String MESSAGE_DELETE_APPOINTMENT_SUCCESS = "Deleted Appointment: %1$s"; + private static Logger logger = LogsCenter.getLogger(Main.class); private final Index targetIndex; @@ -38,6 +42,7 @@ public CommandResult execute(Model model) throws CommandException { // Check for valid index if (targetIndex.getZeroBased() >= lastShownList.size()) { + logger.log(Level.INFO, "Specified index is not valid! (when executing command: deleteappt)"); throw new CommandException(Messages.MESSAGE_INVALID_APPOINTMENT_DISPLAYED_INDEX); } @@ -46,7 +51,7 @@ public CommandResult execute(Model model) throws CommandException { model.deleteAppointment(appointmentToDelete); return new CommandResult(String.format(MESSAGE_DELETE_APPOINTMENT_SUCCESS, - Messages.format(appointmentToDelete))); + Messages.format(appointmentToDelete))); } @Override diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java index f287f5c3f63..db8f04ef350 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java @@ -3,7 +3,11 @@ import static java.util.Objects.requireNonNull; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import seedu.address.Main; +import seedu.address.commons.core.LogsCenter; import seedu.address.commons.core.index.Index; import seedu.address.commons.util.ToStringBuilder; import seedu.address.logic.Messages; @@ -27,6 +31,7 @@ public class DeleteCommand extends Command { public static final String MESSAGE_DELETE_PATIENT_SUCCESS = "Deleted Patient: %1$s"; public static final String MESSAGE_DELETE_DOCTOR_SUCCESS = "Deleted Doctor: %1$s"; + private static Logger logger = LogsCenter.getLogger(Main.class); private final Index targetIndex; @@ -40,6 +45,7 @@ public CommandResult execute(Model model) throws CommandException { List lastShownList = model.getFilteredPersonList(); if (targetIndex.getZeroBased() >= lastShownList.size()) { + logger.log(Level.INFO, "Specified index is not valid! (when executing command: delete)"); throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index a12b4484978..8375855ad64 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -31,8 +31,6 @@ import seedu.address.model.person.Phone; - - /** * Editcommand class enables user to edit a doctor or patient in the person list */ diff --git a/src/main/java/seedu/address/logic/commands/QueryDoctorAppointmentCommand.java b/src/main/java/seedu/address/logic/commands/QueryDoctorAppointmentCommand.java index 5d055a94854..54265b9f6f6 100644 --- a/src/main/java/seedu/address/logic/commands/QueryDoctorAppointmentCommand.java +++ b/src/main/java/seedu/address/logic/commands/QueryDoctorAppointmentCommand.java @@ -22,7 +22,8 @@ public class QueryDoctorAppointmentCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all appointments of doctors whose " + "nrics contain any of the specified keywords (case-insensitive) and displays them as a " + "list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...(Keywords can either be NRICs or Names)\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...(Keywords have to be the " + + "exact NRICs of the doctor(s) in question)\n" + "Example: " + COMMAND_WORD + " alice bob T1234567A S7654321A"; private final AppointmentContainsDoctorPredicate predicate; diff --git a/src/main/java/seedu/address/logic/commands/QueryPatientAppointmentCommand.java b/src/main/java/seedu/address/logic/commands/QueryPatientAppointmentCommand.java index f9cbd1f5b5d..878480064ac 100644 --- a/src/main/java/seedu/address/logic/commands/QueryPatientAppointmentCommand.java +++ b/src/main/java/seedu/address/logic/commands/QueryPatientAppointmentCommand.java @@ -18,7 +18,8 @@ public class QueryPatientAppointmentCommand extends Command { public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all appointments of patients whose " + "nrics contain any of the specified keywords (case-insensitive) and displays them as a " + "list with index numbers.\n" - + "Parameters: KEYWORD [MORE_KEYWORDS]...(Keywords can either be NRICs or Names)\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...(Keywords have to be the " + + "exact NRICs of the patient(s) in question)\n" + "Example: " + COMMAND_WORD + " alice bob T1234567A S7654321A"; private final AppointmentContainsPatientPredicate predicate; diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java deleted file mode 100644 index 23bfcae405e..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -//package seedu.address.logic.parser; -// -//import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -//import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -//import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -//import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -//import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -//import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -// -//import java.util.Set; -//import java.util.stream.Stream; -// -//import seedu.address.logic.commands.AddCommand; -//import seedu.address.logic.parser.exceptions.ParseException; -//import seedu.address.model.person.Address; -//import seedu.address.model.person.Email; -//import seedu.address.model.person.Name; -//import seedu.address.model.person.Phone; -//import seedu.address.model.tag.Tag; -// -///** -// * Parses input arguments and creates a new AddCommand object -// */ -//public class AddCommandParser implements Parser { -// -// /** -// * Parses the given {@code String} of arguments in the context of the AddCommand -// * and returns an AddCommand object for execution. -// * @throws ParseException if the user input does not conform the expected format -// */ -// public AddCommand parse(String args) throws ParseException { -// ArgumentMultimap argMultimap = -// ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); -// -// if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) -// || !argMultimap.getPreamble().isEmpty()) { -// throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); -// } -// -// argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); -// Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); -// Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); -// Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); -// Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); -// Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); -// -// // Person person = new Person(name, phone, email, address, tagList); -// -// return new AddCommand(null); -// } -// -// /** -// * Returns true if none of the prefixes contains empty {@code Optional} values in the given -// * {@code ArgumentMultimap}. -// */ -// private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { -// return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); -// } -// -//} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 6dbce5ff313..e37824284af 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -14,7 +14,6 @@ public class CliSyntax { public static final Prefix PREFIX_EMAIL = new Prefix("e/"); public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); public static final Prefix PREFIX_DATE = new Prefix("ad/"); public static final Prefix PREFIX_PATIENTNRIC = new Prefix("pn/"); diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java index fc05e994d8a..fc49a617b46 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -6,7 +6,6 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_NRIC; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.EditCommand; @@ -26,7 +25,7 @@ public class EditCommandParser implements Parser { public EditCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_NRIC, PREFIX_DOB, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_NRIC, PREFIX_DOB); Index index; diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index 14c337e9e35..fbb8b4f084d 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -2,10 +2,6 @@ import static java.util.Objects.requireNonNull; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; @@ -16,7 +12,6 @@ import seedu.address.model.person.Name; import seedu.address.model.person.Nric; import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; /** * Contains utility methods used for parsing strings in the various *Parser classes. @@ -152,33 +147,6 @@ public static Email parseEmail(String email) throws ParseException { return new Email(trimmedEmail); } - /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code tag} is invalid. - */ - public static Tag parseTag(String tag) throws ParseException { - requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(trimmedTag); - } - - /** - * Parses {@code Collection tags} into a {@code Set}. - */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); - } - return tagSet; - } - /** * Parses AppointmentDate from string to return an AppointmentDate object * @param apptDateTime String to parse diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 6be655fb4c7..20036a239bc 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path addressBookFilePath = Paths.get("data" , "medicli.json"); /** * Creates a {@code UserPrefs} with default values. diff --git a/src/main/java/seedu/address/model/appointment/Appointment.java b/src/main/java/seedu/address/model/appointment/Appointment.java index 0225c4055fd..ff73471f87c 100644 --- a/src/main/java/seedu/address/model/appointment/Appointment.java +++ b/src/main/java/seedu/address/model/appointment/Appointment.java @@ -17,7 +17,7 @@ public class Appointment { private static final String MESSAGE_CONSTRAINTS_INVALID_DATE = - "Appointment should be made with a date today onwards"; + "Appointment should be made with a date-time today onwards"; // The doctor in charge of the appointment private Nric doctorNric; diff --git a/src/main/java/seedu/address/model/appointment/AppointmentDateTime.java b/src/main/java/seedu/address/model/appointment/AppointmentDateTime.java index b3eb74f5f5c..d4371ea1cd6 100644 --- a/src/main/java/seedu/address/model/appointment/AppointmentDateTime.java +++ b/src/main/java/seedu/address/model/appointment/AppointmentDateTime.java @@ -14,7 +14,7 @@ public class AppointmentDateTime { // Message to output in case constraints are not met public static final String MESSAGE_CONSTRAINTS = - "Appointment date should be in the format of yyyy-MM-dd HH:mm."; + "Appointment date-time should be in the format of yyyy-MM-dd HH:mm."; // Variable storing appointment date in a local datetime instance public final LocalDateTime appointmentDateTime; @@ -48,8 +48,6 @@ public static boolean isValidDate(String dateStr) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); try { LocalDateTime temp = LocalDateTime.parse(dateStr, formatter); - //LocalDate today = LocalDate.now(); - //return temp.isAfter(today); } catch (DateTimeParseException e) { return false; } diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java deleted file mode 100644 index f1a0d4e233b..00000000000 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.address.model.tag; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} - */ -public class Tag { - - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; - - public final String tagName; - - /** - * Constructs a {@code Tag}. - * - * @param tagName A valid tag name. - */ - public Tag(String tagName) { - requireNonNull(tagName); - checkArgument(isValidTagName(tagName), MESSAGE_CONSTRAINTS); - this.tagName = tagName; - } - - /** - * Returns true if a given string is a valid tag name. - */ - public static boolean isValidTagName(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof Tag)) { - return false; - } - - Tag otherTag = (Tag) other; - return tagName.equals(otherTag.tagName); - } - - @Override - public int hashCode() { - return tagName.hashCode(); - } - - /** - * Format state as text for viewing. - */ - public String toString() { - return '[' + tagName + ']'; - } - -} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 27cee70e443..c1e060f7356 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -1,9 +1,5 @@ package seedu.address.model.util; -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.person.DoB; @@ -12,7 +8,6 @@ import seedu.address.model.person.Patient; import seedu.address.model.person.Person; import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; /** * Contains utility methods for populating {@code AddressBook} with sample data. @@ -51,13 +46,4 @@ public static ReadOnlyAddressBook getSampleAddressBook() { return sampleAb; } - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java index fa386d49d54..675058e93d4 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java @@ -1,8 +1,5 @@ package seedu.address.storage; -import java.util.ArrayList; -import java.util.List; - import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; @@ -27,7 +24,6 @@ class JsonAdaptedPerson { private final String name; private final String dob; private final String phone; - private final List tags = new ArrayList<>(); /** * Constructs a {@code JsonAdaptedPerson} with the given person details. diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/address/storage/JsonAdaptedTag.java deleted file mode 100644 index 0df22bdb754..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ /dev/null @@ -1,48 +0,0 @@ -package seedu.address.storage; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Tag}. - */ -class JsonAdaptedTag { - - private final String tagName; - - /** - * Constructs a {@code JsonAdaptedTag} with the given {@code tagName}. - */ - @JsonCreator - public JsonAdaptedTag(String tagName) { - this.tagName = tagName; - } - - /** - * Converts a given {@code Tag} into this class for Jackson use. - */ - public JsonAdaptedTag(Tag source) { - tagName = source.tagName; - } - - @JsonValue - public String getTagName() { - return tagName; - } - - /** - * Converts this Jackson-friendly adapted tag object into the model's {@code Tag} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted tag. - */ - public Tag toModelType() throws IllegalValueException { - if (!Tag.isValidTagName(tagName)) { - throw new IllegalValueException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(tagName); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java index 88b20e7cdee..2fbcea72f1e 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java @@ -8,7 +8,6 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; import static seedu.address.logic.parser.CliSyntax.PREFIX_NRIC; import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import static seedu.address.testutil.Assert.assertThrows; import java.text.SimpleDateFormat; @@ -59,20 +58,14 @@ public class CommandTestUtil { public static final String DOB_DESC_BOB = " " + PREFIX_DOB + VALID_DOB_BOB; public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB; - public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; - public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; - public static final String INVALID_NRIC_DESC = " " + PREFIX_NRIC + "A0983"; public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names public static final String INVALID_DOB_DESC = " " + PREFIX_DOB + "22-03-2009"; public static final String INVALID_PHONE_DESC = " " + PREFIX_PHONE + "911a"; // 'a' not allowed in phones public static final String INVALID_EMAIL_DESC = " " + PREFIX_EMAIL + "bob!yahoo"; // missing '@' symbol public static final String INVALID_ADDRESS_DESC = " " + PREFIX_ADDRESS; // empty string not allowed for addresses - public static final String INVALID_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags - public static final String PREAMBLE_WHITESPACE = "\t \r \n"; public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; - public static final EditCommand.EditPersonDescriptor DESC_AMY; public static final EditCommand.EditPersonDescriptor DESC_BOB; diff --git a/src/test/java/seedu/address/logic/parser/AddDoctorCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddDoctorCommandParserTest.java index 792b2782e06..e79872f0ed0 100644 --- a/src/test/java/seedu/address/logic/parser/AddDoctorCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddDoctorCommandParserTest.java @@ -14,9 +14,6 @@ import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; -//import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; import static seedu.address.logic.commands.CommandTestUtil.VALID_DOB_BOB; import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; import static seedu.address.logic.commands.CommandTestUtil.VALID_NRIC_BOB; @@ -153,8 +150,8 @@ public void parse_invalidValue_failure() { DoB.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, NRIC_DESC_BOB + NAME_DESC_BOB + DOB_DESC_BOB + INVALID_PHONE_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, NRIC_DESC_BOB + NAME_DESC_BOB + DOB_DESC_BOB + INVALID_PHONE_DESC, + Phone.MESSAGE_CONSTRAINTS); // two invalid values, only first invalid value reported assertParseFailure(parser, INVALID_NRIC_DESC + NAME_DESC_BOB + DOB_DESC_BOB + INVALID_PHONE_DESC, diff --git a/src/test/java/seedu/address/logic/parser/AddPatientCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddPatientCommandParserTest.java index bc92c368afd..d9b329c5bed 100644 --- a/src/test/java/seedu/address/logic/parser/AddPatientCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddPatientCommandParserTest.java @@ -14,9 +14,6 @@ import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; -//import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; import static seedu.address.logic.commands.CommandTestUtil.VALID_DOB_BOB; import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; import static seedu.address.logic.commands.CommandTestUtil.VALID_NRIC_BOB; @@ -153,8 +150,8 @@ public void parse_invalidValue_failure() { DoB.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, NRIC_DESC_BOB + NAME_DESC_BOB + DOB_DESC_BOB + INVALID_PHONE_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, NRIC_DESC_BOB + NAME_DESC_BOB + DOB_DESC_BOB + INVALID_PHONE_DESC, + Phone.MESSAGE_CONSTRAINTS); // two invalid values, only first invalid value reported assertParseFailure(parser, INVALID_NRIC_DESC + NAME_DESC_BOB + DOB_DESC_BOB + INVALID_PHONE_DESC, diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java index 7f96a0fa79d..937fa85679e 100644 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java @@ -1,16 +1,10 @@ package seedu.address.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; import static seedu.address.testutil.Assert.assertThrows; import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - import org.junit.jupiter.api.Test; import seedu.address.logic.parser.exceptions.ParseException; @@ -19,7 +13,6 @@ import seedu.address.model.person.Email; import seedu.address.model.person.Name; import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; public class ParserUtilTest { private static final String INVALID_NAME = "R@chel"; @@ -149,52 +142,6 @@ public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exc assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace)); } - @Test - public void parseTag_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); - } - - @Test - public void parseTag_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); - } - - @Test - public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); - } - - @Test - public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { - String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); - } - - @Test - public void parseTags_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); - } - - @Test - public void parseTags_collectionWithInvalidTags_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); - } - - @Test - public void parseTags_emptyCollection_returnsEmptySet() throws Exception { - assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); - } - - @Test - public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { - Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); - Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); - - assertEquals(expectedTagSet, actualTagSet); - } - @Test public void parseAppointmentDate_validDate_returnsAppointmentDate() throws ParseException { String date = "2024-09-02 11:02"; diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/address/model/tag/TagTest.java deleted file mode 100644 index 64d07d79ee2..00000000000 --- a/src/test/java/seedu/address/model/tag/TagTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package seedu.address.model.tag; - -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class TagTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Tag(null)); - } - - @Test - public void constructor_invalidTagName_throwsIllegalArgumentException() { - String invalidTagName = ""; - assertThrows(IllegalArgumentException.class, () -> new Tag(invalidTagName)); - } - - @Test - public void isValidTagName() { - // null tag name - assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null)); - } - -}