diff --git a/.gitignore b/.gitignore index 284c4ca7cd9..53f76273993 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ src/test/data/sandbox/ # MacOS custom attributes files created by Finder .DS_Store docs/_site/ + +#VSC files +bin/ diff --git a/README.md b/README.md index 13f5c77403f..6c9581b229e 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,17 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +[![CI Status](https://github.com/AY2324S1-CS2103T-W13-4/tp/actions/workflows/gradle.yml/badge.svg)](https://github.com/AY2324S1-CS2103T-W13-4/tp/actions) + +[![codecov](https://codecov.io/gh/AY2324S1-CS2103T-W13-4/tp/graph/badge.svg?token=JF1YGHQMCK)](https://codecov.io/gh/AY2324S1-CS2103T-W13-4/tp) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +# WellNUS + +WellNUS is a contact management application for NUS Student Counsellors. +WellNUS helps counsellors better schedule their appointments with students, as well as store the information of students for easy referencing. + +Example usages: + * adding, deleting and viewing student profiles + * tag students with risk levels depending on their condition + * adding, deleting and viewing appointments + +This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/build.gradle b/build.gradle index a2951cc709e..88092118205 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,7 @@ dependencies { implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' + testImplementation 'org.testfx:testfx-junit:4.0.15-alpha' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.7.0' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.7.4' @@ -66,7 +67,12 @@ dependencies { } shadowJar { - archiveFileName = 'addressbook.jar' + archiveFileName = 'wellnus.jar' } defaultTasks 'clean', 'test' + +run { + enableAssertions = true +} + diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..44a7667fa52 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,55 +5,57 @@ title: About Us We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). -You can reach us at the email `seer[at]comp.nus.edu.sg` +You can reach us at the email `eric_sim@u.nus.edu` ## Project team -### John Doe +### Qu Zhetao - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/quzhetao01)] +[[portfolio](team/quzhetao01.md)] -* Role: Project Advisor +* Role: Developer +* Responsibilities: Storage -### Jane Doe +### Darren Tan Fanyi - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/darrentfy)] +[[portfolio](team/darrentfy.md)] -* Role: Team Lead +* Role: Developer * Responsibilities: UI -### Johnny Doe +### Eric Sim - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/simwperic)] +[[portfolio](team/simwperic.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Architecture -### Jean Doe +### Stephen - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/sheeepen)] +[[portfolio](team/sheeepen.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Model -### James Doe +### Hannah Caroline Solomonraj - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/hcs1203)] +[[portfolio](team/hcs1203.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Model + diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 8a861859bfd..632abbc1527 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -5,28 +5,27 @@ title: Developer Guide * Table of Contents {:toc} --------------------------------------------------------------------------------------------------------------------- - -## **Acknowledgements** +
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +## 1. Introduction +### 1.1 Acknowledgements --------------------------------------------------------------------------------------------------------------------- +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). -## **Setting up, getting started** +### 1.2 Setting up, getting started Refer to the guide [_Setting up and getting started_](SettingUp.md). --------------------------------------------------------------------------------------------------------------------- +
-## **Design** +## 2. Design
:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
-### Architecture +### 2.1 Architecture @@ -34,7 +33,9 @@ The ***Architecture Diagram*** given above explains the high-level design of the Given below is a quick overview of main components and how they interact with each other. -**Main components of the architecture** +
+ +#### 2.1.1 Main components of the architecture **`Main`** (consisting of classes [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java)) is in charge of the app launch and shut down. * At app launch, it initializes the other components in the correct sequence, and connects them up with each other. @@ -42,19 +43,21 @@ Given below is a quick overview of main components and how they interact with ea The bulk of the app's work is done by the following four components: -* [**`UI`**](#ui-component): The UI of the App. -* [**`Logic`**](#logic-component): The command executor. -* [**`Model`**](#model-component): Holds the data of the App in memory. -* [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. +* [**`UI`**](#22-ui-component): The UI of the App. +* [**`Logic`**](#23-logic-component): The command executor. +* [**`Model`**](#24-model-component): Holds the data of the App in memory. +* [**`Storage`**](#25-storage-component): Reads data from, and writes data to, the hard disk. -[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components. +[**`Commons`**](#26-common-classes) represents a collection of classes used by multiple other components. -**How the architecture components interact with each other** +#### 2.1.2 How the architecture components interact with each other The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +
+ Each of the four main components (also shown in the diagram above), * defines its *API* in an `interface` with the same name as the Component. @@ -65,14 +68,15 @@ For example, the `Logic` component defines its API in the `Logic.java` interface The sections below give more details of each component. +
-### UI component +### 2.2 UI component The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `StudentListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) @@ -81,9 +85,11 @@ The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Student` object residing in the `Model`. -### Logic component +
+ +### 2.3 Logic component **API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) @@ -91,29 +97,36 @@ Here's a (partial) class diagram of the `Logic` component: +
+ The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `delete 1` Command](./images/DeleteSequenceDiagram.png)
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it is passed to an `AddressBookParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command. +1. When `Logic` is called upon to execute a command, it is passed to an `WellNusParser` object which in turn creates a parser that matches the command (e.g., `DeleteCommandParser`) and uses it to parse the command. 1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `DeleteCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to delete a person). +1. The command can communicate with the `Model` when it is executed (e.g. to delete a student). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. +
+ Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command: How the parsing works: -* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. +* When called upon to parse a user command, the `WellNusParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `WellNusParser` returns back as a `Command` object. * All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. -### Model component +
+ +### 2.4 Model component + **API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) @@ -121,127 +134,143 @@ How the parsing works: The `Model` component, -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). -* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the wellnus storage data i.e., all `Student` objects (which are contained in a `UniqueStudentList` object). +* stores the currently 'selected' `Student` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. * does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
- - - -
+
- -### Storage component +### 2.5 Storage component **API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) The `Storage` component, -* can save both address book data and user preference data in JSON format, and read them back into corresponding objects. -* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). +* can save both wellnus storage data and user preference data in JSON format, and read them back into corresponding objects. +* inherits from both `WellNusStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`) -### Common classes +### 2.6 Common classes Classes used by multiple components are in the `seedu.addressbook.commons` package. --------------------------------------------------------------------------------------------------------------------- - -## **Implementation** +
-This section describes some noteworthy details on how certain features are implemented. +## 3. WellNus Implementation -### \[Proposed\] Undo/redo feature +This section describes some noteworthy details on how certain features of WellNus are implemented. +### 3.1 Cancelling an Appointment -#### Proposed Implementation +The canceling of an appointment is facilitated by the `CancelCommand`. It extends `Command` and allows the user to cancel an appointment at a specified index. -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("cancel 1")` as an example. -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +![Interactions inside the Logic component for "cancel 1" command](images/CancelSequenceDiagram.png) -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +**Design Considerations: How an Appointment should be canceled** -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +* **Current Choice:** Create a new `CancelCommand` class which handles the cancellation of an appointment. + * Pros: User input will be shorter and easier to read. + * Cons: More work to implement. -Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state. +* **Alternative 1:** Add appointment cancellation functionality to the existing `DeleteCommand`. + * Pros: Easier to implement. + * Cons: User will have to type longer commands since appointment details can be lengthy, which may lead to more complex commands. -![UndoRedoState0](images/UndoRedoState0.png) +### 3.2 Student Notes feature -Step 2. The user executes `delete 5` command to delete the 5th person in the address book. The `delete` command calls `Model#commitAddressBook()`, causing the modified state of the address book after the `delete 5` command executes to be saved in the `addressBookStateList`, and the `currentStatePointer` is shifted to the newly inserted address book state. +The adding of student notes is facilitated by `NoteCommand`. It extends `Command` and allows the addition of a `Note` +to the student at the index specified by the user. -![UndoRedoState1](images/UndoRedoState1.png) +The sequence diagram below illustrates the interactions within the `Logic` component, +taking `execute("note 1 note/Likes dogs.")` as an example. -Step 3. The user executes `add n/David …​` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`. +![Interactions inside the Logic component for "note 1 note/Likes dogs." command](images/NoteSequenceDiagram.png) -![UndoRedoState2](images/UndoRedoState2.png) +**Design Considerations: How a `Note` should be added to a `Student`** -
:information_source: **Note:** If a command fails its execution, it will not call `Model#commitAddressBook()`, so the address book state will not be saved into the `addressBookStateList`. +* **Alternative 1 (current choice):** Create a new `NoteCommand` class which handles the addition of a note + to a `Student`. + * Pros: User input will be shorter in length and easier to read + * Cons: More work to implement -
+* **Alternative 2:** Add `Note` as a field in the `AddCommand` + * Pros: Easier to implement + * Cons: User will have to type much longer commands, since `Note` can be up to 200 characters long, + leads to very lengthy commands -Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state. +
-![UndoRedoState3](images/UndoRedoState3.png) +### 3.3 View Feature -
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather -than attempting to perform the undo. +This feature is facilitated by the use of the ViewCommand class which extends the Command interface. +The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example. +![Interactions between LogicManager and ModelManager for a view command](images/ViewSequenceDiagram.png) +
:information_source: **Note:** The lifeline for `ViewCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
-The following sequence diagram shows how the undo operation works: - -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) - -
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. - +View Command handles both the viewing of all students and all appointments. The workflow is shown below: +![ViewCommand Control Flow](images/ViewActivityDiagram.png) +
:information_source: **Note:** There should be a diamond connecting the 4 separate branches +but due to a limitation of PlantUML, the 4 branches leads to the "end" individually .
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state. +
-
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo. +### 3.4 Check overlapping appointments feature -
+The *check overlapping appointments* mechanism is facilitated by `UniqueAppointmentList`. It implements the `Iterable` +interface that stores an `ObservableList` of type `Appointment`. When an `Appointment` is scheduled, the +`ScheduleCommand#execute(Model model)` method begins the check. + +The following methods are implemented to facilitate the check clashing appointments process: -Step 5. The user then decides to execute the command `list`. Commands that do not modify the address book, such as `list`, will usually not call `Model#commitAddressBook()`, `Model#undoAddressBook()` or `Model#redoAddressBook()`. Thus, the `addressBookStateList` remains unchanged. +* `Model#hasOverlappingAppointments(Appointment toAdd)` —  The Model responsible for adding the appointment (In this case, `WellNUS.java`). +* `UniqueAppointmentList#hasOverlapWith(Appointment toAdd)` —  Checks if the appointment to add overlaps with any of the current appointments in the `UniqueAppointmentList`. +* `Appointments#isOverlappingAppointments(Appointment other)` —  Checks if this `Appointment` overlaps with the given `Appointment` supplied in the parameter. -![UndoRedoState4](images/UndoRedoState4.png) -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +The following sequence diagram shows how the class operates when an appointment is added: -![UndoRedoState5](images/UndoRedoState5.png) +![OverlapSequenceDiagram](images/OverlapSequenceDiagram.png) -The following activity diagram summarizes what happens when a user executes a new command: - +When the user schedules a new command, the `UniqueAppointmentList` runs its `hasOverlappingAppointments()` method to check if the +new appointment clashes with any existing appointments. It raises a `OverlappingAppointmentsException` if the method returns true, +and prevents the user from scheduling that appointment. -#### Design considerations: +
-**Aspect: How undo & redo executes:** +The following activity diagram summarises what happens when a user schedules an apppointment: -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +![ClashActivityDiagram](images/ClashActivityDiagram.png) +
:information_source: **Note:** There should be a diamond connecting the 3 separate branches +but due to a limitation of PlantUML, the 3 branches leads to the "end" individually . +
-* **Alternative 2:** Individual command knows how to undo/redo by - itself. - * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). - * Cons: We must ensure that the implementation of each individual command are correct. +
-_{more aspects and alternatives to be added}_ +## 4. Planned Enhancements -### \[Proposed\] Data archiving +### 4.1 Customisable color for risk levels +The current colors for risk levels of low, medium and high are green, blue and red respectively. +Risk levels are color coded for easy identification by counsellors. +However, users may find the current risk level color contrast hard to see, hence we plan to make the colors customisable to cater for different preferences. -_{Explain here how the data archiving feature will be implemented}_ +### 4.2 Allow name to include special characters +The current parameter constraints for name is name must only contain alphabetical characters and space, be unique up to 100 characters long, and cannot be blank. +However, we do acknowledge the need for special characters in names such as `Arjun S/O Kapoor`, hence we plan to include special characters to cater for diverse student names. +### 4.3 Command to view notes +The current method of viewing notes requires double-clicking on the `StudentCard` which reflects a GUI approach. To cater to users who prefer the CLI approach, +we plan to introduce a command that achieves the above equivalent function. -------------------------------------------------------------------------------------------------------------------- -## **Documentation, logging, testing, configuration, dev-ops** +## 5. Documentation, logging, testing, configuration, dev-ops * [Documentation guide](Documentation.md) * [Testing guide](Testing.md) @@ -249,83 +278,312 @@ _{Explain here how the data archiving feature will be implemented}_ * [Configuration guide](Configuration.md) * [DevOps guide](DevOps.md) --------------------------------------------------------------------------------------------------------------------- +
-## **Appendix: Requirements** +## 6. Appendix: Requirements -### Product scope +### 6.1 Product scope **Target user profile**: -* has a need to manage a significant number of contacts +* NUS Student Counsellors +* has a need to manage a significant number of students +* need to manage student details as well as appointments * prefer desktop apps over other types * can type fast * prefers typing to mouse interactions * is reasonably comfortable using CLI apps -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: This product is meant to help the counsellors better schedule their appointments with students faster than a typical mouse/GUI driven app. Users will be able to store details like personal information, appointment dates, number of visits, emergency contacts etc. -### User stories +### 6.2 User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | +| Priority | As a …​ | I want to …​ | So that I can…​ | +|----------|------------|----------------------------------------------|--------------------------------------------------------------------------------------| +| `* * *` | counsellor | add a student profile | keep track of each student's information | +| `* * ` | counsellor | set or edit a student's risk profile | monitor the risk profile of each student | +| `* * *` | counsellor | remove a student profile | remove student in the event they do not require any further consultation | +| `* * *` | counsellor | view a student profile | look up a students relevant information, consultation notes etc. | +| `* * *` | counsellor | view all students as a list | look up all students that require consultation | +| `* *` | counsellor | edit a student's profile | update student particulars should there be any changes | +| `* *` | counsellor | filter list by types of student | look up students in particular categories | +| `* *` | counsellor | find students by name or ID | look up particular students | +| `* *` | counsellor | schedule an appointment | keep track of my appointments in the application | +| `* *` | counsellor | cancel an appointment | remove any appointments that have been cancelled | +| `* *` | counsellor | view my appointments | look through my timetable for the day/week | +| `* *` | counsellor | edit appointment info | plan my schedule accordingly if there are any last-minute changes | +| `* *` | counsellor | sort appointments by date | organise my appointments and plan my timetable accordingly | +| `*` | counsellor | block out busy times | prevent clashes in scheduling | +| `*` | counsellor | prevent double booking | prevent clashes in scheduling | +| `*` | counsellor | link students to the respective appointments | have easy access to the student profile that can help me prepare for the appointment | + +
+ +### 6.3 Use cases + +(For all use cases below, the **System** is `WellNUS` and the **Actor** is the `counsellor`, unless specified otherwise) + +#### 6.3.1 #UC01: Add a student -*{More to be added}* +**MSS** + +1. User requests to list students +2. WellNUS shows the list of students +3. User requests to add a new student to the list +4. WellNUS adds the student, and shows confirmation message + + Use case ends. + +**Extensions** + +* 3a. The student name/contact number already exists. + * 3a1. WellNUS shows an error message. + * Use case ends. + +* 3b. The given name is invalid (non-alphabetical input), contact number is invalid (non-numerical input or not 8 digits long) +or address is invalid (> 200 characters long) + * 3b1. WellNUS shows an error message. + * Use case ends. + +* 3c. The given name, contact number or address is omitted + * 3c1. WellNUS shows an error message. + * Use case ends. + +#### 6.3.2 #UC02: View existing students + +**MSS** + +1. User requests to list students +2. WellNUS shows the list of students +3. User can find student index that can be used for other use cases, eg. edit student info + + Use case ends. + +**Extensions** + +* 2a. The list is empty + * Use case ends. + +#### 6.3.3 #UC03: Edit an existing student + +**MSS** + +1. User requests to list students +2. WellNUS shows the list of students +3. User can find student index +4. Edit user by specifying the index and fields to edit +5. Get confirmation of successful edit + + Use case ends. + +**Extensions** + +* 4a. The student index is invalid. + * 4a1. WellNUS shows an error message. + * Use case resumes from step 3. + +* 4b. No fields to edit were specified + * 4b1. WellNUS shows an error message. + * Use case resumes from step 3. + +* 4c. The contact number is invalid (non-numerical input or not 8 digits long) + or address is invalid (> 200 characters long) + * 4c1. WellNUS shows an error message. + * Use case resumes from step 3. + +#### 6.3.4 #UC04: Delete an existing student + +**MSS** + +1. User requests to list students +2. WellNUS shows the list of students +3. User can find student index +4. Delete user by specifying the index +5. Get confirmation of successful delete + + Use case ends. + +**Extensions** + +* 4a. The student index is invalid. + * 4a1. WellNUS shows an error message. + * Use case resumes from step 3. + +
+ +#### 6.3.5 #UC05: Tag student to risk level + +**MSS** + +1. User requests to list students +2. WellNUS shows the list of students +3. User can find student index +4. Tag/change student risk level using the student index +5. Information gets updated for future reference + + Use case ends. + +**Extensions** + +* 4a. The student index is invalid. + * 4a1. WellNUS shows an error message. + * Use case resumes from step 3. + +* 4b. The risk level is invalid (not high/medium/low) + * 4b1. WellNUS shows an error message. + * Use case resumes from step 3. + +#### 6.3.6 #UC06: Add student notes for a specific student + +**MSS** -### Use cases +1. User requests to list students +2. WellNUS shows the list of students +3. User can find student index +4. Add student note using the student index +5. Information gets updated for future reference -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) + Use case ends. + +**Extensions** + +* 4a. The student index is invalid. + * 4a1. WellNUS shows an error message. + * Use case resumes from step 3. -**Use case: Delete a person** +* 4b. The note is invalid (> 500 characters long) + * 4b1. WellNUS shows an error message. + * Use case resumes from step 3. + +
+ +#### 6.3.7 #UC07: Schedule an appointment **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User requests to list appointments +2. WellNUS shows the list of appointments, along with some basic information like time and student +3. User requests to schedule a new appointment to the list +4. WellNUS adds the appointment +5. WellNUS shows confirmation message + + Use case ends. + +**Extensions** + +* 3a. The student index is invalid. + * 3a1. WellNUS shows an error message. + * Use case resumes from step 3. + +* 3b. The given time is invalid (wrong time format). + * 3b1. WellNUS shows an error message. + * Use case resumes from step 3. + +* 3c. The given time overlaps with an existing appointment. + * 3c1. WellNUS shows an error message. + * Use case resumes from step 3. +#### 6.3.8 #UC08: View existing appointments + +**MSS** + +1. User requests to list appointments +2. WellNUS shows the list of appointments +3. User can view appointment information such as student involved and time of appointment + Use case ends. **Extensions** -* 2a. The list is empty. +* 2a. User unable to view appointment information as list is empty + + Use case ends. + +
+ +#### 6.3.9 #UC09: Cancel an existing appointment + +**MSS** + +1. User requests to list appointments +2. WellNUS shows the list of appointments +3. User cancels an appointment at chosen index +4. WellNUS deletes the appointment with index specified by user and display status + + Use case ends. + +**Extensions** + +* 3a. WellNUS detects an error in the entered index + + Use case resumes from step 3. + +#### 6.3.10 #UC10: Find students by name together with their appointments + +1. User requests to find students using their name +2. WellNUS shows the list of students that match the given name +3. WellNUS shows the appointments of the filtered list of students shown +4. User can view the information of the filtered students and his/her appointments + + Use case ends. + +**Extensions** + +* 4a. User does not see any students and appointments as no students match the given name Use case ends. -* 3a. The given index is invalid. +#### 6.3.11 #UC11: Filter an appointment by date - * 3a1. AddressBook shows an error message. - Use case resumes at step 2. +1. User requests to list appointments for a given date +2. WellNUS shows the list of appointments for the date +3. User can view appointment information for the appointments in the given date -*{More to be added}* + Use case ends. + +
-### Non-Functional Requirements +**Extensions** -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +* 3a. User does not see any appointments as there are no appointments scheduled on that day + + Use case ends. + +* 3b. The given date is invalid (wrong date format). + * 3b1. WellNUS shows an error message. + * Use case resumes from step 1. + +### 6.4 Non-Functional Requirements +1. Cross-Platform Compatibility: + - Should work on any _mainstream OS_ as long as it has Java `11` or above installed. +2. Scalability and Performance: + - Should be able to hold up to 1000 students without a noticeable sluggishness in performance for typical usage. + - Should support efficient data retrieval and manipulation for the specified contact volume. +3. Usability and Efficiency: + - A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should + be able to perform the majority of tasks more quickly using _CLI_ commands compared to using a mouse on the _GUI_. + - The _CLI_ interface should prioritise efficiency by providing clear and concise commands, minimising unnecessary prompts, + and offering time-saving shortcuts. +4. Updates and Maintenance: + - Updates should not disrupt the user's workflow or data. *{More to be added}* -### Glossary +### 6.5 Glossary * **Mainstream OS**: Windows, Linux, Unix, OS-X * **Private contact detail**: A contact detail that is not meant to be shared with others +* **CLI**: Command Line Interface. Usually the in-built terminal or in the IDE the application is run on. +* **GUI**: Graphical User Interface. +* **Risk Level**: Students can be classified as high, medium, or low-risk level determined by the counsellor. --------------------------------------------------------------------------------------------------------------------- +
-## **Appendix: Instructions for manual testing** +## 7. Appendix: Instructions for manual testing Given below are instructions to test the app manually. @@ -334,44 +592,248 @@ testers are expected to do more *exploratory* testing.
-### Launch and shutdown +### 7.1 Launch and shutdown 1. Initial launch 1. Download the jar file and copy into an empty folder - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + 2. Double-click the jar file + + Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. -1. Saving window preferences +2. Saving window preferences 1. Resize the window to an optimum size. Move the window to a different location. Close the window. - 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained. + 2. Re-launch the app by double-clicking the jar file.
+ Expected: The most recent window size and location is retained. + +
+ +### 7.2 Student Features +#### 7.2.1 Add Student +Adds a student with their relevant details. + +Format: `add n/STUDENT_NAME c/CONTACT_NUMBER a/HOME_ADDRESS [r/RISK_LEVEL]` + +1. Use the find feature (9.2.#) to ensure student with that name does not exist. +2. Test Case 1: `add n/Ethan Tan c/98765432 a/252 ANG MO KIO AVENUE 4 01-225` + - Expectation: Confirmation message that includes student information. +3. Test Case 2: `add n/Rachel Teo c/87654321 a/Block 30 Kallang Place #01-23/24 r/HIGH` + - Expectation: Same as above +4. Test Case 3: `add n/Ethan Tan c/98765432 a/252 ANG MO KIO AVENUE 4 01-225` again + - Expectation: Warning `This student already exists in the student list` +5. False commands: + - Try missing fields: Get a message stating the format of the input + - Incorrect Phone Number: Get a message stating that phone numbers must be 8 digits long + +#### 7.2.2 Delete Student +Deletes a student and all related data + +Format: `delete STUDENT_INDEX` + +1. List all students using the view command. +2. Test Case 1: `delete 1` + - Expectation: Confirmation message is shown, Appointments and Notes are deleted as well +3. Test Case 2: `delete 0` + - Expectation: Invalid command format message (Index must be positive) +4. Try other invalid commands like `delete a` or using an index greater than the number of students that exists. Displays Error message + +
+ +#### 7.2.3 Adding/Deleting notes for a Student +This command either adds a note to an existing student (overwriting any existing note) or deletes a note +depending on the command format given, as further shown in examples below + +Format: `note STUDENT_INDEX note/NOTE` + +1. Test Case 1: `note 1 note/Exam stress in building` + - Expectation: Confirmation message is shown. Message shown when double-clicking the student +2. Test Case 2: `note 1 note/` or `note 1` + - Expectation: All notes get deleted +3. Other invalid tests would include invalid student index, handled similar to the case in `delete` + +Double-clicking on the Student card displays the Student notes under the Notes section! +The “Notes” column will inform you if there are no student notes for a particular Student. + +#### 7.2.4 Finding Students by Name +Find students and their related appointments based on their name. Can choose to find student based on their first name, last name or full name. -1. _{ more test cases …​ }_ +Format: `find STUDENT_NAME` -### Deleting a person +1. Student name must match exactly to the first name, last name or full name +2. Use existing student information or add students before conducting the following tests. +3. Test Case 1: `find David` + - Expectation: All students with the name 'David' as first name or last name is shown is shown +4. Test Case 2: `find Li` + - Expectation: All students with 'Li' as first name or last name is shown +5. Test Case 3: `find David Li` + - Expectation: The student 'David Li' is shown +6. Test Case 4: `find Abigail` + - Expectation: Message stating `No student found` +7. Other invalid commands should show respective error messages. -1. Deleting a person while all persons are being shown +
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +#### 7.2.5 Assigning risk level to Student +This command either adds a tag to an existing student (overwriting any existing tag), or deletes a tag +depending on the command format given, as further shown in examples below - 1. Test case: `delete 1`
- Expected: First contact is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated. +Format: `tag STUDENT_INDEX r/RISK_LEVEL` - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +1. Student must be found in the list +2. Risk level can only be `low`/`medium`/`high` +3. Test Case 1: `tag 2 r/HIGH` + - Expectation: Show student information with stated risk level +4. Test Case 2: `tag 2 r/moderate` + - Expectation: Message stating `Risk level should be one of the following three: high/medium/low` +5. Other invalid test cases would include invalid student index (Handled similar to above cases) and invalid tags (like Test Case 2) - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. +#### 7.2.6 Editing Student details +Edit a student's contact number or address. -1. _{ more test cases …​ }_ +Format `edit STUDENT_INDEX [c/CONTACT_NUMBER] [a/HOME_ADDRESS]` -### Saving data +1. This feature cannot be used to edit student risk level. Use `tag` instead +2. At least 1 feature should be included +3. Test Case 1: `edit 3 c/97865423` + - Expectation: Confirmation message shown with the updated contact +4. Test Case 2: `edit 3 a/10 Tampines Central 1 #11-14 Tampines 1` + - Expectation: Same as above, with updated address +5. Test Case 3: `edit 3 c/98762345 a/3791 Jalan Bukit Merah 09-27 E-Centre Redhill` + - Expectation: Same as above, both fields updated +6. Test Case 4: `edit 3` + - Expectation: Error Message `At least one field to edit must be provided.` shown -1. Dealing with missing/corrupted data files +
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +### 7.3 Appointment Commands -1. _{ more test cases …​ }_ +#### 7.3.1 Scheduling an Appointment +Schedules a new appointment for a student. + +Format: `schedule n/STUDENT_NAME date/DATE from/START_TIME to/END_TIME d/DESCRIPTION` + +1. Student must exist before an appointment is scheduled +2. Student name must be in full and match exactly +3. Must have a valid date and time +4. Test Case 1: `schedule n/Ethan Tan date/2023-12-30 from/16:30 to/17:30 d/monthly check-up` + - Expectation: Confirmation message for appointment is shown, appointment added to the column according to date and time. +5. Test Case 2: `schedule n/Ethan date/2023-12-28 from/16:30 to/17:30 d/monthly check-up` + - Expectation: `No such student exists for this appointment` Message +6. Test Case 3: `schedule n/Ethan Tan date/2023-12-30 from/16:30 to/17:30 d/monthly check-up` again + - Expectation: `This appointment already exists` Message +7. Test Case 4: `schedule n/David Li date/2023-12-30 from/16:30 to/17:30 d/monthly check-up` + - Expectation: `This appointment overlaps with an existing appointment` Message +8. Test Case 5: `schedule n/David Li date/2025-12-30 from/16:30 to/17:30 d/monthly check-up` + - Expectation: `Appointment can only be scheduled within a year` Message +9. Other invalid test cases includes other forms of invalid date, students that do not exist or clashes. All should show respective error messages. + +
+ +#### 7.3.2 Cancelling an Appointment +Cancels an existing appointment. + +Format: `cancel APPOINTMENT_INDEX` + +1. Appointment must exist +2. Test Case 1: `cancel 1` + - Expectation: Confirmation message is shown +3. Test Case 2: `cancel 0` + - Expectation: Invalid command format message (Index must be positive) +4. Try other invalid commands like `cancel a` or using an index greater than the number of appointments that exists. Displays Error message accordingly. + +#### 7.3.3 Filtering Appointments by Date: `filter` + +Filters appointments based on given date. + +Format: `filter DATE` + +1. Date must be in the yyyy-MM-dd format +2. Test Case 1: `filter 2023-12-14` + - Expectation: Shows appointments on that day +3. Test Case 2: `filter 2023-11-24` + - Expectation: `0 appointments listed` +4. All invalid date format: Message shows required date format + +
+ +### 7.4 Others + +#### 7.4.1 Viewing all Students and/or Appointments + +Shows a list of all available Students and/or Appointments, depending on specified input. + +Format: `view g/CATEGORY` + +1. Category must be `students`, `appointments` or `all` +2. Test Case 1: `view g/all` + - Expectation: Shows all students and appointments +3. Test Case 2: `view g/appointments` + - Expectation: Shows all appointments in the list +4. Test Case 3: `view g/students` + - Expectation: Shows all students in the list +5. Any other command is considered invalid + - Expectation: Error message to direct user to one of the above + +#### 7.4.2 Exiting the program: `exit` + +Exits the program. + +Format: `exit` + +#### 7.4.3 Clearing storage: `clear` + +Resets the storage, deleting **all** Appointments and Students. + +Format: `clear` + +
+ +## 8. Appendix: Effort + +### 8.1 Design Challenges + +* WellNUS, in contrast to AB3 which exclusively manages one entity type: `Person`, +has the intricate challenge of handling two distinct entity types: `Student` and `Appointment`. +* One challenge encountered was the need to manage dependencies between the two entity types. +Specifically, the system had to be designed to prevent the scheduling of appointments unless a corresponding student entity existed. +Additionally, to maintain data integrity, the deletion of a student entity necessitated the simultaneous cancellation of all associated appointments. +* In tackling these challenges, we leveraged existing components to streamline development efforts. +Recognising the `Person` entity in AB3, we opted to adapt the `Person` entity and repurpose to `Student` entity within WellNUS. +* Another challenge we faced was designing a new `Appointment` entity from scratch. The process was challenging and required tedious class refactoring mid-implementation. +Initially, we designed `Appointment` to have an attribute `DateTime` to store the date and time of the appointment. +To facilitate the comparison of appointments and prevent overlaps and clashes, we soon realised there was a need for a start and end time. +However, having `DateTime start` and `DateTime end` results in a duplication of date, as the appointment was bound to be on the same date. +Hence, we refactored `DateTime` into `Date` and `Time` to allow for 3 attributes: `Date appointmentDate`, `Time startTime`, `Time endTime` +* For the UI of WellNUS, we also faced challenges handling `Appointment` and `Note`, specifically, how these +entities are to be displayed to the user. For `Appointment`, we settled on having an `Appointment` list similar to the +`Person` list in AB3, and simultaneously display 2 lists for `Student` and `Appointment`. +* On the other hand, while we initially displayed `Note` under its corresponding `Student`, similar to `Remark` in +AB3, we eventually decided against it. Our `Note` was designed for counsellors to make consultation notes in addition +to remarks for students, and is usually much longer than a typical `Remark` in AB3, this led to each +row in the `Student` list becoming much more cluttered and wordy. Eventually, we created a separate column that +specifically displays a single `Note` of the user's choice. + +### 8.2 Technical Challenges + +* Since WellNUS manages both `Student` and `Appointment` entities, one of the challenges we faced was refactoring our +`Storage` component to handle both entities simultaneously. While saving `Student` and `Appointment` data to the storage +file was much easier since we could reference AB3's `Storage` component, loading data from file proved to be a much +larger challenge especially in a brownfield project. +* The loading in AB3 simply checks if there are any duplicate `Persons`, on the other hand, having +an `Appointment` entity meant that, we had to perform checks for duplicate `Appointments`, overlapping `Appointments` +and also whether the `Student` provided in the `Appointment` exists in the given WellNUS data file. +* In addition, because we decided to refactor `Person` in AB3 into `Student`, the initial refactoring for `Person` was +tedious and proved to be a challenge. Removing redundant fields like `Email` and `Tags` and adding fields like `RiskLevel` +and `Note` meant that a large portion of the AB3 had to be refactored, since the majority of the code in AB3 involved +the `Person` entity, including tests, sample data and UI. +* We decided that adding student notes was essential to our application as it is part of a counsellor's job. While it was +easy to add a student note, it was difficult to find a convenient way to display the `Note` as the `Note` for the student can be very long. +We could not simply just show the `Note` together with rest of the details in the `StudentCard` as it will not be +visually-pleasing. We wanted to make it easy by double-clicking on a student card to display the student notes separately. +It proved to be challenging to handle event-listeners from various components of the UI as only after +trying different workflows, we realised that the best way is pass a FunctionalInterface from the `StudentNotePanel` over to the `StudentCard` +to update the `StudentNotePanel` when the event is triggered. This was especially difficult as most events originally were triggered by +command inputs. diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 288bbc38eba..6a62389c349 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -254,6 +254,7 @@ GEM unf_ext (0.0.8.2) unf_ext (0.0.8.2-x64-mingw32) unicode-display_width (1.8.0) + wdm (0.1.1) webrick (1.8.1) PLATFORMS @@ -263,6 +264,7 @@ PLATFORMS DEPENDENCIES github-pages jekyll + wdm (~> 0.1.0) webrick BUNDLED WITH diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 57437026c7b..b935284462b 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,195 +3,545 @@ layout: page title: User Guide --- -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. +WellNUS is a *desktop application* used by **NUS Counsellors** to **manage and schedule appointments** with their student clients. + +NUS counsellors have a hectic consultation schedule that constantly has to updated based on students' availability. This can lead to burnout due +to administrative task overload. + +WellNUS eases this burden by providing a one-stop application for NUS counsellors to manage students' contact details and scheduling appointments. +It provides better visibility through neatly organized information, while preventing counsellors from making oversights like double booking a timeslot. + +This user guide serves as a beginner-friendly (yet comprehensive!) walkthrough for all NUS counsellors on how to fully utilise the WellNUS application. +Our guide has three main components: +1. Instructions on *downloading and setting up* the application +2. List of *available features*, and +3. Our *Frequently Asked Questions (FAQs)*. Refer to the Table of Contents below to navigate to your area of interest. + +If you are interested in more technical details, please refer to our [Developer Guide](https://ay2324s1-cs2103t-w13-4.github.io/tp/DeveloperGuide.html). + +
+:information_source: +WellNUS is optimised for use via a **Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). +If you can type fast, WellNUS can get your contact management and scheduling tasks done faster than traditional GUI apps. +
+ + +
* Table of Contents {:toc} -------------------------------------------------------------------------------------------------------------------- -## Quick start +
+ +## 1. Getting started + +### 1.1 Accessing the app + +1. Ensure you have Java `11` or above installed in your Computer. + 1. To check the current version of Java installed on your system, refer to [this guide](https://www.java.com/en/download/help/version_manual.html). + 2. If Java is not installed or you have an outdated version, you may download it [here](https://www.oracle.com/sg/java/technologies/javase/jdk11-archive-downloads.html). + +2. Download the latest `wellnus.jar` from [here](https://github.com/AY2324S1-CS2103T-W13-4/tp/releases/tag/v1.4). + 1. Click on the link above to be redirected to our *github repository*. + 2. Under *Assets*, click on the `wellnus.jar` file (underlined in red) to begin the download. + -1. Ensure you have Java `11` or above installed in your Computer. + ![github release page](images/GithubReleasePage.png) -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +3. Copy the file to the folder you want to use as the _home folder_ for WellNUS. + 1. Navigate to the folder `wellnus.jar` has been installed in. By default, it will be downloaded in the *Downloads* folder. + 2. Create a new folder on your Desktop (or any folder you want to put your application in) and move the `wellnus.jar` file into the folder. -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +
+ +4. Start up the WellNUS application. + 1. Open the *Command Prompt* (for Windows users) or *Terminal* (for Mac users). `cd` into the folder you put the jar file in, and use the `java -jar wellnus.jar` command to run the application. + 2. Alternatively, if you moved it into a folder created on Desktop, you may run the following command: + ``` + cd Desktop/{your_folder_name} + java -jar wellnus.jar + ``` + +
+ :information_source: Note: + You may also use any applications (e.g. Windows Powershell, Visual Studio Code) that support command line inputs. +
+ + A window similar to the below should appear in a few seconds. Note how the app contains some sample data. -1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.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) -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.
+5. Type the command in the command box and press Enter to execute it. Some example commands you can try: + * `help` : Opens the help window. + * `view g/all` : View all students and appointments. + * `exit` : Exits the app. - * `list` : Lists all contacts. +6. Refer to the [Features](#2-features) below for details of each command. - * `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book. +
- * `delete 3` : Deletes the 3rd contact shown in the current list. +### 1.2 Icons used in this User Guide - * `clear` : Deletes all contacts. +| Icons | Representation | +|--------------------------------|------------------------------------------| +| :information_source: **Note** | Offers additional useful information | +| :exclamation: **Caution** | Warns about common mistakes | +| :bulb: **Tip** | Provides tips to enhance your experience | - * `exit` : Exits the app. +### 1.3 About the command format -1. Refer to the [Features](#features) below for details of each command. +All instructions executed in WellNUS generally use the following command format: `COMMAND_WORD PREFIX/PARAMETER`. +The list of all available prefixes and parameters, as well as constraints of each parameter, is shown in the table below. +To see a list of all command words, refer to the [Command Summary](#5-command-summary) section. --------------------------------------------------------------------------------------------------------------------- +| Prefix | Parameter | Parameter Meaning | Example Usage | Parameter Constraints | +|-----------|-------------------|----------------------------------|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **n/** | STUDENT_NAME | Name of student | n/Peter Johnson | STUDENT_NAME **must** only contain **alphabetical characters and spaces**, be **unique** up to **100 characters** long, and **cannot be blank**. The first letter of each word **must** be in **upper case**, while every other letter **must** be in **lower case**. | +| **c/** | CONTACT_NUMBER | Contact number of student | c/94738484 | CONTACT_NUMBER **must** only contain **numerical characters**, be **exactly 8 digits long** (without spaces) and **cannot be blank**. Need **not** be unique | +| **a/** | ADDRESS | Address of student | a/Blk 515 Choa Chu Kang Avenue 6 | ADDRESS can take any value up to **200 characters** long, and **cannot be blank**. Need **not** be unique | +| **r/** | RISK_LEVEL | Risk level assigned to student | r/high | RISK_LEVEL **must** be one of the following three values: **high**, **medium**, **low**. **Case-insensitive**. Each student can have at most 1 risk-level tagged to him/her. | +| **note/** | NOTE | Note associated with student | note/Struggles with 3rd grade math | NOTE can take any value up to **500 characters** long. | +| -- | STUDENT_INDEX | Index of student in the list | -- | STUDENT_INDEX **must** be a **positive integer** (i.e. 1, 2, 3, ...) up to the size of the student list. | +| **date/** | DATE | Date of appointment | date/2023-10-12 | DATE **must** be in the following format: `yyyy-MM-dd`. Specified date must be **within a year from the current date**. | +| **from/** | START_TIME | Start time of appointment | from/16:30 | START_TIME **must** be in the following format: `HH:mm`, in **24-hour format**. | +| **to/** | END_TIME | End time of appointment | to/17:30 | END_TIME **must** be in the following format: `HH:mm`, in **24-hour format**. | +| **d/** | DESCRIPTION | Description of appointment | d/3rd counselling session | DESCRIPTION can take any value up to **100 characters** long, and **cannot be blank**. | +| -- | APPOINTMENT_INDEX | Index of appointment in the list | -- | APPOINTMENT_INDEX **must** be a **positive integer** (i.e. 1, 2, 3, ...) up to the size of the appointment list. | +| **g/** | CATEGORY | Category of search | g/appointments | CATEGORY **must** be one of the following three values: **students**, **appointments**, **all**. | -## 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`. + e.g. in `add n/NAME c/CONTACT_NUMBER`, `NAME` and `CONTACT_NUMBER` are parameters + which can be used as `add n/John Doe c/98172645`. * Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. - -* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. + e.g `n/NAME [r/RISK_LEVEL]` can be used as `n/John Doe r/low` or as `n/John Doe`. * 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. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Extraneous parameters for commands that do not take in parameters (such as `help` and `exit`) will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`. * If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
-### Viewing help : `help` +-------------------------------------------------------------------------------------------------------------------- + +
+ +## 2. Features +### 2.1 Utility Commands -Shows a message explaning how to access the help page. +#### 2.1.1 Viewing help: `help` + +Shows a message with a link to this user guide. ![help message](images/helpMessage.png) Format: `help` +### 2.2 Student Commands + +#### 2.2.1 Adding a Student: `add` + +Adds a student with their relevant details. -### Adding a person: `add` +Format: `add n/STUDENT_NAME c/CONTACT_NUMBER a/HOME_ADDRESS [r/RISK_LEVEL]` -Adds a person to the address book. +**Parameters**: +1. Student Name + - Must contain alphabetical characters only. Symbols and numerical characters are not allowed + - Must be unique + - Maximum of 100 characters +2. Contact Number + - Numerical characters only, must be exactly 8 characters long +3. Home Address + - Maximum of 200 characters, cannot be blank +4. Risk Level + - Must be `high`, `medium`, or `low` +
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +Valid examples: +* `add n/John c/81349705 a/Yishun Street 56 Blk 21 #05-07` +* `add n/Sally c/94149785 a/Woodlands Street 11 Blk 888 #08-08 r/low` -
:bulb: **Tip:** -A person can have any number of tags (including 0) +Invalid examples: +* `add n/Sally c/94149785` (missing address) +* `add n/Sally! c/1234 a/Woodlands Street 11 Blk 888 #08-08` (invalid name and phone number) +* `add n/Sally c/94149785 a/` (address is blank) +* `add n/Sally c/1234 a/Woodlands Street 11 Blk 888 #08-08 r/abc` (invalid risk level) + +#### 2.2.2 Deleting a Student: `delete` + +
+:exclamation: **Caution:** +Deleting a Student also cancels all appointments associated to that Student.
-Examples: -* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` -* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal` +Deletes an existing student. -### Listing all persons : `list` +Format: `delete STUDENT_INDEX` -Shows a list of all persons in the address book. +**Parameters**: +1. Student Index + - Must be an integer starting from 1 + - Must be found in the students list -Format: `list` +Valid example: +* `delete 2` -### Editing a person : `edit` +Invalid examples: +* `delete 0` (student index only starts from 1) +* `delete John` (index should be a number) -Edits an existing person in the address book. +
+:bulb: **Tip:** +If you know the name of the student you want to delete, use the `find` command to filter the student list first. +
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ -* At least one of the optional fields must be provided. -* Existing values will be updated to the input values. -* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative. -* You can remove all the person’s tags by typing `t/` without - specifying any tags after it. +#### 2.2.3 Adding/Deleting notes for a Student: `note` -Examples: -* `edit 1 p/91234567 e/johndoe@example.com` Edits the phone number and email address of the 1st person to be `91234567` and `johndoe@example.com` respectively. -* `edit 2 n/Betsy Crower t/` Edits the name of the 2nd person to be `Betsy Crower` and clears all existing tags. +
-### Locating persons by name: `find` +:information_source: **Note:** +Student notes of a student can be viewed by double-clicking on the specific student that you want under the "Student" +column. You can only view student notes for one student at a time. When you perform any commands, the notes column will be cleared +(You will have to double-click once again). -Finds persons whose names contain any of the given keywords. +If you have any feedback on our features pertaining to student notes, feel free to send your feedback to the +developer team! +
-Format: `find KEYWORD [MORE_KEYWORDS]` +This command either adds a note to an existing student (overwriting any existing note) or deletes a note +depending on the command format given, as further shown in examples below -* 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 is searched. -* Only full words will be matched e.g. `Han` will not match `Hans` -* Persons matching at least one keyword will be returned (i.e. `OR` search). - e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang` +Format: `note STUDENT_INDEX note/NOTE` -Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +**Parameters**: +1. Student Index + - Must be an integer starting from 1 + - Must be found in the students list +2. Note + - Maximum of 500 characters + - To be omitted if you want to delete note -### Deleting a person : `delete` +Valid examples: +* `note 1 note/Preferred language: mandarin` +* `note 2` (deletes the note of student at index 2) -Deletes the specified person from the address book. +Invalid examples: +* `note 0` (invalid index) +* `note Alex note/Likes dogs.` (index should be a number) -Format: `delete INDEX` +
+:bulb: **Tip:** +To delete an existing note, simply use `note STUDENT_INDEX` or `note STUDENT_INDEX note/` +
-* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index **must be a positive integer** 1, 2, 3, …​ +
-Examples: -* `list` followed by `delete 2` deletes the 2nd person in the address book. -* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command. +#### 2.2.4 Finding Students by Name: `find` -### Clearing all entries : `clear` +Find students and their related appointments based on their name. Can choose to find student based on their first name, last name or full name. +If the name does not match entirely, the student will not be shown. Refer to the examples below for a better understanding -Clears all entries from the address book. +Format: `find STUDENT_NAME` -Format: `clear` +**Parameters**: +1. Student Name + - Must contain alphabetical characters only + +Valid example: +* `find Mike Oxlong` + +Invalid example: +* `find 39 Jane Street` (contains numerical characters) +* `find @9th Jan!` (contains special characters) + + +
+ +:information_source: **Note:** +The intended behaviour of the find command might be confusing. To illustrate the proper behaviour, consider the example scenario below.
+Given a Student named `Roy Lee` is in the WellNUS student list: +* `find Roy`, `find Lee` and `find Roy Lee` will successfully find the Student. +* `find Royy`, `find Le`, `find Roy L`, `find RoyLee` will not find the Student. +
+ + +![Find feature](images/findFeature.png) +

+Find student Alex Yeoh +

+ +
+ +#### 2.2.5 Assigning risk level to Student: `tag` + +This command either adds a tag to an existing student (overwriting any existing tag), or deletes a tag +depending on the command format given, as further shown in examples below + +Format: `tag STUDENT_INDEX r/RISK_LEVEL` + +**Parameters**: +1. Student Index + - Must be an integer starting from 1 + - Must be found in the students list +2. Risk Level + - Must be `high`, `medium`, or `low` + - Case-insensitive, i.e. `HIGH` is a valid input + - To be omitted if you want to delete the tag + +Valid examples: +* `tag 2 r/high` +* `tag 1 r/MEDIUM` +* `tag 3` (deletes the tag of student at index 3) + +Invalid examples: +* `tag -1 r/high` (invalid index) +* `tag 1 r/lowrisk` (invalid risk level) + +#### 2.2.6 Editing Student details: `edit` + +Edit a student's contact number or address. + +Format `edit STUDENT_INDEX [c/CONTACT_NUMBER] [a/HOME_ADDRESS]` -### Exiting the program : `exit` +**Parameters**: +1. Student Index + - Must be an integer starting from 1 + - Must be found in the students list +2. Contact Number + - Numbers only, must be 8 characters long +3. Home Address + - Maximum of 200 characters, cannot be blank + +
+ +
+:information_source: **Note:** +Providing the same field as before (eg. changing contact from 99998888 to 99998888) will not throw an error +
+ +Valid examples: +* `edit 1 c/98765432 a/Woodlands Street 11 Blk 888 #08-08` +* `edit 2 c/98574321` + +Invalid example: +* `edit 1` (Edit must contain at least 1 field) + +### 2.3 Appointment Commands + +
+:information_source: **Note:** +Appointments will be **automatically sorted** by Date and Time in **ascending order**. +
+ +#### 2.3.1 Scheduling an Appointment: `schedule` + +
+:exclamation: **Caution:** +The Student must exist before an Appointment can be scheduled for the Student! +
+ +Schedules a new appointment for a student. + +Format: `schedule n/STUDENT_NAME date/DATE from/START_TIME to/END_TIME d/DESCRIPTION` + +**Parameters**: +1. Name + - Cannot contain symbols, alphabetical characters only + - Maximum of 100 characters + - Must be the name of a student found in the students list +2. Date + - Must be in the following format: `yyyy-MM-dd` + - Must be within a year from now +3. Start/End Time + - Must be in the following format: `HH:mm` in 24-hour format. +4. Description + - Maximum of 100 characters, cannot be blank + +
+ +Valid examples: +* `schedule n/Jon date/2023-12-30 from/16:30 to/17:30 d/monthly check-up` +* `schedule n/Yin Kiat date/2023-12-09 from/07:00 to/10:45 d/first counselling session` + +Invalid examples: +* `schedule n/Jon date/2001-12-09 from/07:00 to/10:45 d/first counselling session` (date is in the past) +* `schedule n/Jon date/2023-12-09 from/16:00 to/12:45 d/first counselling session` (end time is before start time) +* `schedule n/Jon date/09-12-2023 from/1100 to/1230 d/first counselling session` (date and time are in the wrong format) +* `schedule n/Jon date/2023-12-30 from/16:30 to/17:30 d/` (description is empty) + +
+:information_source: **Note:** +WellNUS automatically checks for overlaps between appointments whenever a new appointment is being added. If there is an +overlap between the new appointment to be scheduled and existing appointments, the new appointment will be **not be scheduled** +and will inform the user to reschedule the appointment. +
+ +
+ +#### 2.3.2 Cancelling an Appointment: `cancel` + +Cancels an existing appointment. + +Format: `cancel APPOINTMENT_INDEX` + +**Parameters**: +1. Appointment Index + - Must be an integer starting from 1 + - Must be found in the appointments list + +Valid example: +* `cancel 2` + +Invalid examples: +* `cancel 0` (invalid index) +* `cancel first appointment` (index should be a number) + +#### 2.3.3 Filtering Appointments by Date: `filter` + +Filters appointments based on given date. + +Format: `filter DATE` + +**Parameters** +1. Date + - Must be in the following format: `yyyy-MM-dd` + +Valid example: +* `filter 2023-10-16` + +Invalid examples: +* `filter 16-10-2023` (date is in wrong format) +* `filter 16 October 2023` (date is in wrong format) + +
+ +![Filter feature](images/filterFeature.png) +

+Filter appointments on 15 December 2023 +

+ +### 2.4 Others + +#### 2.4.1 Viewing all Students and/or Appointments: `view` + +Shows a list of all available Students and/or Appointments, depending on specified input. + +Format: `view g/CATEGORY` + +**Parameters**: +1. Category + - Must be `students`, `appointments` or `all` + +Valid examples: +* `view g/all` +* `view g/appointments` +* `view g/students` + +
+ +#### 2.4.2 Exiting the program: `exit` Exits the program. Format: `exit` -### Saving the data +#### 2.4.3 Clearing storage: `clear` + +Resets the storage, deleting **all** Appointments and Students. + +Format: `clear` + +#### 2.4.4 Saving the data + +WellNUS data is saved in the hard disk automatically after any command that changes the data. There is no need to save manually. + +#### 2.4.5 Editing the data file + +WellNUS data is saved automatically as a JSON file `[JAR file location]/data/wellnus.json`. Advanced users are welcome to update data directly by editing that data file + +
+:exclamation: **Caution:** +If your changes to the data file makes its format invalid, WellNUS will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
+ +The following scenarios will render the data file invalid:
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +* File contains invalid or missing parameters, see `Parameter Constraints` under the [Command Format](#13-about-the-command-format) section
-### Editing the data file +* File contains duplicate `Students` and `Appointments`
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +* File contains overlapping `Appointments`, or `Appointments` without a corresponding `Student`
+ +* File is not in proper JSON format, you may refer to [this guide](https://json.org/example.html) -
:exclamation: **Caution:** -If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-### Archiving data files `[coming in v2.0]` +### 2.5 Tracking TODOS `[Coming soon]` _Details coming soon ..._ -------------------------------------------------------------------------------------------------------------------- -## FAQ +## 3. Frequently Asked Questions (FAQ) + +1. **How do I transfer my data to another Computer ?**
+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 WellNUS home folder. + +2. **How can I view my appointments scheduled today ?**
+You can use the filter command with today's date. +Here's an example: `filter 2023-12-07`. Replace `2023-12-07` with today's date in `yyyy-MM-dd` format. -**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 AddressBook home folder. +3. **How do I clear all data in WellNUS ?**
+To clear all data, use the `clear` command. This resets the storage, deleting all appointments and students. +Exercise caution, as this action cannot be undone. + +4. **How do I exit the WellNUS application ?**
+To exit the program, use the `exit` command. This closes the application. + +5. **I am unsure of a new student's risk level, what should I put ?**
+If you're unsure of a new student's risk level, you can leave the risk level parameter blank when adding the student using the add command. +WellNUS is designed to handle optional parameters. Here's an example: +`add n/John Doe c/98172645 a/821, Bishan, #02-124` +You can always update the risk level later using the tag command when more information becomes available. -------------------------------------------------------------------------------------------------------------------- -## Known issues +## 4. 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. -------------------------------------------------------------------------------------------------------------------- -## Command summary - -Action | Format, Examples ---------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +
+ +## 5. Command summary + +| Action | Format, Valid examples | +|--------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [Help](#211-viewing-help-help) | `help` | +| [Add Student](#221-adding-a-student-add) | `add n/STUDENT_NAME c/CONTACT_NUMBER a/HOME_ADDRESS [r/RISK_LEVEL]`
e.g., `add n/John c/81349705 a/Yishun Street 56 Blk 21 #05-07 r/medium` | +| [Delete Student](#222-deleting-a-student-delete) | `delete STUDENT_INDEX`
e.g., `delete 3` | +| [Add/Delete Student Note](#223-addingdeleting-notes-for-a-student-note) | `note STUDENT_INDEX note/NOTE`
e.g., `note 1 note/Likes dogs` | +| [Find Students](#224-finding-students-by-name-find) | `find STUDENT_NAME`
e.g., `find John` | +| [Assign Risk Level to Student](#225-assigning-risk-level-to-student-tag) | `tag STUDENT_INDEX r/RISK_LEVEL`
e.g.,`tag 4 r/high` | +| [Edit Student details](#226-editing-student-details-edit) | `edit STUDENT_INDEX c/CONTACT_NUMBER A/HOME_ADDRESS`
e.g.,`edit 1 c/91234567` | +| [Schedule Appointment](#231-scheduling-an-appointment-schedule) | `schedule n/STUDENT_NAME date/DATE from/START_TIME to/END_TIME d/DESCRIPTION`
e.g., `schedule n/Jon date/2023-12-30 from/16:30 to/17:30 d/monthly check-up` | +| [Cancel Appointment](#232-cancelling-an-appointment-cancel) | `cancel APPOINTMENT_INDEX`
e.g., `cancel 3` | +| [Filter Appointments](#233-filtering-appointments-by-date-filter) | `filter DATE`
e.g., `filter 2023-10-16` | +| [View all Students and/or Appointments](#241-viewing-all-students-andor-appointments-view) | `view g/CATEGORY`
e.g., `view g/all` | +| [Exit](#242-exiting-the-program-exit) | `exit` | +| [Delete all data](#243-clearing-storage-clear) | `clear` | + diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..5095576d856 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "WellNUS" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2324S1-CS2103T-W13-4/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..2e385dce254 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -288,7 +288,7 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "WellNUS"; font-size: 32px; } } diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index 48b6cc4333c..ef2e3ac46fb 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -14,13 +14,13 @@ activate ui UI_COLOR ui -[UI_COLOR]> logic : execute("delete 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deleteStudent(p) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveWellNus(WellNus) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml deleted file mode 100644 index 598474a5c82..00000000000 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -!include style.puml -skinparam arrowThickness 1.1 -skinparam arrowColor MODEL_COLOR -skinparam classBackgroundColor MODEL_COLOR - -AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList - -UniqueTagList -right-> "*" Tag -UniquePersonList -right-> Person - -Person -up-> "*" Tag - -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -@enduml diff --git a/docs/diagrams/CancelSequenceDiagram.puml b/docs/diagrams/CancelSequenceDiagram.puml new file mode 100644 index 00000000000..7d92c0e8ccc --- /dev/null +++ b/docs/diagrams/CancelSequenceDiagram.puml @@ -0,0 +1,70 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":WellNusParser" as WellNusParser LOGIC_COLOR +participant ":CancelCommandParser" as CancelCommandParser LOGIC_COLOR +participant "d:CancelCommand" as CancelCommand 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("cancel 1") +activate LogicManager + +LogicManager -> WellNusParser : parseCommand("cancel 1") +activate WellNusParser + +create CancelCommandParser +WellNusParser -> CancelCommandParser +activate CancelCommandParser + +CancelCommandParser --> WellNusParser +deactivate CancelCommandParser + +WellNusParser -> CancelCommandParser : parse("1") +activate CancelCommandParser + +create CancelCommand +CancelCommandParser -> CancelCommand +activate CancelCommand + +CancelCommand --> CancelCommandParser : d +deactivate CancelCommand + +CancelCommandParser --> WellNusParser : d +deactivate CancelCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +CancelCommandParser -[hidden]-> WellNusParser +destroy CancelCommandParser + +WellNusParser --> LogicManager : d +deactivate WellNusParser + +LogicManager -> CancelCommand : execute() +activate CancelCommand + +CancelCommand -> Model : cancelAppointment(1) +activate Model + +Model --> CancelCommand +deactivate Model + +create CommandResult +CancelCommand -> CommandResult +activate CommandResult + +CommandResult --> CancelCommand +deactivate CommandResult + +CancelCommand --> LogicManager : result +deactivate CancelCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ClashActivityDiagram.puml b/docs/diagrams/ClashActivityDiagram.puml new file mode 100644 index 00000000000..3af7985e124 --- /dev/null +++ b/docs/diagrams/ClashActivityDiagram.puml @@ -0,0 +1,24 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 +start +:User executes schedule command; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([is not unique]) + :WellNus informs user + appointment is not unique; + :Appointment not added; + +elseif () then ([has clashes])) + :WellNus informs user appointment + clashes with other appointments; + :Appointment not added; +else ([else]) + :Appointment successfully added; +endif +stop +@enduml diff --git a/docs/diagrams/CommitActivityDiagram.puml b/docs/diagrams/CommitActivityDiagram.puml deleted file mode 100644 index 8c0892d6a70..00000000000 --- a/docs/diagrams/CommitActivityDiagram.puml +++ /dev/null @@ -1,18 +0,0 @@ -@startuml -skin rose -skinparam ActivityFontSize 15 -skinparam ArrowFontSize 12 -start -:User executes command; - -'Since the beta syntax does not support placing the condition outside the -'diamond we place it as the true branch instead. - -if () then ([command commits AddressBook]) - :Purge redundant states; - :Save AddressBook to - addressBookStateList; -else ([else]) -endif -stop -@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 40ea6c9dc4c..7a14f1dd876 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -4,7 +4,7 @@ skinparam ArrowFontStyle plain box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":WellNusParser" as WellNusParser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR @@ -17,17 +17,17 @@ end box [-> LogicManager : execute("delete 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> WellNusParser : parseCommand("delete 1") +activate WellNusParser create DeleteCommandParser -AddressBookParser -> DeleteCommandParser +WellNusParser -> DeleteCommandParser activate DeleteCommandParser -DeleteCommandParser --> AddressBookParser +DeleteCommandParser --> WellNusParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +WellNusParser -> DeleteCommandParser : parse("1") activate DeleteCommandParser create DeleteCommand @@ -37,19 +37,19 @@ activate DeleteCommand DeleteCommand --> DeleteCommandParser : d deactivate DeleteCommand -DeleteCommandParser --> AddressBookParser : d +DeleteCommandParser --> WellNusParser : d deactivate DeleteCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser +DeleteCommandParser -[hidden]-> WellNusParser destroy DeleteCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +WellNusParser --> LogicManager : d +deactivate WellNusParser LogicManager -> DeleteCommand : execute() activate DeleteCommand -DeleteCommand -> Model : deletePerson(1) +DeleteCommand -> Model : deleteStudent(1) activate Model Model --> DeleteCommand diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index a57720890ee..581df6a533f 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic as LogicPackage { -Class AddressBookParser +Class WellNusParser Class XYZCommand Class CommandResult Class "{abstract}\nCommand" as Command @@ -27,8 +27,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .right.|> Logic -LogicManager -right->"1" AddressBookParser -AddressBookParser ..> XYZCommand : creates > +LogicManager -right->"1" WellNusParser +WellNusParser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > @@ -38,7 +38,7 @@ LogicManager --> Storage Storage --[hidden] Model Command .[hidden]up.> Storage Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +note right of XYZCommand: XYZCommand = ScheduleCommand, \nFindCommand, etc Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 0de5673070d..95c83755d3f 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,20 +5,20 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model as ModelPackage <>{ -Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook +Class "<>\nReadOnlyWellNus" as ReadOnlyWellNus Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs Class "<>\nModel" as Model -Class AddressBook +Class WellNus Class ModelManager Class UserPrefs -Class UniquePersonList -Class Person +Class UniqueStudentList +Class Student Class Address -Class Email Class Name +Class Note Class Phone -Class Tag +Class RiskLevel Class I #FFFFFF } @@ -26,29 +26,28 @@ Class I #FFFFFF Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +WellNus .up.|> ReadOnlyWellNus ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs -Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +Model .left.> ReadOnlyWellNus +ModelManager -left-> "1" WellNus ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +WellNus *--> "1" UniqueStudentList +UniqueStudentList -left-> "~* all" Student +Student *--> Name +Student *--> Note +Student *--> Phone +Student *--> Address +Student *--> RiskLevel -Person -[hidden]up--> I -UniquePersonList -[hidden]right-> I +Student -[hidden]up--> I +UniqueStudentList -[hidden]right-> I Name -[hidden]right-> Phone Phone -[hidden]right-> Address -Address -[hidden]right-> Email -ModelManager --> "~* filtered" Person +ModelManager --> "~* filtered" Student @enduml diff --git a/docs/diagrams/NoteSequenceDiagram.puml b/docs/diagrams/NoteSequenceDiagram.puml new file mode 100644 index 00000000000..3afc91f1669 --- /dev/null +++ b/docs/diagrams/NoteSequenceDiagram.puml @@ -0,0 +1,78 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":WellNusParser" as WellNusParser LOGIC_COLOR +participant ":NoteCommandParser" as NoteCommandParser LOGIC_COLOR +participant "n:NoteCommand" as NoteCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +participant "editedStudent:Student" as Student LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("note 1 note/Likes dogs.") +activate LogicManager + +LogicManager -> WellNusParser : parseCommand("note 1 note/Likes dogs.") +activate WellNusParser + +create NoteCommandParser +WellNusParser -> NoteCommandParser +activate NoteCommandParser + +NoteCommandParser --> WellNusParser +deactivate NoteCommandParser + +WellNusParser -> NoteCommandParser : parse("1 note/Likes dogs.") +activate NoteCommandParser + +create NoteCommand +NoteCommandParser -> NoteCommand +activate NoteCommand + +NoteCommand --> NoteCommandParser : n +deactivate NoteCommand + +NoteCommandParser --> WellNusParser : n +deactivate NoteCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +NoteCommandParser -[hidden]-> WellNusParser +destroy NoteCommandParser + +WellNusParser --> LogicManager : d +deactivate WellNusParser + +LogicManager -> NoteCommand : execute() +activate NoteCommand + +create Student +NoteCommand -> Student +activate Student + +Student --> NoteCommand +deactivate Student + +NoteCommand -> Model : setStudent(editedStudent) +activate Model + +Model --> NoteCommand +deactivate Model + +create CommandResult +NoteCommand -> CommandResult +activate CommandResult + +CommandResult --> NoteCommand +deactivate CommandResult + +NoteCommand --> LogicManager : result +deactivate NoteCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/OverlapSequenceDiagram.puml b/docs/diagrams/OverlapSequenceDiagram.puml new file mode 100644 index 00000000000..4da5d621664 --- /dev/null +++ b/docs/diagrams/OverlapSequenceDiagram.puml @@ -0,0 +1,70 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":ScheduleCommandParser" as ScheduleCommandParser LOGIC_COLOR +participant "s:ScheduleCommand" as ScheduleCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant ":UniqueAppointmentList" as UniqueAppointmentList MODEL_COLOR +participant ":Appointment" as Appointment MODEL_COLOR +end box + +[-> LogicManager : execute(schedule n/John ...) +activate LogicManager + +LogicManager -> ScheduleCommandParser : parseCommand(schedule n/John ...) +activate ScheduleCommandParser + +create ScheduleCommand +ScheduleCommandParser -> ScheduleCommand +activate ScheduleCommand + +ScheduleCommand -> Appointment +activate Appointment + +Appointment -> Appointment: Appointment(Name...) + + +Appointment --> ScheduleCommand +deactivate Appointment + + +ScheduleCommand --> ScheduleCommandParser +deactivate ScheduleCommand + +ScheduleCommandParser --> LogicManager : s +deactivate ScheduleCommandParser + +LogicManager -> ScheduleCommand : execute() +activate ScheduleCommand + +ScheduleCommand -> Model : execute() +activate Model + +Model -> UniqueAppointmentList : hasOverlappingAppointments(Appointment appt) +activate UniqueAppointmentList + +UniqueAppointmentList -> UniqueAppointmentList : hasOverlapsWith(Appointment appt) + +UniqueAppointmentList --> Model +deactivate UniqueAppointmentList + +Model --> ScheduleCommand +deactivate Model + +ScheduleCommand --> LogicManager : result +deactivate ScheduleCommand + +ScheduleCommand -[hidden]-> LogicManager : result +destroy ScheduleCommand + +[<--LogicManager +deactivate LogicManager + + +@enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..f58f84eb5d6 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -9,7 +9,7 @@ Class XYZCommand package "Parser classes"{ Class "<>\nParser" as Parser -Class AddressBookParser +Class WellNusParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> WellNusParser -AddressBookParser .down.> XYZCommandParser: creates > +WellNusParser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +WellNusParser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index a821e06458c..cca16be6d96 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -14,12 +14,13 @@ Class JsonUserPrefsStorage Class "<>\nStorage" as Storage Class StorageManager -package "AddressBook Storage" #F4F6F6{ -Class "<>\nAddressBookStorage" as AddressBookStorage -Class JsonAddressBookStorage -Class JsonSerializableAddressBook -Class JsonAdaptedPerson -Class JsonAdaptedTag +package "WellNus Storage" #F4F6F6{ +Class "<>\nWellNusStorage" as WellNusStorage +Class JsonWellNusStorage +Class JsonSerializableWellNus +Class JsonAdaptedStudent +Class JsonAdaptedAppointment +Class JsonAdaptedRiskLevel } } @@ -29,15 +30,16 @@ HiddenOutside ..> Storage StorageManager .up.|> Storage StorageManager -up-> "1" UserPrefsStorage -StorageManager -up-> "1" AddressBookStorage +StorageManager -up-> "1" WellNusStorage Storage -left-|> UserPrefsStorage -Storage -right-|> AddressBookStorage +Storage -right-|> WellNusStorage JsonUserPrefsStorage .up.|> UserPrefsStorage -JsonAddressBookStorage .up.|> AddressBookStorage -JsonAddressBookStorage ..> JsonSerializableAddressBook -JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonWellNusStorage .up.|> WellNusStorage +JsonWellNusStorage ..> JsonSerializableWellNus +JsonSerializableWellNus --> "*" JsonAdaptedStudent +JsonSerializableWellNus --> "*" JsonAdaptedAppointment +JsonAdaptedStudent --> JsonAdaptedRiskLevel @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..62985453995 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,8 +11,11 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class StudentListPanel +Class StudentCard +Class StudentNotePanel +Class AppointmentCard +Class AppointmentListPanel Class StatusBarFooter Class CommandBox } @@ -32,26 +35,35 @@ UiManager .left.|> Ui UiManager -down-> "1" MainWindow MainWindow *-down-> "1" CommandBox MainWindow *-down-> "1" ResultDisplay -MainWindow *-down-> "1" PersonListPanel +MainWindow *-down-> "1" StudentListPanel MainWindow *-down-> "1" StatusBarFooter +MainWindow *-down-> "1" StudentNotePanel +MainWindow *-down-> "1" AppointmentListPanel MainWindow --> "0..1" HelpWindow -PersonListPanel -down-> "*" PersonCard +StudentListPanel -down-> "*" StudentCard +AppointmentListPanel -down-> "*" AppointmentCard -MainWindow -left-|> UiPart +MainWindow -left--|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +StudentListPanel --|> UiPart +StudentCard --|> UiPart +AppointmentCard --|> UiPart +AppointmentListPanel --|> UiPart +StudentNotePanel --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart -PersonCard ..> Model +StudentCard ...> Model +AppointmentCard ..> Model +StudentNotePanel ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +StudentListPanel -[hidden]left- HelpWindow +StudentListPanel -[hidden]left- AppointmentListPanel HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/UndoRedoState0.puml b/docs/diagrams/UndoRedoState0.puml deleted file mode 100644 index 43a45903ac9..00000000000 --- a/docs/diagrams/UndoRedoState0.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 -skinparam ClassBackgroundColor #FFFFAA - -title Initial state - -package States { - class State1 as "ab0:AddressBook" - class State2 as "ab1:AddressBook" - class State3 as "ab2:AddressBook" -} -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 -hide State2 -hide State3 - -class Pointer as "Current State" #FFFFFF -Pointer -up-> State1 -@end diff --git a/docs/diagrams/UndoRedoState1.puml b/docs/diagrams/UndoRedoState1.puml deleted file mode 100644 index 5a41e9e1651..00000000000 --- a/docs/diagrams/UndoRedoState1.puml +++ /dev/null @@ -1,23 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 -skinparam ClassBackgroundColor #FFFFAA - -title After command "delete 5" - -package States <> { - class State1 as "ab0:AddressBook" - class State2 as "ab1:AddressBook" - class State3 as "ab2:AddressBook" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -hide State3 - -class Pointer as "Current State" #FFFFFF - -Pointer -up-> State2 -@end diff --git a/docs/diagrams/UndoRedoState2.puml b/docs/diagrams/UndoRedoState2.puml deleted file mode 100644 index ad32fce1b0b..00000000000 --- a/docs/diagrams/UndoRedoState2.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 -skinparam ClassBackgroundColor #FFFFAA - -title After command "add n/David" - -package States <> { - class State1 as "ab0:AddressBook" - class State2 as "ab1:AddressBook" - class State3 as "ab2:AddressBook" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -class Pointer as "Current State" #FFFFFF - -Pointer -up-> State3 -@end diff --git a/docs/diagrams/UndoRedoState3.puml b/docs/diagrams/UndoRedoState3.puml deleted file mode 100644 index 9187a690036..00000000000 --- a/docs/diagrams/UndoRedoState3.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 -skinparam ClassBackgroundColor #FFFFAA - -title After command "undo" - -package States <> { - class State1 as "ab0:AddressBook" - class State2 as "ab1:AddressBook" - class State3 as "ab2:AddressBook" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -class Pointer as "Current State" #FFFFFF - -Pointer -up-> State2 -@end diff --git a/docs/diagrams/UndoRedoState4.puml b/docs/diagrams/UndoRedoState4.puml deleted file mode 100644 index 2bc631ffcd0..00000000000 --- a/docs/diagrams/UndoRedoState4.puml +++ /dev/null @@ -1,21 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 -skinparam ClassBackgroundColor #FFFFAA - -title After command "list" - -package States <> { - class State1 as "ab0:AddressBook" - class State2 as "ab1:AddressBook" - class State3 as "ab2:AddressBook" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -class Pointer as "Current State" #FFFFFF - -Pointer -up-> State2 -@end diff --git a/docs/diagrams/UndoRedoState5.puml b/docs/diagrams/UndoRedoState5.puml deleted file mode 100644 index e77b04104aa..00000000000 --- a/docs/diagrams/UndoRedoState5.puml +++ /dev/null @@ -1,22 +0,0 @@ -@startuml -!include style.puml -skinparam ClassFontColor #000000 -skinparam ClassBorderColor #000000 -skinparam ClassBackgroundColor #FFFFAA - -title After command "clear" - -package States <> { - class State1 as "ab0:AddressBook" - class State2 as "ab1:AddressBook" - class State3 as "ab3:AddressBook" -} - -State1 -[hidden]right-> State2 -State2 -[hidden]right-> State3 - -class Pointer as "Current State" #FFFFFF - -Pointer -up-> State3 -note right on link: State ab2 deleted. -@end diff --git a/docs/diagrams/UndoSequenceDiagram.puml b/docs/diagrams/UndoSequenceDiagram.puml deleted file mode 100644 index 87ff3e9237e..00000000000 --- a/docs/diagrams/UndoSequenceDiagram.puml +++ /dev/null @@ -1,54 +0,0 @@ -@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 "u:UndoCommand" as UndoCommand LOGIC_COLOR -end box - -box Model MODEL_COLOR_T1 -participant ":Model" as Model MODEL_COLOR -participant ":VersionedAddressBook" as VersionedAddressBook MODEL_COLOR -end box -[-> LogicManager : execute(undo) -activate LogicManager - -LogicManager -> AddressBookParser : parseCommand(undo) -activate AddressBookParser - -create UndoCommand -AddressBookParser -> UndoCommand -activate UndoCommand - -UndoCommand --> AddressBookParser -deactivate UndoCommand - -AddressBookParser --> LogicManager : u -deactivate AddressBookParser - -LogicManager -> UndoCommand : execute() -activate UndoCommand - -UndoCommand -> Model : undoAddressBook() -activate Model - -Model -> VersionedAddressBook : undo() -activate VersionedAddressBook - -VersionedAddressBook -> VersionedAddressBook :resetData(ReadOnlyAddressBook) -VersionedAddressBook --> Model : -deactivate VersionedAddressBook - -Model --> UndoCommand -deactivate Model - -UndoCommand --> LogicManager : result -deactivate UndoCommand -UndoCommand -[hidden]-> LogicManager : result -destroy UndoCommand - -[<--LogicManager -deactivate LogicManager -@enduml diff --git a/docs/diagrams/ViewActivityDiagram.puml b/docs/diagrams/ViewActivityDiagram.puml new file mode 100644 index 00000000000..ac53dfca475 --- /dev/null +++ b/docs/diagrams/ViewActivityDiagram.puml @@ -0,0 +1,28 @@ +@startuml +skin rose +skinparam ActivityFontSize 15 +skinparam ArrowFontSize 12 +start +:User executes a View Command; + +'Since the beta syntax does not support placing the condition outside the +'diamond we place it as the true branch instead. + +if () then ([correct format to view all]) + :View all students and appointments; + :Display Success Message in viewing + students and appointments; +elseif () then ([correct format to view students]) + :View all students; + :Display Success Message + in viewing students; +elseif () then ([correct format to view appointments]) + :View all appointments; + :Display Success Message + in viewing appointments; +else ([else]) + :Display message + format error; +endif +stop +@enduml diff --git a/docs/diagrams/ViewSequenceDiagram.puml b/docs/diagrams/ViewSequenceDiagram.puml new file mode 100644 index 00000000000..27039a65feb --- /dev/null +++ b/docs/diagrams/ViewSequenceDiagram.puml @@ -0,0 +1,77 @@ +@startuml +!include style.puml +skinparam ArrowFontStyle plain + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":WellNusParser" as WellNusParser LOGIC_COLOR +participant ":ViewCommandParser" as ViewCommandParser LOGIC_COLOR +participant "v:ViewCommand" as ViewCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box ModelManager MODEL_COLOR_T1 +participant ":ModelManager" as ModelManager MODEL_COLOR +participant "filteredStudents:FilteredList" as FilteredStudents MODEL_COLOR +end box + +[-> LogicManager : execute("view g/students") +activate LogicManager + +LogicManager -> WellNusParser : parseCommand("view g/students") +activate WellNusParser + +create ViewCommandParser +WellNusParser -> ViewCommandParser +activate ViewCommandParser + +ViewCommandParser --> WellNusParser +deactivate ViewCommandParser + +WellNusParser -> ViewCommandParser : parse("g/students") +activate ViewCommandParser + +create ViewCommand +ViewCommandParser -> ViewCommand +activate ViewCommand + +ViewCommand --> ViewCommandParser : v +deactivate ViewCommand + +ViewCommandParser --> WellNusParser : v +deactivate ViewCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +ViewCommandParser -[hidden]-> WellNusParser +destroy ViewCommandParser + +WellNusParser --> LogicManager : v +deactivate WellNusParser + +LogicManager -> ViewCommand : execute() +activate ViewCommand + +ViewCommand -> ModelManager : updateFilteredStudentList(unused -> true) +activate ModelManager + +ModelManager -> FilteredStudents : setPredicate(unused -> true) +activate FilteredStudents + +FilteredStudents --> ModelManager +deactivate FilteredStudents + +ModelManager --> ViewCommand +deactivate ModelManager + +create CommandResult +ViewCommand -> CommandResult +activate CommandResult + +CommandResult --> ViewCommand +deactivate CommandResult + +ViewCommand --> LogicManager : result +deactivate ViewCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/tracing/LogicSequenceDiagram.puml b/docs/diagrams/tracing/LogicSequenceDiagram.puml index 42bf46d3ce8..9e196abc896 100644 --- a/docs/diagrams/tracing/LogicSequenceDiagram.puml +++ b/docs/diagrams/tracing/LogicSequenceDiagram.puml @@ -3,20 +3,20 @@ skinparam ArrowFontStyle plain Participant ":LogicManager" as logic LOGIC_COLOR -Participant ":AddressBookParser" as abp LOGIC_COLOR +Participant ":WellNusParser" as wnp LOGIC_COLOR Participant ":EditCommandParser" as ecp LOGIC_COLOR Participant "command:EditCommand" as ec LOGIC_COLOR [-> logic : execute activate logic -logic -> abp ++: parseCommand(commandText) +logic -> wnp ++: parseCommand(commandText) create ecp -abp -> ecp -abp -> ecp ++: parse(arguments) +wnp -> ecp +wnp -> ecp ++: parse(arguments) create ec ecp -> ec ++: index, editPersonDescriptor ec --> ecp -- -ecp --> abp --: command -abp --> logic --: command +ecp --> wnp --: command +wnp --> logic --: command @enduml diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 37ad06a2803..429d8b0702e 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/CancelSequenceDiagram.png b/docs/images/CancelSequenceDiagram.png new file mode 100644 index 00000000000..96c036fd656 Binary files /dev/null and b/docs/images/CancelSequenceDiagram.png differ diff --git a/docs/images/ClashActivityDiagram.png b/docs/images/ClashActivityDiagram.png new file mode 100644 index 00000000000..b3ef854077c Binary files /dev/null and b/docs/images/ClashActivityDiagram.png differ diff --git a/docs/images/CommitActivityDiagram.png b/docs/images/CommitActivityDiagram.png deleted file mode 100644 index 5b464126b35..00000000000 Binary files a/docs/images/CommitActivityDiagram.png and /dev/null differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index e186f7ba096..31e60998dd5 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/GithubReleasePage.png b/docs/images/GithubReleasePage.png new file mode 100644 index 00000000000..44f7bfb30bb Binary files /dev/null and b/docs/images/GithubReleasePage.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index e3b784310fe..75c788398ad 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index a19fb1b4ac8..72b5b72f980 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/NoteSequenceDiagram.png b/docs/images/NoteSequenceDiagram.png new file mode 100644 index 00000000000..91e87e7d0a0 Binary files /dev/null and b/docs/images/NoteSequenceDiagram.png differ diff --git a/docs/images/OverlapSequenceDiagram.png b/docs/images/OverlapSequenceDiagram.png new file mode 100644 index 00000000000..d94d6618022 Binary files /dev/null and b/docs/images/OverlapSequenceDiagram.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index edfd1ff7897..b44aaadecf1 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 18fa4d0d51f..61cb980843c 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..3704e69e49d 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 11f06d68671..5908ac8eacc 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/UndoRedoState0.png b/docs/images/UndoRedoState0.png deleted file mode 100644 index c5f91b58533..00000000000 Binary files a/docs/images/UndoRedoState0.png and /dev/null differ diff --git a/docs/images/UndoRedoState1.png b/docs/images/UndoRedoState1.png deleted file mode 100644 index 2d3ad09c047..00000000000 Binary files a/docs/images/UndoRedoState1.png and /dev/null differ diff --git a/docs/images/UndoRedoState2.png b/docs/images/UndoRedoState2.png deleted file mode 100644 index 20853694e03..00000000000 Binary files a/docs/images/UndoRedoState2.png and /dev/null differ diff --git a/docs/images/UndoRedoState3.png b/docs/images/UndoRedoState3.png deleted file mode 100644 index 1a9551b31be..00000000000 Binary files a/docs/images/UndoRedoState3.png and /dev/null differ diff --git a/docs/images/UndoRedoState4.png b/docs/images/UndoRedoState4.png deleted file mode 100644 index 46dfae78c94..00000000000 Binary files a/docs/images/UndoRedoState4.png and /dev/null differ diff --git a/docs/images/UndoRedoState5.png b/docs/images/UndoRedoState5.png deleted file mode 100644 index f45889b5fdf..00000000000 Binary files a/docs/images/UndoRedoState5.png and /dev/null differ diff --git a/docs/images/UndoSequenceDiagram.png b/docs/images/UndoSequenceDiagram.png deleted file mode 100644 index c7a7e637266..00000000000 Binary files a/docs/images/UndoSequenceDiagram.png and /dev/null differ diff --git a/docs/images/ViewActivityDiagram.png b/docs/images/ViewActivityDiagram.png new file mode 100644 index 00000000000..0bef458616f Binary files /dev/null and b/docs/images/ViewActivityDiagram.png differ diff --git a/docs/images/ViewSequenceDiagram.png b/docs/images/ViewSequenceDiagram.png new file mode 100644 index 00000000000..ffacf54ed48 Binary files /dev/null and b/docs/images/ViewSequenceDiagram.png differ diff --git a/docs/images/darrentfy.png b/docs/images/darrentfy.png new file mode 100644 index 00000000000..71a9e297ce8 Binary files /dev/null and b/docs/images/darrentfy.png differ diff --git a/docs/images/filterFeature.png b/docs/images/filterFeature.png new file mode 100644 index 00000000000..f6f5850ef3e Binary files /dev/null and b/docs/images/filterFeature.png differ diff --git a/docs/images/findFeature.png b/docs/images/findFeature.png new file mode 100644 index 00000000000..e482de37bb7 Binary files /dev/null and b/docs/images/findFeature.png differ diff --git a/docs/images/hcs1203.png b/docs/images/hcs1203.png new file mode 100644 index 00000000000..940b811b119 Binary files /dev/null and b/docs/images/hcs1203.png differ diff --git a/docs/images/helpMessage.png b/docs/images/helpMessage.png index b1f70470137..c6b7a89336c 100644 Binary files a/docs/images/helpMessage.png and b/docs/images/helpMessage.png differ diff --git a/docs/images/quzhetao01.png b/docs/images/quzhetao01.png new file mode 100644 index 00000000000..b50a1823823 Binary files /dev/null and b/docs/images/quzhetao01.png differ diff --git a/docs/images/sheeepen.png b/docs/images/sheeepen.png new file mode 100644 index 00000000000..63fc14ac206 Binary files /dev/null and b/docs/images/sheeepen.png differ diff --git a/docs/images/simwperic.png b/docs/images/simwperic.png new file mode 100644 index 00000000000..f25e51fc976 Binary files /dev/null and b/docs/images/simwperic.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..32789025afb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,28 @@ --- layout: page -title: AddressBook Level-3 +title: WellNUS --- -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) -[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) +[![CI Status](https://github.com/AY2324S1-CS2103T-W13-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/sAY2324S1-CS2103T-W13-4/tp/actions) +[![codecov](https://codecov.io/gh/AY2324S1-CS2103T-W13-4/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2324S1-CS2103T-W13-4/tp) ![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). +WellNUS is a contact management application for NUS Student Counsellors. +WellNUS helps counsellors better schedule their appointments with students, as well as store the information of students for easy referencing. -* 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. +Example usages: +* adding, deleting and viewing student profiles +* tag students with risk levels depending on their condition +* adding, deleting and viewing appointments + + +If you are interested in using WellNUS, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). + +If you are interested about developing WellNUS, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** * Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) +* This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org). diff --git a/docs/team/darrentfy.md b/docs/team/darrentfy.md new file mode 100644 index 00000000000..ea546e0b344 --- /dev/null +++ b/docs/team/darrentfy.md @@ -0,0 +1,51 @@ +--- +layout: page +title: Darren's Project Portfolio Page +--- + +### Project: WellNUS + +WellNUS is a desktop application used by NUS Counsellors to manage and schedule appointments with their student clients. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. + +Summary of Contributions: + +* **Enhancements implemented:** + +1. Refactored the Person class into Student class [#101](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/101) + * Existing Person class in AddressBook-Level3(AB3) does not align with what we wanted with WellNUS, had some + redundant fields and some missing fields, for example Email class was removed. + * This was quite a large refactor as removing fields from Person involved changes to all other classes and tests + in AB3 which had dependencies or associations with the Person class. + +2. Added the ability to add/delete a note for a Student [#131](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/131) + * Counsellors may want to add additional notes for each student to help them monitor progress. + * This feature allows counsellors to add a note and overwrite existing notes. + +3. Added functionality to the 'delete Student' feature [#127](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/127), + * Initially, when a Student was deleted from WellNUS, the Appointments with the Student's name were not removed, + which resulted in Appointments containing Students that were no longer in WellNUS. + * Added functionality such that deleting a Student will delete the related Appointments. + +4. Wrote tests for the Schedule Command and its parser [#113](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/113) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=darrentfy&breakdown=true) + +* **Documentation**: + * User Guide: + * Added documentation for the features `schedule` and `note` [#48](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/48), + [#151](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/151) + * Added links to relevant sections in Command Summary + * Developer Guide: + * Added implementation details of the `note` feature [#136](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/136) + * Added Use Cases 3, 4 and 5 [#52](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/52) + +* **Contributions to team-based tasks** + * Managed v1.2.1 and v1.3 releases + +* **Review/mentoring contributions** + * PRs reviewed: [#80](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/80), + [#83](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/83), + [#92](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/92), + [#137](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/137), + [#149](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/149) diff --git a/docs/team/hcs1203.md b/docs/team/hcs1203.md new file mode 100644 index 00000000000..98499b1526d --- /dev/null +++ b/docs/team/hcs1203.md @@ -0,0 +1,38 @@ +--- +layout: page +title: Hannah's Project Portfolio Page +--- + +### Project: WellNUS + +WellNUS is a desktop application used by NUS Counsellors to manage and schedule appointments with their student clients. The user interacts with it using a CLI, and it has a GUI created with JavaFX. + +Given below are my contributions to the project. + +Summary of Contributions: + +* **Enhancements implemented:** + +1. Created the functionality to schedule an appointment. + * Added the command that will allow for counsellors to add their appointments into the app [#77](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/77) [#86](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/86) + * Added storage functionality for the scheduled appointments [#100](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/100) + * Added a feature to check if the student exists in the data [#159](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/159) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=hcs1203&breakdown=true) + +* **Documentation**: + * User Guide: + * Add documentation for tag feature [#62](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/62) + * Update command summary for UG [#63](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/63) + * Edited edit command UG documentation to match UG to behaviour [#236](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/236) + * Developer Guide: + * Add UC 9, 10 ,11, 12 to DG [#64](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/64) + * Added implementation details and sequence diagram for Schedule command feature. [#142](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/142) + +* **Review/mentoring contributions** + * PRs Reviewed: [#79](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/79), + [#83](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/83), + [#101](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/101), + [#114](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/114), + [#134](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/134), + [#260](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/260) diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md deleted file mode 100644 index 773a07794e2..00000000000 --- a/docs/team/johndoe.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -layout: page -title: John Doe's Project Portfolio Page ---- - -### Project: AddressBook Level 3 - -AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC. - -Given below are my contributions to the project. - -* **New Feature**: Added the ability to undo/redo previous commands. - * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command. - * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them. - * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands. - * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}* - -* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys. - -* **Code contributed**: [RepoSense link]() - -* **Project management**: - * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub - -* **Enhancements to existing features**: - * Updated the GUI color scheme (Pull requests [\#33](), [\#34]()) - * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]()) - -* **Documentation**: - * User Guide: - * Added documentation for the features `delete` and `find` [\#72]() - * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]() - * Developer Guide: - * Added implementation details of the `delete` feature. - -* **Community**: - * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]() - * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]()) - * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]()) - * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]()) - -* **Tools**: - * Integrated a third party library (Natty) to the project ([\#42]()) - * Integrated a new Github plugin (CircleCI) to the team repo - -* _{you can add/remove categories in the list above}_ diff --git a/docs/team/quzhetao01.md b/docs/team/quzhetao01.md new file mode 100644 index 00000000000..3904be9eac6 --- /dev/null +++ b/docs/team/quzhetao01.md @@ -0,0 +1,97 @@ +--- +layout: page +title: Zhetao's Project Portfolio Page +--- + +### Project: WellNUS + +WellNUS is a desktop application used by NUS Counsellors to manage and schedule appointments with their student clients. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. + +Summary of Contributions: + +* **Enhancements implemented:** + +1. Created the functionality to view appointments + * Added a second column to allow viewing of students and appointments concurrently. [#79](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/79) + * Added a view command that can handle the displaying of both students and appointments. This involved creating new Ui elements + such as AppointmentListCard that shows the details of an appointment. [#92](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/92) + * Adding test cases for the new ViewCommand and ViewCommand parser. [#98](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/98) + +2. Added the functionality to store and load appointments from the json file [#102](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/102) + * Added JsonAdaptedAppointment to handle the storing and loading of appointments to persist the data + * Added Test Cases for this new functionality of loading appointment [#129](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/129) + +3. Added the functionality to filter appointments based on filtered students [#145](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/145) + * Created predicates necessary to filter the UniqueAppointmentList + * Enhanced find feature to include the said functionality + * Add test cases for the improved find command [#153](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/153) + +4. Added view notes functionality + * Added a third column dedicated to displaying a student note (which can be a paragraph long) + * Added double-click functionality on student card to display the student notes, including the design of displaying of student notes + +5. Implemented TypicalWellNus which serves as the default set of data for testing [#153](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/153) + +6. Fix edit command + * Remove name parameter from Edit Command as a person's name should not change [#171](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/171) + * Remove risk parameter from Edit Command as it is handled by tag command [#253](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/253) + +7. Fix bugs + * Fix invalid note stored crashing the application [#249](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/249) + * Fix note panel details persisting after different commands [#252](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/252) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=quzhetao01&breakdown=true) + + +* **Documentation**: + * User Guide: + * Updated UG skeleton to replace AB3 [#34](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/34) + * Added user guide for view feature [#153](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/153) + * Added user guide for find feature [#145](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/145) + * Added details on how to view student notes [#252](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/252) + * Developer Guide: + * Added Use Cases UC01 and UC02 [#49](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/49) + * Added implementation details of the view feature, including a ViewSequenceDiagram and ViewActivityDiagram [#135](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/135), updated it on [#254](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/254) + + +* **Contributions to team-based tasks** +1. Managed the closing of milestone 1.2 and 1.3 +2. Refactored terms with "AddressBook" by replacing it with "WellNUS" [#92](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/92) [#129](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/129) + * Many original files and classes were named based off AddressBook such as "JSONSerializableAddressBook". Our application we design + is not an addressbook, therefore we decide to do away inaccuracy of having "addressbook" in our classes and files. + * Ensuring that pull requests of other members are tagged to the relevant issues and helping to close issues when they are done + +* **Review/mentoring contributions** + * PRs reviewed: [#80](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/80), + [#83](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/83), + [#85](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/85), + [#88](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/88), + [#90](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/90), + [#95](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/95), + [#96](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/96), + [#99](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/99), + [#101](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/101), + [#103](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/103), + [#104](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/104), + [#109](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/109), + [#112](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/112), + [#113](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/113), + [#126](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/126), + [#127](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/127), + [#131](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/131), + [#136](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/136), + [#143](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/143), + [#152](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/152), + [#162](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/162), + [#164](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/164), + [#172](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/172), + [#173](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/173), + [#174](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/174), + [#238](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/238), + [#239](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/239), + [#242](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/242), + + +* **Contributions beyond project team** + * Gave my thoughts on how to resolve a bug I had reported for another group I was reviewing for. [PR Link](https://github.com/AY2324S1-CS2103T-T13-2/tp/issues/180#event-10920070185) diff --git a/docs/team/sheeepen.md b/docs/team/sheeepen.md new file mode 100644 index 00000000000..d8defb76069 --- /dev/null +++ b/docs/team/sheeepen.md @@ -0,0 +1,67 @@ +--- +layout: page +title: Stephen's Project Portfolio Page +--- + +### Project: WellNUS + +WellNUS is a desktop application used by NUS Counsellors to manage and schedule appointments with their student clients. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. + +Summary of Contributions: + +* **Enhancements implemented:** + +1. Refactored Tag class into RiskLevel class. + * Allows for Students to be assigned to a certain Risk Level determined by the counsellor. [#94](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/94) + * Limited the size of RiskLevel to 1 by implementing a LimitedHashSet class. [#99](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/99) + * Added functionality to tag students to risk levels. [#149](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/149) + * Updated test cases accordingly. [#112](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/112) + +2. Implemented check overlapping appointments feature + * WellNUS detects any overlapping/clashing appointments everytime a new appointment is scheduled. [#158](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/158) + * Also include check when loading from the WellNUS storage. [#235](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/235) + * Updating test cases to reflect this was tricky. ModelStub and other classes had to be modified to adjust for the new dependency. [#158](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/158) + +3. Implemented check student exists for appointment feature + * WellNUS ensures the student exists in the database for the appointment created. [#160](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/160) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=sheeepen&breakdown=true) + +* **Documentation**: + * User Guide: + * Constantly updated command summary table [#53](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/53), [#65](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/65) + * Added View Students, Delete Students sections [#57](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/57) + * Updated with new `Tag` command for RiskLevel [#157](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/157) + * Added the constraints table for all prefixes and parameters [#237](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/237) + * Enhanced visuals [#237](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/237) + * Developer Guide: + * Updated NFR and Glossary [#33](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/33) + * Updated User Stories [#109](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/109) + * Added UML diagrams for check overlapping appointments feature. [#137](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/137) + +* **Contributions to team-based tasks** + * Concluded v1.2 postmortem and demo screenshots + * Handled submissions for various deadlines + * Standardised formatting and language use in User Guide +* **Review/mentoring contributions** + * PRs reviewed: [#88](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/88), + [#90](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/90), + [#92](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/92), + [#98](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/98), + [#99](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/99), + [#102](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/102), + [#104](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/104), + [#113](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/113), + [#114](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/114), + [#125](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/125), + [#126](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/126), + [#135](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/135), + [#154](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/154), + [#155](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/155), + [#161](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/161), + [#167](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/167), + [#238](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/238), + [#252](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/252) + + diff --git a/docs/team/simwperic.md b/docs/team/simwperic.md new file mode 100644 index 00000000000..d1586b2a14a --- /dev/null +++ b/docs/team/simwperic.md @@ -0,0 +1,80 @@ +--- +layout: page +title: Eric's Project Portfolio Page +--- + +### Project: WellNUS + +WellNUS is a desktop application used by NUS Counsellors to manage and schedule appointments with their student clients. +The user interacts with it using a CLI, and it has a GUI created with JavaFX. + +Summary of Contributions: + +* **Enhancements implemented:** + +1. Added the functionality to cancel an appointment + * This feature allows counsellors to cancel an appointment, necessary to rectify errors in an existing appointment, or simply + just to delete an invalid appointment. + [#83](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/83), + [#88](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/88), + [#90](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/90), + [#96](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/96) + +2. Added the functionality to filter appointments by date [#134](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/134) + * This feature allows counsellors to view their appointments on a specific date. + * Refactor `DateTime` attribute of `Appointment` into `Date` and `Time` [#126](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/126) + +3. Added the functionality to automatically sort appointments by date and time [#152](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/152) + * This feature allows an organised overview of appointments for counsellors. + * Works in conjunction with other commands such as schedule appointment, cancel appointment, filter appointment by date. + The appointments displayed to counsellors will always be sorted by date and time. + +4. Improved UI of application + * Implemented window resizing. [#125](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/125) + * Added colors for risk levels, low, medium, high to be green, blue and red respectively. [#150](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/150) + * Adjusted window dimensions to accommodate new StudentNotePanel. [#164](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/164) + +* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=simwperic&breakdown=true) + +* **Documentation**: + * User Guide: + * Responsible for the indexing of documentation. [#143](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/143) + * Added documentation for cancel appointment feature, filter appointment feature and automatic appointment sort feature. [#154](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/154) + * Added images for advanced features such as filter appointment feature and find student feature. [#238](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/238) + * Added Frequently Asked Questions [#238](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/238) + * Developer Guide: + * Responsible for the indexing of documentation. [#155](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/155) + * Added Use Cases UC06, UC07 and UC08. [#61](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/61) + * Added implementation details and sequence diagram for cancel appointment feature. [#139](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/139) + +* **Contributions to team-based tasks** + * Set up GitHub team organisation and repository + * Opened v1.2, v.1.3, v1.4 milestones + * Enabled assertions [#128](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/128) + +* **Review/Mentoring Contributions:** + * PRs Reviewed: [#98](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/98), + [#99](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/99), + [#103](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/103), + [#109](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/109), + [#110](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/110), + [#129](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/129), + [#130](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/130), + [#131](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/131), + [#138](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/138), + [#145](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/145), + [#146](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/146), + [#149](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/149), + [#151](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/151), + [#153](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/153), + [#157](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/157), + [#158](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/158), + [#166](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/166), + [#169](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/169), + [#233](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/233), + [#240](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/240), + [#244](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/244), + [#246](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/246), + [#254](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/254), + [#255](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/255), + [#256](https://github.com/AY2324S1-CS2103T-W13-4/tp/pull/256) diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index d98f38982e7..01712c1b013 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -227,7 +227,7 @@ Now that we have all the information that we need, let’s lay the groundwork fo ### Add a new `Remark` class -Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. +Create a new `Remark` in `seedu.address.model.student`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-41bb13c581e280c686198251ad6cc337cd5e27032772f06ed9bf7f1440995ece). Note how `Remark` has no constrains and thus does not require input validation. @@ -240,7 +240,7 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). +Simply add the following to [`seedu.address.ui.StudentCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-639834f1e05afe2276a86372adf0fe5f69314642c2d93cfa543d614ce5a76688). **`PersonCard.java`:** @@ -293,7 +293,7 @@ While the changes to code may be minimal, the test data will have to be updated
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book! +:exclamation: You must delete AddressBook’s storage file located at `/data/wellnus.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
@@ -344,7 +344,7 @@ save it with `Model#setPerson()`. Person personToEdit = lastShownList.get(index.getZeroBased()); Person editedPerson = new Person( personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), - personToEdit.getAddress(), remark, personToEdit.getTags()); + personToEdit.getAddress(), remark, personToEdit.getRiskLevel()); model.setPerson(personToEdit, editedPerson); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index f29169bc924..1f8f05e80f6 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -28,7 +28,7 @@ IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a re ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `seedu.address.model.student.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. * :bulb: To make things simpler, you can unselect the options `Search in comments and strings` and `Search for text occurrences` ![Usages detected](../images/remove/UnsafeDelete.png) diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index 3d6bd06d5af..2953573d9be 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -15,19 +15,19 @@ import seedu.address.commons.util.StringUtil; import seedu.address.logic.Logic; import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.ReadOnlyWellNus; import seedu.address.model.UserPrefs; +import seedu.address.model.WellNus; import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; import seedu.address.storage.JsonUserPrefsStorage; +import seedu.address.storage.JsonWellNusStorage; import seedu.address.storage.Storage; import seedu.address.storage.StorageManager; import seedu.address.storage.UserPrefsStorage; +import seedu.address.storage.WellNusStorage; import seedu.address.ui.Ui; import seedu.address.ui.UiManager; @@ -36,7 +36,7 @@ */ public class MainApp extends Application { - public static final Version VERSION = new Version(0, 2, 2, true); + public static final Version VERSION = new Version(1, 4, 1, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -48,7 +48,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing WellNUS ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -57,8 +57,8 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); - AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); - storage = new StorageManager(addressBookStorage, userPrefsStorage); + WellNusStorage wellNusStorage = new JsonWellNusStorage(userPrefs.getWellNusFilePath()); + storage = new StorageManager(wellNusStorage, userPrefsStorage); model = initModelManager(storage, userPrefs); @@ -68,26 +68,26 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * Returns a {@code ModelManager} with the data from {@code storage}'s wellnus storage and {@code userPrefs}.
+ * The data from the sample wellnus storage will be used instead if {@code storage}'s wellnus storage is not found, + * or an empty wellnus storage will be used instead if errors occur when reading {@code storage}'s wellnus storage. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - logger.info("Using data file : " + storage.getAddressBookFilePath()); + logger.info("Using data file : " + storage.getWellNusFilePath()); - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional wellNusOptional; + ReadOnlyWellNus initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Creating a new data file " + storage.getAddressBookFilePath() - + " populated with a sample AddressBook."); + wellNusOptional = storage.readWellNus(); + if (!wellNusOptional.isPresent()) { + logger.info("Creating a new data file " + storage.getWellNusFilePath() + + " populated with a sample data file."); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = wellNusOptional.orElseGet(SampleDataUtil::getSampleWellNus); } catch (DataLoadingException e) { - logger.warning("Data file at " + storage.getAddressBookFilePath() + " could not be loaded." - + " Will be starting with an empty AddressBook."); - initialData = new AddressBook(); + logger.warning("Data file at " + storage.getWellNusFilePath() + " could not be loaded." + + " Will be starting with an empty data file."); + initialData = new WellNus(); } return new ModelManager(initialData, userPrefs); @@ -170,13 +170,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting WellNUS " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping WellNUS ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..58b7ea11ace 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -7,8 +7,11 @@ import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.ReadOnlyWellNus; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Student; /** * API of the Logic component @@ -24,19 +27,27 @@ public interface Logic { CommandResult execute(String commandText) throws CommandException, ParseException; /** - * Returns the AddressBook. + * Returns the WellNus. * - * @see seedu.address.model.Model#getAddressBook() + * @see seedu.address.model.Model#getWellNusData() */ - ReadOnlyAddressBook getAddressBook(); + ReadOnlyWellNus getWellNus(); - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); + /** Returns an unmodifiable view of the filtered list of students */ + ObservableList getFilteredStudentList(); + /** Returns an unmodifiable view of the filtered list of appointments */ + ObservableList getFilteredAppointmentList(); + + /** Return the Note of a particular student denoted by its index */ + Note getStudentNote(int studentIndex); + + /** Return the Name of a particular student denoted by its index */ + Name getStudentName(int studentIndex); /** - * Returns the user prefs' address book file path. + * Returns the user prefs' wellnus storage file path. */ - Path getAddressBookFilePath(); + Path getWellNusFilePath(); /** * Returns the user prefs' GUI settings. diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 5aa3b91c7d0..c5d93f53942 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -11,11 +11,14 @@ import seedu.address.logic.commands.Command; import seedu.address.logic.commands.CommandResult; import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.logic.parser.AddressBookParser; +import seedu.address.logic.parser.WellNusParser; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; +import seedu.address.model.ReadOnlyWellNus; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Student; import seedu.address.storage.Storage; /** @@ -31,7 +34,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final WellNusParser wellNusParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -39,7 +42,7 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + wellNusParser = new WellNusParser(); } @Override @@ -47,11 +50,11 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = wellNusParser.parseCommand(commandText); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveWellNus(model.getWellNusData()); } catch (AccessDeniedException e) { throw new CommandException(String.format(FILE_OPS_PERMISSION_ERROR_FORMAT, e.getMessage()), e); } catch (IOException ioe) { @@ -62,18 +65,33 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlyWellNus getWellNus() { + return model.getWellNusData(); } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredStudentList() { + return model.getFilteredStudentList(); } @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public ObservableList getFilteredAppointmentList() { + return model.getFilteredAppointmentList(); + } + + @Override + public Note getStudentNote(int studentIndex) { + return getFilteredStudentList().get(studentIndex - 1).getNote(); + } + + @Override + public Name getStudentName(int studentIndex) { + return getFilteredStudentList().get(studentIndex - 1).getName(); + } + + @Override + public Path getWellNusFilePath() { + return model.getWellNusFilePath(); } @Override diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index ecd32c31b53..0e3c3e46036 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -5,7 +5,8 @@ import java.util.stream.Stream; import seedu.address.logic.parser.Prefix; -import seedu.address.model.person.Person; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Student; /** * Container for user visible messages. @@ -14,11 +15,17 @@ public class Messages { public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; + public static final String MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX = "Invalid index! Index not found in the list"; + public static final String MESSAGE_STUDENTS_LISTED_OVERVIEW = "%1$d students listed!"; + public static final String MESSAGE_APPOINTMENTS_LISTED_OVERVIEW = "%1$d appointments listed!"; public static final String MESSAGE_DUPLICATE_FIELDS = "Multiple values specified for the following single-valued field(s): "; + public static final String MESSAGE_INVALID_APPOINTMENT_DISPLAYED_INDEX = + "The appointment index provided is invalid"; + public static final String MESSAGE_INVALID_START_END_TIME = + "Start time must be before end time"; + /** * Returns an error message indicating the duplicate prefixes. */ @@ -32,20 +39,34 @@ public static String getErrorMessageForDuplicatePrefixes(Prefix... duplicatePref } /** - * Formats the {@code person} for display to the user. + * Formats the {@code student} for display to the user. */ - public static String format(Person person) { + public static String format(Student student) { final StringBuilder builder = new StringBuilder(); - builder.append(person.getName()) + builder.append(student.getName()) .append("; Phone: ") - .append(person.getPhone()) - .append("; Email: ") - .append(person.getEmail()) + .append(student.getPhone()) .append("; Address: ") - .append(person.getAddress()) - .append("; Tags: "); - person.getTags().forEach(builder::append); + .append(student.getAddress()) + .append("; Risk Level: "); + student.getRiskLevel().forEach(builder::append); return builder.toString(); } + /** + * Formats the {@code appointment} for display to the user. + */ + public static String format(Appointment appointment) { + final StringBuilder builder = new StringBuilder(); + builder.append(appointment.getName()) + .append("; Date: ") + .append(appointment.getDate()) + .append("; Start Time: ") + .append(appointment.getStartTime()) + .append("; End Time: ") + .append(appointment.getEndTime()) + .append("; Description: ") + .append(appointment.getDescription()); + return builder.toString(); + } } diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 5d7185a9680..d3a1ea80a40 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -2,61 +2,58 @@ import static java.util.Objects.requireNonNull; 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 static seedu.address.logic.parser.CliSyntax.PREFIX_RISK_LEVEL; 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.person.Person; +import seedu.address.model.student.Student; /** - * Adds a person to the address book. + * Adds a student to the wellnus storage. */ public class AddCommand extends Command { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a student to the student list. " + "Parameters: " + PREFIX_NAME + "NAME " + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" + + "[" + PREFIX_RISK_LEVEL + "RISK_LEVEL" + "] " + + "(RISK_LEVEL is optional)\n" + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "John Doe " + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; + + PREFIX_RISK_LEVEL + "high"; - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; + public static final String MESSAGE_SUCCESS = "New student profile added: %1$s"; + public static final String MESSAGE_DUPLICATE_STUDENT = "This student already exists in the student list!"; - private final Person toAdd; + private final Student toAdd; /** - * Creates an AddCommand to add the specified {@code Person} + * Creates an AddCommand to add the specified {@code Student} */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; + public AddCommand(Student student) { + requireNonNull(student); + toAdd = student; } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); + if (model.hasStudent(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_STUDENT); } - model.addPerson(toAdd); + model.addStudent(toAdd); return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd))); } diff --git a/src/main/java/seedu/address/logic/commands/CancelCommand.java b/src/main/java/seedu/address/logic/commands/CancelCommand.java new file mode 100644 index 00000000000..a95467eeb25 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/CancelCommand.java @@ -0,0 +1,71 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +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; + +/** + * Cancels an appointment identified using it's displayed index from the wellnus storage. + */ +public class CancelCommand extends Command { + + public static final String COMMAND_WORD = "cancel"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Cancels the appointment identified by the index number used in the displayed appointment list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_CANCEL_APPOINTMENT_SUCCESS = "Cancelled Appointment: %1$s"; + + private final Index targetIndex; + + public CancelCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredAppointmentList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_APPOINTMENT_DISPLAYED_INDEX); + } + + Appointment appointmentToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteAppointment(appointmentToDelete); + return new CommandResult(String.format(MESSAGE_CANCEL_APPOINTMENT_SUCCESS, + Messages.format(appointmentToDelete))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof CancelCommand)) { + return false; + } + + CancelCommand otherCancelCommand = (CancelCommand) other; + return targetIndex.equals(otherCancelCommand.targetIndex); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("targetIndex", targetIndex) + .toString(); + } +} + diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..835a20ace3d 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -2,22 +2,22 @@ import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; import seedu.address.model.Model; +import seedu.address.model.WellNus; /** - * Clears the address book. + * Clears the WellNus storage. */ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_SUCCESS = "Student and appointment list has been cleared!"; @Override public CommandResult execute(Model model) { requireNonNull(model); - model.setAddressBook(new AddressBook()); + model.setWellNusData(new WellNus()); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java index 1135ac19b74..7b920d6b530 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java @@ -9,21 +9,21 @@ import seedu.address.logic.Messages; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.student.Student; /** - * Deletes a person identified using it's displayed index from the address book. + * Deletes a student identified using it's displayed index from the wellnus storage. */ public class DeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" + + ": Deletes the student identified by the index number used in the displayed student list.\n" + "Parameters: INDEX (must be a positive integer)\n" + "Example: " + COMMAND_WORD + " 1"; - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; + public static final String MESSAGE_DELETE_STUDENT_SUCCESS = "Successfully deleted student: %1$s"; private final Index targetIndex; @@ -34,15 +34,15 @@ public DeleteCommand(Index targetIndex) { @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + List lastShownList = model.getFilteredStudentList(); if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); } - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete))); + Student studentToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteStudent(studentToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_STUDENT_SUCCESS, Messages.format(studentToDelete))); } @Override diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index 4b581c7331e..b154a778d32 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -2,14 +2,9 @@ import static java.util.Objects.requireNonNull; 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 static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS; -import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -21,87 +16,78 @@ import seedu.address.logic.Messages; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Phone; +import seedu.address.model.student.Student; /** - * Edits the details of an existing person in the address book. + * Edits the details of an existing student in the wellnus storage. */ public class EditCommand extends Command { public static final String COMMAND_WORD = "edit"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the student identified " + + "by the index number used in the displayed student list. " + "Existing values will be overwritten by the input values.\n" + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" + + "[" + PREFIX_ADDRESS + "ADDRESS]\n" + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; + + PREFIX_PHONE + "91234567"; - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; + public static final String MESSAGE_EDIT_STUDENT_SUCCESS = "Edited Student: %1$s"; public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; private final Index index; - private final EditPersonDescriptor editPersonDescriptor; + private final EditStudentDescriptor editStudentDescriptor; /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with + * @param index of the student in the filtered student list to edit + * @param editStudentDescriptor details to edit the student with */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { + public EditCommand(Index index, EditStudentDescriptor editStudentDescriptor) { requireNonNull(index); - requireNonNull(editPersonDescriptor); + requireNonNull(editStudentDescriptor); this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); + this.editStudentDescriptor = new EditStudentDescriptor(editStudentDescriptor); } @Override public CommandResult execute(Model model) throws CommandException { requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); + List lastShownList = model.getFilteredStudentList(); if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); } - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); + Student studentToEdit = lastShownList.get(index.getZeroBased()); + Student editedStudent = createEditedStudent(studentToEdit, editStudentDescriptor); - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson))); + model.setStudent(studentToEdit, editedStudent); + model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS); + return new CommandResult(String.format(MESSAGE_EDIT_STUDENT_SUCCESS, Messages.format(editedStudent))); } /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. + * Creates and returns a {@code Student} with the details of {@code studentToEdit} + * edited with {@code editStudentDescriptor}. */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + private static Student createEditedStudent(Student studentToEdit, EditStudentDescriptor editStudentDescriptor) { + assert studentToEdit != null; + Name updatedName = studentToEdit.getName(); + Phone updatedPhone = editStudentDescriptor.getPhone().orElse(studentToEdit.getPhone()); + Address updatedAddress = editStudentDescriptor.getAddress().orElse(studentToEdit.getAddress()); + Set sameTags = studentToEdit.getRiskLevel(); // tags cannot be edited by edit command + Note updatedNote = studentToEdit.getNote(); + + return new Student(updatedName, updatedPhone, updatedAddress, sameTags, updatedNote); } @Override @@ -117,55 +103,42 @@ public boolean equals(Object other) { EditCommand otherEditCommand = (EditCommand) other; return index.equals(otherEditCommand.index) - && editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor); + && editStudentDescriptor.equals(otherEditCommand.editStudentDescriptor); } @Override public String toString() { return new ToStringBuilder(this) .add("index", index) - .add("editPersonDescriptor", editPersonDescriptor) + .add("editStudentDescriptor", editStudentDescriptor) .toString(); } /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. + * Stores the details to edit the student with. Each non-empty field value will replace the + * corresponding field value of the student. */ - public static class EditPersonDescriptor { - private Name name; + public static class EditStudentDescriptor { + private Phone phone; - private Email email; private Address address; - private Set tags; - public EditPersonDescriptor() {} + public EditStudentDescriptor() {} /** * Copy constructor. * A defensive copy of {@code tags} is used internally. */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); + public EditStudentDescriptor(EditStudentDescriptor toCopy) { setPhone(toCopy.phone); - setEmail(toCopy.email); setAddress(toCopy.address); - setTags(toCopy.tags); } /** * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); - } - - public void setName(Name name) { - this.name = name; - } - - public Optional getName() { - return Optional.ofNullable(name); + return CollectionUtil.isAnyNonNull(phone, address); } public void setPhone(Phone phone) { @@ -176,14 +149,6 @@ public Optional getPhone() { return Optional.ofNullable(phone); } - public void setEmail(Email email) { - this.email = email; - } - - public Optional getEmail() { - return Optional.ofNullable(email); - } - public void setAddress(Address address) { this.address = address; } @@ -192,22 +157,6 @@ public Optional
getAddress() { return Optional.ofNullable(address); } - /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. - */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; - } - - /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. - */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); - } @Override public boolean equals(Object other) { @@ -216,26 +165,20 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { + if (!(other instanceof EditStudentDescriptor)) { return false; } - EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other; - return Objects.equals(name, otherEditPersonDescriptor.name) - && Objects.equals(phone, otherEditPersonDescriptor.phone) - && Objects.equals(email, otherEditPersonDescriptor.email) - && Objects.equals(address, otherEditPersonDescriptor.address) - && Objects.equals(tags, otherEditPersonDescriptor.tags); + EditStudentDescriptor otherEditStudentDescriptor = (EditStudentDescriptor) other; + return Objects.equals(phone, otherEditStudentDescriptor.phone) + && Objects.equals(address, otherEditStudentDescriptor.address); } @Override public String toString() { return new ToStringBuilder(this) - .add("name", name) .add("phone", phone) - .add("email", email) .add("address", address) - .add("tags", tags) .toString(); } } diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..b5bd8b87eb4 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -9,7 +9,7 @@ public class ExitCommand extends Command { public static final String COMMAND_WORD = "exit"; - public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ..."; + public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting WellNUS as requested ..."; @Override public CommandResult execute(Model model) { diff --git a/src/main/java/seedu/address/logic/commands/FilterCommand.java b/src/main/java/seedu/address/logic/commands/FilterCommand.java new file mode 100644 index 00000000000..635c992ab5c --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/FilterCommand.java @@ -0,0 +1,58 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.Messages; +import seedu.address.model.Model; +import seedu.address.model.appointment.AppointmentDateMatchesPredicate; + +/** + * Filters and lists appointments in the wellnus storage that match the specified date. + * Date matching is case insensitive. + */ +public class FilterCommand extends Command { + + public static final String COMMAND_WORD = "filter"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters and lists appointments by the specified " + + "date.\n" + "Parameters: DATE (in the format yyyy-MM-dd)\n" + + "Example: " + COMMAND_WORD + " 2023-10-26"; + + private final AppointmentDateMatchesPredicate predicate; + + public FilterCommand(AppointmentDateMatchesPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredAppointmentList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_APPOINTMENTS_LISTED_OVERVIEW, + model.getFilteredAppointmentList().size())); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof FilterCommand)) { + return false; + } + + FilterCommand otherFilterCommand = (FilterCommand) other; + return predicate.equals(otherFilterCommand.predicate); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("predicate", predicate) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java index 72b9eddd3a7..03878d25bf5 100644 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ b/src/main/java/seedu/address/logic/commands/FindCommand.java @@ -2,36 +2,52 @@ import static java.util.Objects.requireNonNull; +import java.util.Arrays; + import seedu.address.commons.util.ToStringBuilder; import seedu.address.logic.Messages; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.appointment.AppointmentContainsNamePredicate; +import seedu.address.model.student.NameContainsKeywordsPredicate; + /** - * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Finds and lists all students in wellnus storage whose name contains any of the argument keywords. * Keyword matching is case insensitive. */ public class FindCommand extends Command { public static final String COMMAND_WORD = "find"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all persons whose names contain any of " + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all students whose names matches all of " + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" - + "Example: " + COMMAND_WORD + " alice bob charlie"; + + "Example (finding by first name): " + COMMAND_WORD + " bernice\n" + + "Example (finding by full name): " + COMMAND_WORD + " bernice yu"; + + private final String[] nameKeywords; - private final NameContainsKeywordsPredicate predicate; + /** + * @param nameKeywords array of name strings to match + */ + public FindCommand(String[] nameKeywords) { + this.nameKeywords = nameKeywords; - public FindCommand(NameContainsKeywordsPredicate predicate) { - this.predicate = predicate; } @Override public CommandResult execute(Model model) { requireNonNull(model); - model.updateFilteredPersonList(predicate); + NameContainsKeywordsPredicate studentPredicate = new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)); + AppointmentContainsNamePredicate appointmentPredicate = + new AppointmentContainsNamePredicate(Arrays.asList(nameKeywords)); + // reset to all students and appointments + model.updateFilteredStudentList(unused -> true); + model.updateFilteredAppointmentList(unused -> true); + model.updateFilteredStudentList(studentPredicate); + model.updateFilteredAppointmentList(appointmentPredicate); return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + String.format(Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW, model.getFilteredStudentList().size())); } @Override @@ -46,13 +62,20 @@ public boolean equals(Object other) { } FindCommand otherFindCommand = (FindCommand) other; - return predicate.equals(otherFindCommand.predicate); + for (int i = 0; i < nameKeywords.length; i++) { + if (!nameKeywords[i].equals(otherFindCommand.nameKeywords[i])) { + return false; + } + } + return true; } @Override public String toString() { - return new ToStringBuilder(this) - .add("predicate", predicate) - .toString(); + ToStringBuilder str = new ToStringBuilder(this); + for (int i = 0; i < nameKeywords.length; i++) { + str.add("name keyword " + String.valueOf(i), nameKeywords[i]); + } + return str.toString(); } } diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java deleted file mode 100644 index 84be6ad2596..00000000000 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import seedu.address.model.Model; - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_SUCCESS = "Listed all persons"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/commands/NoteCommand.java b/src/main/java/seedu/address/logic/commands/NoteCommand.java new file mode 100644 index 00000000000..38ce36861f0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/NoteCommand.java @@ -0,0 +1,93 @@ +package seedu.address.logic.commands; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS; + +import java.util.List; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.student.Note; +import seedu.address.model.student.Student; + +/** + * Changes the note of an existing person in the wellnus storage. + */ +public class NoteCommand extends Command { + + public static final String COMMAND_WORD = "note"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the note of the person identified " + + "by the index number used in the last person listing. " + + "Existing note will be overwritten by the input.\n" + + "Parameters: INDEX (must be a positive integer) " + + PREFIX_NOTE + "[NOTE]\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_NOTE + "Likes to swim."; + + public static final String MESSAGE_ADD_NOTE_SUCCESS = "Added note to Person: %1$s\nNote added: %2$s"; + public static final String MESSAGE_DELETE_NOTE_SUCCESS = "Removed note from Person: %1$s"; + + private final Index index; + private final Note note; + + /** + * @param index of the person in the filtered person list to edit the note + * @param note of the person to be updated to + */ + public NoteCommand(Index index, Note note) { + requireAllNonNull(index, note); + + this.index = index; + this.note = note; + } + @Override + public CommandResult execute(Model model) throws CommandException { + List lastShownList = model.getFilteredStudentList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); + } + + Student studentToEdit = lastShownList.get(index.getZeroBased()); + Student editedStudent = new Student(studentToEdit.getName(), studentToEdit.getPhone(), + studentToEdit.getAddress(), studentToEdit.getRiskLevel(), note); + + model.setStudent(studentToEdit, editedStudent); + model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS); + + return new CommandResult(generateSuccessMessage(editedStudent, note)); + } + + /** + * Generates a command execution success message based on whether the note is added to or removed from + * {@code StudentToEdit}. + */ + private String generateSuccessMessage(Student studentToEdit, Note note) { + String noteString = note.value.length() > 100 + ? note.value.substring(0, 100).trim() + "... (Double-click on the student card to view the full note)" + : note.value; + String message = !note.value.isEmpty() + ? String.format(MESSAGE_ADD_NOTE_SUCCESS, Messages.format(studentToEdit), noteString) + : String.format(MESSAGE_DELETE_NOTE_SUCCESS, Messages.format(studentToEdit)); + return message; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof NoteCommand)) { + return false; + } + + NoteCommand e = (NoteCommand) other; + return index.equals(e.index) + && note.equals(e.note); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ScheduleCommand.java b/src/main/java/seedu/address/logic/commands/ScheduleCommand.java new file mode 100644 index 00000000000..cdddd376948 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ScheduleCommand.java @@ -0,0 +1,120 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_START_TIME; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.temporal.ChronoUnit; + +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; + +/** + * ScheduleCommand represents a command to schedule a new appointment. + */ +public class ScheduleCommand extends Command { + + public static final String COMMAND_WORD = "schedule"; + public static final int DAYS_IN_YEAR = 365; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Schedules a new appointment. " + + "Parameters: " + + PREFIX_NAME + "STUDENT " + + PREFIX_DATE + "DATE " + + PREFIX_START_TIME + "STARTTIME " + + PREFIX_END_TIME + "ENDTIME " + + PREFIX_DESCRIPTION + "DESCRIPTION\n" + + "Example: " + + COMMAND_WORD + " " + + PREFIX_NAME + "John Doe " + + PREFIX_DATE + "2023-12-31 " + + PREFIX_START_TIME + "16:30 " + + PREFIX_END_TIME + "17:30 " + + PREFIX_DESCRIPTION + "First Session"; + + public static final String MESSAGE_SUCCESS = "New appointment scheduled: %1$s"; + public static final String MESSAGE_DUPLICATE_APPOINTMENT = "This appointment already exists"; + public static final String MESSAGE_OVERLAPPING_APPOINTMENTS = "This appointment overlaps " + + "with an existing appointment"; + + public static final String MESSAGE_NO_STUDENT_FOR_APPOINTMENT = "No such student exists for this appointment"; + public static final String MESSAGE_DATE_IN_THE_PAST = "Appointment date and time should be in the future"; + public static final String MESSAGE_DATE_EXCEED_ONE_YEAR = "Appointment can only be scheduled within a year"; + + private final Appointment toAdd; + + /** + * Creates a new ScheduleCommand to schedule the specified appointment. + * + * @param appointment The appointment to be scheduled. Must not be null. + */ + public ScheduleCommand(Appointment appointment) { + requireNonNull(appointment); + toAdd = appointment; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasAppointment(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_APPOINTMENT); + } + + if (model.hasOverlapsWithAppointments(toAdd)) { + throw new CommandException(MESSAGE_OVERLAPPING_APPOINTMENTS); + } + + if (model.hasNoStudentForAppointment(toAdd)) { + throw new CommandException(MESSAGE_NO_STUDENT_FOR_APPOINTMENT); + } + + LocalDate appointmentDate = toAdd.getDate().getLocalDate(); + LocalDate todayDate = LocalDate.now(); + LocalTime appointmentStartTime = toAdd.getStartTime().getLocalTime(); + LocalTime timeNow = LocalTime.now(); + long daysDifference = ChronoUnit.DAYS.between(todayDate, appointmentDate); + + if (appointmentDate.isBefore(todayDate) + || (todayDate.isEqual(appointmentDate) && appointmentStartTime.isBefore(timeNow))) { + throw new CommandException(MESSAGE_DATE_IN_THE_PAST); + } + + if (daysDifference > 365) { + throw new CommandException(MESSAGE_DATE_EXCEED_ONE_YEAR); + } + + model.addAppointment(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, Messages.format(toAdd))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof ScheduleCommand)) { + return false; + } + + ScheduleCommand otherScheduleCommand = (ScheduleCommand) other; + return toAdd.equals(otherScheduleCommand.toAdd); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("toAdd", toAdd) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/TagCommand.java b/src/main/java/seedu/address/logic/commands/TagCommand.java new file mode 100644 index 00000000000..75fd6ca8994 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/TagCommand.java @@ -0,0 +1,91 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RISK_LEVEL; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS; + +import java.util.List; +import java.util.Set; + +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.risklevel.RiskLevel; +import seedu.address.model.student.Student; + +/** + * Edits the details of an existing student in the wellnus storage. + */ +public class TagCommand extends Command { + + public static final String COMMAND_WORD = "tag"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Tags the student identified " + + "by the index number used in the displayed student list to a risk level: high/medium/low.\n" + + "Existing risk level will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_RISK_LEVEL + "RISK LEVEL] " + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_RISK_LEVEL + "high"; + + public static final String MESSAGE_TAG_STUDENT_SUCCESS = "Tagged Student: %1$s"; + private final Index index; + private final Set riskLevel; + + /** + * @param index of the student in the filtered student list to edit + * @param riskLevel details to edit the student with + */ + public TagCommand(Index index, Set riskLevel) { + requireNonNull(index); + requireNonNull(riskLevel); + + this.index = index; + this.riskLevel = riskLevel; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredStudentList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); + } + + Student studentToEdit = lastShownList.get(index.getZeroBased()); + Student editedStudent = new Student(studentToEdit.getName(), studentToEdit.getPhone(), + studentToEdit.getAddress(), riskLevel, studentToEdit.getNote()); + + model.setStudent(studentToEdit, editedStudent); + model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS); + + return new CommandResult(String.format(MESSAGE_TAG_STUDENT_SUCCESS, Messages.format(editedStudent))); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TagCommand)) { + return false; + } + + TagCommand otherTagCommand = (TagCommand) other; + return index.equals(otherTagCommand.index) + && riskLevel.equals(otherTagCommand.riskLevel); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("index", index) + .add("riskLevel", riskLevel) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java new file mode 100644 index 00000000000..34edfeab7ee --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java @@ -0,0 +1,80 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.ViewCommandParser.ALL_CATEGORY; +import static seedu.address.logic.parser.ViewCommandParser.APPOINTMENT_CATEGORY; +import static seedu.address.logic.parser.ViewCommandParser.STUDENT_CATEGORY; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_APPOINTMENTS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.Model; + + +/** + * Lists all appointments or students in WellNus to the user. + */ +public class ViewCommand extends Command { + + public static final String COMMAND_WORD = "view"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": View which data you want to see\n" + + "Parameters: data category (must be 'students', 'appointments' or 'all') " + + "g/ [DATA_CATEGORY]\n" + + "Example: " + COMMAND_WORD + " g/appointments "; + + public static final String MESSAGE_SUCCESS_APPOINTMENT = "Listed all appointments"; + + public static final String MESSAGE_SUCCESS_STUDENT = "Listed all students"; + + public static final String MESSAGE_SUCCESS_ALL = "Listed all students and appointments"; + + public static final String MESSAGE_ARGUMENTS = "Data chosen: %1$s"; + + private final String category; + + public ViewCommand(String category) { + this.category = category; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (category.equals(STUDENT_CATEGORY)) { + model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS); + return new CommandResult(MESSAGE_SUCCESS_STUDENT); + } else if (category.equals(APPOINTMENT_CATEGORY)) { + model.updateFilteredAppointmentList(PREDICATE_SHOW_ALL_APPOINTMENTS); + return new CommandResult(MESSAGE_SUCCESS_APPOINTMENT); + } else if (category.equals(ALL_CATEGORY)) { + model.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS); + model.updateFilteredAppointmentList(PREDICATE_SHOW_ALL_APPOINTMENTS); + return new CommandResult(MESSAGE_SUCCESS_ALL); + } else { + throw new CommandException(String.format(MESSAGE_ARGUMENTS, category)); + } + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof ViewCommand)) { + return false; + } + + ViewCommand e = (ViewCommand) other; + return category.equals(e.category); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("category", category) + .toString(); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java index 4ff1a97ed77..29c161624a2 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java @@ -2,22 +2,21 @@ 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 static seedu.address.logic.parser.CliSyntax.PREFIX_RISK_LEVEL; 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.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Phone; +import seedu.address.model.student.Student; /** * Parses input arguments and creates a new AddCommand object @@ -31,23 +30,24 @@ public class AddCommandParser implements Parser { */ public AddCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_ADDRESS, PREFIX_RISK_LEVEL); - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE) || !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); + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_ADDRESS, PREFIX_RISK_LEVEL); 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)); + Set tagList = ParserUtil.parseRiskLevel(argMultimap.getAllValues(PREFIX_RISK_LEVEL)); + Note note = new Note(" "); - Person person = new Person(name, phone, email, address, tagList); - return new AddCommand(person); + Student student = new Student(name, phone, address, tagList, note); + + return new AddCommand(student); } /** diff --git a/src/main/java/seedu/address/logic/parser/CancelCommandParser.java b/src/main/java/seedu/address/logic/parser/CancelCommandParser.java new file mode 100644 index 00000000000..e6b4ca1aeee --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/CancelCommandParser.java @@ -0,0 +1,29 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.CancelCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new CancelCommand object + */ +public class CancelCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the CancelCommand + * and returns a CancelCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public CancelCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new CancelCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, CancelCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf119..43bc2b6e062 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -7,9 +7,13 @@ public class CliSyntax { /* Prefix definitions */ public static final Prefix PREFIX_NAME = new Prefix("n/"); - public static final Prefix PREFIX_PHONE = new Prefix("p/"); - public static final Prefix PREFIX_EMAIL = new Prefix("e/"); + public static final Prefix PREFIX_PHONE = new Prefix("c/"); public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); - public static final Prefix PREFIX_TAG = new Prefix("t/"); - + public static final Prefix PREFIX_RISK_LEVEL = new Prefix("r/"); + public static final Prefix PREFIX_CATEGORY = new Prefix("g/"); + public static final Prefix PREFIX_DESCRIPTION = new Prefix("d/"); + public static final Prefix PREFIX_DATE = new Prefix("date/"); + public static final Prefix PREFIX_START_TIME = new Prefix("from/"); + public static final Prefix PREFIX_END_TIME = new Prefix("to/"); + public static final Prefix PREFIX_NOTE = new Prefix("note/"); } diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java index 46b3309a78b..8fca9543f0b 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java @@ -3,21 +3,12 @@ import static java.util.Objects.requireNonNull; 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.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.commands.EditCommand.EditStudentDescriptor; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; /** * Parses input arguments and creates a new EditCommand object @@ -32,7 +23,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_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); + ArgumentTokenizer.tokenize(args, PREFIX_PHONE, PREFIX_ADDRESS); Index index; @@ -42,44 +33,21 @@ public EditCommand parse(String args) throws ParseException { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); } - argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS); + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_PHONE, PREFIX_ADDRESS); - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); + EditCommand.EditStudentDescriptor editStudentDescriptor = new EditStudentDescriptor(); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); - } if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); - } - if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + editStudentDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); } if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); + editStudentDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); - if (!editPersonDescriptor.isAnyFieldEdited()) { + if (!editStudentDescriptor.isAnyFieldEdited()) { throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); } - return new EditCommand(index, editPersonDescriptor); - } - - /** - * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. - * If {@code tags} contain only one element which is an empty string, it will be parsed into a - * {@code Set} containing zero tags. - */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; - - if (tags.isEmpty()) { - return Optional.empty(); - } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); + return new EditCommand(index, editStudentDescriptor); } - } diff --git a/src/main/java/seedu/address/logic/parser/FilterCommandParser.java b/src/main/java/seedu/address/logic/parser/FilterCommandParser.java new file mode 100644 index 00000000000..b999695f861 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/FilterCommandParser.java @@ -0,0 +1,36 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.time.LocalDate; +import java.time.format.DateTimeParseException; + +import seedu.address.logic.commands.FilterCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.appointment.AppointmentDateMatchesPredicate; + +/** + * Parses input arguments and creates a new FilterCommand object + */ +public class FilterCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FilterCommand + * and returns a FilterCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FilterCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE)); + } + + try { + LocalDate.parse(trimmedArgs); + return new FilterCommand(new AppointmentDateMatchesPredicate(trimmedArgs)); + } catch (DateTimeParseException e) { + throw new ParseException("Invalid date format. Please use the format yyyy-MM-dd."); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java index 2867bde857b..2e29f987f78 100644 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/FindCommandParser.java @@ -2,11 +2,8 @@ import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import java.util.Arrays; - import seedu.address.logic.commands.FindCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; /** * Parses input arguments and creates a new FindCommand object @@ -27,7 +24,7 @@ public FindCommand parse(String args) throws ParseException { String[] nameKeywords = trimmedArgs.split("\\s+"); - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + return new FindCommand(nameKeywords); } } diff --git a/src/main/java/seedu/address/logic/parser/NoteCommandParser.java b/src/main/java/seedu/address/logic/parser/NoteCommandParser.java new file mode 100644 index 00000000000..cefa695eafc --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/NoteCommandParser.java @@ -0,0 +1,36 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.NoteCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.student.Note; + +/** + * Parses input arguments and creates a new {@code NoteCommand} object + */ +public class NoteCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code NoteCommand} + * and returns a {@code NoteCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public NoteCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NOTE); + Index index; + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NOTE); + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, NoteCommand.MESSAGE_USAGE), e); + } + Note note = ParserUtil.parseNote(argMultimap.getValue(PREFIX_NOTE).orElse("")); + return new NoteCommand(index, note); + } +} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55..f5cfce9421d 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -3,24 +3,27 @@ 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; -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; +import seedu.address.model.appointment.Date; +import seedu.address.model.appointment.Description; +import seedu.address.model.appointment.Time; +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Phone; +import seedu.address.model.util.LimitedHashSet; /** * Contains utility methods used for parsing strings in the various *Parser classes. */ public class ParserUtil { - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_INDEX = "Index should contain numbers greater than 0 only."; /** * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be @@ -43,7 +46,7 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException { */ public static Name parseName(String name) throws ParseException { requireNonNull(name); - String trimmedName = name.trim(); + String trimmedName = name.trim().replaceAll("\\s+", " "); if (!Name.isValidName(trimmedName)) { throw new ParseException(Name.MESSAGE_CONSTRAINTS); } @@ -81,18 +84,48 @@ public static Address parseAddress(String address) throws ParseException { } /** - * Parses a {@code String email} into an {@code Email}. + * Parses a {@code String description} into a {@code Description}. * Leading and trailing whitespaces will be trimmed. * - * @throws ParseException if the given {@code email} is invalid. + * @throws ParseException if the given {@code description} is invalid. */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_CONSTRAINTS); + public static Description parseDescription(String description) throws ParseException { + requireNonNull(description); + String trimmedDesc = description.trim(); + if (!Description.isValidDescription(trimmedDesc)) { + throw new ParseException(Description.MESSAGE_CONSTRAINTS); } - return new Email(trimmedEmail); + return new Description(trimmedDesc); + } + + /** + * Parses a {@code String date} into a {@code Date}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code date} is invalid. + */ + public static Date parseDate(String date) throws ParseException { + requireNonNull(date); + String trimmedDate = date.trim(); + if (!Date.isValidDate(trimmedDate)) { + throw new ParseException(Date.MESSAGE_CONSTRAINTS); + } + return new Date(trimmedDate); + } + + /** + * Parses a {@code String time} into a {@code Time}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code time} is invalid. + */ + public static Time parseTime(String time) throws ParseException { + requireNonNull(time); + String trimmedTime = time.trim(); + if (!Time.isValidTime(trimmedTime)) { + throw new ParseException(Time.MESSAGE_CONSTRAINTS); + } + return new Time(trimmedTime); } /** @@ -101,24 +134,39 @@ public static Email parseEmail(String email) throws ParseException { * * @throws ParseException if the given {@code tag} is invalid. */ - public static Tag parseTag(String tag) throws ParseException { + public static RiskLevel parseRiskLevel(String tag) throws ParseException { requireNonNull(tag); String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); + if (!RiskLevel.isValidRiskLevel(trimmedTag)) { + throw new ParseException(RiskLevel.MESSAGE_CONSTRAINTS); } - return new Tag(trimmedTag); + return new RiskLevel(trimmedTag); } /** * Parses {@code Collection tags} into a {@code Set}. */ - public static Set parseTags(Collection tags) throws ParseException { + public static Set parseRiskLevel(Collection tags) throws ParseException { requireNonNull(tags); - final Set tagSet = new HashSet<>(); + final Set tagSet = new LimitedHashSet<>(1); for (String tagName : tags) { - tagSet.add(parseTag(tagName)); + tagSet.add(parseRiskLevel(tagName)); } return tagSet; } + + /** + * Parses a {@code String note} into a {@code Note}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code note} is invalid. + */ + public static Note parseNote(String note) throws ParseException { + requireNonNull(note); + String trimmedNote = note.trim(); + if (!Note.isValidNote(trimmedNote)) { + throw new ParseException(Note.MESSAGE_CONSTRAINTS); + } + return new Note(trimmedNote); + } } diff --git a/src/main/java/seedu/address/logic/parser/ScheduleCommandParser.java b/src/main/java/seedu/address/logic/parser/ScheduleCommandParser.java new file mode 100644 index 00000000000..1d48639531b --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ScheduleCommandParser.java @@ -0,0 +1,69 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.Messages.MESSAGE_INVALID_START_END_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_START_TIME; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.ScheduleCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.Date; +import seedu.address.model.appointment.Description; +import seedu.address.model.appointment.Time; +import seedu.address.model.appointment.exceptions.InvalidStartEndTimeException; +import seedu.address.model.student.Name; + +/** + * Parses input arguments and creates a new ScheduleCommand object. + */ +public class ScheduleCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ScheduleCommand + * and returns a ScheduleCommand object for execution. + * + * @throws ParseException if the user input does not conform to the expected format + */ + public ScheduleCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, + PREFIX_NAME, PREFIX_DATE, PREFIX_START_TIME, PREFIX_END_TIME, PREFIX_DESCRIPTION); + + if (!arePrefixesPresent(argMultimap, PREFIX_DATE, PREFIX_START_TIME, PREFIX_END_TIME, PREFIX_NAME, + PREFIX_DESCRIPTION) || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScheduleCommand.MESSAGE_USAGE)); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_DATE, PREFIX_START_TIME, PREFIX_END_TIME, + PREFIX_DESCRIPTION); + + Name studentName = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + Time startTime = ParserUtil.parseTime(argMultimap.getValue(PREFIX_START_TIME).get()); + Time endTime = ParserUtil.parseTime(argMultimap.getValue(PREFIX_END_TIME).get()); + Description description = ParserUtil.parseDescription(argMultimap.getValue(PREFIX_DESCRIPTION).get()); + Appointment appointment; + + try { + appointment = new Appointment(date, startTime, endTime, studentName, description); + } catch (InvalidStartEndTimeException e) { + throw new ParseException(MESSAGE_INVALID_START_END_TIME); + } + + return new ScheduleCommand(appointment); + } + + + /** + * Returns true if none of the prefixes contain 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/TagCommandParser.java b/src/main/java/seedu/address/logic/parser/TagCommandParser.java new file mode 100644 index 00000000000..6efc1ffc5bc --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/TagCommandParser.java @@ -0,0 +1,62 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RISK_LEVEL; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.TagCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.risklevel.RiskLevel; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class TagCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the TagCommand + * and returns an TagCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public TagCommand parse(String args) throws ParseException { + requireNonNull(args); + args = args.toLowerCase(); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_RISK_LEVEL); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE), pe); + } + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_RISK_LEVEL); + + Set tagList = ParserUtil.parseRiskLevel(argMultimap.getAllValues(PREFIX_RISK_LEVEL)); + return new TagCommand(index, tagList); + } + + /** + * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. + * If {@code tags} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero tags. + */ + private Optional> parseTagsForEdit(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseRiskLevel(tagSet)); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java new file mode 100644 index 00000000000..76d2dd2dbe0 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java @@ -0,0 +1,51 @@ +package seedu.address.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CATEGORY; + +import seedu.address.logic.commands.ViewCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new ViewCommand object + */ +public class ViewCommandParser implements Parser { + + public static final String STUDENT_CATEGORY = "students"; + public static final String APPOINTMENT_CATEGORY = "appointments"; + public static final String ALL_CATEGORY = "all"; + + /** + * Parses the given {@code String} of arguments in the context of the ViewCommand + * and returns an ViewCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ViewCommand parse(String args) throws ParseException { + requireNonNull(args); + String trimmedArgs = args.trim(); + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE)); + } + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_CATEGORY); + + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_CATEGORY); + + String category = argMultimap.getValue(PREFIX_CATEGORY).orElse(""); + if (!isValidCategory(category)) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE)); + } + + return new ViewCommand(category); + } + + /** + * Check whether the category is valid + */ + public boolean isValidCategory(String category) { + return category.equals(STUDENT_CATEGORY) || category.equals(APPOINTMENT_CATEGORY) + || category.equals(ALL_CATEGORY); + } +} diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/WellNusParser.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/AddressBookParser.java rename to src/main/java/seedu/address/logic/parser/WellNusParser.java index 3149ee07e0b..ef2a93e3c96 100644 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ b/src/main/java/seedu/address/logic/parser/WellNusParser.java @@ -9,26 +9,31 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.CancelCommand; import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.Command; import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.EditCommand; import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.commands.FilterCommand; import seedu.address.logic.commands.FindCommand; import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.NoteCommand; +import seedu.address.logic.commands.ScheduleCommand; +import seedu.address.logic.commands.TagCommand; +import seedu.address.logic.commands.ViewCommand; import seedu.address.logic.parser.exceptions.ParseException; /** * Parses user input. */ -public class AddressBookParser { +public class WellNusParser { /** * Used for initial separation of command word and args. */ private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - private static final Logger logger = LogsCenter.getLogger(AddressBookParser.class); + private static final Logger logger = LogsCenter.getLogger(WellNusParser.class); /** * Parses user input into command for execution. @@ -68,15 +73,30 @@ public Command parseCommand(String userInput) throws ParseException { case FindCommand.COMMAND_WORD: return new FindCommandParser().parse(arguments); - case ListCommand.COMMAND_WORD: - return new ListCommand(); - case ExitCommand.COMMAND_WORD: return new ExitCommand(); case HelpCommand.COMMAND_WORD: return new HelpCommand(); + case ViewCommand.COMMAND_WORD: + return new ViewCommandParser().parse(arguments); + + case CancelCommand.COMMAND_WORD: + return new CancelCommandParser().parse(arguments); + + case ScheduleCommand.COMMAND_WORD: + return new ScheduleCommandParser().parse(arguments); + + case TagCommand.COMMAND_WORD: + return new TagCommandParser().parse(arguments); + + case NoteCommand.COMMAND_WORD: + return new NoteCommandParser().parse(arguments); + + case FilterCommand.COMMAND_WORD: + return new FilterCommandParser().parse(arguments); + default: logger.finer("This user input caused a ParseException: " + userInput); throw new ParseException(MESSAGE_UNKNOWN_COMMAND); diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 73397161e84..00000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,130 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.commons.util.ToStringBuilder; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return new ToStringBuilder(this) - .add("persons", persons) - .toString(); - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof AddressBook)) { - return false; - } - - AddressBook otherAddressBook = (AddressBook) other; - return persons.equals(otherAddressBook.persons); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..56f87d662e1 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -5,14 +5,17 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Student; /** * The API of the Model component. */ public interface Model { + /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + Predicate PREDICATE_SHOW_ALL_APPOINTMENTS = unused -> true; + Predicate PREDICATE_SHOW_ALL_STUDENTS = unused -> true; /** * Replaces user prefs data with the data in {@code userPrefs}. @@ -35,53 +38,77 @@ public interface Model { void setGuiSettings(GuiSettings guiSettings); /** - * Returns the user prefs' address book file path. + * Returns the user prefs' wellnus storage file path. */ - Path getAddressBookFilePath(); + Path getWellNusFilePath(); /** - * Sets the user prefs' address book file path. + * Sets the user prefs' wellnus storage file path. */ - void setAddressBookFilePath(Path addressBookFilePath); + void setWellNusFilePath(Path wellNusFilePath); /** - * Replaces address book data with the data in {@code addressBook}. + * Replaces WellNus data with the data in {@code wellNus}. */ - void setAddressBook(ReadOnlyAddressBook addressBook); + void setWellNusData(ReadOnlyWellNus wellNus); - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); + /** Returns the WellNus */ + ReadOnlyWellNus getWellNusData(); /** - * Returns true if a person with the same identity as {@code person} exists in the address book. + * Returns true if a student with the same identity as {@code student} exists in the wellnus storage. */ - boolean hasPerson(Person person); + boolean hasStudent(Student student); /** - * Deletes the given person. - * The person must exist in the address book. + * Deletes the given student. + * The student must exist in the wellnus storage. */ - void deletePerson(Person target); + void deleteStudent(Student target); /** - * Adds the given person. - * {@code person} must not already exist in the address book. + * Adds the given student. + * {@code student} must not already exist in the wellnus storage. */ - void addPerson(Person person); + void addStudent(Student student); /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. + * Replaces the given student {@code target} with {@code editedStudent}. + * {@code target} must exist in the wellnus storage. + * The student identity of {@code editedStudent} must not be the same as another existing student in the + * wellnus storage. */ - void setPerson(Person target, Person editedPerson); + void setStudent(Student target, Student editedStudent); + + boolean hasAppointment(Appointment appointment); + + boolean hasOverlapsWithAppointments(Appointment appointment); + + boolean hasNoStudentForAppointment(Appointment appointment); - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); + void addAppointment(Appointment appointment); + + /** + * Deletes the given appointment. + * The appointment must exist in the wellnus storage. + */ + void deleteAppointment(Appointment target); + + /** Returns an unmodifiable view of the filtered student list */ + ObservableList getFilteredStudentList(); + + /** Returns an unmodifiable view of the filtered appointment list */ + ObservableList getFilteredAppointmentList(); + + /** + * Updates the filter of the filtered student list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredStudentList(Predicate predicate); /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. + * Updates the filter of the filtered appointment list to filter by the given {@code predicate}. * @throws NullPointerException if {@code predicate} is null. */ - void updateFilteredPersonList(Predicate predicate); + void updateFilteredAppointmentList(Predicate predicate); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 57bc563fde6..074a3ad7f1f 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -11,33 +11,36 @@ import javafx.collections.transformation.FilteredList; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Student; /** - * Represents the in-memory model of the address book data. + * Represents the in-memory model of the Wellnus data. */ public class ModelManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - private final AddressBook addressBook; + private final WellNus wellNus; private final UserPrefs userPrefs; - private final FilteredList filteredPersons; + private final FilteredList filteredAppointments; + private final FilteredList filteredStudents; /** - * Initializes a ModelManager with the given addressBook and userPrefs. + * Initializes a ModelManager with the given Wellnus instance and userPrefs. */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); + public ModelManager(ReadOnlyWellNus wellNus, ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(wellNus, userPrefs); - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); + logger.fine("Initializing with WellNus instance: " + wellNus + " and user prefs " + userPrefs); - this.addressBook = new AddressBook(addressBook); + this.wellNus = new WellNus(wellNus); this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + this.filteredStudents = new FilteredList<>(this.wellNus.getStudentList()); + this.filteredAppointments = new FilteredList<>(this.wellNus.getAppointmentList()); } public ModelManager() { - this(new AddressBook(), new UserPrefs()); + this(new WellNus(), new UserPrefs()); } //=========== UserPrefs ================================================================================== @@ -65,67 +68,113 @@ public void setGuiSettings(GuiSettings guiSettings) { } @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); + public Path getWellNusFilePath() { + return userPrefs.getWellNusFilePath(); } @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); + public void setWellNusFilePath(Path wellNusFilePath) { + requireNonNull(wellNusFilePath); + userPrefs.setWellNusFilePath(wellNusFilePath); } - //=========== AddressBook ================================================================================ + //=========== WellNus ================================================================================ @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); + public void setWellNusData(ReadOnlyWellNus wellNusData) { + this.wellNus.resetData(wellNusData); } @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; + public ReadOnlyWellNus getWellNusData() { + return wellNus; } @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); + public boolean hasStudent(Student student) { + requireNonNull(student); + return wellNus.hasStudent(student); } @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); + public void deleteStudent(Student target) { + wellNus.removeStudent(target); + logger.fine("Deleting appointments which contain the name: " + target.getName()); + wellNus.removeRelatedAppointments(target.getName()); } @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + public void addStudent(Student student) { + wellNus.addStudent(student); + updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS); + updateFilteredAppointmentList(PREDICATE_SHOW_ALL_APPOINTMENTS); } @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); + public void setStudent(Student target, Student editedStudent) { + requireAllNonNull(target, editedStudent); + wellNus.setStudent(target, editedStudent); + } + + @Override + public boolean hasAppointment(Appointment appointment) { + requireNonNull(appointment); + return wellNus.hasAppointment(appointment); + } + + @Override + public boolean hasOverlapsWithAppointments(Appointment appointment) { + requireNonNull(appointment); + return wellNus.hasOverlapsWithAppointments(appointment); + } - addressBook.setPerson(target, editedPerson); + @Override + public boolean hasNoStudentForAppointment(Appointment appointment) { + requireNonNull(appointment); + return wellNus.hasNoStudentForAppointment(appointment); } - //=========== Filtered Person List Accessors ============================================================= + @Override + public void addAppointment(Appointment appointment) { + requireNonNull(appointment); + wellNus.addAppointment(appointment); + updateFilteredAppointmentList(PREDICATE_SHOW_ALL_APPOINTMENTS); + updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS); + } + + @Override + public void deleteAppointment(Appointment target) { + wellNus.removeAppointment(target); + } + + //=========== Filtered Student List Accessors ============================================================= /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} + * Returns an unmodifiable view of the list of {@code Student} */ @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; + public ObservableList getFilteredStudentList() { + return filteredStudents; + } + + /** + * Returns an unmodifiable view of the list of {@code Appointment} + */ + @Override + public ObservableList getFilteredAppointmentList() { + return filteredAppointments; + } + + @Override + public void updateFilteredStudentList(Predicate predicate) { + requireNonNull(predicate); + filteredStudents.setPredicate(predicate); } @Override - public void updateFilteredPersonList(Predicate predicate) { + public void updateFilteredAppointmentList(Predicate predicate) { requireNonNull(predicate); - filteredPersons.setPredicate(predicate); + filteredAppointments.setPredicate(predicate); } @Override @@ -140,9 +189,9 @@ public boolean equals(Object other) { } ModelManager otherModelManager = (ModelManager) other; - return addressBook.equals(otherModelManager.addressBook) + return wellNus.equals(otherModelManager.wellNus) && userPrefs.equals(otherModelManager.userPrefs) - && filteredPersons.equals(otherModelManager.filteredPersons); + && filteredStudents.equals(otherModelManager.filteredStudents); } } diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a29..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java index befd58a4c73..e33666a9998 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java @@ -11,6 +11,6 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); + Path getWellNusFilePath(); } diff --git a/src/main/java/seedu/address/model/ReadOnlyWellNus.java b/src/main/java/seedu/address/model/ReadOnlyWellNus.java new file mode 100644 index 00000000000..2d640514e57 --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyWellNus.java @@ -0,0 +1,20 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Student; + +/** + * Unmodifiable view of an wellnus storage + */ +public interface ReadOnlyWellNus { + + /** + * Returns an unmodifiable view of the students list. + * This list will not contain any duplicate students. + */ + ObservableList getStudentList(); + + ObservableList getAppointmentList(); + +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 6be655fb4c7..60feff69dc2 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 wellNusFilePath = Paths.get("data" , "wellnus.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setWellNusFilePath(newUserPrefs.getWellNusFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +47,13 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getWellNusFilePath() { + return wellNusFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setWellNusFilePath(Path wellNusFilePath) { + requireNonNull(wellNusFilePath); + this.wellNusFilePath = wellNusFilePath; } @Override @@ -69,19 +69,19 @@ public boolean equals(Object other) { UserPrefs otherUserPrefs = (UserPrefs) other; return guiSettings.equals(otherUserPrefs.guiSettings) - && addressBookFilePath.equals(otherUserPrefs.addressBookFilePath); + && wellNusFilePath.equals(otherUserPrefs.wellNusFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, wellNusFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + wellNusFilePath); return sb.toString(); } diff --git a/src/main/java/seedu/address/model/WellNus.java b/src/main/java/seedu/address/model/WellNus.java new file mode 100644 index 00000000000..7adc583e689 --- /dev/null +++ b/src/main/java/seedu/address/model/WellNus.java @@ -0,0 +1,194 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.UniqueAppointmentList; +import seedu.address.model.student.Name; +import seedu.address.model.student.Student; +import seedu.address.model.student.UniqueStudentList; + +/** + * Wraps all data at the address-book level + * Duplicates are not allowed (by .isSameStudent comparison) + */ +public class WellNus implements ReadOnlyWellNus { + + private final UniqueStudentList students; + private final UniqueAppointmentList appointments; + + /* + * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication + * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html + * + * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication + * among constructors. + */ + { + students = new UniqueStudentList(); + appointments = new UniqueAppointmentList(); + } + + public WellNus() {} + + /** + * Creates an WellNus using the Students in the {@code toBeCopied} + */ + public WellNus(ReadOnlyWellNus toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the student list with {@code students}. + * {@code students} must not contain duplicate students. + */ + public void setStudents(List students) { + this.students.setStudents(students); + } + + /** + * Replaces the contents of the appointment list with {@code appointments}. + * {@code appointments} must not contain duplicate appointments. + */ + public void setAppointments(List appointments) { + this.appointments.setAppointments(appointments); + } + + /** + * Resets the existing data of this {@code WellNus} with {@code newData}. + */ + public void resetData(ReadOnlyWellNus newData) { + requireNonNull(newData); + setStudents(newData.getStudentList()); + setAppointments(newData.getAppointmentList()); + } + + //// student-level operations + + /** + * Returns true if a student with the same identity as {@code student} exists in the student list. + */ + public boolean hasStudent(Student student) { + requireNonNull(student); + return students.contains(student); + } + + /** + * Adds a student to the student list. + * The student must not already exist in the student list. + */ + public void addStudent(Student p) { + students.add(p); + } + + /** + * Replaces the given student {@code target} in the list with {@code editedStudent}. + * {@code target} must exist in the student list. + * The student identity of {@code editedStudent} must not be the same as another existing student in the + * student list. + */ + public void setStudent(Student target, Student editedStudent) { + requireNonNull(editedStudent); + + students.setStudent(target, editedStudent); + } + + /** + * Removes {@code key} from this {@code WellNus}. + * {@code key} must exist in the student list. + */ + public void removeStudent(Student key) { + students.remove(key); + } + + //// appointment-level operations + + /** + * Returns true if a person with the same identity as {@code person} exists in the student list. + */ + public boolean hasAppointment(Appointment appointment) { + requireNonNull(appointment); + return appointments.hasAppointment(appointment); + } + + /** + * Returns true if an appointment overlaps with {@code appointment} in the appointment list. + * + * @param appointment The appointment potentially added + * @return true if overlaps, false otherwise. + */ + public boolean hasOverlapsWithAppointments(Appointment appointment) { + requireNonNull(appointment); + return appointments.hasOverlapWith(appointment); + } + + public boolean hasNoStudentForAppointment(Appointment appointment) { + return !students.hasName(appointment.getName()); + } + + /** + * Adds a person to the student list. + * The person must not already exist in the student list. + */ + public void addAppointment(Appointment a) { + appointments.add(a); + } + + /** + * Removes {@code key} from this {@code WellNus}. + * {@code key} must exist in the student list. + */ + public void removeAppointment(Appointment key) { + appointments.remove(key); + } + + public void removeRelatedAppointments(Name key) { + appointments.removeRelatedAppointments(key); + } + + //// util methods + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("students", students) + .toString(); + } + + @Override + public ObservableList getStudentList() { + return students.asUnmodifiableObservableList(); + } + + @Override + public ObservableList getAppointmentList() { + return appointments.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof WellNus)) { + return false; + } + + WellNus otherWellNus = (WellNus) other; + return students.equals(otherWellNus.students); + } + + @Override + public int hashCode() { + return students.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/appointment/Appointment.java b/src/main/java/seedu/address/model/appointment/Appointment.java new file mode 100644 index 00000000000..7adcb86dd93 --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/Appointment.java @@ -0,0 +1,178 @@ +package seedu.address.model.appointment; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Objects; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.appointment.exceptions.InvalidStartEndTimeException; +import seedu.address.model.student.Name; + +/** + * Represents an appointment with a date, start time, end time, a student, and a description. + */ +public class Appointment implements Comparable { + + private final Date date; + private final Time startTime; + private final Time endTime; + private final Name name; + private final Description description; + + /** + * Constructs an Appointment object with the specified date, start time, end time, student, and description. + * + * @param date The date of the appointment. + * @param startTime The start time of the appointment. + * @param endTime The end time of the appointment. + * @param name The name of the student associated with the appointment. + * @param description A description of the appointment. + */ + public Appointment(Date date, Time startTime, Time endTime, Name name, Description description) + throws InvalidStartEndTimeException { + requireAllNonNull(date, startTime, endTime, name, description); + isValidStartEndTime(startTime, endTime); + assert startTime.getLocalTime().isBefore(endTime.getLocalTime()); + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + this.name = name; + this.description = description; + } + + public Date getDate() { + return this.date; + } + + public Time getStartTime() { + return this.startTime; + } + + public Time getEndTime() { + return this.endTime; + } + + public Name getName() { + return this.name; + } + + public Description getDescription() { + return this.description; + } + + /** + * Checks the validity of the start and end times of this appointment. + * + * @param startTime The start time of the appointment. + * @param endTime The end time of the appointment. + * @throws InvalidStartEndTimeException If the start time is not before the end time, an exception is thrown. + */ + public void isValidStartEndTime(Time startTime, Time endTime) throws InvalidStartEndTimeException { + if (!(startTime.getLocalTime().isBefore(endTime.getLocalTime()))) { + throw new InvalidStartEndTimeException(); + } + } + + /** + * Returns true if both appointments have the same date, start time, end time, and name. + * This defines a weaker notion of equality between two appointments. + */ + public boolean isSameAppointment(Appointment other) { + if (other == this) { + return true; + } + + return other != null + && other.getDate().equals(this.getDate()) + && other.getStartTime().equals(this.getStartTime()) + && other.getEndTime().equals(this.getEndTime()) + && other.getName().equals(this.getName()); + } + + + /** + * Checks if this Appointment overlaps with another Appointment. + * Two appointments overlap if both appointments simultaneously occur on the same date and time. + */ + public boolean isOverlappingAppointment(Appointment other) { + if (other == this) { + return true; + } + + if (!this.date.equals(other.getDate())) { + return false; + } + + if (this.endTime.getLocalTime().isAfter(other.startTime.getLocalTime()) + && other.endTime.getLocalTime().isAfter(this.startTime.getLocalTime())) { + return true; + } + + return false; + } + + /** + * Returns true if both appointments have the same fields. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Appointment)) { + return false; + } + + Appointment otherAppointment = (Appointment) other; + return this.name.equals(otherAppointment.name) + && description.equals(otherAppointment.description) + && date.equals(otherAppointment.date) + && startTime.equals(otherAppointment.startTime) + && endTime.equals(otherAppointment.endTime); + } + + @Override + public int hashCode() { + return Objects.hash(name, description, date, startTime, endTime); + } + + /** + * Compares this appointment with another appointment based on their date and start time. + * If two appointments have the same date and start time, they are considered the same appointment, as there cannot + * be overlapping appointments. + * + * @param otherAppointment The appointment to compare to. + * @return A negative integer if this appointment is earlier than the other, a positive integer if it's later, + * and 0 if they are the same appointment. + */ + @Override + public int compareTo(Appointment otherAppointment) { + int dateCompare = this.date.getLocalDate().compareTo(otherAppointment.date.getLocalDate()); + + if (dateCompare != 0) { + return dateCompare; + } + + int startTimeCompare = this.startTime.getLocalTime().compareTo(otherAppointment.startTime.getLocalTime()); + + if (startTimeCompare != 0) { + return startTimeCompare; + } + + assert this.equals(otherAppointment); + + return 0; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("student", name) + .add("description", description) + .add("date", date) + .add("startTime", startTime) + .add("endTime", endTime) + .toString(); + } +} diff --git a/src/main/java/seedu/address/model/appointment/AppointmentContainsNamePredicate.java b/src/main/java/seedu/address/model/appointment/AppointmentContainsNamePredicate.java new file mode 100644 index 00000000000..99ee6284c4b --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/AppointmentContainsNamePredicate.java @@ -0,0 +1,44 @@ +package seedu.address.model.appointment; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; +import seedu.address.commons.util.ToStringBuilder; + +/** + * Tests that a {@code Appointment}'s {@code Name} matches any of the keywords given. + */ +public class AppointmentContainsNamePredicate implements Predicate { + private final List keywords; + + public AppointmentContainsNamePredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Appointment appointment) { + return keywords.size() == 0 ? false : keywords.stream() + .allMatch(keyword -> StringUtil.containsWordIgnoreCase(appointment.getName().value, keyword)); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AppointmentContainsNamePredicate)) { + return false; + } + + AppointmentContainsNamePredicate otherPredicate = (AppointmentContainsNamePredicate) other; + return keywords.equals(otherPredicate.keywords); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("keywords", keywords).toString(); + } +} diff --git a/src/main/java/seedu/address/model/appointment/AppointmentDateMatchesPredicate.java b/src/main/java/seedu/address/model/appointment/AppointmentDateMatchesPredicate.java new file mode 100644 index 00000000000..05fedf76deb --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/AppointmentDateMatchesPredicate.java @@ -0,0 +1,41 @@ +package seedu.address.model.appointment; + +import java.util.function.Predicate; + +import seedu.address.commons.util.ToStringBuilder; + +/** + * Tests that an {@code Appointment}'s date matches the given date. + */ +public class AppointmentDateMatchesPredicate implements Predicate { + private final String date; + + public AppointmentDateMatchesPredicate(String date) { + this.date = date; + } + + @Override + public boolean test(Appointment appointment) { + return appointment.getDate().getDate().equals(date); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof AppointmentDateMatchesPredicate)) { + return false; + } + + AppointmentDateMatchesPredicate otherPredicate = (AppointmentDateMatchesPredicate) other; + return date.equals(otherPredicate.date); + } + + @Override + public String toString() { + return new ToStringBuilder(this).add("date", date).toString(); + } +} diff --git a/src/main/java/seedu/address/model/appointment/Date.java b/src/main/java/seedu/address/model/appointment/Date.java new file mode 100644 index 00000000000..44fdb73bade --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/Date.java @@ -0,0 +1,87 @@ +package seedu.address.model.appointment; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.address.model.util.DateTimeParser.INPUT_DATE_FORMATTER; + +import java.time.DateTimeException; +import java.time.LocalDate; + + +/** + * Represents the date of an appointment. + */ +public class Date { + public static final String MESSAGE_CONSTRAINTS = + "Date should follow yyyy-MM-dd and must be a valid calendar date."; + public static final String VALIDATION_REGEX = + "^(20[0-9]{2})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$"; + public final String value; + + /** To facilitate filtering Appointments by date. **/ + public final LocalDate localDate; + + /** + * Constructs a {@code Date}.a + * + * @param date A valid date. + */ + public Date(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + value = date; + localDate = LocalDate.parse(date, INPUT_DATE_FORMATTER); + } + + /** + * Returns true if a given string is a valid date. + */ + public static boolean isValidDate(String test) { + if (!test.matches(VALIDATION_REGEX)) { + return false; + } + + try { + LocalDate.parse(test); + return true; + } catch (DateTimeException e) { + return false; + } + } + + + + + public String getDate() { + return value; + } + + public LocalDate getLocalDate() { + return localDate; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Date)) { + return false; + } + + Date otherDate = (Date) other; + return value.equals(otherDate.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/appointment/Description.java b/src/main/java/seedu/address/model/appointment/Description.java new file mode 100644 index 00000000000..e53f25dd275 --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/Description.java @@ -0,0 +1,58 @@ +package seedu.address.model.appointment; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a description of an appointment. + */ +public class Description { + + public static final String MESSAGE_CONSTRAINTS = + "Description should have a minimum length of 1 character and maximum length of 100 characters"; + public static final String VALIDATION_REGEX = "^(?!\\s*$).{1,100}$"; + public final String value; + + /** + * Constructs a {@code Description}. + * + * @param description The text description of the appointment. + */ + public Description(String description) { + requireNonNull(description); + checkArgument(isValidDescription(description), MESSAGE_CONSTRAINTS); + value = description; + } + + /** + * Returns true if a given string is a valid description. + */ + public static boolean isValidDescription(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Description)) { + return false; + } + + Description otherDescription = (Description) other; + return value.equals(otherDescription.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/appointment/Time.java b/src/main/java/seedu/address/model/appointment/Time.java new file mode 100644 index 00000000000..74dbf746d7b --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/Time.java @@ -0,0 +1,74 @@ +package seedu.address.model.appointment; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.address.model.util.DateTimeParser.INPUT_TIME_FORMATTER; + +import java.time.LocalTime; + +/** + * Represents the time of an appointment. + */ +public class Time { + public static final String MESSAGE_CONSTRAINTS = + "Time should follow HH:mm"; + public static final String VALIDATION_REGEX = + "([01][0-9]|2[0-3]):([0-5][0-9])$"; + + public final String value; + + /** To facilitate time comparisons. **/ + public final LocalTime localTime; + + /** + * Constructs a {@code Time}.a + * + * @param time A valid time. + */ + public Time(String time) { + requireNonNull(time); + checkArgument(isValidTime(time), MESSAGE_CONSTRAINTS); + value = time; + localTime = LocalTime.parse(time, INPUT_TIME_FORMATTER); + } + + /** + * Returns true if a given string is a valid time. + */ + public static boolean isValidTime(String test) { + return test.matches(VALIDATION_REGEX); + } + + public String getTime() { + return value; + } + + public LocalTime getLocalTime() { + return localTime; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Time)) { + return false; + } + + Time otherTime = (Time) other; + return value.equals(otherTime.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java b/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java new file mode 100644 index 00000000000..080ce30fc33 --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/UniqueAppointmentList.java @@ -0,0 +1,161 @@ +package seedu.address.model.appointment; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.Predicate; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.appointment.exceptions.AppointmentNotFoundException; +import seedu.address.model.appointment.exceptions.DuplicateAppointmentException; +import seedu.address.model.student.Name; + + +/** + * A list of appointments that enforces uniqueness between its elements and does not allow nulls. + */ +public class UniqueAppointmentList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent appointment as the given argument. + */ + public boolean hasAppointment(Appointment toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::equals); + } + + /** + * Returns true if the list contains an overlapping appointment as the given argument. + */ + public boolean hasOverlapWith(Appointment toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isOverlappingAppointment); + } + + /** + * Adds an appointment to the list. + * The appointment must not already exist in the list. + */ + public void add(Appointment toAdd) { + requireNonNull(toAdd); + if (hasAppointment(toAdd)) { + throw new DuplicateAppointmentException(); + } + internalList.add(toAdd); + Collections.sort(internalList); + } + + /** + * Removes the equivalent appointment from the list. + * The appointment must exist in the list. + */ + public void remove(Appointment toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new AppointmentNotFoundException(); + } + Collections.sort(internalList); + } + + /** + * Removes all appointments which contain the name {@code toRemove} from the list. + */ + public void removeRelatedAppointments(Name toRemove) { + requireNonNull(toRemove); + Predicate hasSameName = appointment -> appointment.getName().equals(toRemove); + internalList.removeIf(hasSameName); + Collections.sort(internalList); + } + + public void setAppointments(UniqueAppointmentList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + Collections.sort(internalList); + } + + /** + * Replaces the contents of this list with {@code appointments}. + * {@code appointments} must not contain duplicate appointments. + */ + public void setAppointments(List appointments) { + requireAllNonNull(appointments); + if (!appointmentsAreUnique(appointments)) { + throw new DuplicateAppointmentException(); + } + internalList.setAll(appointments); + Collections.sort(internalList); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof UniqueAppointmentList)) { + return false; + } + + UniqueAppointmentList otherUniqueAppointmentList = (UniqueAppointmentList) other; + return internalList.equals(otherUniqueAppointmentList.internalList); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + @Override + public String toString() { + return internalList.toString(); + } + + /** + * Returns true if {@code appointments} contains only unique appointments. + */ + private boolean appointmentsAreUnique(List appointments) { + for (int i = 0; i < appointments.size() - 1; i++) { + for (int j = i + 1; j < appointments.size(); j++) { + if (appointments.get(i).equals(appointments.get(j))) { + return false; + } + } + } + return true; + } + + /** + * Returns true if {@code appointments} do not overlap with each other. + */ + private boolean appointmentsDoNotOverlap(List appointments) { + for (int i = 0; i < appointments.size() - 1; i++) { + for (int j = i + 1; j < appointments.size(); j++) { + if (appointments.get(i).isOverlappingAppointment(appointments.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/appointment/exceptions/AppointmentNotFoundException.java b/src/main/java/seedu/address/model/appointment/exceptions/AppointmentNotFoundException.java new file mode 100644 index 00000000000..6738a6419b3 --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/exceptions/AppointmentNotFoundException.java @@ -0,0 +1,7 @@ +package seedu.address.model.appointment.exceptions; + +/** + * Signals that the operation is unable to find the specified appointment. + */ +public class AppointmentNotFoundException extends RuntimeException { +} diff --git a/src/main/java/seedu/address/model/appointment/exceptions/DuplicateAppointmentException.java b/src/main/java/seedu/address/model/appointment/exceptions/DuplicateAppointmentException.java new file mode 100644 index 00000000000..59210ce32dd --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/exceptions/DuplicateAppointmentException.java @@ -0,0 +1,11 @@ +package seedu.address.model.appointment.exceptions; + +/** + * Signals that the operation will result in duplicate Appointments (Appointments are considered duplicates if they + * have the same identity). + */ +public class DuplicateAppointmentException extends RuntimeException { + public DuplicateAppointmentException() { + super("Operation would result in duplicate appointments"); + } +} diff --git a/src/main/java/seedu/address/model/appointment/exceptions/InvalidStartEndTimeException.java b/src/main/java/seedu/address/model/appointment/exceptions/InvalidStartEndTimeException.java new file mode 100644 index 00000000000..a5a168d42fb --- /dev/null +++ b/src/main/java/seedu/address/model/appointment/exceptions/InvalidStartEndTimeException.java @@ -0,0 +1,10 @@ +package seedu.address.model.appointment.exceptions; + +/** + * Handle appointments with start time after end time. + */ +public class InvalidStartEndTimeException extends Exception { + public InvalidStartEndTimeException() { + super("Start time cannot be after end time"); + } +} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index c62e512bc29..00000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,79 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "+_.-"; - public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + "). The local-part may not start or end with any special " - + "characters.\n" - + "2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels " - + "separated by periods.\n" - + "The domain name must:\n" - + " - end with a domain label at least 2 characters long\n" - + " - have each domain label start and end with alphanumeric characters\n" - + " - have each domain label consist of alphanumeric characters, separated only by hyphens, if any."; - // alphanumeric and special characters - private static final String ALPHANUMERIC_NO_UNDERSCORE = "[^\\W_]+"; // alphanumeric characters except underscore - private static final String LOCAL_PART_REGEX = "^" + ALPHANUMERIC_NO_UNDERSCORE + "([" + SPECIAL_CHARACTERS + "]" - + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_PART_REGEX = ALPHANUMERIC_NO_UNDERSCORE - + "(-" + ALPHANUMERIC_NO_UNDERSCORE + ")*"; - private static final String DOMAIN_LAST_PART_REGEX = "(" + DOMAIN_PART_REGEX + "){2,}$"; // At least two chars - private static final String DOMAIN_REGEX = "(" + DOMAIN_PART_REGEX + "\\.)*" + DOMAIN_LAST_PART_REGEX; - public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + DOMAIN_REGEX; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof Email)) { - return false; - } - - Email otherEmail = (Email) other; - return value.equals(otherEmail.value); - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index abe8c46b535..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,117 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.commons.util.ToStringBuilder; -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return name.equals(otherPerson.name) - && phone.equals(otherPerson.phone) - && email.equals(otherPerson.email) - && address.equals(otherPerson.address) - && tags.equals(otherPerson.tags); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - return new ToStringBuilder(this) - .add("name", name) - .add("phone", phone) - .add("email", email) - .add("address", address) - .add("tags", tags) - .toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index cc0a68d79f9..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,150 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof UniquePersonList)) { - return false; - } - - UniquePersonList otherUniquePersonList = (UniquePersonList) other; - return internalList.equals(otherUniquePersonList.internalList); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - @Override - public String toString() { - return internalList.toString(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f59442..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/risklevel/RiskLevel.java b/src/main/java/seedu/address/model/risklevel/RiskLevel.java new file mode 100644 index 00000000000..f61e173c7d7 --- /dev/null +++ b/src/main/java/seedu/address/model/risklevel/RiskLevel.java @@ -0,0 +1,71 @@ +package seedu.address.model.risklevel; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.util.HashSet; +import java.util.Set; + +/** + * Represents a RiskLevel in the wellnus storage. + * Guarantees: immutable; name is valid as declared in {@link #isValidRiskLevel(String)} + */ +public class RiskLevel { + + public static final String MESSAGE_CONSTRAINTS = "Risk level should be one of the following three: high/medium/low"; + private static final Set ALLOWED_VALUES = new HashSet<>(); + + static { + ALLOWED_VALUES.add("high"); + ALLOWED_VALUES.add("medium"); + ALLOWED_VALUES.add("low"); + } + public final String riskLevel; + + /** + * Constructs a {@code RiskLevel}. + * + * @param riskLevel A valid risk level. + */ + public RiskLevel(String riskLevel) { + requireNonNull(riskLevel); + checkArgument(isValidRiskLevel(riskLevel), MESSAGE_CONSTRAINTS); + this.riskLevel = riskLevel; + } + + /** + * Returns true if a given string is a valid tag name. + */ + public static boolean isValidRiskLevel(String test) { + requireNonNull(test); + return ALLOWED_VALUES.contains(test); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof RiskLevel)) { + return false; + } + + RiskLevel otherRiskLevel = (RiskLevel) other; + return riskLevel.equals(otherRiskLevel.riskLevel); + } + + @Override + public int hashCode() { + return riskLevel.hashCode(); + } + + /** + * Format state as text for viewing. + */ + public String toString() { + return riskLevel; + } + +} diff --git a/src/main/java/seedu/address/model/risklevel/exceptions/ExceedMaxRiskLevelSizeException.java b/src/main/java/seedu/address/model/risklevel/exceptions/ExceedMaxRiskLevelSizeException.java new file mode 100644 index 00000000000..eb0823816f1 --- /dev/null +++ b/src/main/java/seedu/address/model/risklevel/exceptions/ExceedMaxRiskLevelSizeException.java @@ -0,0 +1,13 @@ +package seedu.address.model.risklevel.exceptions; + + +/** + * This class encapsulates the event when the number of risk level tags exceed the + * specified maximum allowed number of tags. + */ +public class ExceedMaxRiskLevelSizeException extends ArrayIndexOutOfBoundsException { + + public ExceedMaxRiskLevelSizeException(int maxSize) { + super("number of tags cannot exceed " + maxSize + "!"); + } +} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/student/Address.java similarity index 81% rename from src/main/java/seedu/address/model/person/Address.java rename to src/main/java/seedu/address/model/student/Address.java index 469a2cc9a1e..7c888453319 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/seedu/address/model/student/Address.java @@ -1,21 +1,22 @@ -package seedu.address.model.person; +package seedu.address.model.student; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's address in the address book. + * Represents a Student's address in the wellnus storage. * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} */ public class Address { - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; + public static final String MESSAGE_CONSTRAINTS = "Addresses should not be blank, " + + "and can have a maximum of 200 characters"; /* * The first character of the address must not be a whitespace, * otherwise " " (a blank string) becomes a valid input. */ - public static final String VALIDATION_REGEX = "[^\\s].*"; + public static final String VALIDATION_REGEX = "[^\\s].{0,199}"; public final String value; @@ -31,7 +32,7 @@ public Address(String address) { } /** - * Returns true if a given string is a valid email. + * Returns true if a given string is a valid address. */ public static boolean isValidAddress(String test) { return test.matches(VALIDATION_REGEX); diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/student/Name.java similarity index 64% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/address/model/student/Name.java index 173f15b9b00..13097b31725 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/address/model/student/Name.java @@ -1,24 +1,24 @@ -package seedu.address.model.person; +package seedu.address.model.student; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's name in the address book. + * Represents a Student's name in the wellnus storage. * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} */ public class Name { - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; + public static final String MESSAGE_CONSTRAINTS = "Names should only contain alphabetical characters and spaces," + + " have a maximum of 100 characters, and should not be blank"; /* - * The first character of the address must not be a whitespace, + * The first character of the name must not be a whitespace, * otherwise " " (a blank string) becomes a valid input. */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + public static final String VALIDATION_REGEX = "^(?=.{1,100}$)[A-Z][a-z]*([ ][A-Z][a-z]*)*\\s*$"; - public final String fullName; + public final String value; /** * Constructs a {@code Name}. @@ -28,7 +28,7 @@ public class Name { public Name(String name) { requireNonNull(name); checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; + value = name; } /** @@ -41,7 +41,7 @@ public static boolean isValidName(String test) { @Override public String toString() { - return fullName; + return value; } @Override @@ -56,12 +56,12 @@ public boolean equals(Object other) { } Name otherName = (Name) other; - return fullName.equals(otherName.fullName); + return value.equals(otherName.value); } @Override public int hashCode() { - return fullName.hashCode(); + return value.hashCode(); } } diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java similarity index 74% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java index 62d19be2977..592148f0d57 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/student/NameContainsKeywordsPredicate.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.student; import java.util.List; import java.util.function.Predicate; @@ -7,9 +7,9 @@ import seedu.address.commons.util.ToStringBuilder; /** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * Tests that a {@code Student}'s {@code Name} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class NameContainsKeywordsPredicate implements Predicate { private final List keywords; public NameContainsKeywordsPredicate(List keywords) { @@ -17,9 +17,9 @@ public NameContainsKeywordsPredicate(List keywords) { } @Override - public boolean test(Person person) { - return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); + public boolean test(Student student) { + return keywords.size() == 0 ? false : keywords.stream() + .allMatch(keyword -> StringUtil.containsWordIgnoreCase(student.getName().value, keyword)); } @Override diff --git a/src/main/java/seedu/address/model/student/Note.java b/src/main/java/seedu/address/model/student/Note.java new file mode 100644 index 00000000000..533587227ac --- /dev/null +++ b/src/main/java/seedu/address/model/student/Note.java @@ -0,0 +1,58 @@ +package seedu.address.model.student; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a note for a Student in WellNUS + */ +public class Note { + + public static final String MESSAGE_CONSTRAINTS = + "Note should be 500 characters or less"; + public static final String VALIDATION_REGEX = ".{0,500}"; + public final String value; + + /** + * Constructs a {@code Note}. + * + * @param note A valid note. + */ + public Note(String note) { + requireNonNull(note); + checkArgument(isValidNote(note), MESSAGE_CONSTRAINTS); + value = note; + } + + /** + * Returns true if a given string is a valid note. + */ + public static boolean isValidNote(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Note)) { + return false; + } + + Note otherNote = (Note) other; + return value.equals(otherNote.value); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/address/model/student/Phone.java similarity index 83% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/address/model/student/Phone.java index d733f63d739..dc8166ebee8 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/address/model/student/Phone.java @@ -1,18 +1,18 @@ -package seedu.address.model.person; +package seedu.address.model.student; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; /** - * Represents a Person's phone number in the address book. + * Represents a Student's phone number in the wellnus storage. * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} */ public class Phone { public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; + "Phone numbers should only contain numbers, and should be 8 digits long"; + public static final String VALIDATION_REGEX = "\\d{8}"; public final String value; /** diff --git a/src/main/java/seedu/address/model/student/Student.java b/src/main/java/seedu/address/model/student/Student.java new file mode 100644 index 00000000000..bad6661f761 --- /dev/null +++ b/src/main/java/seedu/address/model/student/Student.java @@ -0,0 +1,118 @@ +package seedu.address.model.student; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.Objects; +import java.util.Set; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.util.LimitedHashSet; + +/** + * Represents a Student in the wellnus storage. + * Guarantees: details are present and not null, field values are validated, immutable. + */ +public class Student { + + // Identity fields + private final Name name; + private final Phone phone; + + // Data fields + private final Address address; + + // Each student should only have 1 risk level + private final Set riskLevel = new LimitedHashSet<>(1); + private final Note note; + + /** + * Every field must be present and not null. + */ + public Student(Name name, Phone phone, Address address, Set riskLevel, Note note) { + requireAllNonNull(name, phone, address, riskLevel); + this.name = name; + this.phone = phone; + this.address = address; + this.riskLevel.addAll(riskLevel); + this.note = note; + } + + public Name getName() { + return name; + } + + public Phone getPhone() { + return phone; + } + + public Address getAddress() { + return address; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getRiskLevel() { + return Collections.unmodifiableSet(riskLevel); + } + + public Note getNote() { + return note; + } + + /** + * Returns true if both students have the same name. + * This defines a weaker notion of equality between two students. + */ + public boolean isSameStudent(Student otherStudent) { + if (otherStudent == this) { + return true; + } + + return otherStudent != null + && otherStudent.getName().equals(getName()); + } + + /** + * Returns true if both students have the same identity and data fields. + * This defines a stronger notion of equality between two students. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Student)) { + return false; + } + + Student otherStudent = (Student) other; + return name.equals(otherStudent.name) + && phone.equals(otherStudent.phone) + && address.equals(otherStudent.address) + && riskLevel.equals(otherStudent.riskLevel); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, phone, address, riskLevel); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("name", name) + .add("phone", phone) + .add("address", address) + .add("risk level", riskLevel) + .add("note", note) + .toString(); + } + +} diff --git a/src/main/java/seedu/address/model/student/UniqueStudentList.java b/src/main/java/seedu/address/model/student/UniqueStudentList.java new file mode 100644 index 00000000000..2203228d8da --- /dev/null +++ b/src/main/java/seedu/address/model/student/UniqueStudentList.java @@ -0,0 +1,153 @@ +package seedu.address.model.student; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.student.exceptions.DuplicateStudentException; +import seedu.address.model.student.exceptions.StudentNotFoundException; + +/** + * A list of students that enforces uniqueness between its elements and does not allow nulls. + * A student is considered unique by comparing using {@code Student#isSameStudent(Student)}. As such, adding and + * updating of students uses Student#isSameStudent(Student) for equality to ensure that the student being added or + * updated is unique in terms of identity in the UniqueStudentList. However, the removal of a student uses + * Student#equals(Object) so as to ensure that the student with exactly the same fields will be removed. + * Supports a minimal set of list operations. + * + * @see Student#isSameStudent(Student) + */ +public class UniqueStudentList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent student as the given argument. + */ + public boolean contains(Student toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameStudent); + } + + public boolean hasName(Name name) { + return internalList.stream().anyMatch(student -> student.getName().equals(name)); + } + + /** + * Adds a student to the list. + * The student must not already exist in the list. + */ + public void add(Student toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateStudentException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the student {@code target} in the list with {@code editedStudent}. + * {@code target} must exist in the list. + * The student identity of {@code editedStudent} must not be the same as another existing student in the list. + */ + public void setStudent(Student target, Student editedStudent) { + requireAllNonNull(target, editedStudent); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new StudentNotFoundException(); + } + + if (!target.isSameStudent(editedStudent) && contains(editedStudent)) { + throw new DuplicateStudentException(); + } + + internalList.set(index, editedStudent); + } + + /** + * Removes the equivalent student from the list. + * The student must exist in the list. + */ + public void remove(Student toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new StudentNotFoundException(); + } + } + + public void setStudents(UniqueStudentList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code students}. + * {@code students} must not contain duplicate students. + */ + public void setStudents(List students) { + requireAllNonNull(students); + if (!studentsAreUnique(students)) { + throw new DuplicateStudentException(); + } + + internalList.setAll(students); + } + + /** + * Returns the backing list as an unmodifiable {@code ObservableList}. + */ + public ObservableList asUnmodifiableObservableList() { + return internalUnmodifiableList; + } + + @Override + public Iterator iterator() { + return internalList.iterator(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof UniqueStudentList)) { + return false; + } + + UniqueStudentList otherUniqueStudentList = (UniqueStudentList) other; + return internalList.equals(otherUniqueStudentList.internalList); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + @Override + public String toString() { + return internalList.toString(); + } + + /** + * Returns true if {@code students} contains only unique students. + */ + private boolean studentsAreUnique(List students) { + for (int i = 0; i < students.size() - 1; i++) { + for (int j = i + 1; j < students.size(); j++) { + if (students.get(i).isSameStudent(students.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java b/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java new file mode 100644 index 00000000000..d1a8f9ce635 --- /dev/null +++ b/src/main/java/seedu/address/model/student/exceptions/DuplicateStudentException.java @@ -0,0 +1,11 @@ +package seedu.address.model.student.exceptions; + +/** + * Signals that the operation will result in duplicate Students (Students are considered duplicates if they have the + * same identity). + */ +public class DuplicateStudentException extends RuntimeException { + public DuplicateStudentException() { + super("Operation would result in duplicate students"); + } +} diff --git a/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java b/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java new file mode 100644 index 00000000000..2b41e9e0296 --- /dev/null +++ b/src/main/java/seedu/address/model/student/exceptions/StudentNotFoundException.java @@ -0,0 +1,6 @@ +package seedu.address.model.student.exceptions; + +/** + * Signals that the operation is unable to find the specified student. + */ +public class StudentNotFoundException extends RuntimeException {} 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/DateTimeParser.java b/src/main/java/seedu/address/model/util/DateTimeParser.java new file mode 100644 index 00000000000..6fdf9d44386 --- /dev/null +++ b/src/main/java/seedu/address/model/util/DateTimeParser.java @@ -0,0 +1,52 @@ +package seedu.address.model.util; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; + +/** + * Utility class for formatting time and date strings. + */ +public class DateTimeParser { + /** + * The input time format, expected to be in "HH:mm" format. + */ + public static final DateTimeFormatter INPUT_TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm"); + + /** + * The input date format, expected to be in "yyyy-MM-dd" format. + */ + public static final DateTimeFormatter INPUT_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + /** + * The output time format, formatted as "h.mma". + */ + public static final DateTimeFormatter OUTPUT_TIME_FORMATTER = DateTimeFormatter.ofPattern("h.mma"); + + /** + * The output date format, formatted as "dd MMMM yyyy". + */ + public static final DateTimeFormatter OUTPUT_DATE_FORMATTER = DateTimeFormatter.ofPattern("dd MMMM yyyy"); + + /** + * Formats the given time string to a more readable format. + * + * @param time The time string in "HH:mm" format to be formatted. + * @return A formatted time string in "h.mma" format (e.g., 3.30PM). + */ + public static String formatTime(String time) { + LocalTime localTime = LocalTime.parse(time, INPUT_TIME_FORMATTER); + return localTime.format(OUTPUT_TIME_FORMATTER); + } + + /** + * Formats the given date string to a more readable format. + * + * @param date The date string in "yyyy-MM-dd" format to be formatted. + * @return A formatted date string in "dd MMMM yyyy" format (e.g., 31 December 2023). + */ + public static String formatDate(String date) { + LocalDate localDate = LocalDate.parse(date, INPUT_DATE_FORMATTER); + return localDate.format(OUTPUT_DATE_FORMATTER); + } +} diff --git a/src/main/java/seedu/address/model/util/LimitedHashSet.java b/src/main/java/seedu/address/model/util/LimitedHashSet.java new file mode 100644 index 00000000000..33896528a25 --- /dev/null +++ b/src/main/java/seedu/address/model/util/LimitedHashSet.java @@ -0,0 +1,43 @@ +package seedu.address.model.util; + +import java.util.Collection; +import java.util.HashSet; + +import seedu.address.model.risklevel.exceptions.ExceedMaxRiskLevelSizeException; + +/** + * This class inherits from a HashSet but specifies a maximum size. + * + * @param The type of the element in the LimitedHashSet. + */ +public class LimitedHashSet extends HashSet { + + private int maxSize; + + public LimitedHashSet(int maxSize) { + this.maxSize = maxSize; + } + + @Override + public boolean add(T element) { + checkSize(1); + return super.add(element); + } + + @Override + public boolean addAll(Collection c) { + checkSize(c.size()); + return super.addAll(c); + } + + /** + * Checks if the element(s) to be added will exceed the allowed maximum size of the Hashset. + * + * @param numElementsToAdd The number of elements that will be added. + */ + public void checkSize(int numElementsToAdd) { + if (size() + numElementsToAdd > maxSize) { + throw new ExceedMaxRiskLevelSizeException(maxSize); + } + } +} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facf..87338f517ef 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -4,46 +4,82 @@ import java.util.Set; import java.util.stream.Collectors; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; +import seedu.address.model.ReadOnlyWellNus; +import seedu.address.model.WellNus; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.Date; +import seedu.address.model.appointment.Description; +import seedu.address.model.appointment.Time; +import seedu.address.model.appointment.exceptions.InvalidStartEndTimeException; +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Phone; +import seedu.address.model.student.Student; /** - * Contains utility methods for populating {@code AddressBook} with sample data. + * Contains utility methods for populating {@code WellNus} with sample data. */ public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) + + public static final Note EMPTY_NOTE = new Note(" "); + + + public static Student[] getSampleStudents() { + return new Student[] { + new Student(new Name("Alex Yeoh"), new Phone("87438807"), + new Address("Blk 30 Geylang Street 29, #06-40"), getTagSet("high"), + new Note("Alex is experiencing high levels of stress and anxiety due to academic pressure.")), + new Student(new Name("Bernice Yu"), new Phone("99272758"), + new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), getTagSet("medium"), + new Note("Bernice is seeking counseling for relationship issues and emotional well-being.")), + new Student(new Name("Charlotte Oliveiro"), new Phone("93210283"), + new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), getTagSet("low"), + new Note("Charlotte is dealing with depression and self-esteem issues.")), + new Student(new Name("David Li"), new Phone("91031282"), + new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), getTagSet("high"), + new Note("David is struggling with anxiety and panic attacks.")), + new Student(new Name("Irfan Ibrahim"), new Phone("92492021"), + new Address("Blk 47 Tampines Street 20, #17-35"), getTagSet("medium"), + new Note("Irfan is looking for guidance on managing stress and improving mental health.")), + new Student(new Name("Roy Balakrishnan"), new Phone("92624417"), + new Address("Blk 45 Aljunied Street 85, #11-31"), getTagSet("low"), + new Note("Roy is seeking counseling for coping with grief and loss.")) }; } - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); + public static Appointment[] getSampleAppointments() { + try { + return new Appointment[] { + new Appointment(new Date("2023-12-14"), new Time("10:30"), new Time("11:30"), + new Name("Alex Yeoh"), new Description("First Session")), + new Appointment(new Date("2023-12-14"), new Time("12:00"), new Time("13:00"), + new Name("Bernice Yu"), new Description("Third Session")), + new Appointment(new Date("2023-12-14"), new Time("13:30"), new Time("15:00"), + new Name("David Li"), new Description("Check-up")), + new Appointment(new Date("2023-12-15"), new Time("11:20"), new Time("12:20"), + new Name("Irfan Ibrahim"), new Description("Follow-up")), + new Appointment(new Date("2023-12-15"), new Time("09:30"), new Time("10:30"), + new Name("Roy Balakrishnan"), new Description("Check-up")), + new Appointment(new Date("2023-12-16"), new Time("16:15"), new Time("17:15"), + new Name("Alex Yeoh"), new Description("Second Session")), + new Appointment(new Date("2023-12-17"), new Time("13:00"), new Time("14:00"), + new Name("Charlotte Oliveiro"), new Description("Check-up")), + }; + } catch (InvalidStartEndTimeException e) { + throw new RuntimeException(e); + } + } + + + public static ReadOnlyWellNus getSampleWellNus() { + WellNus sampleAb = new WellNus(); + for (Student sampleStudent : getSampleStudents()) { + sampleAb.addStudent(sampleStudent); + } + for (Appointment sampleAppointment : getSampleAppointments()) { + sampleAb.addAppointment(sampleAppointment); } return sampleAb; } @@ -51,10 +87,9 @@ public static ReadOnlyAddressBook getSampleAddressBook() { /** * Returns a tag set containing the list of strings given. */ - public static Set getTagSet(String... strings) { + public static Set getTagSet(String... strings) { return Arrays.stream(strings) - .map(Tag::new) + .map(RiskLevel::new) .collect(Collectors.toSet()); } - } diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java deleted file mode 100644 index f2e015105ae..00000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * - * @throws DataLoadingException if loading the data from storage failed. - */ - Optional readAddressBook() throws DataLoadingException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) throws DataLoadingException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedAppointment.java b/src/main/java/seedu/address/storage/JsonAdaptedAppointment.java new file mode 100644 index 00000000000..6225e32d5da --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedAppointment.java @@ -0,0 +1,110 @@ +package seedu.address.storage; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_START_END_TIME; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.Date; +import seedu.address.model.appointment.Description; +import seedu.address.model.appointment.Time; +import seedu.address.model.appointment.exceptions.InvalidStartEndTimeException; +import seedu.address.model.student.Name; + + +/** + * Jackson-friendly version of {@link Appointment}. + */ +public class JsonAdaptedAppointment { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Appointment's %s field is missing!"; + private final String date; + private final String startTime; + private final String endTime; + private final String name; + private final String description; + + + /** + * Constructs a {@code JsonAdaptedAppointment} with the given person details. + */ + @JsonCreator + public JsonAdaptedAppointment(@JsonProperty("name") String name, @JsonProperty("date") String date, + @JsonProperty("startTime") String startTime, @JsonProperty("endTime") String endTime, + @JsonProperty("description") String description) { + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + this.name = name; + this.description = description; + } + + /** + * Converts a given {@code Appointment} into this class for JSON use. + */ + public JsonAdaptedAppointment(Appointment source) { + date = source.getDate().value; + startTime = source.getStartTime().value; + endTime = source.getEndTime().value; + name = source.getName().value; + description = source.getDescription().value; + } + + /** + * Converts this Jackson-friendly adapted appointment object into the model's {@code Appointment} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted appointment. + */ + public Appointment toModelType() throws IllegalValueException { + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (date == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName())); + } + if (!Date.isValidDate(date)) { + throw new IllegalValueException(Date.MESSAGE_CONSTRAINTS); + } + final Date modelDate = new Date(date); + + if (startTime == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Time.class.getSimpleName())); + } + if (!Time.isValidTime(startTime)) { + throw new IllegalValueException(Time.MESSAGE_CONSTRAINTS); + } + final Time modelStartTime = new Time(startTime); + + if (endTime == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Time.class.getSimpleName())); + } + if (!Time.isValidTime(endTime)) { + throw new IllegalValueException(Time.MESSAGE_CONSTRAINTS); + } + final Time modelEndTime = new Time(endTime); + + if (description == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + Description.class.getSimpleName())); + } + if (!Description.isValidDescription(description)) { + throw new IllegalValueException(Description.MESSAGE_CONSTRAINTS); + } + final Description modelDescription = new Description(description); + + try { + return new Appointment(modelDate, modelStartTime, modelEndTime, modelName, modelDescription); + } catch (InvalidStartEndTimeException e) { + throw new ParseException(MESSAGE_INVALID_START_END_TIME); + } + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index bd1ca0f56c8..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - private final String name; - private final String phone; - private final String email; - private final String address; - private final List tags = new ArrayList<>(); - - /** - * Constructs a {@code JsonAdaptedPerson} with the given person details. - */ - @JsonCreator - public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone, - @JsonProperty("email") String email, @JsonProperty("address") String address, - @JsonProperty("tags") List tags) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tags != null) { - this.tags.addAll(tags); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tags.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tags) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedRiskLevel.java b/src/main/java/seedu/address/storage/JsonAdaptedRiskLevel.java new file mode 100644 index 00000000000..ed64a30309b --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedRiskLevel.java @@ -0,0 +1,48 @@ +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.risklevel.RiskLevel; + +/** + * Jackson-friendly version of {@link RiskLevel}. + */ +class JsonAdaptedRiskLevel { + + private final String riskLevel; + + /** + * Constructs a {@code JsonAdaptedRiskLevel} with the given {@code riskLevel}. + */ + @JsonCreator + public JsonAdaptedRiskLevel(String riskLevel) { + this.riskLevel = riskLevel; + } + + /** + * Converts a given {@code RiskLevel} into this class for Jackson use. + */ + public JsonAdaptedRiskLevel(RiskLevel source) { + riskLevel = source.riskLevel; + } + + @JsonValue + public String getRiskLevel() { + return riskLevel; + } + + /** + * Converts this Jackson-friendly adapted tag object into the model's {@code RiskLevel} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted risk level. + */ + public RiskLevel toModelType() throws IllegalValueException { + if (!RiskLevel.isValidRiskLevel(riskLevel)) { + throw new IllegalValueException(RiskLevel.MESSAGE_CONSTRAINTS); + } + return new RiskLevel(riskLevel); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedStudent.java b/src/main/java/seedu/address/storage/JsonAdaptedStudent.java new file mode 100644 index 00000000000..9528ba488d5 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedStudent.java @@ -0,0 +1,121 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Phone; +import seedu.address.model.student.Student; + +/** + * Jackson-friendly version of {@link Student}. + */ +class JsonAdaptedStudent { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Student's %s field is missing!"; + public static final String EXCEED_RISK_LEVEL_SIZE_MESSAGE = "Student has more than one risk levels!"; + + private final String name; + private final String phone; + private final String address; + private final List risklevel = new ArrayList<>(); + private final String note; + + /** + * Constructs a {@code JsonAdaptedStudent} with the given student details. + */ + @JsonCreator + public JsonAdaptedStudent(@JsonProperty("name") String name, @JsonProperty("phone") String phone, + @JsonProperty("address") String address, + @JsonProperty("risklevel") List risklevel, + @JsonProperty("note") String note) { + this.name = name; + this.phone = phone; + this.address = address; + if (risklevel != null) { + this.risklevel.addAll(risklevel); + } + this.note = note; + } + + /** + * Converts a given {@code Student} into this class for Jackson use. + */ + public JsonAdaptedStudent(Student source) { + name = source.getName().value; + phone = source.getPhone().value; + address = source.getAddress().value; + risklevel.addAll(source.getRiskLevel().stream() + .map(JsonAdaptedRiskLevel::new) + .collect(Collectors.toList())); + note = source.getNote().value; + } + + /** + * Converts this Jackson-friendly adapted student object into the model's {@code Student} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted student. + */ + public Student toModelType() throws IllegalValueException { + final List studentRiskLevel = new ArrayList<>(); + + System.out.println(name); + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); + } + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + final Name modelName = new Name(name); + + if (phone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); + } + if (!Phone.isValidPhone(phone)) { + throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); + } + final Phone modelPhone = new Phone(phone); + + if (address == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); + } + if (!Address.isValidAddress(address)) { + throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); + } + + if (note == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Note.class.getSimpleName())); + } + + if (!Note.isValidNote(note)) { + throw new IllegalValueException(Note.MESSAGE_CONSTRAINTS); + } + + if (risklevel.size() > 1) { + throw new IllegalValueException(EXCEED_RISK_LEVEL_SIZE_MESSAGE); + } + + assert risklevel != null; + + for (JsonAdaptedRiskLevel riskLevel : risklevel) { + studentRiskLevel.add(riskLevel.toModelType()); + } + + final Note modelNote = new Note(note); + + final Address modelAddress = new Address(address); + final Set modelTags = new HashSet<>(studentRiskLevel); + return new Student(modelName, modelPhone, modelAddress, modelTags, modelNote); + } + +} 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/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java deleted file mode 100644 index 5efd834091d..00000000000 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRootName; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (JsonAdaptedPerson jsonAdaptedPerson : persons) { - Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableWellNus.java b/src/main/java/seedu/address/storage/JsonSerializableWellNus.java new file mode 100644 index 00000000000..c9a33d1abf3 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonSerializableWellNus.java @@ -0,0 +1,85 @@ +package seedu.address.storage; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonRootName; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.ReadOnlyWellNus; +import seedu.address.model.WellNus; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Student; + +/** + * An Immutable WellNus that is serializable to JSON format. + */ +@JsonRootName(value = "wellnus") +class JsonSerializableWellNus { + + public static final String MESSAGE_DUPLICATE_STUDENT = "Students list contains duplicate student(s)."; + public static final String MESSAGE_DUPLICATE_APPOINTMENT = "Appointments list contains duplicate appointment(s)."; + public static final String MESSAGE_STUDENT_NOT_FOUND = "Appointments list contains appointment(s) whose " + + "corresponding student is not found in students list"; + + public static final String MESSAGE_OVERLAPPING_APPOINTMENT = "Appointments list contains overlapping appointments."; + private final List students = new ArrayList<>(); + private final List appointments = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableWellNus} with the given students. + */ + @JsonCreator + public JsonSerializableWellNus(@JsonProperty("students") List students, + @JsonProperty("appointments") List appointments) { + this.students.addAll(students); + this.appointments.addAll(appointments); + } + + /** + * Converts a given {@code ReadOnlyWellNus} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableWellNus}. + */ + public JsonSerializableWellNus(ReadOnlyWellNus source) { + students.addAll(source.getStudentList().stream().map(JsonAdaptedStudent::new).collect(Collectors.toList())); + appointments.addAll(source.getAppointmentList() + .stream().map(JsonAdaptedAppointment::new).collect(Collectors.toList())); + } + + /** + * Converts this WellNus book into the model's {@code WellNus} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public WellNus toModelType() throws IllegalValueException { + WellNus wellNus = new WellNus(); + for (JsonAdaptedStudent jsonAdaptedStudent : students) { + Student student = jsonAdaptedStudent.toModelType(); + if (wellNus.hasStudent(student)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_STUDENT); + } + + wellNus.addStudent(student); + } + for (JsonAdaptedAppointment jsonAdaptedAppointment : appointments) { + Appointment appointment = jsonAdaptedAppointment.toModelType(); + if (wellNus.hasAppointment(appointment)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_APPOINTMENT); + } + if (wellNus.hasNoStudentForAppointment(appointment)) { + throw new IllegalValueException(MESSAGE_STUDENT_NOT_FOUND); + } + + if (wellNus.hasOverlapsWithAppointments(appointment)) { + throw new IllegalValueException(MESSAGE_OVERLAPPING_APPOINTMENT); + } + + wellNus.addAppointment(appointment); + } + return wellNus; + } +} diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonWellNusStorage.java similarity index 50% rename from src/main/java/seedu/address/storage/JsonAddressBookStorage.java rename to src/main/java/seedu/address/storage/JsonWellNusStorage.java index 41e06f264e1..cec8c5a3807 100644 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ b/src/main/java/seedu/address/storage/JsonWellNusStorage.java @@ -12,47 +12,47 @@ import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.commons.util.FileUtil; import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyWellNus; /** - * A class to access AddressBook data stored as a json file on the hard disk. + * A class to access WellNus data stored as a json file on the hard disk. */ -public class JsonAddressBookStorage implements AddressBookStorage { +public class JsonWellNusStorage implements WellNusStorage { - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); + private static final Logger logger = LogsCenter.getLogger(JsonWellNusStorage.class); private Path filePath; - public JsonAddressBookStorage(Path filePath) { + public JsonWellNusStorage(Path filePath) { this.filePath = filePath; } - public Path getAddressBookFilePath() { + public Path getWellNusFilePath() { return filePath; } @Override - public Optional readAddressBook() throws DataLoadingException { - return readAddressBook(filePath); + public Optional readWellNus() throws DataLoadingException { + return readWellNus(filePath); } /** - * Similar to {@link #readAddressBook()}. + * Similar to {@link #readWellNus()}. * * @param filePath location of the data. Cannot be null. * @throws DataLoadingException if loading the data from storage failed. */ - public Optional readAddressBook(Path filePath) throws DataLoadingException { + public Optional readWellNus(Path filePath) throws DataLoadingException { requireNonNull(filePath); - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { + Optional jsonWellNus = JsonUtil.readJsonFile( + filePath, JsonSerializableWellNus.class); + if (!jsonWellNus.isPresent()) { return Optional.empty(); } try { - return Optional.of(jsonAddressBook.get().toModelType()); + return Optional.of(jsonWellNus.get().toModelType()); } catch (IllegalValueException ive) { logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); throw new DataLoadingException(ive); @@ -60,21 +60,21 @@ public Optional readAddressBook(Path filePath) throws DataL } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); + public void saveWellNus(ReadOnlyWellNus wellNus) throws IOException { + saveWellNus(wellNus, filePath); } /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. + * Similar to {@link #saveWellNus(ReadOnlyWellNus)}. * * @param filePath location of the data. Cannot be null. */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); + public void saveWellNus(ReadOnlyWellNus wellNus, Path filePath) throws IOException { + requireNonNull(wellNus); requireNonNull(filePath); FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); + JsonUtil.saveJsonFile(new JsonSerializableWellNus(wellNus), filePath); } } diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java index 9fba0c7a1d6..2f1edbfaa65 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/seedu/address/storage/Storage.java @@ -5,14 +5,14 @@ import java.util.Optional; import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.ReadOnlyWellNus; import seedu.address.model.UserPrefs; /** * API of the Storage component */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { +public interface Storage extends WellNusStorage, UserPrefsStorage { @Override Optional readUserPrefs() throws DataLoadingException; @@ -21,12 +21,12 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage { void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; @Override - Path getAddressBookFilePath(); + Path getWellNusFilePath(); @Override - Optional readAddressBook() throws DataLoadingException; + Optional readWellNus() throws DataLoadingException; @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveWellNus(ReadOnlyWellNus wellNus) throws IOException; } diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java index 8b84a9024d5..02c779ac76e 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/address/storage/StorageManager.java @@ -7,24 +7,24 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.ReadOnlyWellNus; import seedu.address.model.UserPrefs; /** - * Manages storage of AddressBook data in local storage. + * Manages storage of WellNus data in local storage. */ public class StorageManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; + private WellNusStorage wellNusStorage; private UserPrefsStorage userPrefsStorage; /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. + * Creates a {@code StorageManager} with the given {@code WellNusStorage} and {@code UserPrefStorage}. */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - this.addressBookStorage = addressBookStorage; + public StorageManager(WellNusStorage wellNusStorage, UserPrefsStorage userPrefsStorage) { + this.wellNusStorage = wellNusStorage; this.userPrefsStorage = userPrefsStorage; } @@ -46,33 +46,33 @@ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { } - // ================ AddressBook methods ============================== + // ================ WellNus methods ============================== @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); + public Path getWellNusFilePath() { + return wellNusStorage.getWellNusFilePath(); } @Override - public Optional readAddressBook() throws DataLoadingException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); + public Optional readWellNus() throws DataLoadingException { + return readWellNus(wellNusStorage.getWellNusFilePath()); } @Override - public Optional readAddressBook(Path filePath) throws DataLoadingException { + public Optional readWellNus(Path filePath) throws DataLoadingException { logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); + return wellNusStorage.readWellNus(filePath); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); + public void saveWellNus(ReadOnlyWellNus wellNus) throws IOException { + saveWellNus(wellNus, wellNusStorage.getWellNusFilePath()); } @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { + public void saveWellNus(ReadOnlyWellNus wellNus, Path filePath) throws IOException { logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); + wellNusStorage.saveWellNus(wellNus, filePath); } } diff --git a/src/main/java/seedu/address/storage/WellNusStorage.java b/src/main/java/seedu/address/storage/WellNusStorage.java new file mode 100644 index 00000000000..af26a10a326 --- /dev/null +++ b/src/main/java/seedu/address/storage/WellNusStorage.java @@ -0,0 +1,46 @@ +package seedu.address.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataLoadingException; +import seedu.address.model.ReadOnlyWellNus; +import seedu.address.model.WellNus; + +/** + * Represents a storage for {@link WellNus}. + */ +public interface WellNusStorage { + + /** + * Returns the file path of the data file. + */ + Path getWellNusFilePath(); + + /** + * Returns WellNus data as a {@link ReadOnlyWellNus}. + * Returns {@code Optional.empty()} if storage file is not found. + * + * @throws DataLoadingException if loading the data from storage failed. + */ + Optional readWellNus() throws DataLoadingException; + + /** + * @see #getWellNusFilePath() + */ + Optional readWellNus(Path filePath) throws DataLoadingException; + + /** + * Saves the given {@link ReadOnlyWellNus} to the storage. + * @param wellNus cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveWellNus(ReadOnlyWellNus wellNus) throws IOException; + + /** + * @see #saveWellNus(ReadOnlyWellNus) + */ + void saveWellNus(ReadOnlyWellNus wellNus, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/ui/AppointmentCard.java b/src/main/java/seedu/address/ui/AppointmentCard.java new file mode 100644 index 00000000000..96e827c12b6 --- /dev/null +++ b/src/main/java/seedu/address/ui/AppointmentCard.java @@ -0,0 +1,47 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.util.DateTimeParser; + +/** + * An UI component that displays information of a {@code Appointment}. + */ +public class AppointmentCard extends UiPart { + + private static final String FXML = "AppointmentListCard.fxml"; + + public final Appointment appointment; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label date; + @FXML + private Label startTime; + @FXML + private Label endTime; + @FXML + private Label description; + + /** + * Creates a {@code StudentCode} with the given {@code Student} and index to display. + */ + public AppointmentCard(Appointment appointment, int displayedIndex) { + super(FXML); + this.appointment = appointment; + id.setText(displayedIndex + ". "); + name.setText(appointment.getName().value); + date.setText("Date: " + DateTimeParser.formatDate(appointment.getDate().value)); + startTime.setText("From: " + DateTimeParser.formatTime(appointment.getStartTime().value)); + endTime.setText("To: " + DateTimeParser.formatTime(appointment.getEndTime().value)); + description.setText("Description: " + appointment.getDescription().value); + } +} diff --git a/src/main/java/seedu/address/ui/AppointmentListPanel.java b/src/main/java/seedu/address/ui/AppointmentListPanel.java new file mode 100644 index 00000000000..b1910cab952 --- /dev/null +++ b/src/main/java/seedu/address/ui/AppointmentListPanel.java @@ -0,0 +1,56 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.appointment.Appointment; + +/** + * Panel containing the list of appointments. + */ +public class AppointmentListPanel extends UiPart { + private static final String FXML = "AppointmentListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(AppointmentListPanel.class); + + @FXML + private VBox columnContainer; + @FXML + private Label columnTitle; + @FXML + private ListView appointmentListView; + + /** + * Creates a {@code AppointmentListPanel} with the given {@code ObservableList}. + */ + public AppointmentListPanel(ObservableList appointmentList) { + super(FXML); + columnContainer.setAlignment(Pos.CENTER); + appointmentListView.setItems(appointmentList); + appointmentListView.setCellFactory(listView -> new AppointmentListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Appointment} using a {@code AppointmentCard}. + */ + class AppointmentListViewCell extends ListCell { + @Override + protected void updateItem(Appointment appointment, boolean empty) { + super.updateItem(appointment, empty); + + if (empty || appointment == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new AppointmentCard(appointment, getIndex() + 1).getRoot()); + } + } + } +} diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java index 3f16b2fcf26..b28f1fe2abe 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/address/ui/HelpWindow.java @@ -15,7 +15,7 @@ */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; + public static final String USERGUIDE_URL = "https://ay2324s1-cs2103t-w13-4.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 79e74ef37c0..7f925973261 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -31,7 +31,9 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private StudentListPanel studentListPanel; + private AppointmentListPanel appointmentListPanel; + private StudentNotePanel studentNotePanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -42,7 +44,13 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane studentListPanelPlaceholder; + + @FXML + private StackPane appointmentListPanelPlaceholder; + + @FXML + private StackPane studentNotePlaceholder; @FXML private StackPane resultDisplayPlaceholder; @@ -110,13 +118,17 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + studentListPanel = new StudentListPanel(logic.getFilteredStudentList(), this::showNote); + studentListPanelPlaceholder.getChildren().add(studentListPanel.getRoot()); + appointmentListPanel = new AppointmentListPanel(logic.getFilteredAppointmentList()); + appointmentListPanelPlaceholder.getChildren().add(appointmentListPanel.getRoot()); + studentNotePanel = new StudentNotePanel("No student information chosen currently", ""); + studentNotePlaceholder.getChildren().add(studentNotePanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getWellNusFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); @@ -135,6 +147,7 @@ private void setWindowDefaultSize(GuiSettings guiSettings) { } } + /** * Opens the help window or focuses on it if it's already opened. */ @@ -147,6 +160,7 @@ public void handleHelp() { } } + void show() { primaryStage.show(); } @@ -163,8 +177,8 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + public StudentListPanel getStudentListPanel() { + return studentListPanel; } /** @@ -177,6 +191,8 @@ private CommandResult executeCommand(String commandText) throws CommandException CommandResult commandResult = logic.execute(commandText); logger.info("Result: " + commandResult.getFeedbackToUser()); resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser()); + studentNotePanel.resetNotes(); + if (commandResult.isShowHelp()) { handleHelp(); @@ -193,4 +209,9 @@ private CommandResult executeCommand(String commandText) throws CommandException throw e; } } + + private void showNote(int studentIndex) { + studentNotePanel.updateNotes(logic.getStudentName(studentIndex).value, + logic.getStudentNote(studentIndex).value); + } } diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java deleted file mode 100644 index 094c42cda82..00000000000 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.address.ui; - -import java.util.Comparator; - -import javafx.fxml.FXML; -import javafx.scene.control.Label; -import javafx.scene.layout.FlowPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import seedu.address.model.person.Person; - -/** - * An UI component that displays information of a {@code Person}. - */ -public class PersonCard extends UiPart { - - private static final String FXML = "PersonListCard.fxml"; - - /** - * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. - * As a consequence, UI elements' variable names cannot be set to such keywords - * or an exception will be thrown by JavaFX during runtime. - * - * @see The issue on AddressBook level 4 - */ - - public final Person person; - - @FXML - private HBox cardPane; - @FXML - private Label name; - @FXML - private Label id; - @FXML - private Label phone; - @FXML - private Label address; - @FXML - private Label email; - @FXML - private FlowPane tags; - - /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. - */ - public PersonCard(Person person, int displayedIndex) { - super(FXML); - this.person = person; - id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() - .sorted(Comparator.comparing(tag -> tag.tagName)) - .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); - } -} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index f4c501a897b..00000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Panel containing the list of persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - /** - * Creates a {@code PersonListPanel} with the given {@code ObservableList}. - */ - public PersonListPanel(ObservableList personList) { - super(FXML); - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell extends ListCell { - @Override - protected void updateItem(Person person, boolean empty) { - super.updateItem(person, empty); - - if (empty || person == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); - } - } - } - -} diff --git a/src/main/java/seedu/address/ui/StudentCard.java b/src/main/java/seedu/address/ui/StudentCard.java new file mode 100644 index 00000000000..32b036f77b2 --- /dev/null +++ b/src/main/java/seedu/address/ui/StudentCard.java @@ -0,0 +1,97 @@ +package seedu.address.ui; + +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import seedu.address.model.student.Student; + +/** + * An UI component that displays information of a {@code Student}. + */ +public class StudentCard extends UiPart { + + private static final String FXML = "StudentListCard.fxml"; + + /** + * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. + * As a consequence, UI elements' variable names cannot be set to such keywords + * or an exception will be thrown by JavaFX during runtime. + * + * @see The issue on AddressBook level 4 + */ + + public final Student student; + private CommandExecutor showNote; + private int index; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label phone; + @FXML + private Label address; + @FXML + private FlowPane tags; + + /** + * Creates a {@code StudentCode} with the given {@code Student} and index to display. + */ + public StudentCard(Student student, int displayedIndex, CommandExecutor showNote) { + super(FXML); + this.student = student; + this.showNote = showNote; + index = displayedIndex; + id.setText(displayedIndex + ". "); + name.setText(student.getName().value); + phone.setText(student.getPhone().value); + address.setText(student.getAddress().value); + + student.getRiskLevel().forEach(tag -> { + Label tagLabel = new Label(tag.riskLevel); + tagLabel.getStyleClass().add(getTagStyleClass(tag.riskLevel)); + tags.getChildren().add(tagLabel); + }); + } + + @FXML + private void displayNote(MouseEvent event) { + if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) { + showNote.execute(index); + } + } + + + private String getTagStyleClass(String riskLevel) { + switch (riskLevel) { + case "low": + return "low-risk-tag"; + case "medium": + return "medium-risk-tag"; + case "high": + return "high-risk-tag"; + default: + return ""; + } + } + + /** + * Represents a function that can execute commands. + */ + @FunctionalInterface + public interface CommandExecutor { + /** + * Executes the command and returns the result. + * + * @see seedu.address.logic.Logic#execute(String) + */ + void execute(int studentIndex); + } +} diff --git a/src/main/java/seedu/address/ui/StudentListPanel.java b/src/main/java/seedu/address/ui/StudentListPanel.java new file mode 100644 index 00000000000..bd82636da61 --- /dev/null +++ b/src/main/java/seedu/address/ui/StudentListPanel.java @@ -0,0 +1,60 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.student.Student; +import seedu.address.ui.StudentCard.CommandExecutor; + +/** + * Panel containing the list of students. + */ +public class StudentListPanel extends UiPart { + private static final String FXML = "StudentListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(StudentListPanel.class); + + private CommandExecutor showNote; + @FXML + private VBox columnContainer; + @FXML + private Label columnTitle; + @FXML + private ListView studentListView; + + /** + * Creates a {@code StudentListPanel} with the given {@code ObservableList}. + */ + public StudentListPanel(ObservableList studentList, CommandExecutor showNote) { + super(FXML); + this.showNote = showNote; + columnContainer.setAlignment(Pos.CENTER); + studentListView.setItems(studentList); + studentListView.setCellFactory(listView -> new StudentListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Student} using a {@code StudentCard}. + */ + class StudentListViewCell extends ListCell { + @Override + protected void updateItem(Student student, boolean empty) { + super.updateItem(student, empty); + + if (empty || student == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new StudentCard(student, getIndex() + 1, showNote).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/address/ui/StudentNotePanel.java b/src/main/java/seedu/address/ui/StudentNotePanel.java new file mode 100644 index 00000000000..f6ee9d079c2 --- /dev/null +++ b/src/main/java/seedu/address/ui/StudentNotePanel.java @@ -0,0 +1,60 @@ +package seedu.address.ui; + +import java.util.logging.Logger; + +import javafx.fxml.FXML; +import javafx.geometry.Pos; +import javafx.scene.control.Label; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import seedu.address.commons.core.LogsCenter; + +/** + * Panel containing student notes of a specific student. + */ +public class StudentNotePanel extends UiPart { + + private static final String FXML = "StudentNotePanel.fxml"; + private final Logger logger = LogsCenter.getLogger(AppointmentListPanel.class); + + @FXML + private VBox columnContainer; + @FXML + private Label columnTitle; + @FXML + private Label studentName; + @FXML + private Label notesPara; + + /** + * Creates a {@code StudentNotePanel} with the given {@code ObservableList}. + */ + public StudentNotePanel(String name, String notes) { + super(FXML); + columnContainer.setAlignment(Pos.CENTER); + studentName.setText(name); + notesPara.setText(notes); + } + + /** + * Update the notes in the UI + * @param name Name of the chosen student + * @param notes Student Notes of the student + */ + public void updateNotes(String name, String notes) { + if (notes.trim().length() == 0) { + notesPara.setText("You have no notes on this student"); + } else { + notesPara.setText(notes); + } + studentName.setText("Student Name: " + name); + } + + /** + * Reset the note panel in the UI + */ + public void resetNotes() { + studentName.setText("No student information chosen currently"); + notesPara.setText(""); + } +} diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java index fdf024138bc..7957eb6598d 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/address/ui/UiManager.java @@ -65,7 +65,7 @@ void showAlertDialogAndWait(Alert.AlertType type, String title, String headerTex private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, String headerText, String contentText) { final Alert alert = new Alert(type); - alert.getDialogPane().getStylesheets().add("view/DarkTheme.css"); + alert.getDialogPane().getStylesheets().add("css/DarkTheme.css"); alert.initOwner(owner); alert.setTitle(title); alert.setHeaderText(headerText); diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/css/DarkTheme.css similarity index 93% rename from src/main/resources/view/DarkTheme.css rename to src/main/resources/css/DarkTheme.css index 36e6b001cd8..31e0e5c6212 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/css/DarkTheme.css @@ -123,13 +123,21 @@ .cell_big_label { -fx-font-family: "Segoe UI Semibold"; -fx-font-size: 16px; - -fx-text-fill: #010504; + -fx-text-fill: white; } .cell_small_label { -fx-font-family: "Segoe UI"; -fx-font-size: 13px; - -fx-text-fill: #010504; + -fx-text-fill: white; +} + +.column_title { + -fx-alignment: center; + -fx-font-family: "Segoe UI Semibold"; + -fx-font-size: 22px; + -fx-text-fill: white; + -fx-padding: 0 0 10; } .stack-pane { @@ -328,7 +336,7 @@ -fx-text-fill: white; } -#filterField, #personListPanel, #personWebpage { +#filterField, #studentListPanel, #studentWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } @@ -344,9 +352,25 @@ #tags .label { -fx-text-fill: white; - -fx-background-color: #3e7b91; -fx-padding: 1 3 1 3; -fx-border-radius: 2; -fx-background-radius: 2; -fx-font-size: 11; } + +.low-risk-tag { + -fx-background-color: #9ACD32; +} + +.medium-risk-tag { + -fx-background-color: #00BFFF; +} + +.high-risk-tag { + -fx-background-color: #DC143C; +} + +.noteBox { + -fx-background-color: #3c3e3f; + -fx-padding: 10; +} diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/css/Extensions.css similarity index 100% rename from src/main/resources/view/Extensions.css rename to src/main/resources/css/Extensions.css diff --git a/src/main/resources/view/HelpWindow.css b/src/main/resources/css/HelpWindow.css similarity index 100% rename from src/main/resources/view/HelpWindow.css rename to src/main/resources/css/HelpWindow.css diff --git a/src/main/resources/view/AppointmentListCard.fxml b/src/main/resources/view/AppointmentListCard.fxml new file mode 100644 index 00000000000..d8335b8be9b --- /dev/null +++ b/src/main/resources/view/AppointmentListCard.fxml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/AppointmentListPanel.fxml b/src/main/resources/view/AppointmentListPanel.fxml new file mode 100644 index 00000000000..c0d1ccde8a1 --- /dev/null +++ b/src/main/resources/view/AppointmentListPanel.fxml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml index e01f330de33..3bda4dd9f89 100644 --- a/src/main/resources/view/HelpWindow.fxml +++ b/src/main/resources/view/HelpWindow.fxml @@ -16,7 +16,7 @@ - + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 7778f666a0a..f806deca09a 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -3,27 +3,29 @@ + + + title="WellNUS App" minHeight="600" minWidth="920" onCloseRequest="#handleExit"> - - + + - + @@ -38,20 +40,37 @@ + + + + + - - - - - + - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml deleted file mode 100644 index f5e812e25e6..00000000000 --- a/src/main/resources/view/PersonListCard.fxml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml deleted file mode 100644 index a1bb6bbace8..00000000000 --- a/src/main/resources/view/PersonListPanel.fxml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/main/resources/view/StudentListCard.fxml b/src/main/resources/view/StudentListCard.fxml new file mode 100644 index 00000000000..c9bd030e408 --- /dev/null +++ b/src/main/resources/view/StudentListCard.fxml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/StudentListPanel.fxml b/src/main/resources/view/StudentListPanel.fxml new file mode 100644 index 00000000000..91507eab051 --- /dev/null +++ b/src/main/resources/view/StudentListPanel.fxml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/src/main/resources/view/StudentNotePanel.fxml b/src/main/resources/view/StudentNotePanel.fxml new file mode 100644 index 00000000000..90530145ebf --- /dev/null +++ b/src/main/resources/view/StudentNotePanel.fxml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json deleted file mode 100644 index 6a4d2b7181c..00000000000 --- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "persons": [ { - "name": "Valid Person", - "phone": "9482424", - "email": "hans@example.com", - "address": "4th street" - }, { - "name": "Person With Invalid Phone Field", - "phone": "948asdf2424", - "email": "hans@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json deleted file mode 100644 index ccd21f7d1a9..00000000000 --- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Person with invalid name field: Ha!ns Mu@ster", - "phone": "9482424", - "email": "hans@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json deleted file mode 100644 index a7427fe7aa2..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "persons": [ { - "name": "Alice Pauline", - "phone": "94351253", - "email": "alice@example.com", - "address": "123, Jurong West Ave 6, #08-111", - "tags": [ "friends" ] - }, { - "name": "Alice Pauline", - "phone": "94351253", - "email": "pauline@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json deleted file mode 100644 index ad3f135ae42..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Hans Muster", - "phone": "9482424", - "email": "invalid@email!3e", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json deleted file mode 100644 index 72262099d35..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()", - "persons" : [ { - "name" : "Alice Pauline", - "phone" : "94351253", - "email" : "alice@example.com", - "address" : "123, Jurong West Ave 6, #08-111", - "tags" : [ "friends" ] - }, { - "name" : "Benson Meier", - "phone" : "98765432", - "email" : "johnd@example.com", - "address" : "311, Clementi Ave 2, #02-25", - "tags" : [ "owesMoney", "friends" ] - }, { - "name" : "Carl Kurz", - "phone" : "95352563", - "email" : "heinz@example.com", - "address" : "wall street", - "tags" : [ ] - }, { - "name" : "Daniel Meier", - "phone" : "87652533", - "email" : "cornelia@example.com", - "address" : "10th street", - "tags" : [ "friends" ] - }, { - "name" : "Elle Meyer", - "phone" : "9482224", - "email" : "werner@example.com", - "address" : "michegan ave", - "tags" : [ ] - }, { - "name" : "Fiona Kunz", - "phone" : "9482427", - "email" : "lydia@example.com", - "address" : "little tokyo", - "tags" : [ ] - }, { - "name" : "George Best", - "phone" : "9482442", - "email" : "anna@example.com", - "address" : "4th street", - "tags" : [ ] - } ] -} diff --git a/src/test/data/JsonSerializableWellNusTest/appointmentStudentNotFoundWellNus.json b/src/test/data/JsonSerializableWellNusTest/appointmentStudentNotFoundWellNus.json new file mode 100644 index 00000000000..fd672001954 --- /dev/null +++ b/src/test/data/JsonSerializableWellNusTest/appointmentStudentNotFoundWellNus.json @@ -0,0 +1,34 @@ +{ + "students" : [ { + "name" : "Alice Pauline", + "phone" : "94351253", + "address" : "123, Jurong West Ave 6, #08-111", + "risklevel" : [ "high" ], + "note": " " + }, { + "name" : "Benny Dover", + "phone" : "98765432", + "address" : "311, Clementi Ave 2, #02-25", + "risklevel" : [ "medium" ], + "note": " " + } ], + "appointments" : [ { + "name" : "Alice Pauline", + "date" : "2023-10-15", + "startTime" : "16:30", + "endTime" : "17:30", + "description" : "First Session" + }, { + "name" : "Benny Dover", + "date" : "2023-11-01", + "startTime" : "13:30", + "endTime" : "15:30", + "description" : "Second Session" + }, { + "name" : "Daniel Meier", + "date" : "2023-12-02", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Third Session" + } ] +} diff --git a/src/test/data/JsonSerializableWellNusTest/duplicateAppointmentWellNus.json b/src/test/data/JsonSerializableWellNusTest/duplicateAppointmentWellNus.json new file mode 100644 index 00000000000..7280d30204c --- /dev/null +++ b/src/test/data/JsonSerializableWellNusTest/duplicateAppointmentWellNus.json @@ -0,0 +1,64 @@ +{ + "students" : [ { + "name" : "Alice Pauline", + "phone" : "94351253", + "address" : "123, Jurong West Ave 6, #08-111", + "risklevel" : [ "high" ], + "note" : " " + }, { + "name" : "Benson Meier", + "phone" : "98765432", + "address" : "311, Clementi Ave 2, #02-25", + "risklevel" : [ "medium" ], + "note" : " " + }, { + "name" : "Carl Kurz", + "phone" : "95352563", + "address" : "wall street", + "risklevel" : [ ], + "note" : " " + }, { + "name" : "Daniel Meier", + "phone" : "87652533", + "address" : "10th street", + "risklevel" : [ "low" ], + "note" : " " + }, { + "name" : "Elle Meyer", + "phone" : "94822240", + "address" : "michegan ave", + "risklevel" : [ ], + "note" : " " + }, { + "name" : "Fiona Kunz", + "phone" : "94824270", + "address" : "little tokyo", + "risklevel" : [ ], + "note" : " " + }, { + "name" : "George Best", + "phone" : "94824420", + "address" : "4th street", + "risklevel" : [ ], + "note" : " " + } ], + "appointments" : [ { + "name" : "George Best", + "date" : "2023-11-30", + "startTime" : "16:30", + "endTime" : "17:30", + "description" : "First Session" + }, { + "name" : "Fiona Kunz", + "date" : "2023-10-14", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Second Session" + }, { + "name" : "George Best", + "date" : "2023-11-30", + "startTime" : "16:30", + "endTime" : "17:30", + "description" : "First Session" + } ] +} diff --git a/src/test/data/JsonSerializableWellNusTest/duplicateStudentWellNus.json b/src/test/data/JsonSerializableWellNusTest/duplicateStudentWellNus.json new file mode 100644 index 00000000000..f8d1434bdd0 --- /dev/null +++ b/src/test/data/JsonSerializableWellNusTest/duplicateStudentWellNus.json @@ -0,0 +1,57 @@ +{ + "students": [ { + "name": "Alice Pauline", + "phone": "94351253", + "address": "123, Jurong West Ave 6, #08-111", + "risklevel": [ "high" ], + "note" : " " + }, { + "name": "Alice Pauline", + "phone": "94351253", + "address": "4th street", + "note" : " " + }, { + "name" : "Daniel Meier", + "phone" : "87652533", + "address" : "10th street", + "risklevel" : [ "low" ], + "note" : " " + }], + "appointments" : [ { + "name" : "Daniel Meier", + "date" : "2023-10-15", + "startTime" : "10:00", + "endTime" : "11:00", + "description" : "Third Session" + }, { + "name" : "Daniel Meier", + "date" : "2023-10-16", + "startTime" : "14:45", + "endTime" : "15:45", + "description" : "Check-up" + }, { + "name" : "Daniel Meier", + "date" : "2023-10-17", + "startTime" : "11:20", + "endTime" : "12:20", + "description" : "Follow-up" + }, { + "name" : "Daniel Meier", + "date" : "2023-10-18", + "startTime" : "09:30", + "endTime" : "10:30", + "description" : "Check-up" + }, { + "name" : "Daniel Meier", + "date" : "2023-10-19", + "startTime" : "16:15", + "endTime" : "17:15", + "description" : "Second Session" + }, { + "name" : "Daniel Meier", + "date" : "2023-10-20", + "startTime" : "13:00", + "endTime" : "14:00", + "description" : "Check-up" + } ] +} diff --git a/src/test/data/JsonSerializableWellNusTest/invalidAppointmentWellNus.json b/src/test/data/JsonSerializableWellNusTest/invalidAppointmentWellNus.json new file mode 100644 index 00000000000..2fa49977182 --- /dev/null +++ b/src/test/data/JsonSerializableWellNusTest/invalidAppointmentWellNus.json @@ -0,0 +1,64 @@ +{ + "students" : [ { + "name" : "Alice Pauline", + "phone" : "94351253", + "address" : "123, Jurong West Ave 6, #08-111", + "risklevel" : [ "high" ], + "note" : " " + }, { + "name" : "Benson Meier", + "phone" : "98765432", + "address" : "311, Clementi Ave 2, #02-25", + "risklevel" : [ "medium" ], + "note" : " " + }, { + "name" : "Carl Kurz", + "phone" : "95352563", + "address" : "wall street", + "risklevel" : [ ], + "note" : " " + }, { + "name" : "Daniel Meier", + "phone" : "87652533", + "address" : "10th street", + "risklevel" : [ "low" ], + "note" : " " + }, { + "name" : "Elle Meyer", + "phone" : "94822240", + "address" : "michegan ave", + "risklevel" : [ ], + "note" : " " + }, { + "name" : "Fiona Kunz", + "phone" : "94824270", + "address" : "little tokyo", + "risklevel" : [ ], + "note" : " " + }, { + "name" : "George Best", + "phone" : "94824420", + "address" : "4th street", + "risklevel" : [ ], + "note" : " " + } ], + "appointments" : [ { + "name" : "Daniel Meier", + "date" : "202311-30", + "startTime" : "16:30", + "endTime" : "17:30", + "description" : "First Session" + }, { + "name" : "George Best", + "date" : "2023-10-14", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Second Session" + }, { + "name" : "Elle Meyer", + "date" : "2024-02-06", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Third Session" + } ] +} diff --git a/src/test/data/JsonSerializableWellNusTest/invalidStudentWellNus.json b/src/test/data/JsonSerializableWellNusTest/invalidStudentWellNus.json new file mode 100644 index 00000000000..f3e6a7813fb --- /dev/null +++ b/src/test/data/JsonSerializableWellNusTest/invalidStudentWellNus.json @@ -0,0 +1,45 @@ +{ + "students": [ { + "name": "Hans Muster", + "phone": "9", + "address": "4th street", + "note" : " " + } ], + "appointments" : [ { + "name" : "Hans Muster", + "date" : "2023-10-15", + "startTime" : "10:00", + "endTime" : "11:00", + "description" : "Third Session" + }, { + "name" : "Hans Muster", + "date" : "2023-10-16", + "startTime" : "14:45", + "endTime" : "15:45", + "description" : "Check-up" + }, { + "name" : "Hans Muster", + "date" : "2023-10-17", + "startTime" : "11:20", + "endTime" : "12:20", + "description" : "Follow-up" + }, { + "name" : "Hans Muster", + "date" : "2023-10-18", + "startTime" : "09:30", + "endTime" : "10:30", + "description" : "Check-up" + }, { + "name" : "Hans Muster", + "date" : "2023-10-19", + "startTime" : "16:15", + "endTime" : "17:15", + "description" : "Second Session" + }, { + "name" : "Hans Muster", + "date" : "2023-10-20", + "startTime" : "13:00", + "endTime" : "14:00", + "description" : "Check-up" + } ] +} diff --git a/src/test/data/JsonSerializableWellNusTest/overlapAppointmentWellNus.json b/src/test/data/JsonSerializableWellNusTest/overlapAppointmentWellNus.json new file mode 100644 index 00000000000..937b81f224c --- /dev/null +++ b/src/test/data/JsonSerializableWellNusTest/overlapAppointmentWellNus.json @@ -0,0 +1,64 @@ +{ + "students" : [ { + "name" : "Alice Pauline", + "phone" : "94351253", + "address" : "123, Jurong West Ave 6, #08-111", + "risklevel" : [ "high" ], + "note": " " + }, { + "name" : "Benny Dover", + "phone" : "98765432", + "address" : "311, Clementi Ave 2, #02-25", + "risklevel" : [ "medium" ], + "note": " " + }, { + "name" : "Carl Kurz", + "phone" : "95352563", + "address" : "wall street", + "risklevel" : [ ], + "note": " " + }, { + "name" : "Daniel Meier", + "phone" : "87652533", + "address" : "10th street", + "risklevel" : [ "low" ], + "note": " " + }, { + "name" : "Elle Meyer", + "phone" : "94822240", + "address" : "michegan ave", + "risklevel" : [ ], + "note": " " + }, { + "name" : "Fiona Kunz", + "phone" : "94824270", + "address" : "little tokyo", + "risklevel" : [ ], + "note": " " + }, { + "name" : "George Best", + "phone" : "94824420", + "address" : "4th street", + "risklevel" : [ ], + "note": " " + } ], + "appointments" : [ { + "name" : "George Best", + "date" : "2023-11-30", + "startTime" : "16:30", + "endTime" : "17:30", + "description" : "First Session" + }, { + "name" : "Fiona Kunz", + "date" : "2023-11-30", + "startTime" : "17:00", + "endTime" : "18:00", + "description" : "Second Session" + }, { + "name" : "Elle Meyer", + "date" : "2024-02-06", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Third Session" + } ] +} diff --git a/src/test/data/JsonSerializableWellNusTest/typicalWellNus.json b/src/test/data/JsonSerializableWellNusTest/typicalWellNus.json new file mode 100644 index 00000000000..d12f2d97c98 --- /dev/null +++ b/src/test/data/JsonSerializableWellNusTest/typicalWellNus.json @@ -0,0 +1,65 @@ +{ + "_comment": "WellNus save file which contains the same values as in TypicalStudents#getTypicalWellNus()", + "students" : [ { + "name" : "Alice Pauline", + "phone" : "94351253", + "address" : "123, Jurong West Ave 6, #08-111", + "risklevel" : [ "high" ], + "note": " " + }, { + "name" : "Benny Dover", + "phone" : "98765432", + "address" : "311, Clementi Ave 2, #02-25", + "risklevel" : [ "medium" ], + "note": " " + }, { + "name" : "Carl Kurz", + "phone" : "95352563", + "address" : "wall street", + "risklevel" : [ ], + "note": " " + }, { + "name" : "Daniel Meier", + "phone" : "87652533", + "address" : "10th street", + "risklevel" : [ "low" ], + "note": " " + }, { + "name" : "Elle Meyer", + "phone" : "94822240", + "address" : "michegan ave", + "risklevel" : [ ], + "note": " " + }, { + "name" : "Fiona Kunz", + "phone" : "94824270", + "address" : "little tokyo", + "risklevel" : [ ], + "note": " " + }, { + "name" : "George Best", + "phone" : "94824420", + "address" : "4th street", + "risklevel" : [ ], + "note": " " + } ], + "appointments" : [ { + "name" : "George Best", + "date" : "2023-11-30", + "startTime" : "16:30", + "endTime" : "17:30", + "description" : "First Session" + }, { + "name" : "Fiona Kunz", + "date" : "2023-10-14", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Second Session" + }, { + "name" : "Elle Meyer", + "date" : "2024-02-06", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Third Session" + } ] +} diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json index 1037548a9cd..665448d62ce 100644 --- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json @@ -9,5 +9,5 @@ "z" : 99 } }, - "addressBookFilePath" : "addressbook.json" + "wellNusFilePath" : "data/wellnus.json" } diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json index b819bed900a..1a84521c51d 100644 --- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json @@ -7,5 +7,5 @@ "y" : 100 } }, - "addressBookFilePath" : "addressbook.json" + "wellNusFilePath" : "data/wellnus.json" } diff --git a/src/test/data/JsonWellNusStorageTest/invalidAndValidAppointmentWellNus.json b/src/test/data/JsonWellNusStorageTest/invalidAndValidAppointmentWellNus.json new file mode 100644 index 00000000000..b33f1aed2f0 --- /dev/null +++ b/src/test/data/JsonWellNusStorageTest/invalidAndValidAppointmentWellNus.json @@ -0,0 +1,23 @@ +{ + "students": [ { + "name": "John Loh", + "phone": "9482424", + "address": "4th street", + "note" : " " + } ], + "appointments" : [ + { + "name" : "John Loh", + "date" : "2023-10-14", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Second Session" + }, + { + "name": "John Loh", + "date": "202311-30", + "startTime": "16:30", + "endTime": "17:30", + "description": "First Session" + } ] +} diff --git a/src/test/data/JsonWellNusStorageTest/invalidAndValidStudentWellNus.json b/src/test/data/JsonWellNusStorageTest/invalidAndValidStudentWellNus.json new file mode 100644 index 00000000000..c32eae5f79a --- /dev/null +++ b/src/test/data/JsonWellNusStorageTest/invalidAndValidStudentWellNus.json @@ -0,0 +1,32 @@ +{ + "students": [ { + "name": "Valid Student", + "phone": "94824240", + "address": "4th street", + "note" : " " + }, { + "name": "Student With Invalid Phone Field", + "phone": "948asdf2424", + "address": "4th street", + "note" : " " + } ], + "appointments" : [ { + "name" : "Valid Student", + "date" : "2023-11-30", + "startTime" : "16:30", + "endTime" : "17:30", + "description" : "First Session" + }, { + "name" : "Valid Student", + "date" : "2023-10-14", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Second Session" + }, { + "name" : "Valid Student", + "date" : "2024-02-06", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Third Session" + } ] +} diff --git a/src/test/data/JsonWellNusStorageTest/invalidAppointmentWellNus.json b/src/test/data/JsonWellNusStorageTest/invalidAppointmentWellNus.json new file mode 100644 index 00000000000..fecd102ed3b --- /dev/null +++ b/src/test/data/JsonWellNusStorageTest/invalidAppointmentWellNus.json @@ -0,0 +1,16 @@ +{ + "students": [ { + "name": "Student with invalid name field: Ha!ns Mu@ster", + "phone": "9482424", + "address": "4th street", + "note" : " " + } ], + "appointments" : [ + { + "name": "John Doe", + "date": "202311-30", + "startTime": "16:30", + "endTime": "17:30", + "description": "First Session" + } ] +} diff --git a/src/test/data/JsonWellNusStorageTest/invalidStudentWellNus.json b/src/test/data/JsonWellNusStorageTest/invalidStudentWellNus.json new file mode 100644 index 00000000000..25eb87bb9f4 --- /dev/null +++ b/src/test/data/JsonWellNusStorageTest/invalidStudentWellNus.json @@ -0,0 +1,27 @@ +{ + "students": [ { + "name": "Student with invalid name field: Ha!ns Mu@ster", + "phone": "9482424", + "address": "4th street", + "note" : " " + } ], + "appointments" : [ { + "name" : "John Doe", + "date" : "2023-11-30", + "startTime" : "16:30", + "endTime" : "17:30", + "description" : "First Session" + }, { + "name" : "John Loh", + "date" : "2023-10-14", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Second Session" + }, { + "name" : "John Doe", + "date" : "2024-02-06", + "startTime" : "17:30", + "endTime" : "18:30", + "description" : "Third Session" + } ] +} diff --git a/src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonWellNusStorageTest/notJsonFormatWellNus.json similarity index 100% rename from src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json rename to src/test/data/JsonWellNusStorageTest/notJsonFormatWellNus.json diff --git a/src/test/java/seedu/address/commons/core/index/IndexTest.java b/src/test/java/seedu/address/commons/core/index/IndexTest.java index fc395ab964b..5e660d409b8 100644 --- a/src/test/java/seedu/address/commons/core/index/IndexTest.java +++ b/src/test/java/seedu/address/commons/core/index/IndexTest.java @@ -39,23 +39,23 @@ public void createZeroBasedIndex() { @Test public void equals() { - final Index fifthPersonIndex = Index.fromOneBased(5); + final Index fifthStudentIndex = Index.fromOneBased(5); // same values -> returns true - assertTrue(fifthPersonIndex.equals(Index.fromOneBased(5))); - assertTrue(fifthPersonIndex.equals(Index.fromZeroBased(4))); + assertTrue(fifthStudentIndex.equals(Index.fromOneBased(5))); + assertTrue(fifthStudentIndex.equals(Index.fromZeroBased(4))); // same object -> returns true - assertTrue(fifthPersonIndex.equals(fifthPersonIndex)); + assertTrue(fifthStudentIndex.equals(fifthStudentIndex)); // null -> returns false - assertFalse(fifthPersonIndex.equals(null)); + assertFalse(fifthStudentIndex.equals(null)); // different types -> returns false - assertFalse(fifthPersonIndex.equals(5.0f)); + assertFalse(fifthStudentIndex.equals(5.0f)); // different index -> returns false - assertFalse(fifthPersonIndex.equals(Index.fromOneBased(1))); + assertFalse(fifthStudentIndex.equals(Index.fromOneBased(1))); } @Test diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java index baf8ce336a2..255b437ca5a 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/address/logic/LogicManagerTest.java @@ -1,14 +1,13 @@ package seedu.address.logic; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; +import static seedu.address.logic.Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX; import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.AMY; +import static seedu.address.testutil.TypicalStudents.AMY; import java.io.IOException; import java.nio.file.AccessDeniedException; @@ -20,18 +19,17 @@ import seedu.address.logic.commands.AddCommand; import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.ListCommand; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyWellNus; import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.storage.JsonAddressBookStorage; +import seedu.address.model.student.Student; import seedu.address.storage.JsonUserPrefsStorage; +import seedu.address.storage.JsonWellNusStorage; import seedu.address.storage.StorageManager; -import seedu.address.testutil.PersonBuilder; +import seedu.address.testutil.StudentBuilder; public class LogicManagerTest { private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy IO exception"); @@ -45,10 +43,10 @@ public class LogicManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); + JsonWellNusStorage wellNusStorage = + new JsonWellNusStorage(temporaryFolder.resolve("wellNus.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + StorageManager storage = new StorageManager(wellNusStorage, userPrefsStorage); logic = new LogicManager(model, storage); } @@ -61,13 +59,7 @@ public void execute_invalidCommandFormat_throwsParseException() { @Test public void execute_commandExecutionError_throwsCommandException() { String deleteCommand = "delete 9"; - assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void execute_validCommand_success() throws Exception { - String listCommand = ListCommand.COMMAND_WORD; - assertCommandSuccess(listCommand, ListCommand.MESSAGE_SUCCESS, model); + assertCommandException(deleteCommand, MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); } @Test @@ -83,8 +75,8 @@ public void execute_storageThrowsAdException_throwsCommandException() { } @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); + public void getFilteredStudentList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredStudentList().remove(0)); } /** @@ -123,7 +115,7 @@ private void assertCommandException(String inputCommand, String expectedMessage) */ private void assertCommandFailure(String inputCommand, Class expectedException, String expectedMessage) { - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + Model expectedModel = new ModelManager(model.getWellNusData(), new UserPrefs()); assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); } @@ -149,10 +141,10 @@ private void assertCommandFailure(String inputCommand, Class new AddCommand(null)); } + // EP: Valid student @Test - public void execute_personAcceptedByModel_addSuccessful() throws Exception { - ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded(); - Person validPerson = new PersonBuilder().build(); + public void execute_studentAcceptedByModel_addSuccessful() throws Exception { + ModelStubAcceptingStudentAdded modelStub = new ModelStubAcceptingStudentAdded(); + Student validStudent = new StudentBuilder().build(); - CommandResult commandResult = new AddCommand(validPerson).execute(modelStub); + CommandResult commandResult = new AddCommand(validStudent).execute(modelStub); - assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)), + assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validStudent)), commandResult.getFeedbackToUser()); - assertEquals(Arrays.asList(validPerson), modelStub.personsAdded); + assertEquals(Arrays.asList(validStudent), modelStub.studentsAdded); } + // EP: Duplicate student @Test - public void execute_duplicatePerson_throwsCommandException() { - Person validPerson = new PersonBuilder().build(); - AddCommand addCommand = new AddCommand(validPerson); - ModelStub modelStub = new ModelStubWithPerson(validPerson); + public void execute_duplicateStudent_throwsCommandException() { + Student validStudent = new StudentBuilder().build(); + AddCommand addCommand = new AddCommand(validStudent); + ModelStub modelStub = new ModelStubWithStudent(validStudent); - assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub)); + assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_STUDENT, () -> addCommand.execute(modelStub)); } @Test public void equals() { - Person alice = new PersonBuilder().withName("Alice").build(); - Person bob = new PersonBuilder().withName("Bob").build(); + Student alice = new StudentBuilder().withName("Alice").build(); + Student bob = new StudentBuilder().withName("Bob").build(); AddCommand addAliceCommand = new AddCommand(alice); AddCommand addBobCommand = new AddCommand(bob); @@ -73,7 +71,7 @@ public void equals() { // null -> returns false assertFalse(addAliceCommand.equals(null)); - // different person -> returns false + // different student -> returns false assertFalse(addAliceCommand.equals(addBobCommand)); } @@ -85,119 +83,44 @@ public void toStringMethod() { } /** - * A default model stub that have all of the methods failing. + * A Model stub that contains a single student. */ - private class ModelStub implements Model { - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - throw new AssertionError("This method should not be called."); - } - - @Override - public GuiSettings getGuiSettings() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - throw new AssertionError("This method should not be called."); - } - - @Override - public Path getAddressBookFilePath() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void addPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBook(ReadOnlyAddressBook newData) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - throw new AssertionError("This method should not be called."); - } - - @Override - public boolean hasPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void deletePerson(Person target) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ObservableList getFilteredPersonList() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - throw new AssertionError("This method should not be called."); - } - } - - /** - * A Model stub that contains a single person. - */ - private class ModelStubWithPerson extends ModelStub { - private final Person person; + private class ModelStubWithStudent extends ModelStub { + private final Student student; - ModelStubWithPerson(Person person) { - requireNonNull(person); - this.person = person; + ModelStubWithStudent(Student student) { + requireNonNull(student); + this.student = student; } @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return this.person.isSamePerson(person); + public boolean hasStudent(Student student) { + requireNonNull(student); + return this.student.isSameStudent(student); } } /** - * A Model stub that always accept the person being added. + * A Model stub that always accept the student being added. */ - private class ModelStubAcceptingPersonAdded extends ModelStub { - final ArrayList personsAdded = new ArrayList<>(); + private class ModelStubAcceptingStudentAdded extends ModelStub { + final ArrayList studentsAdded = new ArrayList<>(); @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return personsAdded.stream().anyMatch(person::isSamePerson); + public boolean hasStudent(Student student) { + requireNonNull(student); + return studentsAdded.stream().anyMatch(student::isSameStudent); } @Override - public void addPerson(Person person) { - requireNonNull(person); - personsAdded.add(person); + public void addStudent(Student student) { + requireNonNull(student); + studentsAdded.add(student); } @Override - public ReadOnlyAddressBook getAddressBook() { - return new AddressBook(); + public ReadOnlyWellNus getWellNusData() { + return new WellNus(); } } diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java index 80d9110c03a..19d31287cdc 100644 --- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java @@ -1,30 +1,32 @@ package seedu.address.logic.commands; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; import org.junit.jupiter.api.Test; -import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; +import seedu.address.model.WellNus; public class ClearCommandTest { + // EP: Empty WellNus @Test - public void execute_emptyAddressBook_success() { + public void execute_emptyWellNus_success() { Model model = new ModelManager(); Model expectedModel = new ModelManager(); assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); } + // EP: Non-empty WellNus @Test - public void execute_nonEmptyAddressBook_success() { - Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel.setAddressBook(new AddressBook()); + public void execute_nonEmptyWellNus_success() { + Model model = new ModelManager(getTypicalWellNus(), new UserPrefs()); + Model expectedModel = new ModelManager(getTypicalWellNus(), new UserPrefs()); + expectedModel.setWellNusData(new WellNus()); assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); } diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java index 643a1d08069..38a179ac6e6 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java @@ -3,10 +3,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; 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_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME; 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 static seedu.address.logic.parser.CliSyntax.PREFIX_RISK_LEVEL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_START_TIME; import static seedu.address.testutil.Assert.assertThrows; import java.util.ArrayList; @@ -15,11 +18,11 @@ import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; +import seedu.address.model.WellNus; +import seedu.address.model.student.NameContainsKeywordsPredicate; +import seedu.address.model.student.Student; +import seedu.address.testutil.EditStudentDescriptorBuilder; /** * Contains helper methods for testing commands. @@ -27,46 +30,73 @@ public class CommandTestUtil { public static final String VALID_NAME_AMY = "Amy Bee"; + + public static final String VALID_NAME_ALICE = "Alice Pauline"; public static final String VALID_NAME_BOB = "Bob Choo"; public static final String VALID_PHONE_AMY = "11111111"; public static final String VALID_PHONE_BOB = "22222222"; - public static final String VALID_EMAIL_AMY = "amy@example.com"; - public static final String VALID_EMAIL_BOB = "bob@example.com"; public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1"; public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3"; - public static final String VALID_TAG_HUSBAND = "husband"; - public static final String VALID_TAG_FRIEND = "friend"; + public static final String VALID_RISK_LEVEL_LOW = "low"; + public static final String VALID_RISK_LEVEL_HIGH = "high"; + public static final String VALID_NOTE_AMY = "course: psychology"; + public static final String VALID_NOTE_BOB = "likes playing football"; + public static final String VALID_CATEGORY_APPOINTMENT = "appointments"; + public static final String VALID_CATEGORY_STUDENT = "students"; + + public static final String VALID_DATE_AMY = "2023-12-31"; + public static final String VALID_START_TIME_AMY = "16:30"; + public static final String VALID_END_TIME_AMY = "17:00"; + + public static final String VALID_DATE_BOB = "2024-01-01"; + public static final String VALID_START_TIME_BOB = "12:00"; + public static final String VALID_END_TIME_BOB = "13:00"; + + public static final String VALID_DESCRIPTION_AMY = "First Session"; + public static final String VALID_DESCRIPTION_BOB = "Second session"; public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_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 EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY; - public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB; public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY; public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_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 RISK_DESC_HIGH = " " + PREFIX_RISK_LEVEL + VALID_RISK_LEVEL_HIGH; + public static final String RISK_DESC_LOW = " " + PREFIX_RISK_LEVEL + VALID_RISK_LEVEL_LOW; + + public static final String DATE_DESC_AMY = " " + PREFIX_DATE + VALID_DATE_AMY; + public static final String DATE_DESC_BOB = " " + PREFIX_DATE + VALID_DATE_BOB; + public static final String START_TIME_DESC_AMY = " " + PREFIX_START_TIME + VALID_START_TIME_AMY; + public static final String START_TIME_DESC_BOB = " " + PREFIX_START_TIME + VALID_START_TIME_BOB; + public static final String END_TIME_DESC_AMY = " " + PREFIX_END_TIME + VALID_END_TIME_AMY; + public static final String END_TIME_DESC_BOB = " " + PREFIX_END_TIME + VALID_END_TIME_BOB; + + public static final String DESCRIPTION_DESC_AMY = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION_AMY; + public static final String DESCRIPTION_DESC_BOB = " " + PREFIX_DESCRIPTION + VALID_DESCRIPTION_BOB; public static final String INVALID_NAME_DESC = " " + PREFIX_NAME + "James&"; // '&' not allowed in names 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 INVALID_RISK_LEVEL_DESC = " " + + PREFIX_RISK_LEVEL + "dangerous"; // only 'high', 'medium', or 'low' allowed for risk level + public static final String INVALID_DATE_DESC = " " + PREFIX_DATE + + "2023-12-31a"; // 'a' not allowed in date + public static final String INVALID_START_TIME_DESC = " " + PREFIX_START_TIME + + "16:30a"; // 'a' not allowed in start time + public static final String INVALID_END_TIME_DESC = " " + PREFIX_END_TIME + + "17:30a"; // 'a' not allowed in end time + public static final String INVALID_DESCRIPTION_DESC = " " + + PREFIX_DESCRIPTION; // empty string not allowed for descriptions 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; + public static final EditCommand.EditStudentDescriptor DESC_AMY; + public static final EditCommand.EditStudentDescriptor DESC_BOB; static { - DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_FRIEND).build(); - DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); + DESC_AMY = new EditStudentDescriptorBuilder().withPhone(VALID_PHONE_AMY).withAddress(VALID_ADDRESS_AMY).build(); + DESC_BOB = new EditStudentDescriptorBuilder().withPhone(VALID_PHONE_BOB).withAddress(VALID_ADDRESS_BOB).build(); } /** @@ -99,30 +129,29 @@ public static void assertCommandSuccess(Command command, Model actualModel, Stri * Executes the given {@code command}, confirms that
* - a {@code CommandException} is thrown
* - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged + * - the wellnus storage, filtered student list and selected student in {@code actualModel} remain unchanged */ public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { // we are unable to defensively copy the model for comparison later, so we can // only do so by copying its components. - AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); - List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList()); - + WellNus expectedWellNus = new WellNus(actualModel.getWellNusData()); + List expectedFilteredList = new ArrayList<>(actualModel.getFilteredStudentList()); assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); - assertEquals(expectedAddressBook, actualModel.getAddressBook()); - assertEquals(expectedFilteredList, actualModel.getFilteredPersonList()); + assertEquals(expectedWellNus, actualModel.getWellNusData()); + assertEquals(expectedFilteredList, actualModel.getFilteredStudentList()); } /** - * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the - * {@code model}'s address book. + * Updates {@code model}'s filtered list to show only the student at the given {@code targetIndex} in the + * {@code model}'s wellnus storage. */ - public static void showPersonAtIndex(Model model, Index targetIndex) { - assertTrue(targetIndex.getZeroBased() < model.getFilteredPersonList().size()); + public static void showStudentAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getFilteredStudentList().size()); - Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased()); - final String[] splitName = person.getName().fullName.split("\\s+"); - model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); + Student student = model.getFilteredStudentList().get(targetIndex.getZeroBased()); + final String[] splitName = student.getName().value.split("\\s+"); + model.updateFilteredStudentList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); - assertEquals(1, model.getFilteredPersonList().size()); + assertEquals(1, model.getFilteredStudentList().size()); } } diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java index b6f332eabca..dcb0891a8a8 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java @@ -5,10 +5,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.logic.commands.CommandTestUtil.showStudentAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_STUDENT; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; import org.junit.jupiter.api.Test; @@ -17,7 +17,7 @@ import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; +import seedu.address.model.student.Student; /** * Contains integration tests (interaction with the Model) and unit tests for @@ -25,70 +25,74 @@ */ public class DeleteCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager(getTypicalWellNus(), new UserPrefs()); + // Unfiltered list, valid index @Test public void execute_validIndexUnfilteredList_success() { - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); + Student studentToDelete = model.getFilteredStudentList().get(INDEX_FIRST_STUDENT.getZeroBased()); + DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_STUDENT); - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, - Messages.format(personToDelete)); + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_STUDENT_SUCCESS, + Messages.format(studentToDelete)); - ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); + ModelManager expectedModel = new ModelManager(model.getWellNusData(), new UserPrefs()); + expectedModel.deleteStudent(studentToDelete); assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); } + // Unfiltered List, invalid index @Test public void execute_invalidIndexUnfilteredList_throwsCommandException() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredStudentList().size() + 1); DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); } + // Filtered List, valid index @Test public void execute_validIndexFilteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + showStudentAtIndex(model, INDEX_FIRST_STUDENT); - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); + Student studentToDelete = model.getFilteredStudentList().get(INDEX_FIRST_STUDENT.getZeroBased()); + DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_STUDENT); - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, - Messages.format(personToDelete)); + String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_STUDENT_SUCCESS, + Messages.format(studentToDelete)); - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - showNoPerson(expectedModel); + Model expectedModel = new ModelManager(model.getWellNusData(), new UserPrefs()); + expectedModel.deleteStudent(studentToDelete); + showNoStudent(expectedModel); assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); } + // Filtered list, invalid index @Test public void execute_invalidIndexFilteredList_throwsCommandException() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + showStudentAtIndex(model, INDEX_FIRST_STUDENT); - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); + Index outOfBoundIndex = INDEX_SECOND_STUDENT; + // ensures that outOfBoundIndex is still in bounds of wellnus storage list + assertTrue(outOfBoundIndex.getZeroBased() < model.getWellNusData().getStudentList().size()); DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); } @Test public void equals() { - DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON); - DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON); + DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_STUDENT); + DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_STUDENT); // same object -> returns true assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); // same values -> returns true - DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON); + DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_STUDENT); assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); // different types -> returns false @@ -97,7 +101,7 @@ public void equals() { // null -> returns false assertFalse(deleteFirstCommand.equals(null)); - // different person -> returns false + // different student -> returns false assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); } @@ -112,9 +116,9 @@ public void toStringMethod() { /** * Updates {@code model}'s filtered list to show no one. */ - private void showNoPerson(Model model) { - model.updateFilteredPersonList(p -> false); + private void showNoStudent(Model model) { + model.updateFilteredStudentList(p -> false); - assertTrue(model.getFilteredPersonList().isEmpty()); + assertTrue(model.getFilteredStudentList().isEmpty()); } } diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java index 469dd97daa7..94874692b7c 100644 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/EditCommandTest.java @@ -5,154 +5,151 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.logic.commands.CommandTestUtil.showStudentAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_STUDENT; +import static seedu.address.testutil.TypicalStudents.ALICE; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; import org.junit.jupiter.api.Test; import seedu.address.commons.core.index.Index; import seedu.address.logic.Messages; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.AddressBook; +import seedu.address.logic.commands.EditCommand.EditStudentDescriptor; import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; +import seedu.address.model.WellNus; +import seedu.address.model.student.Student; +import seedu.address.testutil.EditStudentDescriptorBuilder; +import seedu.address.testutil.StudentBuilder; /** * Contains integration tests (interaction with the Model) and unit tests for EditCommand. */ public class EditCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager(getTypicalWellNus(), new UserPrefs()); + // Unfiltered List, Change all fields @Test public void execute_allFieldsSpecifiedUnfilteredList_success() { - Person editedPerson = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor); + Student editedStudent = new StudentBuilder(ALICE).withPhone(VALID_PHONE_BOB) + .withAddress(VALID_ADDRESS_BOB).build(); + EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder(editedStudent).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_STUDENT, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_STUDENT_SUCCESS, + Messages.format(editedStudent)); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); + Model expectedModel = new ModelManager(new WellNus(model.getWellNusData()), new UserPrefs()); + expectedModel.setStudent(model.getFilteredStudentList().get(0), editedStudent); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } + // Unfiltered List, change a single field @Test public void execute_someFieldsSpecifiedUnfilteredList_success() { - Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size()); - Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased()); + Index indexLastStudent = Index.fromOneBased(model.getFilteredStudentList().size()); + Student lastStudent = model.getFilteredStudentList().get(indexLastStudent.getZeroBased()); - PersonBuilder personInList = new PersonBuilder(lastPerson); - Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withTags(VALID_TAG_HUSBAND).build(); + StudentBuilder studentInList = new StudentBuilder(lastStudent); + Student editedStudent = studentInList.withPhone(VALID_PHONE_BOB).build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build(); - EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); + // only phone field is changed + EditCommand.EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder() + .withPhone(VALID_PHONE_BOB).build(); + EditCommand editCommand = new EditCommand(indexLastStudent, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_STUDENT_SUCCESS, + Messages.format(editedStudent)); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(lastPerson, editedPerson); + Model expectedModel = new ModelManager(new WellNus(model.getWellNusData()), new UserPrefs()); + expectedModel.setStudent(lastStudent, editedStudent); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - @Test - public void execute_noFieldSpecifiedUnfilteredList_success() { - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); - Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + // only address field is changed - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)); + indexLastStudent = Index.fromOneBased(model.getFilteredStudentList().size()); + lastStudent = model.getFilteredStudentList().get(indexLastStudent.getZeroBased()); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + studentInList = new StudentBuilder(lastStudent); + editedStudent = studentInList.withAddress(VALID_ADDRESS_BOB).build(); - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_filteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + descriptor = new EditStudentDescriptorBuilder().withAddress(VALID_ADDRESS_BOB).build(); + editCommand = new EditCommand(indexLastStudent, descriptor); - Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); + expectedMessage = String.format(EditCommand.MESSAGE_EDIT_STUDENT_SUCCESS, + Messages.format(editedStudent)); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); + expectedModel = new ModelManager(new WellNus(model.getWellNusData()), new UserPrefs()); + expectedModel.setStudent(lastStudent, editedStudent); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } + // Filtered List Success + @Test - public void execute_duplicatePersonUnfilteredList_failure() { - Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor); + public void execute_filteredList_success() { + showStudentAtIndex(model, INDEX_FIRST_STUDENT); - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); - } + Student studentInFilteredList = model.getFilteredStudentList().get(INDEX_FIRST_STUDENT.getZeroBased()); + Student editedStudent = new StudentBuilder(studentInFilteredList).withPhone(VALID_PHONE_BOB).build(); + EditCommand editCommand = new EditCommand(INDEX_FIRST_STUDENT, + new EditStudentDescriptorBuilder().withPhone(VALID_PHONE_BOB).build()); - @Test - public void execute_duplicatePersonFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); + String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_STUDENT_SUCCESS, + Messages.format(editedStudent)); - // edit person in filtered list into a duplicate in address book - Person personInList = model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder(personInList).build()); + Model expectedModel = new ModelManager(new WellNus(model.getWellNusData()), new UserPrefs()); + expectedModel.setStudent(model.getFilteredStudentList().get(0), editedStudent); - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } + // Invalid index, unfiltered list @Test - public void execute_invalidPersonIndexUnfilteredList_failure() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build(); + public void execute_invalidStudentIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredStudentList().size() + 1); + EditCommand.EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder() + .withPhone(VALID_PHONE_BOB).build(); EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); } + // Invalid index, filtered list /** * Edit filtered list where index is larger than size of filtered list, - * but smaller than size of address book + * but smaller than size of wellnus storage */ @Test - public void execute_invalidPersonIndexFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - Index outOfBoundIndex = INDEX_SECOND_PERSON; - // ensures that outOfBoundIndex is still in bounds of address book list - assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); + public void execute_invalidStudentIndexFilteredList_failure() { + showStudentAtIndex(model, INDEX_FIRST_STUDENT); + Index outOfBoundIndex = INDEX_SECOND_STUDENT; + // ensures that outOfBoundIndex is still in bounds of wellnus storage list + assertTrue(outOfBoundIndex.getZeroBased() < model.getWellNusData().getStudentList().size()); EditCommand editCommand = new EditCommand(outOfBoundIndex, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); + new EditStudentDescriptorBuilder().withPhone(VALID_PHONE_BOB).build()); - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); } @Test public void equals() { - final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY); + final EditCommand standardCommand = new EditCommand(INDEX_FIRST_STUDENT, DESC_AMY); // same values -> returns true - EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY); - EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor); + EditCommand.EditStudentDescriptor copyDescriptor = new EditCommand.EditStudentDescriptor(DESC_AMY); + EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_STUDENT, copyDescriptor); assertTrue(standardCommand.equals(commandWithSameValues)); // same object -> returns true @@ -165,19 +162,19 @@ public void equals() { assertFalse(standardCommand.equals(new ClearCommand())); // different index -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY))); + assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_STUDENT, DESC_AMY))); // different descriptor -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB))); + assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_STUDENT, DESC_BOB))); } @Test public void toStringMethod() { Index index = Index.fromOneBased(1); - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - EditCommand editCommand = new EditCommand(index, editPersonDescriptor); - String expected = EditCommand.class.getCanonicalName() + "{index=" + index + ", editPersonDescriptor=" - + editPersonDescriptor + "}"; + EditCommand.EditStudentDescriptor editStudentDescriptor = new EditCommand.EditStudentDescriptor(); + EditCommand editCommand = new EditCommand(index, editStudentDescriptor); + String expected = EditCommand.class.getCanonicalName() + "{index=" + index + ", editStudentDescriptor=" + + editStudentDescriptor + "}"; assertEquals(expected, editCommand.toString()); } diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java deleted file mode 100644 index b17c1f3d5c2..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditPersonDescriptorTest { - - @Test - public void equals() { - // same values -> returns true - EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY); - assertTrue(DESC_AMY.equals(descriptorWithSameValues)); - - // same object -> returns true - assertTrue(DESC_AMY.equals(DESC_AMY)); - - // null -> returns false - assertFalse(DESC_AMY.equals(null)); - - // different types -> returns false - assertFalse(DESC_AMY.equals(5)); - - // different values -> returns false - assertFalse(DESC_AMY.equals(DESC_BOB)); - - // different name -> returns false - EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different phone -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different email -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different address -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different tags -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - } - - @Test - public void toStringMethod() { - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - String expected = EditPersonDescriptor.class.getCanonicalName() + "{name=" - + editPersonDescriptor.getName().orElse(null) + ", phone=" - + editPersonDescriptor.getPhone().orElse(null) + ", email=" - + editPersonDescriptor.getEmail().orElse(null) + ", address=" - + editPersonDescriptor.getAddress().orElse(null) + ", tags=" - + editPersonDescriptor.getTags().orElse(null) + "}"; - assertEquals(expected, editPersonDescriptor.toString()); - } -} diff --git a/src/test/java/seedu/address/logic/commands/EditStudentDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditStudentDescriptorTest.java new file mode 100644 index 00000000000..37d49902fa8 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/EditStudentDescriptorTest.java @@ -0,0 +1,54 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; +import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.EditCommand.EditStudentDescriptor; +import seedu.address.testutil.EditStudentDescriptorBuilder; + +public class EditStudentDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditCommand.EditStudentDescriptor descriptorWithSameValues = new EditCommand.EditStudentDescriptor(DESC_AMY); + assertTrue(DESC_AMY.equals(descriptorWithSameValues)); + + // same object -> returns true + assertTrue(DESC_AMY.equals(DESC_AMY)); + + // null -> returns false + assertFalse(DESC_AMY.equals(null)); + + // different types -> returns false + assertFalse(DESC_AMY.equals(5)); + + // different values -> returns false + assertFalse(DESC_AMY.equals(DESC_BOB)); + + // different phone -> returns false + EditCommand.EditStudentDescriptor editedAmy = new EditStudentDescriptorBuilder(DESC_AMY) + .withPhone(VALID_PHONE_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + + // different address -> returns false + editedAmy = new EditStudentDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); + } + + @Test + public void toStringMethod() { + EditCommand.EditStudentDescriptor editStudentDescriptor = new EditStudentDescriptor(); + String expected = EditCommand.EditStudentDescriptor.class.getCanonicalName() + "{phone=" + + editStudentDescriptor.getPhone().orElse(null) + ", address=" + + editStudentDescriptor.getAddress().orElse(null) + "}"; + assertEquals(expected, editStudentDescriptor.toString()); + } +} diff --git a/src/test/java/seedu/address/logic/commands/FilterCommandTest.java b/src/test/java/seedu/address/logic/commands/FilterCommandTest.java new file mode 100644 index 00000000000..69685ef6479 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/FilterCommandTest.java @@ -0,0 +1,100 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.Messages.MESSAGE_APPOINTMENTS_LISTED_OVERVIEW; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalAppointments.ALICE_APPOINTMENT; +import static seedu.address.testutil.TypicalAppointments.ALICE_SECOND_APPOINTMENT; +import static seedu.address.testutil.TypicalAppointments.BENNY_APPOINTMENT; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.appointment.AppointmentDateMatchesPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FilterCommand}. + */ +public class FilterCommandTest { + private Model model = new ModelManager(getTypicalWellNus(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalWellNus(), new UserPrefs()); + + @Test + public void equals() { + AppointmentDateMatchesPredicate firstPredicate = + new AppointmentDateMatchesPredicate(ALICE_APPOINTMENT.getDate().getDate()); + AppointmentDateMatchesPredicate secondPredicate = + new AppointmentDateMatchesPredicate(ALICE_SECOND_APPOINTMENT.getDate().getDate()); + + FilterCommand filterFirstCommand = new FilterCommand(firstPredicate); + FilterCommand filterSecondCommand = new FilterCommand(secondPredicate); + + // same object -> returns true + assertTrue(filterFirstCommand.equals(filterFirstCommand)); + + // same values -> returns true + FilterCommand filterFirstCommandCopy = new FilterCommand(firstPredicate); + assertTrue(filterFirstCommand.equals(filterFirstCommandCopy)); + + // different types -> returns false + assertFalse(filterFirstCommand.equals(1)); + + // null -> returns false + assertFalse(filterFirstCommand.equals(null)); + + // different predicate -> returns false + assertFalse(filterFirstCommand.equals(filterSecondCommand)); + } + + // Find single appointment + @Test + public void execute_validDate_singleAppointmentFound() { + String expectedMessage = String.format(MESSAGE_APPOINTMENTS_LISTED_OVERVIEW, 1); + AppointmentDateMatchesPredicate predicate = new AppointmentDateMatchesPredicate(ALICE_APPOINTMENT + .getDate().getDate()); + FilterCommand command = new FilterCommand(predicate); + expectedModel.updateFilteredAppointmentList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(expectedModel.getFilteredAppointmentList(), Collections.singletonList(ALICE_APPOINTMENT)); + } + + // Find multiple appointments + @Test + public void execute_validDate_multipleAppointmentsFound() { + String expectedMessage = String.format(MESSAGE_APPOINTMENTS_LISTED_OVERVIEW, 2); + AppointmentDateMatchesPredicate predicate = new AppointmentDateMatchesPredicate(ALICE_SECOND_APPOINTMENT + .getDate().getDate()); + FilterCommand command = new FilterCommand(predicate); + expectedModel.updateFilteredAppointmentList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(expectedModel.getFilteredAppointmentList(), Arrays.asList(ALICE_SECOND_APPOINTMENT, + BENNY_APPOINTMENT)); + } + + // Find no appointment + @Test + public void execute_noAppointmentsFound() { + String expectedMessage = String.format(MESSAGE_APPOINTMENTS_LISTED_OVERVIEW, 0); + AppointmentDateMatchesPredicate predicate = new AppointmentDateMatchesPredicate("2023-10-26"); + FilterCommand command = new FilterCommand(predicate); + expectedModel.updateFilteredAppointmentList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(expectedModel.getFilteredAppointmentList(), Collections.emptyList()); + } + + @Test + public void toStringMethod() { + AppointmentDateMatchesPredicate predicate = new AppointmentDateMatchesPredicate("2023-10-26"); + FilterCommand filterCommand = new FilterCommand(predicate); + String expected = FilterCommand.class.getCanonicalName() + "{predicate=" + predicate + "}"; + assertEquals(expected, filterCommand.toString()); + } +} diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java index b8b7dbba91a..f615b5f44a3 100644 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/FindCommandTest.java @@ -3,12 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; +import static seedu.address.logic.Messages.MESSAGE_STUDENTS_LISTED_OVERVIEW; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.CARL; -import static seedu.address.testutil.TypicalPersons.ELLE; -import static seedu.address.testutil.TypicalPersons.FIONA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalStudents.CARL; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; import java.util.Arrays; import java.util.Collections; @@ -18,30 +16,28 @@ import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.UserPrefs; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.student.NameContainsKeywordsPredicate; /** * Contains integration tests (interaction with the Model) for {@code FindCommand}. */ public class FindCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager(getTypicalWellNus(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalWellNus(), new UserPrefs()); @Test public void equals() { - NameContainsKeywordsPredicate firstPredicate = - new NameContainsKeywordsPredicate(Collections.singletonList("first")); - NameContainsKeywordsPredicate secondPredicate = - new NameContainsKeywordsPredicate(Collections.singletonList("second")); - FindCommand findFirstCommand = new FindCommand(firstPredicate); - FindCommand findSecondCommand = new FindCommand(secondPredicate); + String[] keywords = new String[] {"Bernice"}; + String[] otherKeywords = new String[] {"Alice"}; + FindCommand findFirstCommand = new FindCommand(keywords); + FindCommand findSecondCommand = new FindCommand(otherKeywords); // same object -> returns true assertTrue(findFirstCommand.equals(findFirstCommand)); // same values -> returns true - FindCommand findFirstCommandCopy = new FindCommand(firstPredicate); + FindCommand findFirstCommandCopy = new FindCommand(keywords); assertTrue(findFirstCommand.equals(findFirstCommandCopy)); // different types -> returns false @@ -50,42 +46,63 @@ public void equals() { // null -> returns false assertFalse(findFirstCommand.equals(null)); - // different person -> returns false + // different command -> returns false assertFalse(findFirstCommand.equals(findSecondCommand)); } + // Find empty string @Test - public void execute_zeroKeywords_noPersonFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); - NameContainsKeywordsPredicate predicate = preparePredicate(" "); - FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); + public void execute_zeroKeywords_noStudentFound() { + String expectedMessage = String.format(MESSAGE_STUDENTS_LISTED_OVERVIEW, 0); + String input = " "; + String[] keywords = input.split("\\s+"); + FindCommand command = new FindCommand(keywords); + expectedModel.updateFilteredStudentList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Collections.emptyList(), model.getFilteredPersonList()); + assertEquals(Collections.emptyList(), model.getFilteredStudentList()); } + // Find first name @Test - public void execute_multipleKeywords_multiplePersonsFound() { - String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3); - NameContainsKeywordsPredicate predicate = preparePredicate("Kurz Elle Kunz"); - FindCommand command = new FindCommand(predicate); - expectedModel.updateFilteredPersonList(predicate); + public void execute_firstName_studentFound() { + String expectedMessage = String.format(MESSAGE_STUDENTS_LISTED_OVERVIEW, 1); + String input = "Carl"; + String[] keywords = input.split("\\s+"); + FindCommand command = new FindCommand(keywords); + expectedModel.updateFilteredStudentList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList()); + assertEquals(Arrays.asList(CARL), model.getFilteredStudentList()); } + // Find last name @Test - public void toStringMethod() { - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Arrays.asList("keyword")); - FindCommand findCommand = new FindCommand(predicate); - String expected = FindCommand.class.getCanonicalName() + "{predicate=" + predicate + "}"; - assertEquals(expected, findCommand.toString()); + public void execute_lastName_studentFound() { + String expectedMessage = String.format(MESSAGE_STUDENTS_LISTED_OVERVIEW, 1); + String input = "Kurz"; + String[] keywords = input.split("\\s+"); + FindCommand command = new FindCommand(keywords); + expectedModel.updateFilteredStudentList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(CARL), model.getFilteredStudentList()); + } + + // Find full name + @Test + public void execute_fullName_studentFound() { + String expectedMessage = String.format(MESSAGE_STUDENTS_LISTED_OVERVIEW, 1); + String input = "Carl Kurz"; + String[] keywords = input.split("\\s+"); + FindCommand command = new FindCommand(keywords); + expectedModel.updateFilteredStudentList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(CARL), model.getFilteredStudentList()); } - /** - * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}. - */ - private NameContainsKeywordsPredicate preparePredicate(String userInput) { - return new NameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); + @Test + public void toStringMethod() { + String[] keywords = new String[] {"Bernice"}; + FindCommand findCommand = new FindCommand(keywords); + String expected = FindCommand.class.getCanonicalName() + "{name keyword 0=Bernice}"; + assertEquals(expected, findCommand.toString()); } } diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java deleted file mode 100644 index 435ff1f7275..00000000000 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; - -/** - * Contains integration tests (interaction with the Model) and unit tests for ListCommand. - */ -public class ListCommandTest { - - private Model model; - private Model expectedModel; - - @BeforeEach - public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - } - - @Test - public void execute_listIsNotFiltered_showsSameList() { - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); - } - - @Test - public void execute_listIsFiltered_showsEverything() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); - } -} diff --git a/src/test/java/seedu/address/logic/commands/NoteCommandTest.java b/src/test/java/seedu/address/logic/commands/NoteCommandTest.java new file mode 100644 index 00000000000..d6f60ebe026 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/NoteCommandTest.java @@ -0,0 +1,162 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NOTE_AMY; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NOTE_BOB; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showStudentAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_STUDENT; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.Messages; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; +import seedu.address.model.WellNus; +import seedu.address.model.student.Note; +import seedu.address.model.student.Student; +import seedu.address.testutil.StudentBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for NoteCommand. + */ +public class NoteCommandTest { + + private static final String NOTE_STUB = "Some note"; + private static final String HUNDRED_CHAR_NOTE_STUB = "Lorem Ipsum is simply dummy text of the printing and " + + "typesetting industry. Lorem Ipsum has been the industry’s standard"; + + private Model model = new ModelManager(getTypicalWellNus(), new UserPrefs()); + + // Add note < 100 chars + @Test + public void execute_addNoteUnfilteredList_success() { + Student firstStudent = model.getFilteredStudentList().get(INDEX_FIRST_STUDENT.getZeroBased()); + Student editedStudent = new StudentBuilder(firstStudent).withNote(NOTE_STUB).build(); + + NoteCommand noteCommand = new NoteCommand(INDEX_FIRST_STUDENT, new Note(editedStudent.getNote().value)); + + String expectedMessage = String.format(NoteCommand.MESSAGE_ADD_NOTE_SUCCESS, + Messages.format(editedStudent), NOTE_STUB); + + Model expectedModel = new ModelManager(new WellNus(model.getWellNusData()), new UserPrefs()); + expectedModel.setStudent(firstStudent, editedStudent); + + assertCommandSuccess(noteCommand, model, expectedMessage, expectedModel); + } + + // Add note of length between 100 and 500 chars + @Test + public void execute_addNoteMoreThanHundredChars_success() { + Student firstStudent = model.getFilteredStudentList().get(INDEX_FIRST_STUDENT.getZeroBased()); + Student editedStudent = new StudentBuilder(firstStudent).withNote(HUNDRED_CHAR_NOTE_STUB).build(); + + NoteCommand noteCommand = new NoteCommand(INDEX_FIRST_STUDENT, new Note(editedStudent.getNote().value)); + + String expectedMessage = String.format(NoteCommand.MESSAGE_ADD_NOTE_SUCCESS, + Messages.format(editedStudent), HUNDRED_CHAR_NOTE_STUB.substring(0, 100).trim() + + "... (Double-click on the student card to view the full note)"); + + Model expectedModel = new ModelManager(new WellNus(model.getWellNusData()), new UserPrefs()); + expectedModel.setStudent(firstStudent, editedStudent); + + assertCommandSuccess(noteCommand, model, expectedMessage, expectedModel); + } + + // Delete note + @Test + public void execute_deleteNoteUnfilteredList_success() { + Student firstStudent = model.getFilteredStudentList().get(INDEX_FIRST_STUDENT.getZeroBased()); + Student editedStudent = new StudentBuilder(firstStudent).withNote("").build(); + + NoteCommand noteCommand = new NoteCommand(INDEX_FIRST_STUDENT, + new Note(editedStudent.getNote().toString())); + + String expectedMessage = String.format(NoteCommand.MESSAGE_DELETE_NOTE_SUCCESS, + Messages.format(editedStudent)); + + Model expectedModel = new ModelManager(new WellNus(model.getWellNusData()), new UserPrefs()); + expectedModel.setStudent(firstStudent, editedStudent); + + assertCommandSuccess(noteCommand, model, expectedMessage, expectedModel); + } + + // Add note on filtered list + @Test + public void execute_filteredList_success() { + showStudentAtIndex(model, INDEX_FIRST_STUDENT); + + Student firstStudent = model.getFilteredStudentList().get(INDEX_FIRST_STUDENT.getZeroBased()); + Student editedStudent = new StudentBuilder(model.getFilteredStudentList().get(INDEX_FIRST_STUDENT + .getZeroBased())).withNote(NOTE_STUB).build(); + + NoteCommand noteCommand = new NoteCommand(INDEX_FIRST_STUDENT, new Note(editedStudent.getNote().value)); + + String expectedMessage = String.format(NoteCommand.MESSAGE_ADD_NOTE_SUCCESS, + Messages.format(editedStudent), NOTE_STUB); + + Model expectedModel = new ModelManager(new WellNus(model.getWellNusData()), new UserPrefs()); + expectedModel.setStudent(firstStudent, editedStudent); + + assertCommandSuccess(noteCommand, model, expectedMessage, expectedModel); + } + + // Invalid index, unfiltered list + @Test + public void execute_invalidStudentIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredStudentList().size() + 1); + NoteCommand noteCommand = new NoteCommand(outOfBoundIndex, new Note(VALID_NOTE_BOB)); + + assertCommandFailure(noteCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of wellnus storage + */ + @Test + public void execute_invalidStudentIndexFilteredList_failure() { + showStudentAtIndex(model, INDEX_FIRST_STUDENT); + Index outOfBoundIndex = INDEX_SECOND_STUDENT; + // ensures that outOfBoundIndex is still in bounds of wellnus storage list + assertTrue(outOfBoundIndex.getZeroBased() < model.getWellNusData().getStudentList().size()); + + NoteCommand noteCommand = new NoteCommand(outOfBoundIndex, new Note(VALID_NOTE_BOB)); + + assertCommandFailure(noteCommand, model, Messages.MESSAGE_INVALID_STUDENT_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final NoteCommand standardCommand = new NoteCommand(INDEX_FIRST_STUDENT, + new Note(VALID_NOTE_AMY)); + + // same values -> returns true + NoteCommand commandWithSameValues = new NoteCommand(INDEX_FIRST_STUDENT, + new Note(VALID_NOTE_AMY)); + assertTrue(standardCommand.equals(commandWithSameValues)); + + // same object -> returns true + assertTrue(standardCommand.equals(standardCommand)); + + // null -> returns false + assertFalse(standardCommand.equals(null)); + + // different types -> returns false + assertFalse(standardCommand.equals(new ClearCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new NoteCommand(INDEX_SECOND_STUDENT, + new Note(VALID_NOTE_AMY)))); + + // different note -> returns false + assertFalse(standardCommand.equals(new NoteCommand(INDEX_FIRST_STUDENT, + new Note(VALID_NOTE_BOB)))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ScheduleCommandTest.java b/src/test/java/seedu/address/logic/commands/ScheduleCommandTest.java new file mode 100644 index 00000000000..275b3637a5f --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ScheduleCommandTest.java @@ -0,0 +1,162 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalStudents.JON_ANG; + +import java.util.ArrayList; +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.Messages; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.model.ReadOnlyWellNus; +import seedu.address.model.WellNus; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.exceptions.InvalidStartEndTimeException; +import seedu.address.model.student.Student; +import seedu.address.testutil.AppointmentBuilder; +import seedu.address.testutil.ModelStub; + +public class ScheduleCommandTest { + + // Null Appointment + @Test + public void constructor_nullAppointment_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new ScheduleCommand(null)); + } + + // Valid appointment + @Test + public void execute_appointmentAcceptedByModel_addSuccessful() throws Exception { + ScheduleCommandTest.ModelStubAcceptingAppointmentAdded modelStub = new ModelStubAcceptingAppointmentAdded(); + Appointment validAppointment = new AppointmentBuilder().build(); + + CommandResult commandResult = new ScheduleCommand(validAppointment).execute(modelStub); + + assertEquals(String.format(ScheduleCommand.MESSAGE_SUCCESS, Messages.format(validAppointment)), + commandResult.getFeedbackToUser()); + assertEquals(Arrays.asList(validAppointment), modelStub.appointmentsAdded); + } + + // Duplicate appointment + @Test + public void execute_duplicateAppointment_throwsCommandException() throws InvalidStartEndTimeException { + Appointment validAppointment = new AppointmentBuilder().build(); + ScheduleCommand scheduleCommand = new ScheduleCommand(validAppointment); + ModelStub modelStub = new ModelStubWithAppointment(validAppointment); + assertThrows(CommandException.class, ScheduleCommand.MESSAGE_DUPLICATE_APPOINTMENT, () -> + scheduleCommand.execute(modelStub)); + } + + // Overlapping appointment + @Test + public void execute_overlappingAppointment_throwsCommandException() throws InvalidStartEndTimeException { + Appointment validAppointment = new AppointmentBuilder().build(); + Appointment existingAppointment = new AppointmentBuilder().withStartTime("17:00").withEndTime("18:00").build(); + ScheduleCommand scheduleCommand = new ScheduleCommand(validAppointment); + ModelStub modelStub = new ModelStubWithAppointment(existingAppointment); + assertThrows(CommandException.class, ScheduleCommand.MESSAGE_OVERLAPPING_APPOINTMENTS, () -> + scheduleCommand.execute(modelStub)); + } + + @Test + public void equals() throws InvalidStartEndTimeException { + Appointment alice = new AppointmentBuilder().withName("Alice").build(); + Appointment bob = new AppointmentBuilder().withName("Bob").build(); + ScheduleCommand scheduleAliceCommand = new ScheduleCommand(alice); + ScheduleCommand scheduleBobCommand = new ScheduleCommand(bob); + + // same object -> returns true + assertTrue(scheduleAliceCommand.equals(scheduleAliceCommand)); + + // same values -> returns true + ScheduleCommand scheduleAliceCommandCopy = new ScheduleCommand(alice); + assertTrue(scheduleAliceCommand.equals(scheduleAliceCommandCopy)); + + // different types -> returns false + assertFalse(scheduleAliceCommand.equals(1)); + + // null -> returns false + assertFalse(scheduleAliceCommand.equals(null)); + + // different student -> returns false + assertFalse(scheduleAliceCommand.equals(scheduleBobCommand)); + } + + @Test + public void toStringMethod() throws InvalidStartEndTimeException { + Appointment alice = new AppointmentBuilder().withName("John Doe").withStartTime("16:30").withEndTime("17:30") + .withDescription("First Session").build(); + ScheduleCommand scheduleCommand = new ScheduleCommand(alice); + String expected = ScheduleCommand.class.getCanonicalName() + "{toAdd=" + alice + "}"; + assertEquals(expected, scheduleCommand.toString()); + } + + /** + * A Model stub that contains a single appointment. + */ + private class ModelStubWithAppointment extends ModelStub { + private final Appointment appointment; + + ModelStubWithAppointment(Appointment appointment) { + requireNonNull(appointment); + this.appointment = appointment; + } + + @Override + public boolean hasAppointment(Appointment appointment) { + requireNonNull(appointment); + return this.appointment.isSameAppointment(appointment); + } + + @Override + public boolean hasOverlapsWithAppointments(Appointment appointment) { + return this.appointment.isOverlappingAppointment(appointment); + } + } + + /** + * A Model stub that always accept the appointment being added. + */ + private class ModelStubAcceptingAppointmentAdded extends ModelStub { + final ArrayList appointmentsAdded = new ArrayList<>(); + final ArrayList studentsAdded = new ArrayList<>(); + public ModelStubAcceptingAppointmentAdded() { + studentsAdded.add(JON_ANG); + } + + @Override + public boolean hasAppointment(Appointment appointment) { + requireNonNull(appointment); + return appointmentsAdded.stream().anyMatch(appointment::isSameAppointment); + } + + @Override + public boolean hasOverlapsWithAppointments(Appointment appointment) { + requireNonNull(appointment); + return appointmentsAdded.stream().anyMatch(appointment::isOverlappingAppointment); + } + + @Override + public boolean hasNoStudentForAppointment(Appointment appointment) { + requireNonNull(appointment); + return studentsAdded.stream().anyMatch(student -> !student.getName().equals(appointment.getName())); + } + + @Override + public void addAppointment(Appointment appointment) { + requireNonNull(appointment); + appointmentsAdded.add(appointment); + } + + @Override + public ReadOnlyWellNus getWellNusData() { + return new WellNus(); + } + } +} diff --git a/src/test/java/seedu/address/logic/commands/ViewCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java new file mode 100644 index 00000000000..7c9d9a69d70 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java @@ -0,0 +1,78 @@ +package seedu.address.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_CATEGORY_APPOINTMENT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_CATEGORY_STUDENT; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.UserPrefs; + +public class ViewCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalWellNus(), new UserPrefs()); + expectedModel = new ModelManager(model.getWellNusData(), new UserPrefs()); + } + + @Test + public void equals() { + final ViewCommand standardCommand = new ViewCommand(VALID_CATEGORY_APPOINTMENT); + + // same values -> returns true + ViewCommand commandWithSameValues = new ViewCommand(VALID_CATEGORY_APPOINTMENT); + assertTrue(standardCommand.equals(commandWithSameValues)); + + // same object -> returns true + assertTrue(standardCommand.equals(standardCommand)); + + // null -> returns false + assertFalse(standardCommand.equals(null)); + + // different types -> returns false + assertFalse(standardCommand.equals(new ClearCommand())); + + // different category -> returns false + assertFalse(standardCommand.equals(new ViewCommand(VALID_CATEGORY_STUDENT))); + } + + // View appointments only + @Test + public void execute_viewAppointments() { + assertCommandSuccess(new ViewCommand("appointments"), model, + ViewCommand.MESSAGE_SUCCESS_APPOINTMENT, expectedModel); + } + + // View students only + @Test + public void execute_viewStudents() { + assertCommandSuccess(new ViewCommand("students"), model, + ViewCommand.MESSAGE_SUCCESS_STUDENT, expectedModel); + } + + // View all students and appointments + @Test + public void execute_viewAll() { + assertCommandSuccess(new ViewCommand("all"), model, + ViewCommand.MESSAGE_SUCCESS_ALL, expectedModel); + } + + @Test + public void toStringMethod() { + String category = "appointments"; + ViewCommand viewCommand = new ViewCommand(category); + String expected = ViewCommand.class.getCanonicalName() + "{category=" + category + "}"; + assertEquals(expected, viewCommand.toString()); + } +} diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java index 5bc11d3cdaa..5a6a1b9d41c 100644 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java @@ -3,138 +3,112 @@ import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_RISK_LEVEL_DESC; import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB; 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.RISK_DESC_LOW; import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RISK_LEVEL_LOW; 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_RISK_LEVEL; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalPersons.AMY; -import static seedu.address.testutil.TypicalPersons.BOB; +import static seedu.address.testutil.TypicalStudents.AMY; +import static seedu.address.testutil.TypicalStudents.BOB; import org.junit.jupiter.api.Test; import seedu.address.logic.Messages; import seedu.address.logic.commands.AddCommand; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.PersonBuilder; +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Phone; +import seedu.address.model.student.Student; +import seedu.address.testutil.StudentBuilder; public class AddCommandParserTest { private AddCommandParser parser = new AddCommandParser(); @Test public void parse_allFieldsPresent_success() { - Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); + Student expectedStudent = new StudentBuilder(BOB).withTags(VALID_RISK_LEVEL_LOW).build(); // whitespace only preamble - assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - - // multiple tags - all accepted - Person expectedPersonMultipleTags = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) - .build(); - assertParseSuccess(parser, - NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, - new AddCommand(expectedPersonMultipleTags)); + assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + + ADDRESS_DESC_BOB + RISK_DESC_LOW, new AddCommand(expectedStudent)); } @Test public void parse_repeatedNonTagValue_failure() { - String validExpectedPersonString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND; + String validExpectedStudentString = NAME_DESC_BOB + PHONE_DESC_BOB + + ADDRESS_DESC_BOB + RISK_DESC_LOW; // multiple names - assertParseFailure(parser, NAME_DESC_AMY + validExpectedPersonString, + assertParseFailure(parser, NAME_DESC_AMY + validExpectedStudentString, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME)); // multiple phones - assertParseFailure(parser, PHONE_DESC_AMY + validExpectedPersonString, + assertParseFailure(parser, PHONE_DESC_AMY + validExpectedStudentString, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE)); - // multiple emails - assertParseFailure(parser, EMAIL_DESC_AMY + validExpectedPersonString, - Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL)); - // multiple addresses - assertParseFailure(parser, ADDRESS_DESC_AMY + validExpectedPersonString, + assertParseFailure(parser, ADDRESS_DESC_AMY + validExpectedStudentString, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS)); // multiple fields repeated assertParseFailure(parser, - validExpectedPersonString + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY + ADDRESS_DESC_AMY - + validExpectedPersonString, - Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE)); + validExpectedStudentString + PHONE_DESC_AMY + NAME_DESC_AMY + ADDRESS_DESC_AMY + + validExpectedStudentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, + PREFIX_PHONE, PREFIX_RISK_LEVEL)); // invalid value followed by valid value // invalid name - assertParseFailure(parser, INVALID_NAME_DESC + validExpectedPersonString, + assertParseFailure(parser, INVALID_NAME_DESC + validExpectedStudentString, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME)); - // invalid email - assertParseFailure(parser, INVALID_EMAIL_DESC + validExpectedPersonString, - Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL)); - // invalid phone - assertParseFailure(parser, INVALID_PHONE_DESC + validExpectedPersonString, + assertParseFailure(parser, INVALID_PHONE_DESC + validExpectedStudentString, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE)); // invalid address - assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedPersonString, + assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedStudentString, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS)); // valid value followed by invalid value // invalid name - assertParseFailure(parser, validExpectedPersonString + INVALID_NAME_DESC, + assertParseFailure(parser, validExpectedStudentString + INVALID_NAME_DESC, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME)); - // invalid email - assertParseFailure(parser, validExpectedPersonString + INVALID_EMAIL_DESC, - Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL)); - // invalid phone - assertParseFailure(parser, validExpectedPersonString + INVALID_PHONE_DESC, + assertParseFailure(parser, validExpectedStudentString + INVALID_PHONE_DESC, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE)); // invalid address - assertParseFailure(parser, validExpectedPersonString + INVALID_ADDRESS_DESC, + assertParseFailure(parser, validExpectedStudentString + INVALID_ADDRESS_DESC, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS)); } @Test public void parse_optionalFieldsMissing_success() { // zero tags - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); - assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, - new AddCommand(expectedPerson)); + Student expectedStudent = new StudentBuilder(AMY).withTags().build(); + assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + ADDRESS_DESC_AMY, + new AddCommand(expectedStudent)); } @Test @@ -142,55 +116,47 @@ public void parse_compulsoryFieldMissing_failure() { String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); // missing name prefix - assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, + assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + ADDRESS_DESC_BOB, expectedMessage); // missing phone prefix - assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing email prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, + assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + ADDRESS_DESC_BOB, expectedMessage); // missing address prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_ADDRESS_BOB, expectedMessage); // all prefixes missing - assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, + assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_ADDRESS_BOB, expectedMessage); } @Test public void parse_invalidValue_failure() { // invalid name - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + ADDRESS_DESC_BOB + + RISK_DESC_LOW, Name.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); - - // invalid email - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + ADDRESS_DESC_BOB + + RISK_DESC_LOW, Phone.MESSAGE_CONSTRAINTS); // invalid address - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_ADDRESS_DESC + + RISK_DESC_LOW, Address.MESSAGE_CONSTRAINTS); // invalid tag - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + ADDRESS_DESC_BOB + + INVALID_RISK_LEVEL_DESC, RiskLevel.MESSAGE_CONSTRAINTS); // two invalid values, only first invalid value reported - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, + assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + INVALID_ADDRESS_DESC, Name.MESSAGE_CONSTRAINTS); // non-empty preamble - assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, + assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + + ADDRESS_DESC_BOB + RISK_DESC_LOW, String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); } } diff --git a/src/test/java/seedu/address/logic/parser/CancelCommandParserTest.java b/src/test/java/seedu/address/logic/parser/CancelCommandParserTest.java new file mode 100644 index 00000000000..a6fc3dcd924 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/CancelCommandParserTest.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.CancelCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside of the CancelCommand code. For example, inputs "1" and "1 abc" take the + * same path through the CancelCommand, and therefore we test only one of them. + * The path variation for those two cases occur inside the ParserUtil, and + * therefore should be covered by the ParserUtilTest. + */ +public class CancelCommandParserTest { + + private CancelCommandParser parser = new CancelCommandParser(); + + @Test + public void parse_validArgs_returnsCancelCommand() { + assertParseSuccess(parser, "1", new CancelCommand(INDEX_FIRST_STUDENT)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, CancelCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java index 6a40e14a649..33bdab39bb6 100644 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java @@ -3,7 +3,7 @@ import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; import org.junit.jupiter.api.Test; @@ -22,7 +22,7 @@ public class DeleteCommandParserTest { @Test public void parse_validArgs_returnsDeleteCommand() { - assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); + assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_STUDENT)); } @Test diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java index cc7175172d4..6ac5229c056 100644 --- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java @@ -3,51 +3,35 @@ import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; 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.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; 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_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.logic.parser.CliSyntax.PREFIX_RISK_LEVEL; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_STUDENT; +import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_STUDENT; import org.junit.jupiter.api.Test; import seedu.address.commons.core.index.Index; import seedu.address.logic.Messages; import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -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; -import seedu.address.testutil.EditPersonDescriptorBuilder; +import seedu.address.logic.commands.EditCommand.EditStudentDescriptor; +import seedu.address.model.student.Address; +import seedu.address.model.student.Phone; +import seedu.address.testutil.EditStudentDescriptorBuilder; public class EditCommandParserTest { - private static final String TAG_EMPTY = " " + PREFIX_TAG; + private static final String TAG_EMPTY = " " + PREFIX_RISK_LEVEL; private static final String MESSAGE_INVALID_FORMAT = String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); @@ -57,7 +41,7 @@ public class EditCommandParserTest { @Test public void parse_missingParts_failure() { // no index specified - assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT); + assertParseFailure(parser, PHONE_DESC_AMY, MESSAGE_INVALID_FORMAT); // no field specified assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); @@ -69,10 +53,10 @@ public void parse_missingParts_failure() { @Test public void parse_invalidPreamble_failure() { // negative index - assertParseFailure(parser, "-5" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); + assertParseFailure(parser, "-5" + PHONE_DESC_AMY, MESSAGE_INVALID_FORMAT); // zero index - assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); + assertParseFailure(parser, "0" + PHONE_DESC_AMY, MESSAGE_INVALID_FORMAT); // invalid arguments being parsed as preamble assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT); @@ -83,47 +67,21 @@ public void parse_invalidPreamble_failure() { @Test public void parse_invalidValue_failure() { - assertParseFailure(parser, "1" + INVALID_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address - assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag - - // invalid phone followed by valid email - assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS); - - // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, - // parsing it together with a valid tag results in error - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); // multiple invalid values, but only the first invalid value is captured - assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, - Name.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + INVALID_PHONE_DESC + VALID_ADDRESS_AMY, + Phone.MESSAGE_CONSTRAINTS); } @Test public void parse_allFieldsSpecified_success() { - Index targetIndex = INDEX_SECOND_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND - + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; + Index targetIndex = INDEX_SECOND_STUDENT; + String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + ADDRESS_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_someFieldsSpecified_success() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_AMY).build(); + EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder().withPhone(VALID_PHONE_BOB) + .withAddress(VALID_ADDRESS_AMY).build(); EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); @@ -131,34 +89,17 @@ public void parse_someFieldsSpecified_success() { @Test public void parse_oneFieldSpecified_success() { - // name - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - // phone - userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); + Index targetIndex = INDEX_THIRD_STUDENT; + String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; + EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); + EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - // email - userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); // address userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // tags - userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; - descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); + descriptor = new EditStudentDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build(); expectedCommand = new EditCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); } @@ -169,7 +110,7 @@ public void parse_multipleRepeatedFields_failure() { // AddCommandParserTest#parse_repeatedNonTagValue_failure() // valid followed by invalid - Index targetIndex = INDEX_FIRST_PERSON; + Index targetIndex = INDEX_FIRST_STUDENT; String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB; assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE)); @@ -180,29 +121,19 @@ public void parse_multipleRepeatedFields_failure() { assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE)); // mulltiple valid fields repeated - userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY - + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND - + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; + // note: risk level test cases has to be expanded if the hash set is expanded + userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + PHONE_DESC_AMY + ADDRESS_DESC_AMY + + PHONE_DESC_BOB + ADDRESS_DESC_BOB; assertParseFailure(parser, userInput, - Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS)); + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_ADDRESS)); // multiple invalid values - userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC - + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC; + userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC; assertParseFailure(parser, userInput, - Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS)); + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_ADDRESS)); } - @Test - public void parse_resetTags_success() { - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + TAG_EMPTY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } } diff --git a/src/test/java/seedu/address/logic/parser/FilterCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FilterCommandParserTest.java new file mode 100644 index 00000000000..1f895f0cedc --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/FilterCommandParserTest.java @@ -0,0 +1,31 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.FilterCommand; +import seedu.address.model.appointment.AppointmentDateMatchesPredicate; + +public class FilterCommandParserTest { + + private FilterCommandParser parser = new FilterCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsFilterCommand() { + // no leading and trailing whitespaces + FilterCommand expectedFilterCommand = + new FilterCommand(new AppointmentDateMatchesPredicate("2023-10-26")); + assertParseSuccess(parser, "2023-10-26", expectedFilterCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n 2023-10-26 \n", expectedFilterCommand); + } +} diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java index d92e64d12f9..e787b89bdf8 100644 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java @@ -4,12 +4,9 @@ import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import java.util.Arrays; - import org.junit.jupiter.api.Test; import seedu.address.logic.commands.FindCommand; -import seedu.address.model.person.NameContainsKeywordsPredicate; public class FindCommandParserTest { @@ -23,8 +20,9 @@ public void parse_emptyArg_throwsParseException() { @Test public void parse_validArgs_returnsFindCommand() { // no leading and trailing whitespaces + String[] keywords = new String[]{"Alice", "Bob"}; FindCommand expectedFindCommand = - new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"))); + new FindCommand(keywords); assertParseSuccess(parser, "Alice Bob", expectedFindCommand); // multiple whitespaces between keywords diff --git a/src/test/java/seedu/address/logic/parser/NoteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/NoteCommandParserTest.java new file mode 100644 index 00000000000..13cea9d58c3 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/NoteCommandParserTest.java @@ -0,0 +1,38 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.NoteCommand; +import seedu.address.model.student.Note; + +public class NoteCommandParserTest { + private NoteCommandParser parser = new NoteCommandParser(); + private final String nonEmptyNote = "Some note."; + + @Test + public void parse_indexSpecified_success() { + // have note + Index targetIndex = INDEX_FIRST_STUDENT; + String userInput = targetIndex.getOneBased() + " " + PREFIX_NOTE + nonEmptyNote; + NoteCommand expectedCommand = new NoteCommand(INDEX_FIRST_STUDENT, new Note(nonEmptyNote)); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_missingCompulsoryField_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, NoteCommand.MESSAGE_USAGE); + + // no parameters + assertParseFailure(parser, NoteCommand.COMMAND_WORD, expectedMessage); + + // no index + assertParseFailure(parser, NoteCommand.COMMAND_WORD + " " + nonEmptyNote, expectedMessage); + } +} diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java index 4256788b1a7..eafc5699f05 100644 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java @@ -4,7 +4,7 @@ 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 static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; import java.util.Arrays; import java.util.Collections; @@ -14,25 +14,22 @@ import org.junit.jupiter.api.Test; 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; +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Phone; public class ParserUtilTest { private static final String INVALID_NAME = "R@chel"; private static final String INVALID_PHONE = "+651234"; private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - + private static final String INVALID_RISK_LEVEL = "#friend"; private static final String VALID_NAME = "Rachel Walker"; - private static final String VALID_PHONE = "123456"; + private static final String VALID_NAME_2 = "Ben"; + private static final String VALID_PHONE = "12345678"; private static final String VALID_ADDRESS = "123 Main Street #0505"; - private static final String VALID_EMAIL = "rachel@example.com"; - private static final String VALID_TAG_1 = "friend"; - private static final String VALID_TAG_2 = "neighbour"; + private static final String VALID_RISK_LEVEL_1 = "high"; + private static final String VALID_RISK_LEVEL_2 = "low"; private static final String WHITESPACE = " \t\r\n"; @@ -50,10 +47,10 @@ public void parseIndex_outOfRangeInput_throwsParseException() { @Test public void parseIndex_validInput_success() throws Exception { // No whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1")); + assertEquals(INDEX_FIRST_STUDENT, ParserUtil.parseIndex("1")); // Leading and trailing whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 ")); + assertEquals(INDEX_FIRST_STUDENT, ParserUtil.parseIndex(" 1 ")); } @Test @@ -79,6 +76,14 @@ public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Excep assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace)); } + @Test + public void parseName_validValueWithExtraWhitespace_returnsTrimmedName() throws Exception { + String nameWithExtraWhitespace = VALID_NAME + WHITESPACE + WHITESPACE + VALID_NAME_2; + String nameWithoutExtraWhitespace = VALID_NAME + " " + VALID_NAME_2; + Name expectedName = new Name(nameWithoutExtraWhitespace); + assertEquals(expectedName, ParserUtil.parseName(nameWithExtraWhitespace)); + } + @Test public void parsePhone_null_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null)); @@ -125,71 +130,52 @@ public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace)); } - @Test - public void parseEmail_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null)); - } - - @Test - public void parseEmail_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception { - String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE; - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace)); - } + // Ambiguous method call because of overloaded method + //@Test + //public void parseRiskLevel_null_throwsNullPointerException() { + // assertThrows(NullPointerException.class, () -> ParserUtil.parseRiskLevel(null)); + //} @Test - public void parseTag_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); + public void parseRiskLevel_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseRiskLevel(INVALID_RISK_LEVEL)); } @Test - public void parseTag_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); + public void parseRiskLevel_validValueWithoutWhitespace_returnsTag() throws Exception { + RiskLevel expectedTag = new RiskLevel(VALID_RISK_LEVEL_1); + assertEquals(expectedTag, ParserUtil.parseRiskLevel(VALID_RISK_LEVEL_1)); } @Test - public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); + public void parseRiskLevel_validValueWithWhitespace_returnsTrimmedTag() throws Exception { + String tagWithWhitespace = WHITESPACE + VALID_RISK_LEVEL_1 + WHITESPACE; + RiskLevel expectedTag = new RiskLevel(VALID_RISK_LEVEL_1); + assertEquals(expectedTag, ParserUtil.parseRiskLevel(tagWithWhitespace)); } - @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)); - } + // Ambiguous method due overload + //@Test + //public void parseRiskLevel_null_throwsNullPointerException() { + // assertThrows(NullPointerException.class, () -> ParserUtil.parseRiskLevel(null)); + //} @Test - public void parseTags_collectionWithInvalidTags_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); + public void parseRiskLevel_collectionWithInvalidTags_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseRiskLevel(Arrays.asList( + VALID_RISK_LEVEL_1, INVALID_RISK_LEVEL))); } @Test - public void parseTags_emptyCollection_returnsEmptySet() throws Exception { - assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); + public void parseRiskLevel_emptyCollection_returnsEmptySet() throws Exception { + assertTrue(ParserUtil.parseRiskLevel(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))); + public void parseRiskLevel_collectionWithValidTags_returnsTagSet() throws Exception { + Set actualTagSet = ParserUtil.parseRiskLevel(Arrays.asList(VALID_RISK_LEVEL_1)); + Set expectedTagSet = new HashSet(Arrays.asList( + new RiskLevel(VALID_RISK_LEVEL_1))); assertEquals(expectedTagSet, actualTagSet); } diff --git a/src/test/java/seedu/address/logic/parser/ScheduleCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ScheduleCommandParserTest.java new file mode 100644 index 00000000000..ffbf85e3536 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/ScheduleCommandParserTest.java @@ -0,0 +1,175 @@ +package seedu.address.logic.parser; + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_AMY; +import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_BOB; +import static seedu.address.logic.commands.CommandTestUtil.DESCRIPTION_DESC_AMY; +import static seedu.address.logic.commands.CommandTestUtil.DESCRIPTION_DESC_BOB; +import static seedu.address.logic.commands.CommandTestUtil.END_TIME_DESC_AMY; +import static seedu.address.logic.commands.CommandTestUtil.END_TIME_DESC_BOB; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_DATE_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_DESCRIPTION_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_END_TIME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_START_TIME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static seedu.address.logic.commands.CommandTestUtil.NAME_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.START_TIME_DESC_AMY; +import static seedu.address.logic.commands.CommandTestUtil.START_TIME_DESC_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DESCRIPTION_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_END_TIME_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_START_TIME_BOB; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_START_TIME; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.Messages; +import seedu.address.logic.commands.ScheduleCommand; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.Date; +import seedu.address.model.appointment.Description; +import seedu.address.model.appointment.Time; +import seedu.address.model.appointment.exceptions.InvalidStartEndTimeException; +import seedu.address.model.student.Name; +import seedu.address.testutil.AppointmentBuilder; + +public class ScheduleCommandParserTest { + private ScheduleCommandParser parser = new ScheduleCommandParser(); + + @Test + public void parse_allFieldsPresent_success() throws InvalidStartEndTimeException { + Appointment expectedAppointment = new AppointmentBuilder().withName(VALID_NAME_BOB) + .withDate(VALID_DATE_BOB).withStartTime("12:00").withEndTime(VALID_END_TIME_BOB) + .withDescription(VALID_DESCRIPTION_BOB).build(); + + // Whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + DATE_DESC_BOB + + START_TIME_DESC_BOB + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, + new ScheduleCommand(expectedAppointment)); + } + + @Test + public void parse_repeatedNonTagValue_failure() { + String validExpectedAppointmentString = NAME_DESC_BOB + DATE_DESC_BOB + START_TIME_DESC_BOB + + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB; + + // Multiple names + assertParseFailure(parser, NAME_DESC_AMY + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME)); + + // Multiple dates + assertParseFailure(parser, DATE_DESC_AMY + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DATE)); + + // Multiple start times + assertParseFailure(parser, START_TIME_DESC_AMY + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_START_TIME)); + + // Multiple end times + assertParseFailure(parser, END_TIME_DESC_AMY + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_END_TIME)); + + // Multiple descriptions + assertParseFailure(parser, DESCRIPTION_DESC_AMY + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DESCRIPTION)); + + // Multiple fields repeated + assertParseFailure(parser, validExpectedAppointmentString + NAME_DESC_AMY + DATE_DESC_AMY + START_TIME_DESC_AMY + + END_TIME_DESC_AMY + DESCRIPTION_DESC_AMY + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_DATE, PREFIX_START_TIME, + PREFIX_END_TIME, PREFIX_DESCRIPTION)); + + // Invalid value followed by valid value + + // Invalid name + assertParseFailure(parser, INVALID_NAME_DESC + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME)); + + // Invalid date + assertParseFailure(parser, INVALID_DATE_DESC + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DATE)); + + // Invalid start time + assertParseFailure(parser, INVALID_START_TIME_DESC + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_START_TIME)); + + // Invalid end time + assertParseFailure(parser, INVALID_END_TIME_DESC + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_END_TIME)); + + // Invalid description + assertParseFailure(parser, INVALID_DESCRIPTION_DESC + validExpectedAppointmentString, + Messages.getErrorMessageForDuplicatePrefixes(PREFIX_DESCRIPTION)); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScheduleCommand.MESSAGE_USAGE); + + // Missing name prefix + assertParseFailure(parser, VALID_DATE_BOB + START_TIME_DESC_BOB + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, + expectedMessage); + + // Missing date prefix + assertParseFailure(parser, NAME_DESC_BOB + START_TIME_DESC_BOB + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, + expectedMessage); + + // Missing start time prefix + assertParseFailure(parser, NAME_DESC_BOB + DATE_DESC_BOB + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, + expectedMessage); + + // Missing end time prefix + assertParseFailure(parser, NAME_DESC_BOB + DATE_DESC_BOB + START_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, + expectedMessage); + + // Missing description prefix + assertParseFailure(parser, NAME_DESC_BOB + DATE_DESC_BOB + START_TIME_DESC_BOB + END_TIME_DESC_BOB, + expectedMessage); + + // All prefixes missing + assertParseFailure(parser, VALID_NAME_BOB + VALID_DATE_BOB + VALID_START_TIME_BOB + + VALID_END_TIME_BOB + VALID_DESCRIPTION_BOB, expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // Invalid name + assertParseFailure(parser, INVALID_NAME_DESC + DATE_DESC_BOB + START_TIME_DESC_BOB + + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, Name.MESSAGE_CONSTRAINTS); + + // Invalid date + assertParseFailure(parser, NAME_DESC_BOB + INVALID_DATE_DESC + START_TIME_DESC_BOB + + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, Date.MESSAGE_CONSTRAINTS); + + // Invalid start time + assertParseFailure(parser, NAME_DESC_BOB + DATE_DESC_BOB + INVALID_START_TIME_DESC + + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, Time.MESSAGE_CONSTRAINTS); + + // Invalid end time + assertParseFailure(parser, NAME_DESC_BOB + DATE_DESC_BOB + START_TIME_DESC_BOB + + INVALID_END_TIME_DESC + DESCRIPTION_DESC_BOB, Time.MESSAGE_CONSTRAINTS); + + // Invalid description + assertParseFailure(parser, NAME_DESC_BOB + DATE_DESC_BOB + START_TIME_DESC_BOB + + END_TIME_DESC_BOB + INVALID_DESCRIPTION_DESC, Description.MESSAGE_CONSTRAINTS); + + // Two invalid values, only the first invalid value reported + assertParseFailure(parser, INVALID_NAME_DESC + INVALID_DATE_DESC + START_TIME_DESC_BOB + + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, Name.MESSAGE_CONSTRAINTS); + + // Non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + DATE_DESC_BOB + START_TIME_DESC_BOB + + END_TIME_DESC_BOB + DESCRIPTION_DESC_BOB, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ScheduleCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java new file mode 100644 index 00000000000..dafe0f649ba --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java @@ -0,0 +1,51 @@ +package seedu.address.logic.parser; + + +import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CATEGORY; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.ViewCommand; + + + +public class ViewCommandParserTest { + private ViewCommandParser parser = new ViewCommandParser(); + private final String validCategory1 = "appointments"; + private final String validCategory2 = "students"; + + @Test + public void parse_indexSpecified_success() { + // have remark + String userInput = " " + PREFIX_CATEGORY + validCategory1; + ViewCommand expectedCommand = new ViewCommand(validCategory1); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_indexSpecified2_success() { + // have remark + String userInput = " " + PREFIX_CATEGORY + validCategory2; + ViewCommand expectedCommand = new ViewCommand(validCategory2); + assertParseSuccess(parser, userInput, expectedCommand); + } + + + @Test + public void parse_missingCompulsoryField_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); + // no parameters + assertParseFailure(parser, "", expectedMessage); + } + + @Test + public void parse_invalidField_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE); + String userInput = " " + PREFIX_CATEGORY + "not-a-category"; + assertParseFailure(parser, userInput, expectedMessage); + } + +} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/WellNusParserTest.java similarity index 54% rename from src/test/java/seedu/address/logic/parser/AddressBookParserTest.java rename to src/test/java/seedu/address/logic/parser/WellNusParserTest.java index 5a1ab3dbc0c..3a6dc895999 100644 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ b/src/test/java/seedu/address/logic/parser/WellNusParserTest.java @@ -4,11 +4,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CATEGORY; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NOTE; import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_STUDENT; import java.util.Arrays; -import java.util.List; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; @@ -17,27 +18,27 @@ import seedu.address.logic.commands.ClearCommand; import seedu.address.logic.commands.DeleteCommand; import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; import seedu.address.logic.commands.ExitCommand; import seedu.address.logic.commands.FindCommand; import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; +import seedu.address.logic.commands.NoteCommand; +import seedu.address.logic.commands.ViewCommand; import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; -import seedu.address.testutil.PersonUtil; +import seedu.address.model.student.Note; +import seedu.address.model.student.Student; +import seedu.address.testutil.EditStudentDescriptorBuilder; +import seedu.address.testutil.StudentBuilder; +import seedu.address.testutil.StudentUtil; -public class AddressBookParserTest { +public class WellNusParserTest { - private final AddressBookParser parser = new AddressBookParser(); + private final WellNusParser parser = new WellNusParser(); @Test public void parseCommand_add() throws Exception { - Person person = new PersonBuilder().build(); - AddCommand command = (AddCommand) parser.parseCommand(PersonUtil.getAddCommand(person)); - assertEquals(new AddCommand(person), command); + Student student = new StudentBuilder().build(); + AddCommand command = (AddCommand) parser.parseCommand(StudentUtil.getAddCommand(student)); + assertEquals(new AddCommand(student), command); } @Test @@ -49,17 +50,17 @@ public void parseCommand_clear() throws Exception { @Test public void parseCommand_delete() throws Exception { DeleteCommand command = (DeleteCommand) parser.parseCommand( - DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased()); - assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command); + DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_STUDENT.getOneBased()); + assertEquals(new DeleteCommand(INDEX_FIRST_STUDENT), command); } @Test public void parseCommand_edit() throws Exception { - Person person = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); + Student student = new StudentBuilder().build(); + EditCommand.EditStudentDescriptor descriptor = new EditStudentDescriptorBuilder(student).build(); EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " - + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); - assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); + + INDEX_FIRST_STUDENT.getOneBased() + " " + StudentUtil.getEditStudentDescriptorDetails(descriptor)); + assertEquals(new EditCommand(INDEX_FIRST_STUDENT, descriptor), command); } @Test @@ -70,10 +71,12 @@ public void parseCommand_exit() throws Exception { @Test public void parseCommand_find() throws Exception { - List keywords = Arrays.asList("foo", "bar", "baz"); + String[] keywords = new String[]{"foo", "bar", "baz"}; FindCommand command = (FindCommand) parser.parseCommand( - FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); - assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); + FindCommand.COMMAND_WORD + " " + + Arrays.asList(keywords).stream().collect(Collectors.joining(" "))); + FindCommand other = new FindCommand(keywords); + assertEquals(new FindCommand(keywords), command); } @Test @@ -83,9 +86,19 @@ public void parseCommand_help() throws Exception { } @Test - public void parseCommand_list() throws Exception { - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand); - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); + public void parseCommand_view() throws Exception { + final String category = "appointments"; + ViewCommand command = (ViewCommand) parser.parseCommand(ViewCommand.COMMAND_WORD + " " + + PREFIX_CATEGORY + category); + assertEquals(new ViewCommand(category), command); + } + + @Test + public void parseCommand_note() throws Exception { + final Note note = new Note("Some note."); + NoteCommand command = (NoteCommand) parser.parseCommand(NoteCommand.COMMAND_WORD + " " + + INDEX_FIRST_STUDENT.getOneBased() + " " + PREFIX_NOTE + note.value); + assertEquals(new NoteCommand(INDEX_FIRST_STUDENT, note), command); } @Test diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java deleted file mode 100644 index 68c8c5ba4d5..00000000000 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package seedu.address.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.testutil.PersonBuilder; - -public class AddressBookTest { - - private final AddressBook addressBook = new AddressBook(); - - @Test - public void constructor() { - assertEquals(Collections.emptyList(), addressBook.getPersonList()); - } - - @Test - public void resetData_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.resetData(null)); - } - - @Test - public void resetData_withValidReadOnlyAddressBook_replacesData() { - AddressBook newData = getTypicalAddressBook(); - addressBook.resetData(newData); - assertEquals(newData, addressBook); - } - - @Test - public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { - // Two persons with the same identity fields - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - List newPersons = Arrays.asList(ALICE, editedAlice); - AddressBookStub newData = new AddressBookStub(newPersons); - - assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData)); - } - - @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null)); - } - - @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - assertTrue(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(addressBook.hasPerson(editedAlice)); - } - - @Test - public void getPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0)); - } - - @Test - public void toStringMethod() { - String expected = AddressBook.class.getCanonicalName() + "{persons=" + addressBook.getPersonList() + "}"; - assertEquals(expected, addressBook.toString()); - } - - /** - * A stub ReadOnlyAddressBook whose persons list can violate interface constraints. - */ - private static class AddressBookStub implements ReadOnlyAddressBook { - private final ObservableList persons = FXCollections.observableArrayList(); - - AddressBookStub(Collection persons) { - this.persons.setAll(persons); - } - - @Override - public ObservableList getPersonList() { - return persons; - } - } - -} diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java index 2cf1418d116..90fbbcaf05f 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/seedu/address/model/ModelManagerTest.java @@ -3,10 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_STUDENTS; import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BENSON; +import static seedu.address.testutil.TypicalStudents.ALICE; +import static seedu.address.testutil.TypicalStudents.BENNY; import java.nio.file.Path; import java.nio.file.Paths; @@ -15,8 +15,8 @@ import org.junit.jupiter.api.Test; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.testutil.AddressBookBuilder; +import seedu.address.model.student.NameContainsKeywordsPredicate; +import seedu.address.testutil.WellNusBuilder; public class ModelManagerTest { @@ -26,7 +26,7 @@ public class ModelManagerTest { public void constructor() { assertEquals(new UserPrefs(), modelManager.getUserPrefs()); assertEquals(new GuiSettings(), modelManager.getGuiSettings()); - assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook())); + assertEquals(new WellNus(), new WellNus(modelManager.getWellNusData())); } @Test @@ -37,14 +37,14 @@ public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { @Test public void setUserPrefs_validUserPrefs_copiesUserPrefs() { UserPrefs userPrefs = new UserPrefs(); - userPrefs.setAddressBookFilePath(Paths.get("address/book/file/path")); + userPrefs.setWellNusFilePath(Paths.get("address/book/file/path")); userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4)); modelManager.setUserPrefs(userPrefs); assertEquals(userPrefs, modelManager.getUserPrefs()); // Modifying userPrefs should not modify modelManager's userPrefs UserPrefs oldUserPrefs = new UserPrefs(userPrefs); - userPrefs.setAddressBookFilePath(Paths.get("new/address/book/file/path")); + userPrefs.setWellNusFilePath(Paths.get("new/address/book/file/path")); assertEquals(oldUserPrefs, modelManager.getUserPrefs()); } @@ -61,47 +61,47 @@ public void setGuiSettings_validGuiSettings_setsGuiSettings() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setAddressBookFilePath(null)); + public void setWellNusFilePath_nullPath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setWellNusFilePath(null)); } @Test - public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { + public void setWellNusFilePath_validPath_setsWellNusFilePath() { Path path = Paths.get("address/book/file/path"); - modelManager.setAddressBookFilePath(path); - assertEquals(path, modelManager.getAddressBookFilePath()); + modelManager.setWellNusFilePath(path); + assertEquals(path, modelManager.getWellNusFilePath()); } @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null)); + public void hasStudent_nullStudent_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.hasStudent(null)); } @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(modelManager.hasPerson(ALICE)); + public void hasStudent_studentNotInWellNus_returnsFalse() { + assertFalse(modelManager.hasStudent(ALICE)); } @Test - public void hasPerson_personInAddressBook_returnsTrue() { - modelManager.addPerson(ALICE); - assertTrue(modelManager.hasPerson(ALICE)); + public void hasStudent_studentInWellNus_returnsTrue() { + modelManager.addStudent(ALICE); + assertTrue(modelManager.hasStudent(ALICE)); } @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0)); + public void getFilteredStudentList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredStudentList().remove(0)); } @Test public void equals() { - AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); - AddressBook differentAddressBook = new AddressBook(); + WellNus wellNus = new WellNusBuilder().withStudent(ALICE).withStudent(BENNY).build(); + WellNus differentWellNus = new WellNus(); UserPrefs userPrefs = new UserPrefs(); // same values -> returns true - modelManager = new ModelManager(addressBook, userPrefs); - ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs); + modelManager = new ModelManager(wellNus, userPrefs); + ModelManager modelManagerCopy = new ModelManager(wellNus, userPrefs); assertTrue(modelManager.equals(modelManagerCopy)); // same object -> returns true @@ -113,20 +113,20 @@ public void equals() { // different types -> returns false assertFalse(modelManager.equals(5)); - // different addressBook -> returns false - assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs))); + // different WellNus -> returns false + assertFalse(modelManager.equals(new ModelManager(differentWellNus, userPrefs))); // different filteredList -> returns false - String[] keywords = ALICE.getName().fullName.split("\\s+"); - modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); - assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs))); + String[] keywords = ALICE.getName().value.split("\\s+"); + modelManager.updateFilteredStudentList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); + assertFalse(modelManager.equals(new ModelManager(wellNus, userPrefs))); // resets modelManager to initial state for upcoming tests - modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + modelManager.updateFilteredStudentList(PREDICATE_SHOW_ALL_STUDENTS); // different userPrefs -> returns false UserPrefs differentUserPrefs = new UserPrefs(); - differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath")); - assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs))); + differentUserPrefs.setWellNusFilePath(Paths.get("differentFilePath")); + assertFalse(modelManager.equals(new ModelManager(wellNus, differentUserPrefs))); } } diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/seedu/address/model/UserPrefsTest.java index b1307a70d52..c26125e936d 100644 --- a/src/test/java/seedu/address/model/UserPrefsTest.java +++ b/src/test/java/seedu/address/model/UserPrefsTest.java @@ -13,9 +13,9 @@ public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { + public void setWellNusFilePath_nullPath_throwsNullPointerException() { UserPrefs userPrefs = new UserPrefs(); - assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null)); + assertThrows(NullPointerException.class, () -> userPrefs.setWellNusFilePath(null)); } } diff --git a/src/test/java/seedu/address/model/WellNusTest.java b/src/test/java/seedu/address/model/WellNusTest.java new file mode 100644 index 00000000000..b43335e41ed --- /dev/null +++ b/src/test/java/seedu/address/model/WellNusTest.java @@ -0,0 +1,115 @@ +package seedu.address.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RISK_LEVEL_LOW; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalStudents.ALICE; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Student; +import seedu.address.model.student.exceptions.DuplicateStudentException; +import seedu.address.testutil.StudentBuilder; + +public class WellNusTest { + + private final WellNus wellNus = new WellNus(); + + @Test + public void constructor() { + assertEquals(Collections.emptyList(), wellNus.getStudentList()); + } + + @Test + public void resetData_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> wellNus.resetData(null)); + } + + @Test + public void resetData_withValidReadOnlyWellNus_replacesData() { + WellNus newData = getTypicalWellNus(); + wellNus.resetData(newData); + assertEquals(newData, wellNus); + } + + @Test + public void resetData_withDuplicateStudents_throwsDuplicateStudentException() { + // Two students with the same identity fields + Student editedAlice = new StudentBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_RISK_LEVEL_LOW) + .build(); + List newStudents = Arrays.asList(ALICE, editedAlice); + WellNusStub newData = new WellNusStub(newStudents); + + assertThrows(DuplicateStudentException.class, () -> wellNus.resetData(newData)); + } + + @Test + public void hasStudent_nullStudent_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> wellNus.hasStudent(null)); + } + + @Test + public void hasStudent_studentNotInWellNus_returnsFalse() { + assertFalse(wellNus.hasStudent(ALICE)); + } + + @Test + public void hasStudent_studentInWellNus_returnsTrue() { + wellNus.addStudent(ALICE); + assertTrue(wellNus.hasStudent(ALICE)); + } + + @Test + public void hasStudent_studentWithSameIdentityFieldsInWellNus_returnsTrue() { + wellNus.addStudent(ALICE); + Student editedAlice = new StudentBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_RISK_LEVEL_LOW) + .build(); + assertTrue(wellNus.hasStudent(editedAlice)); + } + + @Test + public void getStudentList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> wellNus.getStudentList().remove(0)); + } + + @Test + public void toStringMethod() { + String expected = WellNus.class.getCanonicalName() + "{students=" + wellNus.getStudentList() + "}"; + assertEquals(expected, wellNus.toString()); + } + + /** + * A stub ReadOnlyWellNus whose students list can violate interface constraints. + */ + private static class WellNusStub implements ReadOnlyWellNus { + private final ObservableList students = FXCollections.observableArrayList(); + private final ObservableList appointments = FXCollections.observableArrayList(); + + WellNusStub(Collection students) { + this.students.setAll(students); + } + + @Override + public ObservableList getStudentList() { + return students; + } + + @Override + public ObservableList getAppointmentList() { + return appointments; + } + } + +} diff --git a/src/test/java/seedu/address/model/appointment/AppointmentContainsNamePredicateTest.java b/src/test/java/seedu/address/model/appointment/AppointmentContainsNamePredicateTest.java new file mode 100644 index 00000000000..580ff81a5d8 --- /dev/null +++ b/src/test/java/seedu/address/model/appointment/AppointmentContainsNamePredicateTest.java @@ -0,0 +1,79 @@ +package seedu.address.model.appointment; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.AppointmentBuilder; + +public class AppointmentContainsNamePredicateTest { + + @Test + public void equals() { + List firstPredicateKeywordList = Collections.singletonList("first"); + List secondPredicateKeywordList = Arrays.asList("first", "second"); + + AppointmentContainsNamePredicate firstPredicate = + new AppointmentContainsNamePredicate(firstPredicateKeywordList); + AppointmentContainsNamePredicate secondPredicate = + new AppointmentContainsNamePredicate(secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + AppointmentContainsNamePredicate firstPredicateCopy = + new AppointmentContainsNamePredicate(firstPredicateKeywordList); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different student -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_nameContainsKeywords_returnsTrue() { + // Matching first name + AppointmentContainsNamePredicate predicate = + new AppointmentContainsNamePredicate(Collections.singletonList("Alice")); + assertTrue(predicate.test(new AppointmentBuilder().withName("Alice Bob").build())); + + // Matching full Name + predicate = new AppointmentContainsNamePredicate(Arrays.asList("Alice", "Bob")); + assertTrue(predicate.test(new AppointmentBuilder().withName("Alice Bob").build())); + + // Matching last name + predicate = new AppointmentContainsNamePredicate(Arrays.asList("Carol")); + assertTrue(predicate.test(new AppointmentBuilder().withName("Alice Carol").build())); + + // Mixed-case keywords + predicate = new AppointmentContainsNamePredicate(Arrays.asList("aLIce", "bOB")); + assertTrue(predicate.test(new AppointmentBuilder().withName("Alice Bob").build())); + } + + @Test + public void test_nameDoesNotContainKeywords_returnsFalse() { + // Zero keywords + AppointmentContainsNamePredicate predicate = new AppointmentContainsNamePredicate(Collections.emptyList()); + assertFalse(predicate.test(new AppointmentBuilder().withName("Alice").build())); + + // Non-matching keyword + predicate = new AppointmentContainsNamePredicate(Arrays.asList("Carol")); + assertFalse(predicate.test(new AppointmentBuilder().withName("Alice Bob").build())); + + // Keywords match datetime and description, but does not match name + predicate = new AppointmentContainsNamePredicate(Arrays.asList("2023-12-31", "15:30", "16:30", "description")); + assertFalse(predicate.test(new AppointmentBuilder().withName("Alice").withDate("2023-12-31") + .withStartTime("15:30").withEndTime("16:30").withDescription("description").build())); + } +} diff --git a/src/test/java/seedu/address/model/appointment/AppointmentTest.java b/src/test/java/seedu/address/model/appointment/AppointmentTest.java new file mode 100644 index 00000000000..bb1fc13b983 --- /dev/null +++ b/src/test/java/seedu/address/model/appointment/AppointmentTest.java @@ -0,0 +1,100 @@ +package seedu.address.model.appointment; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DESCRIPTION_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_END_TIME_AMY; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_START_TIME_AMY; +import static seedu.address.testutil.TypicalAppointments.ALICE_APPOINTMENT; +import static seedu.address.testutil.TypicalAppointments.ALICE_SECOND_APPOINTMENT; +import static seedu.address.testutil.TypicalAppointments.BENNY_APPOINTMENT; +import static seedu.address.testutil.TypicalAppointments.DAVID_APPOINTMENT; +import static seedu.address.testutil.TypicalAppointments.JOHN_APPOINTMENT; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.AppointmentBuilder; + +public class AppointmentTest { + + @Test + public void isSameAppointment() { + // same object -> returns true + assertTrue(ALICE_APPOINTMENT.isSameAppointment(ALICE_APPOINTMENT)); + + // null -> returns false + assertFalse(ALICE_APPOINTMENT.isSameAppointment(null)); + } + + @Test + public void equals() { + // same values -> returns true + Appointment alexAppointmentCopy = new AppointmentBuilder(ALICE_APPOINTMENT).build(); + assertTrue(ALICE_APPOINTMENT.equals(alexAppointmentCopy)); + + // same object -> returns true + assertTrue(ALICE_APPOINTMENT.equals(ALICE_APPOINTMENT)); + + // null -> returns false + assertFalse(ALICE_APPOINTMENT.equals(null)); + + // different type -> returns false + assertFalse(ALICE_APPOINTMENT.equals(5)); + + // different appointment -> returns false + assertFalse(ALICE_APPOINTMENT.equals(ALICE_SECOND_APPOINTMENT)); + assertFalse(ALICE_APPOINTMENT.equals(BENNY_APPOINTMENT)); + + // different name -> returns false + Appointment editedAlexAppointment = new AppointmentBuilder(ALICE_APPOINTMENT).withName(VALID_NAME_BOB).build(); + assertFalse(ALICE_APPOINTMENT.equals(editedAlexAppointment)); + + // different description -> returns false + editedAlexAppointment = new AppointmentBuilder(ALICE_APPOINTMENT) + .withDescription(VALID_DESCRIPTION_BOB).build(); + assertFalse(ALICE_APPOINTMENT.equals(editedAlexAppointment)); + + // different startTime and endTime-> returns false + editedAlexAppointment = new AppointmentBuilder(ALICE_APPOINTMENT).withStartTime(VALID_START_TIME_AMY) + .withEndTime(VALID_END_TIME_AMY).build(); + assertFalse(ALICE_APPOINTMENT.equals(editedAlexAppointment)); + + // different date -> returns false + editedAlexAppointment = new AppointmentBuilder(ALICE_APPOINTMENT).withDate(VALID_DATE_BOB).build(); + assertFalse(ALICE_APPOINTMENT.equals(editedAlexAppointment)); + + } + + @Test + public void toStringMethod() { + String expected = Appointment.class.getCanonicalName() + + "{student=" + ALICE_APPOINTMENT.getName() + "," + + " description=" + ALICE_APPOINTMENT.getDescription() + "," + + " date=" + ALICE_APPOINTMENT.getDate() + "," + + " startTime=" + ALICE_APPOINTMENT.getStartTime() + "," + + " endTime=" + ALICE_APPOINTMENT.getEndTime() + "}"; + assertEquals(expected, ALICE_APPOINTMENT.toString()); + } + + @Test + public void compareToMethod() { + // ALEX_APPOINTMENT on "2023-10-31" from "12:00" to "13:00" + // ALEX_SECOND_APPOINTMENT on "2023-11-16" from "14:00" to "15:00" + + // Different Dates, time don't matter + assertEquals(ALICE_APPOINTMENT.compareTo(ALICE_SECOND_APPOINTMENT), -1); + assertEquals(ALICE_APPOINTMENT.compareTo(ALICE_APPOINTMENT), 0); + assertEquals(ALICE_SECOND_APPOINTMENT.compareTo(ALICE_APPOINTMENT), 1); + + // Same Date, compare time + // DAVID_APPOINTMENT on "2023-10-31" from "11:00" to "12:00" + // ALEX_APPOINTMENT on "2023-10-31" from "12:00" to "13:00" + // JOHN_APPOINTMENT on "2023-10-31" from "13:00" to "14:00" + assertEquals(DAVID_APPOINTMENT.compareTo(ALICE_APPOINTMENT), -1); + assertEquals(JOHN_APPOINTMENT.compareTo(JOHN_APPOINTMENT), 0); + assertEquals(JOHN_APPOINTMENT.compareTo(ALICE_APPOINTMENT), 1); + } +} diff --git a/src/test/java/seedu/address/model/appointment/DateTest.java b/src/test/java/seedu/address/model/appointment/DateTest.java new file mode 100644 index 00000000000..1e52e6ff460 --- /dev/null +++ b/src/test/java/seedu/address/model/appointment/DateTest.java @@ -0,0 +1,84 @@ +package seedu.address.model.appointment; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class DateTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Date(null)); + } + + @Test + public void constructor_invalidDate_throwsIllegalArgumentException() { + String invalidDate = ""; + assertThrows(IllegalArgumentException.class, () -> new Date(invalidDate)); + } + + @Test + public void isValidDate() { + // valid Date + // EP: follows yyyy-MM-dd format + assertTrue(Date.isValidDate("2023-10-14")); + assertTrue(Date.isValidDate("2023-11-14")); + assertTrue(Date.isValidDate("2023-10-31")); + + // invalid Date: does not follow yyyy-MM-dd format + // EP: extra characters + assertFalse(Date.isValidDate("2023-10-14 15:30")); // includes time + assertFalse(Date.isValidDate("2023-10-14 15:")); // incomplete + assertFalse(Date.isValidDate("2023-10-14 15:301")); // extra digit + + // EP: too little characters + assertFalse(Date.isValidDate("15:30")); // only time, no date + + // EP: non-numerical characters + assertFalse(Date.isValidDate("2023-10-14 15:AM")); // contains non-digit + + // EP: not using dashes + assertFalse(Date.isValidDate("2023:10:14")); // contains ":" + assertFalse(Date.isValidDate("2023/10/14")); // contains "/" + + // invalid Date: out of bounds date and month + + // EP: out of bounds for date and month + assertFalse(Date.isValidDate("2023-13-14")); // month 13 + assertFalse(Date.isValidDate("2023-10-32")); // day 32 + assertFalse(Date.isValidDate("1999-10-14")); // year 1999, in the past + assertFalse(Date.isValidDate("8000-10-14")); // year 8000, too far in the future, must be within a year + + + // February special cases + + // EP: february max 28 days on non-leap year + assertFalse(Date.isValidDate("2023-02-29")); // non-leap year + assertTrue(Date.isValidDate("2023-02-28")); + + // EP: february 29th day on leap year + assertTrue(Date.isValidDate("2024-02-29")); + } + + @Test + public void equals() { + Date date = new Date("2023-10-14"); + + // same values -> returns true + assertTrue(date.equals(new Date("2023-10-14"))); + + // same object -> returns true + assertTrue(date.equals(date)); + + // null -> returns false + assertFalse(date.equals(null)); + + // different types -> returns false + assertFalse(date.equals(5.0f)); + + // different values -> returns false + assertFalse(date.equals(new Date("2023-10-15"))); + } +} diff --git a/src/test/java/seedu/address/model/appointment/DescriptionTest.java b/src/test/java/seedu/address/model/appointment/DescriptionTest.java new file mode 100644 index 00000000000..10aa4ea910e --- /dev/null +++ b/src/test/java/seedu/address/model/appointment/DescriptionTest.java @@ -0,0 +1,66 @@ +package seedu.address.model.appointment; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class DescriptionTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Description(null)); + } + + @Test + public void constructor_invalidDescription_throwsIllegalArgumentException() { + String invalidDescription = ""; + assertThrows(IllegalArgumentException.class, () -> new Description(invalidDescription)); + } + + @Test + public void isValidDescription() { + String hundredCharDescription = + "2jT#L8p!@o9QYdG*cZr$uAqXtW%vI3hN6fE5bJ1mKzV4nSsD7iU0lFyRwC2jT#L8p!@o9QYdG*cZr$uAqXtW%vI3hN6fE5bJ1mKz"; + + // EP: null description + assertThrows(NullPointerException.class, () -> new Description(null)); + + // invalid descriptions + // EP: empty string + assertFalse(Description.isValidDescription("")); // empty string + assertFalse(Description.isValidDescription(" ")); // empty string + + // EP: more than 100 characters + assertFalse(Description.isValidDescription(hundredCharDescription + "a")); // 101 characters + + // valid descriptions + // EP: 1 character + assertTrue(Description.isValidDescription("a")); // exactly 1 character + + // EP: 100 characters, including special characters + assertTrue(Description.isValidDescription(hundredCharDescription)); // exactly 100 characters + } + + @Test + public void equals() { + Description description = new Description("test"); + + // same values -> returns true + assertTrue(description.equals(new Description("test"))); + + // same object -> returns true + assertTrue(description.equals(description)); + + // null -> returns false + assertFalse(description.equals(null)); + + // different types -> returns false + assertFalse(description.equals(5.0f)); + + // different values -> returns false + assertFalse(description.equals(new Description("test2"))); + } + +} diff --git a/src/test/java/seedu/address/model/appointment/TimeTest.java b/src/test/java/seedu/address/model/appointment/TimeTest.java new file mode 100644 index 00000000000..978d0fc903e --- /dev/null +++ b/src/test/java/seedu/address/model/appointment/TimeTest.java @@ -0,0 +1,68 @@ +package seedu.address.model.appointment; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class TimeTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Time(null)); + } + + @Test + public void constructor_invalidTime_throwsIllegalArgumentException() { + String invalidTime = ""; + assertThrows(IllegalArgumentException.class, () -> new Time(invalidTime)); + } + + @Test + public void isValidTime() { + // valid Time + assertTrue(Time.isValidTime("15:30")); + assertTrue(Time.isValidTime("12:10")); + assertTrue(Time.isValidTime("00:00")); + + // invalid Time: does not follow HH:mm format + // EP: extra characters + assertFalse(Time.isValidTime("2023-10-14 15:30")); // includes date + assertFalse(Time.isValidTime("15:301")); // extra digit + + // EP: too little characters + assertFalse(Time.isValidTime("15:")); // incomplete + + // EP: non-numerical characters + assertFalse(Time.isValidTime("15:AM")); // contains alphabetical characters + assertFalse(Time.isValidTime("15:##")); // contains special characters + + // EP: outside of 00:00 to 24:00 + assertFalse(Time.isValidTime("25:00")); + assertFalse(Time.isValidTime("24:01")); + + // EP: outside of xx:00 to xx:59 + assertFalse(Time.isValidTime("15:60")); // 60 minutes + } + + @Test + public void equals() { + Time time = new Time("15:30"); + + // same values -> returns true + assertTrue(time.equals(new Time("15:30"))); + + // same object -> returns true + assertTrue(time.equals(time)); + + // null -> returns false + assertFalse(time.equals(null)); + + // different types -> returns false + assertFalse(time.equals(5.0f)); + + // different values -> returns false + assertFalse(time.equals(new Time("12:50"))); + } +} diff --git a/src/test/java/seedu/address/model/appointment/UniqueAppointmentListTest.java b/src/test/java/seedu/address/model/appointment/UniqueAppointmentListTest.java new file mode 100644 index 00000000000..0285e66e28f --- /dev/null +++ b/src/test/java/seedu/address/model/appointment/UniqueAppointmentListTest.java @@ -0,0 +1,169 @@ +package seedu.address.model.appointment; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalAppointments.ALICE_APPOINTMENT; +import static seedu.address.testutil.TypicalAppointments.ALICE_SECOND_APPOINTMENT; +import static seedu.address.testutil.TypicalAppointments.APPOINTMENT_1; +import static seedu.address.testutil.TypicalAppointments.APPOINTMENT_2; +import static seedu.address.testutil.TypicalAppointments.APPOINTMENT_3; +import static seedu.address.testutil.TypicalAppointments.APPOINTMENT_4; +import static seedu.address.testutil.TypicalAppointments.APPOINTMENT_5; + +import java.util.Iterator; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.appointment.exceptions.AppointmentNotFoundException; +import seedu.address.model.appointment.exceptions.DuplicateAppointmentException; +import seedu.address.testutil.AppointmentBuilder; + +public class UniqueAppointmentListTest { + + private final UniqueAppointmentList uniqueAppointmentList = new UniqueAppointmentList(); + + // Test for contains method + + // null object + @Test + public void hasAppointment_nullAppointment_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueAppointmentList.hasAppointment(null)); + } + + // appointment not in list + @Test + public void hasAppointment_appointmentNotInList_returnsFalse() { + assertFalse(uniqueAppointmentList.hasAppointment(ALICE_APPOINTMENT)); + } + + @Test + public void hasAppointment_appointmentInList_returnsTrue() { + uniqueAppointmentList.add(ALICE_APPOINTMENT); + assertTrue(uniqueAppointmentList.hasAppointment(ALICE_APPOINTMENT)); + } + + @Test + public void hasAppointment_appointmentWithSameFieldsInList_returnsTrue() { + uniqueAppointmentList.add(ALICE_APPOINTMENT); + Appointment editedAlex = new AppointmentBuilder().withName("Alice Pauline") + .withDate("2023-10-31").withStartTime("12:00").withEndTime("13:00") + .withDescription("First Session").build(); + assertTrue(uniqueAppointmentList.hasAppointment(editedAlex)); + } + + // Tests for add method + + @Test + public void add_nullAppointment_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueAppointmentList.add(null)); + } + + @Test + public void add_duplicateAppointment_throwsDuplicateAppointmentException() { + uniqueAppointmentList.add(ALICE_APPOINTMENT); + assertThrows(DuplicateAppointmentException.class, () -> uniqueAppointmentList.add(ALICE_APPOINTMENT)); + } + + // Tests for remove method + + @Test + public void remove_nullAppointment_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueAppointmentList.remove(null)); + } + + @Test + public void remove_appointmentDoesNotExist_throwsAppointmentNotFoundException() { + assertThrows(AppointmentNotFoundException.class, () -> uniqueAppointmentList.remove(ALICE_APPOINTMENT)); + } + + @Test + public void remove_existingAppointment_removesAppointment() { + uniqueAppointmentList.add(ALICE_APPOINTMENT); + uniqueAppointmentList.remove(ALICE_APPOINTMENT); + UniqueAppointmentList expectedUniqueAppointmentList = new UniqueAppointmentList(); + assertEquals(expectedUniqueAppointmentList, uniqueAppointmentList); + } + + // Tests for removeRelatedAppointments method + @Test + public void removeRelatedAppointments_existingAppointment_removesAppointment() { + uniqueAppointmentList.add(ALICE_APPOINTMENT); + uniqueAppointmentList.removeRelatedAppointments(ALICE_APPOINTMENT.getName()); + UniqueAppointmentList expectedUniqueAppointmentList = new UniqueAppointmentList(); + assertEquals(expectedUniqueAppointmentList, uniqueAppointmentList); + } + + @Test + public void removeRelatedAppointments_existingAppointment_removesAllAppointmentsWithSameName() { + uniqueAppointmentList.add(ALICE_APPOINTMENT); + uniqueAppointmentList.add(ALICE_SECOND_APPOINTMENT); + uniqueAppointmentList.removeRelatedAppointments(ALICE_APPOINTMENT.getName()); + UniqueAppointmentList expectedUniqueAppointmentList = new UniqueAppointmentList(); + assertEquals(expectedUniqueAppointmentList, uniqueAppointmentList); + } + + // UnmodifiableObservableList cannot be modified + @Test + public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () + -> uniqueAppointmentList.asUnmodifiableObservableList().remove(0)); + } + + @Test + public void toStringMethod() { + assertEquals(uniqueAppointmentList.asUnmodifiableObservableList().toString(), + uniqueAppointmentList.toString()); + } + + @Test + public void sortAppointmentsAfterAddition() { + // APPOINTMENT_1 on "2023-11-01" from "09:00" to "10:00" + // APPOINTMENT_2 on "2023-11-02" from "10:00" to "11:00" + // APPOINTMENT_3 on "2023-11-03" from "09:00" to "10:00" + // APPOINTMENT_4 on "2023-11-03" from "10:00" to "11:00" + // APPOINTMENT_5 on "2023-11-04" from "09:00" to "10:00" + + UniqueAppointmentList uniqueAppointmentList = new UniqueAppointmentList(); + + uniqueAppointmentList.add(APPOINTMENT_5); + uniqueAppointmentList.add(APPOINTMENT_4); + uniqueAppointmentList.add(APPOINTMENT_3); + uniqueAppointmentList.add(APPOINTMENT_2); + uniqueAppointmentList.add(APPOINTMENT_1); + + Iterator iterator = uniqueAppointmentList.iterator(); + + // Check if appointments are sorted correctly after addition + assertEquals(APPOINTMENT_1, iterator.next()); + assertEquals(APPOINTMENT_2, iterator.next()); + assertEquals(APPOINTMENT_3, iterator.next()); + assertEquals(APPOINTMENT_4, iterator.next()); + assertEquals(APPOINTMENT_5, iterator.next()); + } + + @Test + public void sortAppointmentsAfterDeletion() { + UniqueAppointmentList uniqueAppointmentList = new UniqueAppointmentList(); + + uniqueAppointmentList.add(APPOINTMENT_5); + uniqueAppointmentList.add(APPOINTMENT_4); + uniqueAppointmentList.add(APPOINTMENT_3); + uniqueAppointmentList.add(APPOINTMENT_2); + uniqueAppointmentList.add(APPOINTMENT_1); + + uniqueAppointmentList.remove(APPOINTMENT_3); + + Iterator iterator = uniqueAppointmentList.iterator(); + + // Check if appointments are sorted correctly after deletion + assertEquals(APPOINTMENT_1, iterator.next()); + assertEquals(APPOINTMENT_2, iterator.next()); + assertEquals(APPOINTMENT_4, iterator.next()); + assertEquals(APPOINTMENT_5, iterator.next()); + + // Ensure that the removed appointment is no longer in the list + assertFalse(uniqueAppointmentList.hasAppointment(APPOINTMENT_3)); + } +} diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/person/EmailTest.java deleted file mode 100644 index f08cdff0a64..00000000000 --- a/src/test/java/seedu/address/model/person/EmailTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class EmailTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Email(null)); - } - - @Test - public void constructor_invalidEmail_throwsIllegalArgumentException() { - String invalidEmail = ""; - assertThrows(IllegalArgumentException.class, () -> new Email(invalidEmail)); - } - - @Test - public void isValidEmail() { - // null email - assertThrows(NullPointerException.class, () -> Email.isValidEmail(null)); - - // blank email - assertFalse(Email.isValidEmail("")); // empty string - assertFalse(Email.isValidEmail(" ")); // spaces only - - // missing parts - assertFalse(Email.isValidEmail("@example.com")); // missing local part - assertFalse(Email.isValidEmail("peterjackexample.com")); // missing '@' symbol - assertFalse(Email.isValidEmail("peterjack@")); // missing domain name - - // invalid parts - assertFalse(Email.isValidEmail("peterjack@-")); // invalid domain name - assertFalse(Email.isValidEmail("peterjack@exam_ple.com")); // underscore in domain name - assertFalse(Email.isValidEmail("peter jack@example.com")); // spaces in local part - assertFalse(Email.isValidEmail("peterjack@exam ple.com")); // spaces in domain name - assertFalse(Email.isValidEmail(" peterjack@example.com")); // leading space - assertFalse(Email.isValidEmail("peterjack@example.com ")); // trailing space - assertFalse(Email.isValidEmail("peterjack@@example.com")); // double '@' symbol - assertFalse(Email.isValidEmail("peter@jack@example.com")); // '@' symbol in local part - assertFalse(Email.isValidEmail("-peterjack@example.com")); // local part starts with a hyphen - assertFalse(Email.isValidEmail("peterjack-@example.com")); // local part ends with a hyphen - assertFalse(Email.isValidEmail("peter..jack@example.com")); // local part has two consecutive periods - assertFalse(Email.isValidEmail("peterjack@example@com")); // '@' symbol in domain name - assertFalse(Email.isValidEmail("peterjack@.example.com")); // domain name starts with a period - assertFalse(Email.isValidEmail("peterjack@example.com.")); // domain name ends with a period - assertFalse(Email.isValidEmail("peterjack@-example.com")); // domain name starts with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.com-")); // domain name ends with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.c")); // top level domain has less than two chars - - // valid email - assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); // underscore in local part - assertTrue(Email.isValidEmail("PeterJack.1190@example.com")); // period in local part - assertTrue(Email.isValidEmail("PeterJack+1190@example.com")); // '+' symbol in local part - assertTrue(Email.isValidEmail("PeterJack-1190@example.com")); // hyphen in local part - assertTrue(Email.isValidEmail("a@bc")); // minimal - assertTrue(Email.isValidEmail("test@localhost")); // alphabets only - assertTrue(Email.isValidEmail("123@145")); // numeric local part and domain name - assertTrue(Email.isValidEmail("a1+be.d@example1.com")); // mixture of alphanumeric and special characters - assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain name - assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local part - assertTrue(Email.isValidEmail("e1234567@u.nus.edu")); // more than one period in domain - } - - @Test - public void equals() { - Email email = new Email("valid@email"); - - // same values -> returns true - assertTrue(email.equals(new Email("valid@email"))); - - // same object -> returns true - assertTrue(email.equals(email)); - - // null -> returns false - assertFalse(email.equals(null)); - - // different types -> returns false - assertFalse(email.equals(5.0f)); - - // different values -> returns false - assertFalse(email.equals(new Email("other.valid@email"))); - } -} diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java deleted file mode 100644 index 31a10d156c9..00000000000 --- a/src/test/java/seedu/address/model/person/PersonTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class PersonTest { - - @Test - public void asObservableList_modifyList_throwsUnsupportedOperationException() { - Person person = new PersonBuilder().build(); - assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0)); - } - - @Test - public void isSamePerson() { - // same object -> returns true - assertTrue(ALICE.isSamePerson(ALICE)); - - // null -> returns false - assertFalse(ALICE.isSamePerson(null)); - - // same name, all other attributes different -> returns true - Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); - assertTrue(ALICE.isSamePerson(editedAlice)); - - // different name, all other attributes same -> returns false - editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.isSamePerson(editedAlice)); - - // name differs in case, all other attributes same -> returns false - Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build(); - assertFalse(BOB.isSamePerson(editedBob)); - - // name has trailing spaces, all other attributes same -> returns false - String nameWithTrailingSpaces = VALID_NAME_BOB + " "; - editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build(); - assertFalse(BOB.isSamePerson(editedBob)); - } - - @Test - public void equals() { - // same values -> returns true - Person aliceCopy = new PersonBuilder(ALICE).build(); - assertTrue(ALICE.equals(aliceCopy)); - - // same object -> returns true - assertTrue(ALICE.equals(ALICE)); - - // null -> returns false - assertFalse(ALICE.equals(null)); - - // different type -> returns false - assertFalse(ALICE.equals(5)); - - // different person -> returns false - assertFalse(ALICE.equals(BOB)); - - // different name -> returns false - Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different phone -> returns false - editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different email -> returns false - editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different address -> returns false - editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different tags -> returns false - editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(ALICE.equals(editedAlice)); - } - - @Test - public void toStringMethod() { - String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone() - + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", tags=" + ALICE.getTags() + "}"; - assertEquals(expected, ALICE.toString()); - } -} diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java deleted file mode 100644 index 17ae501df08..00000000000 --- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java +++ /dev/null @@ -1,175 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; -import seedu.address.testutil.PersonBuilder; - -public class UniquePersonListTest { - - private final UniquePersonList uniquePersonList = new UniquePersonList(); - - @Test - public void contains_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null)); - } - - @Test - public void contains_personNotInList_returnsFalse() { - assertFalse(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personInList_returnsTrue() { - uniquePersonList.add(ALICE); - assertTrue(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personWithSameIdentityFieldsInList_returnsTrue() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(uniquePersonList.contains(editedAlice)); - } - - @Test - public void add_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.add(null)); - } - - @Test - public void add_duplicatePerson_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE)); - } - - @Test - public void setPerson_nullTargetPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE)); - } - - @Test - public void setPerson_nullEditedPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null)); - } - - @Test - public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); - } - - @Test - public void setPerson_editedPersonIsSamePerson_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(ALICE); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasSameIdentity_success() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - uniquePersonList.setPerson(ALICE, editedAlice); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(editedAlice); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasDifferentIdentity_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, BOB); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - uniquePersonList.add(BOB); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); - } - - @Test - public void remove_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null)); - } - - @Test - public void remove_personDoesNotExist_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE)); - } - - @Test - public void remove_existingPerson_removesPerson() { - uniquePersonList.add(ALICE); - uniquePersonList.remove(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullUniquePersonList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null)); - } - - @Test - public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() { - uniquePersonList.add(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - uniquePersonList.setPersons(expectedUniquePersonList); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null)); - } - - @Test - public void setPersons_list_replacesOwnListWithProvidedList() { - uniquePersonList.add(ALICE); - List personList = Collections.singletonList(BOB); - uniquePersonList.setPersons(personList); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { - List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons)); - } - - @Test - public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () - -> uniquePersonList.asUnmodifiableObservableList().remove(0)); - } - - @Test - public void toStringMethod() { - assertEquals(uniquePersonList.asUnmodifiableObservableList().toString(), uniquePersonList.toString()); - } -} diff --git a/src/test/java/seedu/address/model/risklevel/RiskLevelTest.java b/src/test/java/seedu/address/model/risklevel/RiskLevelTest.java new file mode 100644 index 00000000000..6a08537e412 --- /dev/null +++ b/src/test/java/seedu/address/model/risklevel/RiskLevelTest.java @@ -0,0 +1,62 @@ +package seedu.address.model.risklevel; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class RiskLevelTest { + + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new RiskLevel(null)); + } + + @Test + public void constructor_invalidTagName_throwsIllegalArgumentException() { + // EP: empty strings + assertThrows(IllegalArgumentException.class, () -> new RiskLevel("")); + assertThrows(IllegalArgumentException.class, () -> new RiskLevel(" ")); + + // EP: not valid string + assertThrows(IllegalArgumentException.class, () -> new RiskLevel("oogabooga")); + } + + @Test + public void isValidRiskLevel() { + // null tag name + assertThrows(NullPointerException.class, () -> RiskLevel.isValidRiskLevel(null)); + + // EP: empty strings + assertFalse(RiskLevel.isValidRiskLevel("")); + assertFalse(RiskLevel.isValidRiskLevel(" ")); + + // EP: not valid string + assertFalse(RiskLevel.isValidRiskLevel("heehaa")); + + // EP: Invalid due to upper case + assertFalse(RiskLevel.isValidRiskLevel("HIGH")); + assertFalse(RiskLevel.isValidRiskLevel("meDium")); + assertFalse(RiskLevel.isValidRiskLevel("LoW")); + + // EP: Valid strings + assertTrue(RiskLevel.isValidRiskLevel("high")); + assertTrue(RiskLevel.isValidRiskLevel("medium")); + assertTrue(RiskLevel.isValidRiskLevel("low")); + } + + @Test + public void equals_sameObject_returnsTrue() { + RiskLevel riskLevel = new RiskLevel("high"); + assertEquals(true, riskLevel.equals(riskLevel)); + } + + @Test + public void equals_withNull_returnsFalse() { + RiskLevel riskLevel = new RiskLevel("high"); + assertEquals(false, riskLevel.equals(null)); + } +} diff --git a/src/test/java/seedu/address/model/risklevel/exceptions/ExceedMaxRiskLevelSizeExceptionTest.java b/src/test/java/seedu/address/model/risklevel/exceptions/ExceedMaxRiskLevelSizeExceptionTest.java new file mode 100644 index 00000000000..703cadfbcc3 --- /dev/null +++ b/src/test/java/seedu/address/model/risklevel/exceptions/ExceedMaxRiskLevelSizeExceptionTest.java @@ -0,0 +1,16 @@ +package seedu.address.model.risklevel.exceptions; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class ExceedMaxRiskLevelSizeExceptionTest { + + @Test + public void constructor_validInput_returnsCorrectMessage() { + int maxSize = 10; + ExceedMaxRiskLevelSizeException exception = new ExceedMaxRiskLevelSizeException(maxSize); + + assertEquals("number of tags cannot exceed " + maxSize + "!", exception.getMessage()); + } +} diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/student/AddressTest.java similarity index 62% rename from src/test/java/seedu/address/model/person/AddressTest.java rename to src/test/java/seedu/address/model/student/AddressTest.java index 314885eca26..8cc029720f6 100644 --- a/src/test/java/seedu/address/model/person/AddressTest.java +++ b/src/test/java/seedu/address/model/student/AddressTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.student; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -21,17 +21,33 @@ public void constructor_invalidAddress_throwsIllegalArgumentException() { @Test public void isValidAddress() { - // null address + String twoHundredCharAddress = "Krung Thep Mahanakhon Amon Rattanakosin Mahinthara Ayuthaya Mahadilok Phop " + + "Noppharat Ratchathani Burirom Udomratchaniwet Mahasathan Amon Piman Awatan Sathit Sakkathattiya " + + "Witsanukam Prasit aka Bangkok"; + + + // Invalid addresses + + // EP: null address assertThrows(NullPointerException.class, () -> Address.isValidAddress(null)); - // invalid addresses + + // EP: empty strings (0 characters or blank space) assertFalse(Address.isValidAddress("")); // empty string - assertFalse(Address.isValidAddress(" ")); // spaces only + assertFalse(Address.isValidAddress(" ")); // blank space + + // EP: exceed 200 characters + assertFalse(Address.isValidAddress(twoHundredCharAddress + "a")); // 201 characters + + + // Valid addresses + + // EP: exactly one character + assertTrue(Address.isValidAddress("-")); + + // EP: exactly 200 characters + assertTrue(Address.isValidAddress(twoHundredCharAddress)); - // valid addresses - assertTrue(Address.isValidAddress("Blk 456, Den Road, #01-355")); - assertTrue(Address.isValidAddress("-")); // one character - assertTrue(Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long address } @Test diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/student/NameContainsKeywordsPredicateTest.java similarity index 71% rename from src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java rename to src/test/java/seedu/address/model/student/NameContainsKeywordsPredicateTest.java index 6b3fd90ade7..1b121542de1 100644 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ b/src/test/java/seedu/address/model/student/NameContainsKeywordsPredicateTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.student; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.PersonBuilder; +import seedu.address.testutil.StudentBuilder; public class NameContainsKeywordsPredicateTest { @@ -35,43 +35,43 @@ public void equals() { // null -> returns false assertFalse(firstPredicate.equals(null)); - // different person -> returns false + // different student -> returns false assertFalse(firstPredicate.equals(secondPredicate)); } @Test public void test_nameContainsKeywords_returnsTrue() { - // One keyword + // Matching first name NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + assertTrue(predicate.test(new StudentBuilder().withName("Alice Bob").build())); - // Multiple keywords + // Matching full Name predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + assertTrue(predicate.test(new StudentBuilder().withName("Alice Bob").build())); - // Only one matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build())); + // Matching last name + predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); + assertTrue(predicate.test(new StudentBuilder().withName("Alice Carol").build())); // Mixed-case keywords predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + assertTrue(predicate.test(new StudentBuilder().withName("Alice Bob").build())); } @Test public void test_nameDoesNotContainKeywords_returnsFalse() { // Zero keywords NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.emptyList()); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").build())); + assertFalse(predicate.test(new StudentBuilder().withName("Alice").build())); // Non-matching keyword predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice Bob").build())); + assertFalse(predicate.test(new StudentBuilder().withName("Alice Bob").build())); - // Keywords match phone, email and address, but does not match name - predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street")); - assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345") - .withEmail("alice@email.com").withAddress("Main Street").build())); + // Keywords match phone and address, but does not match name + predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345678", "Main", "Street")); + assertFalse(predicate.test(new StudentBuilder().withName("Alice").withPhone("12345678") + .withAddress("Main Street").build())); } @Test diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/student/NameTest.java similarity index 56% rename from src/test/java/seedu/address/model/person/NameTest.java rename to src/test/java/seedu/address/model/student/NameTest.java index 94e3dd726bd..f304ca5ee03 100644 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ b/src/test/java/seedu/address/model/student/NameTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.student; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -21,21 +21,36 @@ public void constructor_invalidName_throwsIllegalArgumentException() { @Test public void isValidName() { - // null name + String hundredCharName = "Ali Ben Ching Dover Elephant Fishballs Ginormous Hugh Indiana Jelly Krispy " + + "Lambasted Mamamia Nutella"; + + // EP: null name assertThrows(NullPointerException.class, () -> Name.isValidName(null)); - // invalid name + // invalid names + // EP: empty strings assertFalse(Name.isValidName("")); // empty string - assertFalse(Name.isValidName(" ")); // spaces only - assertFalse(Name.isValidName("^")); // only non-alphanumeric characters - assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters - - // valid name - assertTrue(Name.isValidName("peter jack")); // alphabets only - assertTrue(Name.isValidName("12345")); // numbers only - assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters - assertTrue(Name.isValidName("Capital Tan")); // with capital letters - assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names + assertFalse(Name.isValidName(" ")); // spaces only + + // EP : non-alphabetical characters + assertFalse(Name.isValidName("^")); // only non-alphabetical characters + assertFalse(Name.isValidName("peter*")); // contains non-alphabetical characters + assertFalse(Name.isValidName("12345")); // numbers only + assertFalse(Name.isValidName("peter the 2nd")); // contains numbers + + // EP: more than 100 characters + assertFalse(Name.isValidName(hundredCharName + "a")); // 101 characters + + + // valid names + // EP: one character alphabet only + assertTrue(Name.isValidName("A")); + + // EP: Alphabetical characters with capital letters + assertTrue(Name.isValidName("Capital Tan")); + + // EP: 100 alphabetical characters + assertTrue(Name.isValidName(hundredCharName)); } @Test diff --git a/src/test/java/seedu/address/model/student/NoteTest.java b/src/test/java/seedu/address/model/student/NoteTest.java new file mode 100644 index 00000000000..4e8cb261ea5 --- /dev/null +++ b/src/test/java/seedu/address/model/student/NoteTest.java @@ -0,0 +1,79 @@ +package seedu.address.model.student; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +public class NoteTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Note(null)); + } + + @Test + public void constructor_invalidNote_throwsIllegalArgumentException() { + String invalidNote = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy" + + "zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopq" + + "rstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi" + + "jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyza" + + "bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrs" + + "tuvwxyzabcdefghijklmnopqrstuvwxyzabcdefa"; + assertThrows(IllegalArgumentException.class, () -> new Note(invalidNote)); + } + + @Test + public void isValidNote() { + String fiveHundredCharNote = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy" + + "zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopq" + + "rstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi" + + "jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyza" + + "bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrs" + + "tuvwxyzabcdefghijklmnopqrstuvwxyzabcdef"; + + // EP: null note + assertThrows(NullPointerException.class, () -> Note.isValidNote(null)); + + // invalid notes + // EP: more than 500 characters + assertFalse(Note.isValidNote(fiveHundredCharNote + "a")); //501 characters + + // valid notes + // EP: 0 characters or empty strings + assertTrue(Note.isValidNote("")); // empty string + assertTrue(Note.isValidNote(" ")); // blank space + + // EP: Exactly 1 character + assertTrue(Note.isValidNote("a")); // 1 character + + // EP: Exactly 500 characters + assertTrue(Note.isValidNote(fiveHundredCharNote)); // exactly 500 characters + + // EP: With special characters + assertTrue(Note.isValidNote("The 3rd session was very ****fire****")); + } + + @Test + public void equals() { + Note note = new Note("Hello"); + + // same object -> returns true + assertTrue(note.equals(note)); + + // same values -> returns true + Note noteCopy = new Note(note.value); + assertTrue(note.equals(noteCopy)); + + // different types -> returns false + assertFalse(note.equals(1)); + + // null -> returns false + assertFalse(note.equals(null)); + + // different note -> returns false + Note differentNote = new Note("Bye"); + assertFalse(note.equals(differentNote)); + } +} diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/student/PhoneTest.java similarity index 61% rename from src/test/java/seedu/address/model/person/PhoneTest.java rename to src/test/java/seedu/address/model/student/PhoneTest.java index deaaa5ba190..b4266418750 100644 --- a/src/test/java/seedu/address/model/person/PhoneTest.java +++ b/src/test/java/seedu/address/model/student/PhoneTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.student; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -21,29 +21,40 @@ public void constructor_invalidPhone_throwsIllegalArgumentException() { @Test public void isValidPhone() { - // null phone number + // EP: null phone number assertThrows(NullPointerException.class, () -> Phone.isValidPhone(null)); // invalid phone numbers + // EP: empty strings assertFalse(Phone.isValidPhone("")); // empty string assertFalse(Phone.isValidPhone(" ")); // spaces only - assertFalse(Phone.isValidPhone("91")); // less than 3 numbers - assertFalse(Phone.isValidPhone("phone")); // non-numeric - assertFalse(Phone.isValidPhone("9011p041")); // alphabets within digits + + // EP: non-numeric strings + assertFalse(Phone.isValidPhone("Stephens")); // contains alphabetical characters + assertFalse(Phone.isValidPhone("!@#$%^&*")); // contains special characters + + // EP: 7 characters + assertFalse(Phone.isValidPhone("1234567")); // 7 characters exactly + + // EP: 9 characters + assertFalse(Phone.isValidPhone("123456789")); // 9 characters exactly + + // EP: spaces between digits assertFalse(Phone.isValidPhone("9312 1534")); // spaces within digits + // valid phone numbers - assertTrue(Phone.isValidPhone("911")); // exactly 3 numbers - assertTrue(Phone.isValidPhone("93121534")); - assertTrue(Phone.isValidPhone("124293842033123")); // long phone numbers + // EP: 8 characters + assertTrue(Phone.isValidPhone("91141523")); // exactly 8 numbers + assertTrue(Phone.isValidPhone("12345678")); // any 8 } @Test public void equals() { - Phone phone = new Phone("999"); + Phone phone = new Phone("99999999"); // same values -> returns true - assertTrue(phone.equals(new Phone("999"))); + assertTrue(phone.equals(new Phone("99999999"))); // same object -> returns true assertTrue(phone.equals(phone)); @@ -55,6 +66,6 @@ public void equals() { assertFalse(phone.equals(5.0f)); // different values -> returns false - assertFalse(phone.equals(new Phone("995"))); + assertFalse(phone.equals(new Phone("99555599"))); } } diff --git a/src/test/java/seedu/address/model/student/StudentTest.java b/src/test/java/seedu/address/model/student/StudentTest.java new file mode 100644 index 00000000000..e257a4a06b8 --- /dev/null +++ b/src/test/java/seedu/address/model/student/StudentTest.java @@ -0,0 +1,91 @@ +package seedu.address.model.student; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RISK_LEVEL_LOW; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalStudents.ALICE; +import static seedu.address.testutil.TypicalStudents.BOB; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.StudentBuilder; + +public class StudentTest { + + @Test + public void asObservableList_modifyList_throwsUnsupportedOperationException() { + Student student = new StudentBuilder().build(); + assertThrows(UnsupportedOperationException.class, () -> student.getRiskLevel().remove(0)); + } + + @Test + public void isSameStudent() { + // same object -> returns true + assertTrue(ALICE.isSameStudent(ALICE)); + + // null -> returns false + assertFalse(ALICE.isSameStudent(null)); + + // same name, all other attributes different -> returns true + Student editedAlice = new StudentBuilder(ALICE).withPhone(VALID_PHONE_BOB) + .withAddress(VALID_ADDRESS_BOB).withTags(VALID_RISK_LEVEL_LOW).build(); + assertTrue(ALICE.isSameStudent(editedAlice)); + + // different name, all other attributes same -> returns false + editedAlice = new StudentBuilder(ALICE).withName(VALID_NAME_BOB).build(); + assertFalse(ALICE.isSameStudent(editedAlice)); + + // name has trailing spaces, all other attributes same -> returns false + String nameWithTrailingSpaces = VALID_NAME_BOB + " "; + Student editedBob = new StudentBuilder(BOB).withName(nameWithTrailingSpaces).build(); + assertFalse(BOB.isSameStudent(editedBob)); + } + + @Test + public void equals() { + // same values -> returns true + Student aliceCopy = new StudentBuilder(ALICE).build(); + assertTrue(ALICE.equals(aliceCopy)); + + // same object -> returns true + assertTrue(ALICE.equals(ALICE)); + + // null -> returns false + assertFalse(ALICE.equals(null)); + + // different type -> returns false + assertFalse(ALICE.equals(5)); + + // different student -> returns false + assertFalse(ALICE.equals(BOB)); + + // different name -> returns false + Student editedAlice = new StudentBuilder(ALICE).withName(VALID_NAME_BOB).build(); + assertFalse(ALICE.equals(editedAlice)); + + // different phone -> returns false + editedAlice = new StudentBuilder(ALICE).withPhone(VALID_PHONE_BOB).build(); + assertFalse(ALICE.equals(editedAlice)); + + // different address -> returns false + editedAlice = new StudentBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build(); + assertFalse(ALICE.equals(editedAlice)); + + // different tags -> returns false + editedAlice = new StudentBuilder(ALICE).withTags(VALID_RISK_LEVEL_LOW).build(); + assertFalse(ALICE.equals(editedAlice)); + } + + @Test + public void toStringMethod() { + String expected = Student.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone() + + ", address=" + ALICE.getAddress() + ", risk level=" + ALICE.getRiskLevel() + + ", note=" + ALICE.getNote() + "}"; + assertEquals(expected, ALICE.toString()); + } +} diff --git a/src/test/java/seedu/address/model/student/UniqueStudentListTest.java b/src/test/java/seedu/address/model/student/UniqueStudentListTest.java new file mode 100644 index 00000000000..bcd727a9bb4 --- /dev/null +++ b/src/test/java/seedu/address/model/student/UniqueStudentListTest.java @@ -0,0 +1,211 @@ +package seedu.address.model.student; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RISK_LEVEL_LOW; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalStudents.ALICE; +import static seedu.address.testutil.TypicalStudents.BOB; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.student.exceptions.DuplicateStudentException; +import seedu.address.model.student.exceptions.StudentNotFoundException; +import seedu.address.testutil.StudentBuilder; + +public class UniqueStudentListTest { + + private final UniqueStudentList uniqueStudentList = new UniqueStudentList(); + + // Tests for contains method + + // null object + @Test + public void contains_nullStudent_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueStudentList.contains(null)); + } + + // student not in list + @Test + public void contains_studentNotInList_returnsFalse() { + assertFalse(uniqueStudentList.contains(ALICE)); + } + + // exact student in list + @Test + public void contains_studentInList_returnsTrue() { + uniqueStudentList.add(ALICE); + assertTrue(uniqueStudentList.contains(ALICE)); + } + + // student with same name in list + @Test + public void contains_studentWithSameIdentityFieldsInList_returnsTrue() { + uniqueStudentList.add(ALICE); + Student editedAlice = new StudentBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_RISK_LEVEL_LOW) + .build(); + assertTrue(uniqueStudentList.contains(editedAlice)); + } + + + // Tests for add method + // null object + @Test + public void add_nullStudent_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueStudentList.add(null)); + } + + // duplicate student, throws error + @Test + public void add_duplicateStudent_throwsDuplicateStudentException() { + uniqueStudentList.add(ALICE); + assertThrows(DuplicateStudentException.class, () -> uniqueStudentList.add(ALICE)); + } + + // different student name + @Test + public void add_uniqueStudent_success() { + uniqueStudentList.add(ALICE); + Student newStudent = new StudentBuilder(ALICE).withName("Bobby Low").build(); + uniqueStudentList.add(newStudent); + } + + // Tests for setStudent method + // null objects + @Test + public void setStudent_nullTargetStudent_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueStudentList.setStudent(null, ALICE)); + } + + @Test + public void setStudent_nullEditedStudent_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueStudentList.setStudent(ALICE, null)); + } + + // student does not exist (list is currently empty) + @Test + public void setStudent_targetStudentNotInList_throwsStudentNotFoundException() { + assertThrows(StudentNotFoundException.class, () -> uniqueStudentList.setStudent(ALICE, ALICE)); + } + + // student can be found in list -> success + @Test + public void setStudent_editedStudentIsSameStudent_success() { + uniqueStudentList.add(ALICE); + uniqueStudentList.setStudent(ALICE, ALICE); + UniqueStudentList expectedUniqueStudentList = new UniqueStudentList(); + expectedUniqueStudentList.add(ALICE); + assertEquals(expectedUniqueStudentList, uniqueStudentList); + } + + + @Test + public void setStudent_editedStudentHasSameIdentity_success() { + uniqueStudentList.add(ALICE); + Student editedAlice = new StudentBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_RISK_LEVEL_LOW) + .build(); + uniqueStudentList.setStudent(ALICE, editedAlice); + UniqueStudentList expectedUniqueStudentList = new UniqueStudentList(); + expectedUniqueStudentList.add(editedAlice); + assertEquals(expectedUniqueStudentList, uniqueStudentList); + } + + @Test + public void setStudent_editedStudentHasDifferentIdentity_success() { + uniqueStudentList.add(ALICE); + uniqueStudentList.setStudent(ALICE, BOB); + UniqueStudentList expectedUniqueStudentList = new UniqueStudentList(); + expectedUniqueStudentList.add(BOB); + assertEquals(expectedUniqueStudentList, uniqueStudentList); + } + + // Edit student to have same name will not work + @Test + public void setStudent_editedStudentHasNonUniqueIdentity_throwsDuplicateStudentException() { + uniqueStudentList.add(ALICE); + uniqueStudentList.add(BOB); + assertThrows(DuplicateStudentException.class, () -> uniqueStudentList.setStudent(ALICE, BOB)); + } + + + // Tests for remove method + // null object + @Test + public void remove_nullStudent_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueStudentList.remove(null)); + } + + // Student does not exist + @Test + public void remove_studentDoesNotExist_throwsStudentNotFoundException() { + assertThrows(StudentNotFoundException.class, () -> uniqueStudentList.remove(ALICE)); + } + + // Success case + @Test + public void remove_existingStudent_removesStudent() { + uniqueStudentList.add(ALICE); + uniqueStudentList.remove(ALICE); + UniqueStudentList expectedUniqueStudentList = new UniqueStudentList(); + assertEquals(expectedUniqueStudentList, uniqueStudentList); + } + + + // Tests for setStudents method, different from setStudent method + // null objects + @Test + public void setStudents_nullUniqueStudentList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueStudentList.setStudents((UniqueStudentList) null)); + } + + @Test + public void setStudents_nullList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueStudentList.setStudents((List) null)); + } + + // Success cases + @Test + public void setStudents_uniqueStudentList_replacesOwnListWithProvidedUniqueStudentList() { + uniqueStudentList.add(ALICE); + UniqueStudentList expectedUniqueStudentList = new UniqueStudentList(); + expectedUniqueStudentList.add(BOB); + uniqueStudentList.setStudents(expectedUniqueStudentList); + assertEquals(expectedUniqueStudentList, uniqueStudentList); + } + + + @Test + public void setStudents_list_replacesOwnListWithProvidedList() { + uniqueStudentList.add(ALICE); + List studentList = Collections.singletonList(BOB); + uniqueStudentList.setStudents(studentList); + UniqueStudentList expectedUniqueStudentList = new UniqueStudentList(); + expectedUniqueStudentList.add(BOB); + assertEquals(expectedUniqueStudentList, uniqueStudentList); + } + + // Duplicate list -> throws error + @Test + public void setStudents_listWithDuplicateStudents_throwsDuplicateStudentException() { + List listWithDuplicateStudents = Arrays.asList(ALICE, ALICE); + assertThrows(DuplicateStudentException.class, () -> uniqueStudentList.setStudents(listWithDuplicateStudents)); + } + + // UnmodifiableObservableList cannot be modified + @Test + public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () + -> uniqueStudentList.asUnmodifiableObservableList().remove(0)); + } + + @Test + public void toStringMethod() { + assertEquals(uniqueStudentList.asUnmodifiableObservableList().toString(), uniqueStudentList.toString()); + } +} 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)); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedAppointmentTest.java b/src/test/java/seedu/address/storage/JsonAdaptedAppointmentTest.java new file mode 100644 index 00000000000..e9c40ae232e --- /dev/null +++ b/src/test/java/seedu/address/storage/JsonAdaptedAppointmentTest.java @@ -0,0 +1,128 @@ +package seedu.address.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.storage.JsonAdaptedAppointment.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalAppointments.ALICE_APPOINTMENT; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.appointment.Date; +import seedu.address.model.appointment.Description; +import seedu.address.model.appointment.Time; +import seedu.address.model.student.Name; + + +public class JsonAdaptedAppointmentTest { + + private static final String INVALID_NAME = "R@chel"; + private static final String INVALID_DATE = "10-15-2023"; + private static final String INVALID_START_TIME = "24:04"; + private static final String INVALID_END_TIME = "23-01"; + private static final String INVALID_DESCRIPTION = ""; + + private static final String VALID_NAME = ALICE_APPOINTMENT.getName().toString(); + private static final String VALID_DATE = ALICE_APPOINTMENT.getDate().toString(); + private static final String VALID_START_TIME = ALICE_APPOINTMENT.getStartTime().toString(); + private static final String VALID_END_TIME = ALICE_APPOINTMENT.getEndTime().toString(); + private static final String VALID_DESCRIPTION = ALICE_APPOINTMENT.getDescription().toString(); + + // EP: Valid Appointment Details + @Test + public void toModelType_validAppointmentDetails_returnsAppointment() throws Exception { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(ALICE_APPOINTMENT); + assertEquals(ALICE_APPOINTMENT, appointment.toModelType()); + } + + // Heuristic: No more than one invalid input in a test case + // EP: Invalid name + @Test + public void toModelType_invalidName_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(INVALID_NAME, VALID_DATE, + VALID_START_TIME, VALID_END_TIME, VALID_DESCRIPTION); + String expectedMessage = Name.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Null name + @Test + public void toModelType_nullName_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(null, VALID_DATE, + VALID_START_TIME, VALID_END_TIME, VALID_DESCRIPTION); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Invalid Date + @Test + public void toModelType_invalidDate_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(VALID_NAME, INVALID_DATE, + VALID_START_TIME, VALID_END_TIME, VALID_DESCRIPTION); + String expectedMessage = Date.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Null date + @Test + public void toModelType_nullDate_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(VALID_NAME, null, + VALID_START_TIME, VALID_END_TIME, VALID_DESCRIPTION); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Invalid start time + @Test + public void toModelType_invalidStartTime_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(VALID_NAME, VALID_DATE, + INVALID_START_TIME, VALID_END_TIME, VALID_DESCRIPTION); + String expectedMessage = Time.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Null start time + @Test + public void toModelType_nullStartTime_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(VALID_NAME, VALID_DATE, + null, VALID_END_TIME, VALID_DESCRIPTION); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Time.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Invalid End Time + @Test + public void toModelType_invalidEndTime_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(VALID_NAME, VALID_DATE, + VALID_START_TIME, INVALID_END_TIME, VALID_DESCRIPTION); + String expectedMessage = Time.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Null end time + @Test + public void toModelType_nullEndTime_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(VALID_NAME, VALID_DATE, + VALID_START_TIME, null, VALID_DESCRIPTION); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Time.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Invalid description + @Test + public void toModelType_invalidDescription_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(VALID_NAME, VALID_DATE, + VALID_START_TIME, VALID_END_TIME, INVALID_DESCRIPTION); + String expectedMessage = Description.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } + + // EP: Null description + @Test + public void toModelType_nullDescription_throwsIllegalValueException() { + JsonAdaptedAppointment appointment = new JsonAdaptedAppointment(VALID_NAME, VALID_DATE, + VALID_START_TIME, VALID_END_TIME, null); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Description.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, appointment::toModelType); + } +} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java deleted file mode 100644 index 83b11331cdb..00000000000 --- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.BENSON; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; - -public class JsonAdaptedPersonTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = BENSON.getName().toString(); - private static final String VALID_PHONE = BENSON.getPhone().toString(); - private static final String VALID_EMAIL = BENSON.getEmail().toString(); - private static final String VALID_ADDRESS = BENSON.getAddress().toString(); - private static final List VALID_TAGS = BENSON.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList()); - - @Test - public void toModelType_validPersonDetails_returnsPerson() throws Exception { - JsonAdaptedPerson person = new JsonAdaptedPerson(BENSON); - assertEquals(BENSON, person.toModelType()); - } - - @Test - public void toModelType_invalidName_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Name.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullName_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Phone.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Email.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS); - String expectedMessage = Address.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidTags_throwsIllegalValueException() { - List invalidTags = new ArrayList<>(VALID_TAGS); - invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags); - assertThrows(IllegalValueException.class, person::toModelType); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedStudentTest.java b/src/test/java/seedu/address/storage/JsonAdaptedStudentTest.java new file mode 100644 index 00000000000..b1ab1544a26 --- /dev/null +++ b/src/test/java/seedu/address/storage/JsonAdaptedStudentTest.java @@ -0,0 +1,129 @@ +package seedu.address.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.storage.JsonAdaptedStudent.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalStudents.BENNY; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Phone; + +public class JsonAdaptedStudentTest { + private static final String INVALID_NAME = "R@chel"; + private static final String INVALID_PHONE = "+651234"; + private static final String INVALID_ADDRESS = " "; + private static final String INVALID_TAG = "#friend"; + + private static final String VALID_NAME = BENNY.getName().toString(); + private static final String VALID_PHONE = BENNY.getPhone().toString(); + private static final String VALID_ADDRESS = BENNY.getAddress().toString(); + private static final List VALID_TAGS = BENNY.getRiskLevel().stream() + .map(JsonAdaptedRiskLevel::new) + .collect(Collectors.toList()); + private static final String VALID_NOTE = BENNY.getNote().toString(); + private static final String INVALID_NOTE = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopq" + + "rstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopq" + + "rstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi" + + "jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyza" + + "bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrs" + + "tuvwxyzabcdefghijklmnopqrstuvwxyzabcdefa"; + + // EP: Valid student details + @Test + public void toModelType_validStudentDetails_returnsStudent() throws Exception { + JsonAdaptedStudent student = new JsonAdaptedStudent(BENNY); + assertEquals(BENNY, student.toModelType()); + } + + // Heuristic: No more than one invalid input in a test case + // EP: Invalid name + @Test + public void toModelType_invalidName_throwsIllegalValueException() { + JsonAdaptedStudent student = + new JsonAdaptedStudent(INVALID_NAME, VALID_PHONE, VALID_ADDRESS, VALID_TAGS, VALID_NOTE); + String expectedMessage = Name.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + // EP: Null name + @Test + public void toModelType_nullName_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(null, VALID_PHONE, VALID_ADDRESS, + VALID_TAGS, VALID_NOTE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + // EP: Invalid phone + @Test + public void toModelType_invalidPhone_throwsIllegalValueException() { + JsonAdaptedStudent student = + new JsonAdaptedStudent(VALID_NAME, INVALID_PHONE, VALID_ADDRESS, VALID_TAGS, VALID_NOTE); + String expectedMessage = Phone.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + // EP: Null phone + @Test + public void toModelType_nullPhone_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_NAME, null, VALID_ADDRESS, + VALID_TAGS, VALID_NOTE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + // EP: Invalid Address + @Test + public void toModelType_invalidAddress_throwsIllegalValueException() { + JsonAdaptedStudent student = + new JsonAdaptedStudent(VALID_NAME, VALID_PHONE, INVALID_ADDRESS, VALID_TAGS, VALID_NOTE); + String expectedMessage = Address.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + // EP: Null address + @Test + public void toModelType_nullAddress_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_NAME, VALID_PHONE, null, + VALID_TAGS, VALID_NOTE); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + // EP: Invalid tags + @Test + public void toModelType_invalidTags_throwsIllegalValueException() { + List invalidTags = new ArrayList<>(VALID_TAGS); + invalidTags.add(new JsonAdaptedRiskLevel(INVALID_TAG)); + JsonAdaptedStudent student = + new JsonAdaptedStudent(VALID_NAME, VALID_PHONE, VALID_ADDRESS, invalidTags, VALID_NOTE); + assertThrows(IllegalValueException.class, student::toModelType); + } + + // EP: Null note + @Test + public void toModelType_nullNote_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_NAME, VALID_PHONE, VALID_ADDRESS, + VALID_TAGS, null); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Note.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } + + // EP: Invalid note + @Test + public void toModelType_invalidNote_throwsIllegalValueException() { + JsonAdaptedStudent student = new JsonAdaptedStudent(VALID_NAME, VALID_PHONE, VALID_ADDRESS, + VALID_TAGS, INVALID_NOTE); + String expectedMessage = Note.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, student::toModelType); + } +} diff --git a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java b/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java deleted file mode 100644 index 4e5ce9200c8..00000000000 --- a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.HOON; -import static seedu.address.testutil.TypicalPersons.IDA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import seedu.address.commons.exceptions.DataLoadingException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; - -public class JsonAddressBookStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonAddressBookStorageTest"); - - @TempDir - public Path testFolder; - - @Test - public void readAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> readAddressBook(null)); - } - - private java.util.Optional readAddressBook(String filePath) throws Exception { - return new JsonAddressBookStorage(Paths.get(filePath)).readAddressBook(addToTestDataPathIfNotNull(filePath)); - } - - private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { - return prefsFileInTestDataFolder != null - ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) - : null; - } - - @Test - public void read_missingFile_emptyResult() throws Exception { - assertFalse(readAddressBook("NonExistentFile.json").isPresent()); - } - - @Test - public void read_notJsonFormat_exceptionThrown() { - assertThrows(DataLoadingException.class, () -> readAddressBook("notJsonFormatAddressBook.json")); - } - - @Test - public void readAddressBook_invalidPersonAddressBook_throwDataLoadingException() { - assertThrows(DataLoadingException.class, () -> readAddressBook("invalidPersonAddressBook.json")); - } - - @Test - public void readAddressBook_invalidAndValidPersonAddressBook_throwDataLoadingException() { - assertThrows(DataLoadingException.class, () -> readAddressBook("invalidAndValidPersonAddressBook.json")); - } - - @Test - public void readAndSaveAddressBook_allInOrder_success() throws Exception { - Path filePath = testFolder.resolve("TempAddressBook.json"); - AddressBook original = getTypicalAddressBook(); - JsonAddressBookStorage jsonAddressBookStorage = new JsonAddressBookStorage(filePath); - - // Save in new file and read back - jsonAddressBookStorage.saveAddressBook(original, filePath); - ReadOnlyAddressBook readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Modify data, overwrite exiting file, and read back - original.addPerson(HOON); - original.removePerson(ALICE); - jsonAddressBookStorage.saveAddressBook(original, filePath); - readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Save and read without specifying file path - original.addPerson(IDA); - jsonAddressBookStorage.saveAddressBook(original); // file path not specified - readBack = jsonAddressBookStorage.readAddressBook().get(); // file path not specified - assertEquals(original, new AddressBook(readBack)); - - } - - @Test - public void saveAddressBook_nullAddressBook_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(null, "SomeFile.json")); - } - - /** - * Saves {@code addressBook} at the specified {@code filePath}. - */ - private void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) { - try { - new JsonAddressBookStorage(Paths.get(filePath)) - .saveAddressBook(addressBook, addToTestDataPathIfNotNull(filePath)); - } catch (IOException ioe) { - throw new AssertionError("There should not be an error writing to the file.", ioe); - } - } - - @Test - public void saveAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(new AddressBook(), null)); - } -} diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java deleted file mode 100644 index 188c9058d20..00000000000 --- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.AddressBook; -import seedu.address.testutil.TypicalPersons; - -public class JsonSerializableAddressBookTest { - - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableAddressBookTest"); - private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); - private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); - private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); - - @Test - public void toModelType_typicalPersonsFile_success() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE, - JsonSerializableAddressBook.class).get(); - AddressBook addressBookFromFile = dataFromFile.toModelType(); - AddressBook typicalPersonsAddressBook = TypicalPersons.getTypicalAddressBook(); - assertEquals(addressBookFromFile, typicalPersonsAddressBook); - } - - @Test - public void toModelType_invalidPersonFile_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(INVALID_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, dataFromFile::toModelType); - } - - @Test - public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, - dataFromFile::toModelType); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonSerializableWellNusTest.java b/src/test/java/seedu/address/storage/JsonSerializableWellNusTest.java new file mode 100644 index 00000000000..e0a91e9adad --- /dev/null +++ b/src/test/java/seedu/address/storage/JsonSerializableWellNusTest.java @@ -0,0 +1,91 @@ +package seedu.address.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.WellNus; + +public class JsonSerializableWellNusTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableWellNusTest"); + private static final Path TYPICAL_WELLNUS_FILE = TEST_DATA_FOLDER.resolve("typicalWellNus.json"); + private static final Path INVALID_STUDENT_FILE = TEST_DATA_FOLDER.resolve("invalidStudentWellNus.json"); + private static final Path DUPLICATE_STUDENT_FILE = TEST_DATA_FOLDER.resolve("duplicateStudentWellNus.json"); + private static final Path INVALID_APPOINTMENT_FILE = TEST_DATA_FOLDER.resolve("invalidAppointmentWellNus.json"); + private static final Path DUPLICATE_APPOINTMENT_FILE = TEST_DATA_FOLDER.resolve("duplicateAppointmentWellNus.json"); + private static final Path APPOINTMENT_OVERLAP_FILE = TEST_DATA_FOLDER + .resolve("overlapAppointmentWellNus.json"); + private static final Path APPOINTMENT_STUDENT_NOT_FOUND_FILE = TEST_DATA_FOLDER + .resolve("appointmentStudentNotFoundWellNus.json"); + + + // EP: Valid file + @Test + public void toModelType_typicalWellNusFile_success() throws Exception { + JsonSerializableWellNus dataFromFile = JsonUtil.readJsonFile(TYPICAL_WELLNUS_FILE, + JsonSerializableWellNus.class).get(); + WellNus wellNusFromFile = dataFromFile.toModelType(); + WellNus typicalStudentsWellNus = getTypicalWellNus(); + assertEquals(wellNusFromFile, typicalStudentsWellNus); + } + + // EP: Invalid student in json file + @Test + public void toModelType_invalidStudentWellNusFile_throwsIllegalValueException() throws Exception { + JsonSerializableWellNus dataFromFile = JsonUtil.readJsonFile(INVALID_STUDENT_FILE, + JsonSerializableWellNus.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + // EP: Duplicate student in json file + @Test + public void toModelType_duplicateStudents_throwsIllegalValueException() throws Exception { + JsonSerializableWellNus dataFromFile = JsonUtil.readJsonFile(DUPLICATE_STUDENT_FILE, + JsonSerializableWellNus.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableWellNus.MESSAGE_DUPLICATE_STUDENT, + dataFromFile::toModelType); + } + + // EP: Invalid appointments in json file + @Test + public void toModelType_invalidAppointments_throwsIllegalValueException() throws Exception { + JsonSerializableWellNus dataFromFile = JsonUtil.readJsonFile(INVALID_APPOINTMENT_FILE, + JsonSerializableWellNus.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + // EP: Duplicate appointments in json file + @Test + public void toModelType_duplicateAppointments_throwsIllegalValueException() throws Exception { + JsonSerializableWellNus dataFromFile = JsonUtil.readJsonFile(DUPLICATE_APPOINTMENT_FILE, + JsonSerializableWellNus.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableWellNus.MESSAGE_DUPLICATE_APPOINTMENT, + dataFromFile::toModelType); + } + + // EP: Overlapping appointments in json file + @Test + public void toModelType_overlappingAppointments_throwsIllegalValueException() throws Exception { + JsonSerializableWellNus dataFromFile = JsonUtil.readJsonFile(APPOINTMENT_OVERLAP_FILE, + JsonSerializableWellNus.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableWellNus.MESSAGE_OVERLAPPING_APPOINTMENT, + dataFromFile::toModelType); + } + + // Case where appointment in storage does not correspond to a student in the json file + @Test + public void toModelType_appointmentStudentNotFound_throwsIllegalValueException() throws Exception { + JsonSerializableWellNus dataFromFile = JsonUtil.readJsonFile(APPOINTMENT_STUDENT_NOT_FOUND_FILE, + JsonSerializableWellNus.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableWellNus.MESSAGE_STUDENT_NOT_FOUND, + dataFromFile::toModelType); + } +} diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java index ed0a413526a..0873f23ce1c 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java @@ -73,7 +73,7 @@ public void readUserPrefs_extraValuesInFile_extraValuesIgnored() throws DataLoad private UserPrefs getTypicalUserPrefs() { UserPrefs userPrefs = new UserPrefs(); userPrefs.setGuiSettings(new GuiSettings(1000, 500, 300, 100)); - userPrefs.setAddressBookFilePath(Paths.get("addressbook.json")); + userPrefs.setWellNusFilePath(Paths.get("data/wellnus.json")); return userPrefs; } diff --git a/src/test/java/seedu/address/storage/JsonWellNusStorageTest.java b/src/test/java/seedu/address/storage/JsonWellNusStorageTest.java new file mode 100644 index 00000000000..cb14a84698e --- /dev/null +++ b/src/test/java/seedu/address/storage/JsonWellNusStorageTest.java @@ -0,0 +1,120 @@ +package seedu.address.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalStudents.ELLE; +import static seedu.address.testutil.TypicalStudents.HOON; +import static seedu.address.testutil.TypicalStudents.IDA; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; + +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import seedu.address.commons.exceptions.DataLoadingException; +import seedu.address.model.ReadOnlyWellNus; +import seedu.address.model.WellNus; + +public class JsonWellNusStorageTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonWellNusStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readWellNus_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readWellNus(null)); + } + + private java.util.Optional readWellNus(String filePath) throws Exception { + return new JsonWellNusStorage(Paths.get(filePath)).readWellNus(addToTestDataPathIfNotNull(filePath)); + } + + private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { + return prefsFileInTestDataFolder != null + ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) + : null; + } + + @Test + public void read_missingFile_emptyResult() throws Exception { + assertFalse(readWellNus("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataLoadingException.class, () -> readWellNus("notJsonFormatWellNus.json")); + } + + @Test + public void readWellNus_invalidStudentWellNus_throwDataLoadingException() { + assertThrows(DataLoadingException.class, () -> readWellNus("invalidStudentWellNus.json")); + } + + @Test + public void readWellNus_invalidAndValidStudentWellNus_throwDataLoadingException() { + assertThrows(DataLoadingException.class, () -> readWellNus("invalidAndValidStudentWellNus.json")); + } + + @Test + public void readWellNus_invalidAppointmentWellNus_throwDataLoadingException() { + assertThrows(DataLoadingException.class, () -> readWellNus("invalidAppointmentWellNus.json")); + } + + @Test + public void readWellNus_invalidAndValidAppointmentWellNus_throwDataLoadingException() { + assertThrows(DataLoadingException.class, () -> readWellNus("invalidAndValidAppointmentWellNus.json")); + } + + @Test + public void readAndSaveWellNus_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempWellNus.json"); + WellNus original = getTypicalWellNus(); + JsonWellNusStorage jsonWellNusStorage = new JsonWellNusStorage(filePath); + + // Save in new file and read back + jsonWellNusStorage.saveWellNus(original, filePath); + ReadOnlyWellNus readBack = jsonWellNusStorage.readWellNus(filePath).get(); + assertEquals(original, new WellNus(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addStudent(HOON); + original.removeStudent(ELLE); + jsonWellNusStorage.saveWellNus(original, filePath); + readBack = jsonWellNusStorage.readWellNus(filePath).get(); + assertEquals(original, new WellNus(readBack)); + + // Save and read without specifying file path + original.addStudent(IDA); + jsonWellNusStorage.saveWellNus(original); // file path not specified + readBack = jsonWellNusStorage.readWellNus().get(); // file path not specified + assertEquals(original, new WellNus(readBack)); + + } + + @Test + public void saveWellNus_nullWellNus_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveWellNus(null, "SomeFile.json")); + } + + /** + * Saves {@code wellNus} at the specified {@code filePath}. + */ + private void saveWellNus(ReadOnlyWellNus wellNus, String filePath) { + try { + new JsonWellNusStorage(Paths.get(filePath)) + .saveWellNus(wellNus, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveWellNus_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveWellNus(new WellNus(), null)); + } +} diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/address/storage/StorageManagerTest.java index 99a16548970..e2ac0e45fa9 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/address/storage/StorageManagerTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalWellNus.getTypicalWellNus; import java.nio.file.Path; @@ -11,9 +11,9 @@ import org.junit.jupiter.api.io.TempDir; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyWellNus; import seedu.address.model.UserPrefs; +import seedu.address.model.WellNus; public class StorageManagerTest { @@ -24,9 +24,9 @@ public class StorageManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + JsonWellNusStorage wellNusStorage = new JsonWellNusStorage(getTempFilePath("ab")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + storageManager = new StorageManager(wellNusStorage, userPrefsStorage); } private Path getTempFilePath(String fileName) { @@ -48,21 +48,21 @@ public void prefsReadSave() throws Exception { } @Test - public void addressBookReadSave() throws Exception { + public void wellNusReadSave() throws Exception { /* * Note: This is an integration test that verifies the StorageManager is properly wired to the - * {@link JsonAddressBookStorage} class. - * More extensive testing of UserPref saving/reading is done in {@link JsonAddressBookStorageTest} class. + * {@link JsonWellNusStorage} class. + * More extensive testing of UserPref saving/reading is done in {@link JsonWellNusStorageTest} class. */ - AddressBook original = getTypicalAddressBook(); - storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); - assertEquals(original, new AddressBook(retrieved)); + WellNus original = getTypicalWellNus(); + storageManager.saveWellNus(original); + ReadOnlyWellNus retrieved = storageManager.readWellNus().get(); + assertEquals(original, new WellNus(retrieved)); } @Test - public void getAddressBookFilePath() { - assertNotNull(storageManager.getAddressBookFilePath()); + public void getWellNusFilePath() { + assertNotNull(storageManager.getWellNusFilePath()); } } diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java deleted file mode 100644 index d53799fd110..00000000000 --- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java +++ /dev/null @@ -1,34 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class to help with building Addressbook objects. - * Example usage:
- * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").build();} - */ -public class AddressBookBuilder { - - private AddressBook addressBook; - - public AddressBookBuilder() { - addressBook = new AddressBook(); - } - - public AddressBookBuilder(AddressBook addressBook) { - this.addressBook = addressBook; - } - - /** - * Adds a new {@code Person} to the {@code AddressBook} that we are building. - */ - public AddressBookBuilder withPerson(Person person) { - addressBook.addPerson(person); - return this; - } - - public AddressBook build() { - return addressBook; - } -} diff --git a/src/test/java/seedu/address/testutil/AppointmentBuilder.java b/src/test/java/seedu/address/testutil/AppointmentBuilder.java new file mode 100644 index 00000000000..5e4ebf67750 --- /dev/null +++ b/src/test/java/seedu/address/testutil/AppointmentBuilder.java @@ -0,0 +1,101 @@ +package seedu.address.testutil; + +import seedu.address.model.appointment.Appointment; +import seedu.address.model.appointment.Date; +import seedu.address.model.appointment.Description; +import seedu.address.model.appointment.Time; +import seedu.address.model.appointment.exceptions.InvalidStartEndTimeException; +import seedu.address.model.student.Name; + +/** + * A utility class to help with building Appointment objects. + */ +public class AppointmentBuilder { + + public static final String DEFAULT_NAME = "Jon Ang"; + public static final String DEFAULT_DATE = "2023-12-31"; + public static final String DEFAULT_START_TIME = "16:30"; + public static final String DEFAULT_END_TIME = "17:30"; + public static final String DEFAULT_DESCRIPTION = "First Session"; + + private Name name; + private Date date; + private Time startTime; + private Time endTime; + private Description description; + + /** + * Creates an {@code AppointmentBuilder} with the default details. + */ + public AppointmentBuilder() { + name = new Name(DEFAULT_NAME); + date = new Date(DEFAULT_DATE); + startTime = new Time(DEFAULT_START_TIME); + endTime = new Time(DEFAULT_END_TIME); + description = new Description(DEFAULT_DESCRIPTION); + } + + /** + * Initializes the AppointmentBuilder with the data of {@code appointmentToCopy}. + */ + public AppointmentBuilder(Appointment appointmentToCopy) { + name = appointmentToCopy.getName(); + date = appointmentToCopy.getDate(); + startTime = appointmentToCopy.getStartTime(); + endTime = appointmentToCopy.getEndTime(); + description = appointmentToCopy.getDescription(); + } + + /** + * Sets the {@code Name} of the {@code Appointment} that we are building. + */ + public AppointmentBuilder withName(String name) { + this.name = new Name(name); + return this; + } + + /** + * Sets the {@code Date} of the {@code Appointment} that we are building. + */ + public AppointmentBuilder withDate(String date) { + this.date = new Date(date); + return this; + } + + /** + * Sets the {@code StartTime} of the {@code Appointment} that we are building. + */ + public AppointmentBuilder withStartTime(String startTime) { + this.startTime = new Time(startTime); + return this; + } + + /** + * Sets the {@code EndTime} of the {@code Appointment} that we are building. + */ + public AppointmentBuilder withEndTime(String endTime) { + this.endTime = new Time(endTime); + return this; + } + + /** + * Sets the {@code Description} of the {@code Appointment} that we are building. + */ + public AppointmentBuilder withDescription(String description) { + this.description = new Description(description); + return this; + } + + /** + * Builds an {@code Appointment} with the provided details + * @return Appointment with the given details + */ + public Appointment build() { + try { + Appointment appt = new Appointment(date, startTime, endTime, name, description); + return appt; + } catch (InvalidStartEndTimeException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java deleted file mode 100644 index 4584bd5044e..00000000000 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.testutil; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * A utility class to help with building EditPersonDescriptor objects. - */ -public class EditPersonDescriptorBuilder { - - private EditPersonDescriptor descriptor; - - public EditPersonDescriptorBuilder() { - descriptor = new EditPersonDescriptor(); - } - - public EditPersonDescriptorBuilder(EditPersonDescriptor descriptor) { - this.descriptor = new EditPersonDescriptor(descriptor); - } - - /** - * Returns an {@code EditPersonDescriptor} with fields containing {@code person}'s details - */ - public EditPersonDescriptorBuilder(Person person) { - descriptor = new EditPersonDescriptor(); - descriptor.setName(person.getName()); - descriptor.setPhone(person.getPhone()); - descriptor.setEmail(person.getEmail()); - descriptor.setAddress(person.getAddress()); - descriptor.setTags(person.getTags()); - } - - /** - * Sets the {@code Name} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withName(String name) { - descriptor.setName(new Name(name)); - return this; - } - - /** - * Sets the {@code Phone} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withPhone(String phone) { - descriptor.setPhone(new Phone(phone)); - return this; - } - - /** - * Sets the {@code Email} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withEmail(String email) { - descriptor.setEmail(new Email(email)); - return this; - } - - /** - * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withAddress(String address) { - descriptor.setAddress(new Address(address)); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor} - * that we are building. - */ - public EditPersonDescriptorBuilder withTags(String... tags) { - Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); - descriptor.setTags(tagSet); - return this; - } - - public EditPersonDescriptor build() { - return descriptor; - } -} diff --git a/src/test/java/seedu/address/testutil/EditStudentDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditStudentDescriptorBuilder.java new file mode 100644 index 00000000000..6130d757940 --- /dev/null +++ b/src/test/java/seedu/address/testutil/EditStudentDescriptorBuilder.java @@ -0,0 +1,52 @@ +package seedu.address.testutil; + +import seedu.address.logic.commands.EditCommand; +import seedu.address.logic.commands.EditCommand.EditStudentDescriptor; +import seedu.address.model.student.Address; +import seedu.address.model.student.Phone; +import seedu.address.model.student.Student; + +/** + * A utility class to help with building EditStudentDescriptor objects. + */ +public class EditStudentDescriptorBuilder { + + private EditStudentDescriptor descriptor; + + public EditStudentDescriptorBuilder() { + descriptor = new EditStudentDescriptor(); + } + + public EditStudentDescriptorBuilder(EditStudentDescriptor descriptor) { + this.descriptor = new EditStudentDescriptor(descriptor); + } + + /** + * Returns an {@code EditStudentDescriptor} with fields containing {@code student}'s details + */ + public EditStudentDescriptorBuilder(Student student) { + descriptor = new EditStudentDescriptor(); + descriptor.setPhone(student.getPhone()); + descriptor.setAddress(student.getAddress()); + } + + /** + * Sets the {@code Phone} of the {@code EditStudentDescriptor} that we are building. + */ + public EditStudentDescriptorBuilder withPhone(String phone) { + descriptor.setPhone(new Phone(phone)); + return this; + } + + /** + * Sets the {@code Address} of the {@code EditStudentDescriptor} that we are building. + */ + public EditStudentDescriptorBuilder withAddress(String address) { + descriptor.setAddress(new Address(address)); + return this; + } + + public EditCommand.EditStudentDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/address/testutil/ModelStub.java b/src/test/java/seedu/address/testutil/ModelStub.java new file mode 100644 index 00000000000..ab7173f26cb --- /dev/null +++ b/src/test/java/seedu/address/testutil/ModelStub.java @@ -0,0 +1,122 @@ +package seedu.address.testutil; + +import java.nio.file.Path; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.address.commons.core.GuiSettings; +import seedu.address.model.Model; +import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.ReadOnlyWellNus; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Student; + +/** + * A ModelStub class used for unit testing + */ +public class ModelStub implements Model { + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + throw new AssertionError("This method should not be called."); + } + + @Override + public GuiSettings getGuiSettings() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + throw new AssertionError("This method should not be called."); + } + + @Override + public Path getWellNusFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setWellNusFilePath(Path wellNusFilePath) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addStudent(Student student) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setWellNusData(ReadOnlyWellNus newData) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyWellNus getWellNusData() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasStudent(Student student) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteStudent(Student target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setStudent(Student target, Student editedStudent) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addAppointment(Appointment appointment) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasAppointment(Appointment appointment) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasOverlapsWithAppointments(Appointment appointment) { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasNoStudentForAppointment(Appointment appointment) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteAppointment(Appointment target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredStudentList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredAppointmentList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredStudentList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredAppointmentList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } +} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java deleted file mode 100644 index 6be381d39ba..00000000000 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -package seedu.address.testutil; - -import java.util.HashSet; -import java.util.Set; - -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.model.util.SampleDataUtil; - -/** - * A utility class to help with building Person objects. - */ -public class PersonBuilder { - - public static final String DEFAULT_NAME = "Amy Bee"; - public static final String DEFAULT_PHONE = "85355255"; - public static final String DEFAULT_EMAIL = "amy@gmail.com"; - public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111"; - - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - /** - * Creates a {@code PersonBuilder} with the default details. - */ - public PersonBuilder() { - name = new Name(DEFAULT_NAME); - phone = new Phone(DEFAULT_PHONE); - email = new Email(DEFAULT_EMAIL); - address = new Address(DEFAULT_ADDRESS); - tags = new HashSet<>(); - } - - /** - * Initializes the PersonBuilder with the data of {@code personToCopy}. - */ - public PersonBuilder(Person personToCopy) { - name = personToCopy.getName(); - phone = personToCopy.getPhone(); - email = personToCopy.getEmail(); - address = personToCopy.getAddress(); - tags = new HashSet<>(personToCopy.getTags()); - } - - /** - * Sets the {@code Name} of the {@code Person} that we are building. - */ - public PersonBuilder withName(String name) { - this.name = new Name(name); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building. - */ - public PersonBuilder withTags(String ... tags) { - this.tags = SampleDataUtil.getTagSet(tags); - return this; - } - - /** - * Sets the {@code Address} of the {@code Person} that we are building. - */ - public PersonBuilder withAddress(String address) { - this.address = new Address(address); - return this; - } - - /** - * Sets the {@code Phone} of the {@code Person} that we are building. - */ - public PersonBuilder withPhone(String phone) { - this.phone = new Phone(phone); - return this; - } - - /** - * Sets the {@code Email} of the {@code Person} that we are building. - */ - public PersonBuilder withEmail(String email) { - this.email = new Email(email); - return this; - } - - public Person build() { - return new Person(name, phone, email, address, tags); - } - -} diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java deleted file mode 100644 index 90849945183..00000000000 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.address.testutil; - -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 seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Person; -import seedu.address.model.tag.Tag; - -/** - * A utility class for Person. - */ -public class PersonUtil { - - /** - * Returns an add command string for adding the {@code person}. - */ - public static String getAddCommand(Person person) { - return AddCommand.COMMAND_WORD + " " + getPersonDetails(person); - } - - /** - * Returns the part of command string for the given {@code person}'s details. - */ - public static String getPersonDetails(Person person) { - StringBuilder sb = new StringBuilder(); - sb.append(PREFIX_NAME + person.getName().fullName + " "); - sb.append(PREFIX_PHONE + person.getPhone().value + " "); - sb.append(PREFIX_EMAIL + person.getEmail().value + " "); - sb.append(PREFIX_ADDRESS + person.getAddress().value + " "); - person.getTags().stream().forEach( - s -> sb.append(PREFIX_TAG + s.tagName + " ") - ); - return sb.toString(); - } - - /** - * Returns the part of command string for the given {@code EditPersonDescriptor}'s details. - */ - public static String getEditPersonDescriptorDetails(EditPersonDescriptor descriptor) { - StringBuilder sb = new StringBuilder(); - descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); - descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); - descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); - descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); - if (descriptor.getTags().isPresent()) { - Set tags = descriptor.getTags().get(); - if (tags.isEmpty()) { - sb.append(PREFIX_TAG); - } else { - tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" ")); - } - } - return sb.toString(); - } -} diff --git a/src/test/java/seedu/address/testutil/StudentBuilder.java b/src/test/java/seedu/address/testutil/StudentBuilder.java new file mode 100644 index 00000000000..906359210e7 --- /dev/null +++ b/src/test/java/seedu/address/testutil/StudentBuilder.java @@ -0,0 +1,96 @@ +package seedu.address.testutil; + +import java.util.HashSet; +import java.util.Set; + +import seedu.address.model.risklevel.RiskLevel; +import seedu.address.model.student.Address; +import seedu.address.model.student.Name; +import seedu.address.model.student.Note; +import seedu.address.model.student.Phone; +import seedu.address.model.student.Student; +import seedu.address.model.util.SampleDataUtil; + +/** + * A utility class to help with building Student objects. + */ +public class StudentBuilder { + + public static final String DEFAULT_NAME = "Amy Bee"; + public static final String DEFAULT_PHONE = "85355255"; + public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111"; + public static final String DEFAULT_NOTE = "Likes dogs."; + + private Name name; + private Phone phone; + private Address address; + private Set riskLevel; + private Note note; + + /** + * Creates a {@code StudentBuilder} with the default details. + */ + public StudentBuilder() { + name = new Name(DEFAULT_NAME); + phone = new Phone(DEFAULT_PHONE); + address = new Address(DEFAULT_ADDRESS); + riskLevel = new HashSet<>(); + note = new Note(DEFAULT_NOTE); + } + + /** + * Initializes the StudentBuilder with the data of {@code studentToCopy}. + */ + public StudentBuilder(Student studentToCopy) { + name = studentToCopy.getName(); + phone = studentToCopy.getPhone(); + address = studentToCopy.getAddress(); + riskLevel = new HashSet<>(studentToCopy.getRiskLevel()); + note = studentToCopy.getNote(); + } + + /** + * Sets the {@code Name} of the {@code Student} that we are building. + */ + public StudentBuilder withName(String name) { + this.name = new Name(name); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set} and set it to the {@code Student} that we are building. + */ + public StudentBuilder withTags(String ... tags) { + this.riskLevel = SampleDataUtil.getTagSet(tags); + return this; + } + + /** + * Sets the {@code Address} of the {@code Student} that we are building. + */ + public StudentBuilder withAddress(String address) { + this.address = new Address(address); + return this; + } + + /** + * Sets the {@code Phone} of the {@code Student} that we are building. + */ + public StudentBuilder withPhone(String phone) { + this.phone = new Phone(phone); + return this; + } + + /** + * Sets the {@code Note} of the {@code Person} that we are building. + */ + public StudentBuilder withNote(String note) { + this.note = new Note(note); + return this; + } + + public Student build() { + return new Student(name, phone, address, riskLevel, note); + } + +} diff --git a/src/test/java/seedu/address/testutil/StudentUtil.java b/src/test/java/seedu/address/testutil/StudentUtil.java new file mode 100644 index 00000000000..202ca82631a --- /dev/null +++ b/src/test/java/seedu/address/testutil/StudentUtil.java @@ -0,0 +1,47 @@ +package seedu.address.testutil; + +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +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_RISK_LEVEL; + +import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.EditCommand; +import seedu.address.model.student.Student; + +/** + * A utility class for Student. + */ +public class StudentUtil { + + /** + * Returns an add command string for adding the {@code student}. + */ + public static String getAddCommand(Student student) { + return AddCommand.COMMAND_WORD + " " + getStudentDetails(student); + } + + /** + * Returns the part of command string for the given {@code student}'s details. + */ + public static String getStudentDetails(Student student) { + StringBuilder sb = new StringBuilder(); + sb.append(PREFIX_NAME + student.getName().value + " "); + sb.append(PREFIX_PHONE + student.getPhone().value + " "); + sb.append(PREFIX_ADDRESS + student.getAddress().value + " "); + student.getRiskLevel().stream().forEach( + s -> sb.append(PREFIX_RISK_LEVEL + s.riskLevel + " ") + ); + return sb.toString(); + } + + /** + * Returns the part of command string for the given {@code EditStudentDescriptor}'s details. + */ + public static String getEditStudentDescriptorDetails(EditCommand.EditStudentDescriptor descriptor) { + StringBuilder sb = new StringBuilder(); + descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); + descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); + return sb.toString(); + } +} diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/seedu/address/testutil/TestUtil.java index 896d103eb0b..d9aac87d8c6 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/seedu/address/testutil/TestUtil.java @@ -7,7 +7,7 @@ import seedu.address.commons.core.index.Index; import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.address.model.student.Student; /** * A utility class for test cases. @@ -33,23 +33,23 @@ public static Path getFilePathInSandboxFolder(String fileName) { } /** - * Returns the middle index of the person in the {@code model}'s person list. + * Returns the middle index of the student in the {@code model}'s student list. */ public static Index getMidIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size() / 2); + return Index.fromOneBased(model.getFilteredStudentList().size() / 2); } /** - * Returns the last index of the person in the {@code model}'s person list. + * Returns the last index of the student in the {@code model}'s student list. */ public static Index getLastIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size()); + return Index.fromOneBased(model.getFilteredStudentList().size()); } /** - * Returns the person in the {@code model}'s person list at {@code index}. + * Returns the student in the {@code model}'s student list at {@code index}. */ - public static Person getPerson(Model model, Index index) { - return model.getFilteredPersonList().get(index.getZeroBased()); + public static Student getStudent(Model model, Index index) { + return model.getFilteredStudentList().get(index.getZeroBased()); } } diff --git a/src/test/java/seedu/address/testutil/TypicalAppointments.java b/src/test/java/seedu/address/testutil/TypicalAppointments.java new file mode 100644 index 00000000000..82872e5940b --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalAppointments.java @@ -0,0 +1,45 @@ +package seedu.address.testutil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.address.model.appointment.Appointment; + +/** + * A utility class containing a list of {@code Appointment} objects to be used in tests. + */ +public class TypicalAppointments { + + public static final Appointment ALICE_APPOINTMENT = new AppointmentBuilder().withName("Alice Pauline") + .withDate("2023-10-31").withStartTime("12:00").withEndTime("13:00") + .withDescription("First Session").build(); + public static final Appointment ALICE_SECOND_APPOINTMENT = new AppointmentBuilder().withName("Alice Pauline") + .withDate("2023-11-16").withStartTime("14:00").withEndTime("15:00") + .withDescription("Second Session").build(); + public static final Appointment BENNY_APPOINTMENT = new AppointmentBuilder().withName("Benny Dover") + .withDate("2023-11-16").withStartTime("16:00").withEndTime("17:00") + .withDescription("First Session").build(); + public static final Appointment JOHN_APPOINTMENT = new AppointmentBuilder().withName("John Tan") + .withDate("2023-10-31").withStartTime("13:00").withEndTime("14:00") + .withDescription("Second Session").build(); + public static final Appointment DAVID_APPOINTMENT = new AppointmentBuilder().withName("David Kim") + .withDate("2023-10-31").withStartTime("11:00").withEndTime("12:00") + .withDescription("Second Session").build(); + + public static final Appointment APPOINTMENT_1 = new AppointmentBuilder().withName("Alice") + .withDate("2023-11-01").withStartTime("09:00").withEndTime("10:00").build(); + public static final Appointment APPOINTMENT_2 = new AppointmentBuilder().withName("Bob") + .withDate("2023-11-02").withStartTime("10:00").withEndTime("11:00").build(); + public static final Appointment APPOINTMENT_3 = new AppointmentBuilder().withName("Charlie") + .withDate("2023-11-03").withStartTime("09:00").withEndTime("10:00").build(); + public static final Appointment APPOINTMENT_4 = new AppointmentBuilder() + .withName("David").withDate("2023-11-03").withStartTime("10:00").withEndTime("11:00").build(); + public static final Appointment APPOINTMENT_5 = new AppointmentBuilder().withName("Eve") + .withDate("2023-11-04").withStartTime("09:00").withEndTime("10:00").build(); + + + public static List getTypicalAppointments() { + return new ArrayList<>(Arrays.asList(ALICE_APPOINTMENT, ALICE_SECOND_APPOINTMENT, BENNY_APPOINTMENT)); + } +} diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java index 1e613937657..cfa8cf8b11c 100644 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ b/src/test/java/seedu/address/testutil/TypicalIndexes.java @@ -6,7 +6,7 @@ * A utility class containing a list of {@code Index} objects to be used in tests. */ public class TypicalIndexes { - public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1); - public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2); - public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3); + public static final Index INDEX_FIRST_STUDENT = Index.fromOneBased(1); + public static final Index INDEX_SECOND_STUDENT = Index.fromOneBased(2); + public static final Index INDEX_THIRD_STUDENT = Index.fromOneBased(3); } diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java deleted file mode 100644 index fec76fb7129..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class containing a list of {@code Person} objects to be used in tests. - */ -public class TypicalPersons { - - public static final Person ALICE = new PersonBuilder().withName("Alice Pauline") - .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com") - .withPhone("94351253") - .withTags("friends").build(); - public static final Person BENSON = new PersonBuilder().withName("Benson Meier") - .withAddress("311, Clementi Ave 2, #02-25") - .withEmail("johnd@example.com").withPhone("98765432") - .withTags("owesMoney", "friends").build(); - public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563") - .withEmail("heinz@example.com").withAddress("wall street").build(); - public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533") - .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build(); - public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224") - .withEmail("werner@example.com").withAddress("michegan ave").build(); - public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427") - .withEmail("lydia@example.com").withAddress("little tokyo").build(); - public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442") - .withEmail("anna@example.com").withAddress("4th street").build(); - - // Manually added - public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424") - .withEmail("stefan@example.com").withAddress("little india").build(); - public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131") - .withEmail("hans@example.com").withAddress("chicago ave").build(); - - // Manually added - Person's details found in {@code CommandTestUtil} - public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) - .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build(); - public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) - .build(); - - public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER - - private TypicalPersons() {} // prevents instantiation - - /** - * Returns an {@code AddressBook} with all the typical persons. - */ - public static AddressBook getTypicalAddressBook() { - AddressBook ab = new AddressBook(); - for (Person person : getTypicalPersons()) { - ab.addPerson(person); - } - return ab; - } - - public static List getTypicalPersons() { - return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); - } -} diff --git a/src/test/java/seedu/address/testutil/TypicalStudents.java b/src/test/java/seedu/address/testutil/TypicalStudents.java new file mode 100644 index 00000000000..f2cbccd4c44 --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalStudents.java @@ -0,0 +1,65 @@ +package seedu.address.testutil; + +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RISK_LEVEL_HIGH; +import static seedu.address.logic.commands.CommandTestUtil.VALID_RISK_LEVEL_LOW; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.address.model.student.Student; + +/** + * A utility class containing a list of {@code Student} objects to be used in tests. + */ +public class TypicalStudents { + + public static final Student ALICE = new StudentBuilder().withName("Alice Pauline") + .withAddress("123, Jurong West Ave 6, #08-111").withPhone("94351253") + .withTags("high").withNote("course: computer science").build(); + public static final Student BENNY = new StudentBuilder().withName("Benny Dover") + .withAddress("311, Clementi Ave 2, #02-25") + .withPhone("98765432") + .withTags("medium").withNote("Likes dogs").build(); + public static final Student CARL = new StudentBuilder().withName("Carl Kurz").withPhone("95352563") + .withAddress("wall street").build(); + public static final Student DANIEL = new StudentBuilder().withName("Daniel Meier").withPhone("87652533") + .withAddress("10th street").withTags("low").build(); + public static final Student ELLE = new StudentBuilder().withName("Elle Meyer").withPhone("94822240") + .withAddress("michegan ave").build(); + public static final Student FIONA = new StudentBuilder().withName("Fiona Kunz").withPhone("94824270") + .withAddress("little tokyo").build(); + public static final Student GEORGE = new StudentBuilder().withName("George Best").withPhone("94824420") + .withAddress("4th street").build(); + + // Manually added + public static final Student HOON = new StudentBuilder().withName("Hoon Meier").withPhone("84824240") + .withAddress("little india").build(); + public static final Student IDA = new StudentBuilder().withName("Ida Mueller").withPhone("84821310") + .withAddress("chicago ave").build(); + + // To be used in conjunction with default appointment builder to test in ScheduleCommandTest + public static final Student JON_ANG = new StudentBuilder().withName("Jon Ang").withPhone("97980852") + .withAddress("Blk 349 Woodlands Ave 3").build(); + + // Manually added - Student's details found in {@code CommandTestUtil} + public static final Student AMY = new StudentBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) + .withAddress(VALID_ADDRESS_AMY).withTags(VALID_RISK_LEVEL_HIGH).build(); + public static final Student BOB = new StudentBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) + .withAddress(VALID_ADDRESS_BOB).withTags(VALID_RISK_LEVEL_LOW).build(); + + + public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER + + private TypicalStudents() {} // prevents instantiation + + public static List getTypicalStudents() { + return new ArrayList<>(Arrays.asList(ALICE, BENNY, CARL, DANIEL, ELLE, FIONA, GEORGE)); + } +} diff --git a/src/test/java/seedu/address/testutil/TypicalWellNus.java b/src/test/java/seedu/address/testutil/TypicalWellNus.java new file mode 100644 index 00000000000..b944f0b1cc8 --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalWellNus.java @@ -0,0 +1,28 @@ +package seedu.address.testutil; + +import static seedu.address.testutil.TypicalAppointments.getTypicalAppointments; +import static seedu.address.testutil.TypicalStudents.getTypicalStudents; + +import seedu.address.model.WellNus; +import seedu.address.model.appointment.Appointment; +import seedu.address.model.student.Student; + + +/** + * A utility class return a {@code WellNUS} object to be used in tests. + */ +public class TypicalWellNus { + + private TypicalWellNus() {} // prevents instantiation + + public static WellNus getTypicalWellNus() { + WellNus ab = new WellNus(); + for (Appointment appointment : getTypicalAppointments()) { + ab.addAppointment(appointment); + } + for (Student student : getTypicalStudents()) { + ab.addStudent(student); + } + return ab; + } +} diff --git a/src/test/java/seedu/address/testutil/WellNusBuilder.java b/src/test/java/seedu/address/testutil/WellNusBuilder.java new file mode 100644 index 00000000000..b160773f1e4 --- /dev/null +++ b/src/test/java/seedu/address/testutil/WellNusBuilder.java @@ -0,0 +1,34 @@ +package seedu.address.testutil; + +import seedu.address.model.WellNus; +import seedu.address.model.student.Student; + +/** + * A utility class to help with building WellNus objects. + * Example usage:
+ * {@code WellNus wn = new WellNusBuilder().withStudent("John", "Doe").build();} + */ +public class WellNusBuilder { + + private WellNus wellNus; + + public WellNusBuilder() { + wellNus = new WellNus(); + } + + public WellNusBuilder(WellNus wellNus) { + this.wellNus = wellNus; + } + + /** + * Adds a new {@code Student} to the {@code WellNus} that we are building. + */ + public WellNusBuilder withStudent(Student student) { + wellNus.addStudent(student); + return this; + } + + public WellNus build() { + return wellNus; + } +} diff --git a/src/test/java/seedu/address/ui/MainAppTest.java b/src/test/java/seedu/address/ui/MainAppTest.java new file mode 100644 index 00000000000..11076796f02 --- /dev/null +++ b/src/test/java/seedu/address/ui/MainAppTest.java @@ -0,0 +1,37 @@ +package seedu.address.ui; + +import java.util.concurrent.TimeoutException; + +import org.junit.After; +import org.junit.Before; +import org.testfx.api.FxToolkit; +import org.testfx.framework.junit.ApplicationTest; + +import javafx.scene.input.KeyCode; +import javafx.scene.input.MouseButton; +import javafx.stage.Stage; +import seedu.address.MainApp; + +public class MainAppTest extends ApplicationTest { + + private MainApp mainApp; + + @Before + public void setUp() throws Exception { + ApplicationTest.launch(MainApp.class); + } + + @Override + public void start(Stage stage) { + mainApp = new MainApp(); + mainApp.start(stage); + stage.show(); + } + + @After + public void afterEachTest() throws TimeoutException { + FxToolkit.hideStage(); + release(new KeyCode[]{}); + release(new MouseButton[]{}); + } +} diff --git a/src/test/java/seedu/address/ui/MainWindowTest.java b/src/test/java/seedu/address/ui/MainWindowTest.java new file mode 100644 index 00000000000..ec023128c62 --- /dev/null +++ b/src/test/java/seedu/address/ui/MainWindowTest.java @@ -0,0 +1,23 @@ +package seedu.address.ui; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +public class MainWindowTest extends MainAppTest { + + + @Test + public void testFillInnerParts() { + // verifyThat("#personListPanel", isVisible()); + + // verifyThat("#appointmentListPanelPlaceholder #personListPanel", isVisible()); + + // verifyThat("#statusbarPlaceholder #statusBarFooter", isVisible()); + + // verifyThat("#commandBoxPlaceholder", NodeMatchers.isVisible()); + + assertEquals(1, 1); + + } +}