diff --git a/README.md b/README.md index 13f5c77403f..e2e95e5c58c 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,28 @@ -[![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/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/AY2122S2-CS2103T-W14-2/tp) ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
+# Coach2K22 + +* **Coach2K22** is a contact management application that helps busy sports coaches organise their overwhelming lists of contacts and messy weekly schedules. it also provides them with a platform to visualise defensive and offensive plays as the game unfolds.
+ 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. + * as a contact management utility for his players and relevant contacts + * as a visualiser for real-time play to aid decision making on the fly + * as an organisation tool for teams and organisations + +## Features + +- Full contact management + - List all your contacts + - Add, delete and edit them + - Add notes to contacts + - Add tags to your contacts + - Filter and search for them based on tags for quick organisation + +- Built-in user manual + +* 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 be2d2905dde..49f94144748 100644 --- a/build.gradle +++ b/build.gradle @@ -56,6 +56,9 @@ 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' + implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-swing', version: javaFxVersion, classifier: 'linux' 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 +69,11 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'Coach2K22.jar' +} + +run { + enableAssertions = true } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..deb76679b17 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -9,51 +9,56 @@ You can reach us at the email `seer[at]comp.nus.edu.sg` ## Project team -### John Doe +### Lyu Xiaoteng - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/Rye-Catcher)] +[[portfolio](team/rye-catcher.md)] +[[blog](https://x1a0teng.page/posts/about/)] -* Role: Project Advisor +* Role: Documentation and Integration +* Responsibilities: Ensure project documentation in good quality +and various parts of codes can be integrated to create a whole. -### Jane Doe +### Tan Juay Hee - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/juayhee)] +[[portfolio](team/juayhee.md)] -* Role: Team Lead -* Responsibilities: UI +* Role: Code Quality and Testing +* Responsibilities: Ensures the testing of the project is done properly and on time; looks +after code quality and ensures adherence to coding standards. -### Johnny Doe +### Abdulelah Faisal S Al Ghrairy - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/aalghrairy)] [[portfolio](team/aalghrairy.md)] -* Role: Developer -* Responsibilities: Data +* Role: Deliverables and Deadline +* Responsibilities: Ensure project deliverables are done on time and in the right format -### Jean Doe +### Sim Jun Heng - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/simjunheng)] +[[portfolio](team/simjunheng.md)] -* Role: Developer -* Responsibilities: Dev Ops + Threading +* Role: Documentation and Integration +* Responsibilities: In charge of versioning, repository maintenance, and code integration. Assist and ensure +that all documentations are in-order. -### James Doe - +### Pragya Jha -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] + -* Role: Developer -* Responsibilities: UI +[[github](http://github.com/prgj)] +[[portfolio](team/prgj.md)] + +* Role: Scheduling and tracking +* Responsibilities: In charge of defining, assigning, and tracking project tasks. diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..ede35c08427 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -7,26 +7,30 @@ title: Developer Guide -------------------------------------------------------------------------------------------------------------------- -## **Acknowledgements** +## **1. Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* http://java-buddy.blogspot.com/2013/07/move-node-to-front.html +* https://stackoverflow.com/questions/38028825/javafx-save-view-of-pane-to-image +* https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html -------------------------------------------------------------------------------------------------------------------- -## **Setting up, getting started** +
+ +## **2. Setting up, getting started** Refer to the guide [_Setting up and getting started_](SettingUp.md). -------------------------------------------------------------------------------------------------------------------- -## **Design** +## **3. Design**
:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/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 +### 3.1 Architecture @@ -34,9 +38,11 @@ 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** -**`Main`** has two classes called [`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). It is responsible for, +**`Main`** has two classes called [`Main`](https://github.com/AY2122S2-CS2103T-W14-2/tp/blob/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2122S2-CS2103T-W14-2/tp/blob/src/main/java/seedu/address/MainApp.java). It is responsible for, * At app launch: Initializes the components in the correct sequence, and connects them up with each other. * At shut down: Shuts down the components and invokes cleanup methods where necessary. @@ -49,6 +55,7 @@ The rest of the App consists of four components. * [**`Model`**](#model-component): Holds the data of the App in memory. * [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk. +
**How the architecture components interact with each other** @@ -59,7 +66,7 @@ The *Sequence Diagram* below shows how the components interact with each other f 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. -* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. +* implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point). For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. @@ -67,9 +74,11 @@ For example, the `Logic` component defines its API in the `Logic.java` interface The sections below give more details of each component. -### 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) +### 3.2 UI component + +The **API** of this component is specified in [`Ui.java`](https://github.com/AY2122S2-CS2103T-W14-2/tp/blob/master/src/main/java/seedu/address/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) @@ -84,20 +93,24 @@ The `UI` component, * 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`. -### Logic component +
+ +### 3.3 Logic component -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +**API** : [`Logic.java`](https://github.com/AY2122S2-CS2103T-W14-2/tp/blob/master/src/main/java/seedu/address/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: -1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. +1. When `Logic` is called upon to execute a command, it uses the `Coach2K22Parser` class to parse the user command. 1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. 1. The command can communicate with the `Model` when it is executed (e.g. to add a person). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. +
+ The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. ![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) @@ -105,16 +118,20 @@ The Sequence Diagram below illustrates the interactions within the `Logic` compo
: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.
+
+ 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 `Coach2K22Parser` 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 `Coach2K22Parser` 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 -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +
+ +### 3.4 Model component +**API** : [`Model.java`](https://github.com/AY2122S2-CS2103T-W14-2/tp/blob/master/src/main/java/seedu/address/model/Model.java) @@ -124,18 +141,14 @@ 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 a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. +* stores `Note` objects in three separate lists for each `Person` object. * 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.
- - - -
+
+### 3.5 Storage component -### Storage component - -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](https://github.com/AY2122S2-CS2103T-W14-2/tp/blob/master/src/main/java/seedu/address/storage/Storage.java) @@ -144,234 +157,1872 @@ The `Storage` component, * inherits from both `AddressBookStorage` 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 +### 3.6 Common classes Classes used by multiple components are in the `seedu.addressbook.commons` package. -------------------------------------------------------------------------------------------------------------------- -## **Implementation** +
+ +## **4. Implementation** This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +### 4.1 Contact Management -#### Proposed Implementation +#### 4.1.1 Add Feature -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: +##### Implementation -* `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. +This feature allows the user to add persons to the person list. It is facilitated by `ModelManager` which +makes use of the method `#addPerson()` to add a new person to the person list. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +Given below is an example usage scenario of how the add person mechanism behaves at each step. -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +Step 1: The user inputs `add-p n/Johnson p/83918273 a/Woodlands Avenue 4 e/johnson@gmail.com` to add a new person to the person list. -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. +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `AddPersonCommandParser` where its method `#parse()` is called to process the user inputs. -![UndoRedoState0](images/UndoRedoState0.png) +Step 3: It then returns a newly initialised `AddPersonCommand` back to the `LogicManager` for command execution. -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. +Step 4: During the command execution, the `ModelManager#addPerson()` is called which adds the new person to an internal list and updates the GUI display. A new contact named "Johnson" with his relevant details is then shown in the person list. -![UndoRedoState1](images/UndoRedoState1.png) +
-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`. +The steps above are summarised using a sequence diagram as shown below. +![AddPersonSequenceDiagram](images/AddPersonSequenceDiagram.png) -![UndoRedoState2](images/UndoRedoState2.png) +##### Design Consideration -
: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`. +**Aspect: Should there be an abstraction for persons:** -
+* **Alternative 1 (current choice):** Separate `add-p` command for creating a person. + * Pros: Easy to extend and modify. + * Cons: Not as intuitive for the user. +* **Alternative 2:** Single `add` command that adds tasks/persons depending on parameters. + * Pros: More intuitive for the user. -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) +#### 4.1.2 Delete 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. +##### Implementation -
+This feature allows the user to delete persons from the person list. It is facilitated by `ModelManager` which +makes use of the method `#deletePerson()` to delete a person from the person list. The method `#setTask()` is also called to ensure that the deleted person is removed from all tasks. -The following sequence diagram shows how the undo operation works: +Given below is an example usage scenario of how the delete person mechanism behaves at each step. -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +Step 1: The user inputs `del-p 1` to delete the first person in the person list. -
: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. +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `DeletePersonCommandParser` where its method `#parse()` is called to process the user inputs. -
+Step 3: It then returns a newly initialised `DeletePersonCommand` back to the `LogicManager` for command execution. -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. +Step 4: During the command execution, the `ModelManager#setTask()` method is called multiple times to remove the +corresponding person from all the tasks in the task list. After which, the `ModelManager#deletePerson()` method is called to delete the specified person from the person list. -
: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. +Step 5: The GUI display is then updated to show a new contact and task list without the deleted person. -
+
-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. +The steps above are summarised using a sequence diagram as shown below. +![DeletePersonSequenceDiagram](images/DeletePersonSequenceDiagram.png) -![UndoRedoState4](images/UndoRedoState4.png) +##### Design Consideration -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. +**Aspect: Should there be an abstraction for persons:** -![UndoRedoState5](images/UndoRedoState5.png) +* **Alternative 1 (current choice):** Separate `del-p` command for deleting a person. + * Pros: Easy to extend and modify. + * Cons: Not as intuitive for the user. +* **Alternative 2:** Single `del` command that deletes tasks/persons depending on parameters. + * Pros: More intuitive for the user. -The following activity diagram summarizes what happens when a user executes a new command: +
- +#### 4.1.3 Edit Feature -#### Design considerations: +##### Implementation -**Aspect: How undo & redo executes:** +This feature allows the user to edit persons from the person list. It is facilitated by `ModelManager` which +makes use of the method `#setPerson()` to update a person from the person list. If the person's name is edited in this process, the method `#setTask()` will be called to ensure all tasks tagged with this person is updated according. -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +Given below is an example usage scenario of how the edit person mechanism behaves at each step. -* **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. +Step 1: The user inputs `edit-p 1 n/Johnson` to edit the first person in the person list. -_{more aspects and alternatives to be added}_ +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `EditPersonCommandParser` where its method `#parse()` is called to process the user inputs. -### \[Proposed\] Data archiving +Step 3: It then returns a newly initialised `EditPersonCommand` back to the `LogicManager` for command execution. -_{Explain here how the data archiving feature will be implemented}_ +Step 4: During the command execution, the `ModelManager#setTask()` method is called multiple times to update the +name of the corresponding person in all the tasks. After which, the `ModelManager#setPerson()` method is called to update the specified person in the person list. +Step 5: The GUI display is then updated to show a new contact and task list with the updated person details. --------------------------------------------------------------------------------------------------------------------- +
-## **Documentation, logging, testing, configuration, dev-ops** +The steps above are summarised using a sequence diagram as shown below. +![EditPersonSequenceDiagram](images/EditPersonSequenceDiagram.png) -* [Documentation guide](Documentation.md) -* [Testing guide](Testing.md) -* [Logging guide](Logging.md) -* [Configuration guide](Configuration.md) -* [DevOps guide](DevOps.md) +##### Design Consideration --------------------------------------------------------------------------------------------------------------------- +**Aspect: Should there be an abstraction for persons:** -## **Appendix: Requirements** +* **Alternative 1 (current choice):** Separate `edit-p` command for editing a person. + * Pros: Easy to extend and modify. + * Cons: Not as intuitive for the user. +* **Alternative 2:** Single `edit` command that edits tasks/persons depending on parameters. + * Pros: More intuitive for the user. -### Product scope +
-**Target user profile**: +#### 4.1.4 Clear Feature -* has a need to manage a significant number of contacts -* prefer desktop apps over other types -* can type fast -* prefers typing to mouse interactions -* is reasonably comfortable using CLI apps +##### Implementation -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +This feature allows users to clear all persons from the person list. +It is facilitated by the `ModelManager` which sets a new `AddressBook` object to the `ModelManager` to clear the person list. The method `#setTask()` will also be called to ensure all tasks do not have any persons tagged to it. +Given below is an example usage scenario of how the clear person mechanism behaves at each step. -### User stories +Step 1: The user inputs `clear-p` to clear all persons from the person list. -Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` +Step 2: This argument is passed into the `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable +parser class which corresponds with the provided command to parse the user's inputs. -| 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 | +Step 3: Since there are no arguments for this command, a newly initialised `ClearPersonCommand` is returned to the `LogicManager` for command execution. -*{More to be added}* +Step 4: During the command execution, the `ModelManager#setTask()` method is called multiple times to delete persons in all the tasks. After which, a new `AddressBook` object is passed into the `ModelManager#setAddressBook()` method which clears out the person list. -### Use cases +Step 5: The GUI display is then updated to show an empty contact and new task list without any persons tagged to the tasks. -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +
-**Use case: Delete a person** +The steps above are summarised using a sequence diagram as shown below. +![ClearPersonSequenceDiagram](images/ClearPersonSequenceDiagram.png) -**MSS** +##### Design Consideration -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 +**Aspect: Should there be separate clear commands for clearing tasks and persons:** - Use case ends. +* **Alternative 1 (current choice):** A separate command for clearing tasks and persons. + * Pros: Easy to implement. + * Cons: Hard to extend. +* **Alternative 2:** A combined command for clearing tasks and persons. + earPer * Pros: Easier and more intuitive for the user to understand + * Cons: Hard to implement. -**Extensions** +
-* 2a. The list is empty. +#### 4.1.5 Add Tags Feature - Use case ends. +##### Implementation +This feature allows the user to add tags to contacts in the list. It is facilitated by `ModelManager` which +makes use of the method `#setPerson()` and `#updateFilteredPersonList()` to add tags to a contact. -* 3a. The given index is invalid. +Given below is an example usage scenario of how the add tag mechanism behaves at each step. - * 3a1. AddressBook shows an error message. +Step 1: The user inputs `tag-add-p 1 friend` to add the tag "friend" to the first contact in the list. - Use case resumes at step 2. +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `AddPersonTagCommandParser` where its method `#parse` is called to process the user inputs. -*{More to be added}* +Step 3: It then returns a newly initialised `AddPersonTagCommand` back to the `LogicManager` for command execution. This `AddPersonTagCommand` contains information about the new tag (in this case, "friend") -### Non-Functional Requirements +Step 4: During the command execution, the `ModelManager#setPerson()` is called which edits the tags of the person with the user-supplied tags. The filtered person list is updated with `ModelManager#updateFilteredPersonList` to display the new information to the user. -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. +
-*{More to be added}* +The steps above are summarised using a sequence diagram as shown below. +![AddPersonTagSequenceDiagram](images/AddPersonTagSequenceDiagram.png) -### Glossary -* **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +##### Design Consideration --------------------------------------------------------------------------------------------------------------------- +**Aspect: Should the implementation use the existing edit functionalities in Coach2K22:** +* **Alternative 1:** Use the current EditCommand class to edit a person's tags. + * Pros: Maintains abstraction and reuses code instead of writing new code. + * Cons: Creates a cyclic dependency, making the code base harder to maintain later on -## **Appendix: Instructions for manual testing** +* **Alternative 2 (current choice):** Implement AddPersonTagCommand independently, rewriting similar code + * Pros: Cleaner code and less dependencies + * Cons: Repetitive code that is not abstracted -Given below are instructions to test the app manually. +
-
:information_source: **Note:** These instructions only provide a starting point for testers to work on; -testers are expected to do more *exploratory* testing. +#### 4.1.6 Delete Tags Feature -
+##### Implementation +This feature allows the user to delete tags from contacts in the list. It is facilitated by `ModelManager` which +makes use of the method `#setPerson()` and `#updateFilteredPersonList()` to delete tags from a contact. -### Launch and shutdown +Given below is an example usage scenario of how the delete tag mechanism behaves at each step. -1. Initial launch +Step 1: The user `tag-del-p 1 friend` to delete the tag "friend" from the first contact in the list. - 1. Download the jar file and copy into an empty folder +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `DeletePersonTagCommandParser` where its method `#parse` is called to process the user inputs. - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. +Step 3: It then returns a newly initialised `DeletePersonTagCommand` back to the `LogicManager` for command execution. This `DeletePersonTagCommand` contains information about the new tag (in this case, "friend") -1. Saving window preferences +Step 4: During the command execution, the `ModelManager#setPerson()` is called which edits the tags of the person with the user-supplied tags. The filtered perosn list is updated with `ModelManager#updateFilteredPersonList` to display the new information to the user. - 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. +The steps above are summarised using a sequence diagram as shown below. +![DeletePersonTagSequenceDiagram](images/DeletePersonTagSequenceDiagram.png) -1. _{ more test cases …​ }_ -### Deleting a person +##### Design Consideration -1. Deleting a person while all persons are being shown +**Aspect: Should the implementation use the existing edit functionalities in AB3:** +* **Alternative 1:** Use the current EditCommand class to edit a person's tags. + * Pros: Maintains abstraction and reuses code instead of writing new code. + * Cons: Creates a cyclic dependency, making the code base harder to maintain later on + +* **Alternative 2 (current choice):** Implement DeletePersonTagCommand independently, rewriting similar code + * Pros: Cleaner code and less dependencies + * Cons: Repetitive code that is not abstracted + +
+ +#### 4.1.7 Find Feature + +##### Implementation + +This feature allows the user to display selected persons in the contact list. It is facilitated by `ModelManager` which +makes use of the method `#updateFilteredPersonList()` to find persons by name or tag. + +Given below is an example usage scenario of how the find person mechanism behaves at each step. + +Step 1: The user inputs `find n/Alex t/friends` to find selected persons. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `FindPersonCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `FindPersonCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#updateFilteredPersonList()` is called which updates the GUI display with only selected persons shown in the contact list. The command results are then generated and shown to the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![FindPersonSequenceDiagram](images/FindPersonSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be a separate find command for name and tag:** + +* **Alternative 1 (current choice):** A combined command for finding name and tag. + * Pros: Easy to implement. + * Cons: Users may have to remember more prefixes. + +* **Alternative 2:** A separate command for finding name and tag. + * Pros: Commands may be more intuitive to the users. + * Cons: Possible violation of the DRY principle. + +**Aspect: Should there be separate find commands for finding tasks and persons:** + +* **Alternative 1 (current choice):** A separate command for finding tasks and persons. + * Pros: Easy to implement. + * Cons: Hard to extend. + +* **Alternative 2:** A combined command for finding tasks and persons. + * Pros: Easier and more intuitive for the user to understand. + * Cons: Hard to implement. + +
+ +#### 4.1.7 Add Strength/Weakness/Misc Features + +##### Implementation + +These features allow the user to add a strength, weakness, or miscellaneous note to a person in the person list . It is facilitated by `ModelManager` which +makes use of the method `#setPerson()` to replace the particular person with the new person with modified note list (depending on the given command). + +Given below is an example usage scenario of how the Add Strength mechanism behaves at each step._Note that three commands have similar implementations, so only the Add Strength feature implementation will be provided here._ + +Step 1: The user inputs `strength-add 1 great endurance` to add the strength `great endurance` to the person in index `1` of the person list. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `AddStrengthCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `AddStrengthCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, `ModelManager#setPerson()` method is called to update the specified person in the person list with the new strength list. + +
+ +The steps above are summarised using a sequence diagram as shown below. Note that all occurrences of `Strength` or `AddStrengthCommand` (and similar instances) will be generalized to `Note` and `AddNoteCommand` respectively to represent the implementation of the Add Strength, Weakness, and Misc features. +![AddNoteSequenceDiagram](images/AddNoteSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be different note lists for each person:** + +* **Alternative 1 (current choice):** Separate `note-add` command into three different commands. + * Pros: Easier to use and more intuitive. + * Cons: Difficult to extend and modify. +* **Alternative 2:** Single `note-add` command for adding to each list with provided prefixes. + * Pros: Easier to extend and modify. + * Cons: Not intuitive for the user and more prone to feature flaws + +
+ +#### 4.1.8 Delete Strength/Weakness/Misc Features + +##### Implementation + +These features allow the user to delete a strength, weakness, or miscellaneous note from a person in the person list . It is facilitated by `ModelManager` which +makes use of the method `#setPerson()` to replace the particular person with the new person with modified note list (depending on the given command). + +Given below is an example usage scenario of how the Delete Strength mechanism behaves at each step._Note that three commands have similar implementations, so only the Delete Strength feature implementation will be provided here._ + +Step 1: The user inputs `strength-del 1 1` to delete the first strength from the person in index `1` of the person list. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `DeleteStrengthCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `DeleteStrengthCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, `ModelManager#setPerson()` method is called to update the specified person in the person list with the new strength list. + +
+ +The steps above are summarised using a sequence diagram as shown below. Note that all occurrences of `Strength` or `DeleteStrengthCommand` (and similar instances) will be generalized to `Note` and `AddNoteCommand` respectively to represent the implementation of the Delete Strength, Weakness, and Misc features. +![DeleteNoteSequenceDiagram](images/DeleteNoteSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be different note lists for each person:** + +* **Alternative 1 (current choice):** Separate `note-del` command into three different commands. + * Pros: Easier to use and more intuitive. + * Cons: Difficult to extend and modify. +* **Alternative 2:** Single `note-del` command for adding to each list with provided prefixes. + * Pros: Easier to extend and modify. + * Cons: Not intuitive for the user and more prone to feature flaws + +
+ +#### 4.1.9 Sort by Strength/Weakness Features + +##### Implementation + +These features allow the user to sort the person list by the total number of strengths/weaknesses in descending order. It is facilitated by `ModelManager` which +makes use of the method `#setAddressBook()` to replace the address book with the newly sorted person list without modification. + +Given below is an example usage scenario of how the Sort Strength mechanism behaves at each step._Note that two commands have similar implementations, so only the Sort Strength feature implementation will be provided here._ + +Step 1: The user inputs `sort-strength` to sort the entire person list by total number of strengths in descending order. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find the command class to process the command. + +Step 3: It then returns a newly initialised `SortStrengthCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, `ModelManager#setAddressBook()` method is called to update the person list with the newly sorted list of persons based on total number of strengths in descending order. + +
+ +The steps above are summarised using a sequence diagram as shown below. Note that all occurrences of `Strength` or `SortStrengthCommand` (and similar instances) will be generalized to `Note` and `SortNoteCommand` respectively to represent the implementation of the Sort by Strength and Weakness features. +![SortNoteSequenceDiagram](images/SortNoteSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be different sorting functions for address book and schedule:** + +* **Alternative 1 (current choice):** Use different commands for sorting the address book and schedule. + * Pros: Easier to use and more intuitive. + * Cons: Difficult to extend and modify. +* **Alternative 2:** Single `sort` command for sorting the `AddressBook` and `TaskBook`. + * Pros: Easier to extend and modify. + * Cons: Not intuitive for the user and difficult to implement + +
+ +### 4.2 Task Management + +#### 4.2.1 Add Feature + +##### Implementation + +This feature allows the user to add tasks to the task list. It is facilitated by `ModelManager` which +makes use of the method `#addTask()` to add a new task to the task list. + +Given below is an example usage scenario of how the add task mechanism behaves at each step. + +Step 1: The user inputs `add-t n/Meet d/11-11-2022 st/11:00 et/01:00` to add a new task to the task list. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `AddTaskCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `AddTaskCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#addTask()` is called which adds the new task to an internal list and updates the GUI display. A new task named "Meet" with the subsequent date and time details is then shown in the task list. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![AddTaskSequenceDiagram](images/AddTaskSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be an abstraction for tasks:** + +* **Alternative 1 (current choice):** Separate `add-t` command for creating a task. + * Pros: Easy to extend and modify. + * Cons: Not as intuitive for the user. +* **Alternative 2:** Single `add` command that adds tasks/persons depending on parameters. + * Pros: More intuitive for the user. + +
+ +#### 4.2.2 Delete Feature + +##### Implementation + +This feature allows the user to delete tasks from the task list. It is facilitated by `ModelManager` which +makes use of the method `#deleteTask()` to delete a task from the task list. - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +Given below is an example usage scenario of how the delete task mechanism behaves at each step. + +Step 1: The user inputs `del-t 1` to delete the first task in the task list. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `DeleteTaskCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `DeleteTaskCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#deleteTask()` is called which deletes the specified person from an internal list and updates the GUI display. A new task list without the deleted task then shown. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![DeleteTaskSequenceDiagram](images/DeleteTaskSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be an abstraction for tasks:** + +* **Alternative 1 (current choice):** Separate `del-t` command for deleting a task. + * Pros: Easy to extend and modify. + * Cons: Not as intuitive for the user. +* **Alternative 2:** Single `del` command that deletes tasks/persons depending on parameters. + * Pros: More intuitive for the user. + +
+ +#### 4.2.3 Add Tags Feature + +##### Implementation +This feature allows the user to add tags to tasks in the list. It is facilitated by `ModelManager` which +makes use of the method `#setTask()` and `#updateFilteredTaskList()` to add tags to a task. + +Given below is an example usage scenario of how the add tag mechanism behaves at each step. + +Step 1: The user inputs `tag-add-t 1 important` to add the tag "friend" to the first task in the list. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `AddTaskTagCommandParser` where its method `#parse` is called to process the user inputs. + +Step 3: It then returns a newly initialised `AddTaskTagCommand` back to the `LogicManager` for command execution. This `AddTaskTagCommand` contains information about the new tag (in this case, "important") + +Step 4: During the command execution, the `ModelManager#setTask()` is called which edits the tags of the task with the user-supplied tags. The filtered task list is updated with `ModelManager#updateFilteredTaskList` to display the new information to the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![AddTaskTagSequenceDiagram](images/AddTaskTagSequenceDiagram.png) + +
+ +#### 4.2.4 Delete Tags Feature + +##### Implementation +This feature allows the user to delete tags from tasks in the list. It is facilitated by `ModelManager` which +makes use of the method `#setTask()` and `#updateFilteredTaskList()` to delete tags from a task. + +Given below is an example usage scenario of how the delete tag mechanism behaves at each step. + +Step 1: The user inputs `tag-del-t 1 important` to delete the tag "important" from the first task in the list. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `DeleteTaskTagCommandParser` where its method `#parse` is called to process the user inputs. + +Step 3: It then returns a newly initialised `DeleteTaskTagCommand` back to the `LogicManager` for command execution. This `DeleteTaskTagCommand` contains information about the tag to be deleted (in this case, "important") + +Step 4: During the command execution, the `ModelManager#setTask()` is called which edits out the tag from the task. The filtered task list is updated with `ModelManager#updateFilteredTaskList` to display the new information to the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![DeleteTaskTagSequenceDiagram](images/DeleteTaskTagSequenceDiagram.png) + +
+ +#### 4.2.5 Edit Feature + +##### Implementation + +This feature allows the user to edit tasks from the task list. It is facilitated by `ModelManager` which +makes use of the method `#setTask()` to update a task from the task list. + +Given below is an example usage scenario of how the edit task mechanism behaves at each step. + +Step 1: The user inputs `edit-p 1 n/Meeting` to edit the first task in the task list. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `EditTaskCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `EditTaskCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#setTask()` is called which edits the specified task from an internal list and updates the GUI display. A new task list with the updated task details is then shown. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![EditTaskSequenceDiagram](images/EditTaskSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be an abstraction for tasks:** + +* **Alternative 1 (current choice):** Separate `edit-t` command for editing a task. + * Pros: Easy to extend and modify. + * Cons: Not as intuitive for the user. +* **Alternative 2:** Single `edit` command that edits tasks/persons depending on parameters. + * Pros: More intuitive for the user. + +
+ +#### 4.2.6 Clear Feature + +##### Implementation + +This feature allows users to clear all tasks from the task list, or only tasks that correspond with a given date. +It is facilitated by the `ModelManager` which utilizes the method `deleteTask()` to delete each corresponding task one +by one, or sets a new `TaskBook` object to the `ModelManager` to refresh the task list. + +Given below is an example usage scenario of how the clear task mechanism behaves at each step. + +Step 1: The user inputs `clear-t 2022-10-10` to clear all tasks that correspond with the date `2022-10-10` in the task list. + +Step 2: This argument is passed into the `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable +parser class which corresponds with the provided command to parse the user's inputs. This initializes the `ClearTaskCommandParser`, +where its method `parse()` is called to process the user inputs. + +Step 3: The newly initialized `ClearTaskCommandParser` is then returned to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#deleteTask()` method is called multiple times to remove the +corresponding tasks from the internal task list. Inside the function call, the `ModelManager#updateFilteredTaskList()` +is also called, which updates the GUI to display the new task list. The command results are then generated and shown to +the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![ClearTaskSequenceDiagram](images/ClearTaskSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be separate clear commands for clearing tasks and players:** + +* **Alternative 1 (current choice):** A separate command for clearing tasks and players. + * Pros: Easy to implement. + * Cons: Hard to extend. +* **Alternative 2:** A combined command for clearing tasks and player. + * Pros: Easier and more intuitive for the user to understand + * Cons: Hard to implement. + +
+ +#### 4.2.7 Find Feature + +##### Implementation + +This feature allows the user to display selected tasks in the task list. It is facilitated by `ModelManager` which +makes use of the method `#updateFilteredTaskList()` to find tasks by name or tag. + +Given below is an example usage scenario of how the find task mechanism behaves at each step. + +Step 1: The user inputs `find n/meeting t/friends` to find selected tasks. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `FindTaskCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `FindTaskCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#updateFilteredTaskList()` is called which updates the GUI display with only selected tasks shown in the task list. The command results are then generated and shown to the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![FindTaskSequenceDiagram](images/FindTaskSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be a separate find command for name and tag:** + +* **Alternative 1 (current choice):** A combined command for finding name and tag. + * Pros: Easy to implement. + * Cons: Users may have to remember more prefixes. + +* **Alternative 2:** A separate command for finding name and tag. + * Pros: Commands may be more intuitive to the users. + * Cons: Possible violation of the DRY principle. + +**Aspect: Should there be separate find commands for finding tasks and persons:** + +* **Alternative 1 (current choice):** A separate command for finding tasks and persons. + * Pros: Easy to implement. + * Cons: Hard to extend. + +* **Alternative 2:** A combined command for finding tasks and persons. + * Pros: Easier and more intuitive for the user to understand. + * Cons: Hard to implement. + +
+ +#### 4.2.8 Get Person Feature + +##### Implementation + +This feature allows the user to retrieve contact details of persons tagged to a selected task in the task list. It is facilitated by `ModelManager` which +makes use of the method `#updateFilteredPersonList()` to get persons tagged to a task. + +Given below is an example usage scenario of how the get person mechanism behaves at each step. + +Step 1: The user inputs `get-person 1` to retrieve contact details of persons tagged to the first task. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `GetPersonCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `GetPersonCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#updateFilteredPersonList()` is called. The GUI display then updates the person list - showing only the contact details of persons tagged to the specified task. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![GetPersonSequenceDiagram](images/GetPersonSequenceDiagram.png) + +
+ +#### 4.2.9 Sort by Date Feature + +##### Implementation + +This feature allows the user to sort the task list in chronological order. The resulting task list will be sorted by earliest date and time first. +It is facilitated by `ModelManager` which makes use of the method `#getUnfilteredTaskList()` to get the list of tasks. + +Given below is an example usage scenario of how the sort date mechanism behaves at each step. + +Step 1: The user inputs `sort-date` to sort the task list by date. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22#parseCommand()`. Since this command does not require parameters, the SortTaskByDateCommand object is created directly instead of through a Parser. + +Step 3: The newly initialised `SortTaskByDateCommand` is returned back to the `LogicManager` for command execution. + +Step 4: During the command execution, the current list of tasks is sorted using an internal sorting algorithm, then a new TaskBook object is created to store the new ordered list of tasks. + +Step 5: The model replaces its old TaskBook with the new sorted TaskBook using `ModelManager#setTaskBook`, and the GUI updates the task list accordingly. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![SortTaskByDateSequenceDiagram](images/SortTaskByDateSequenceDiagram.png) + +
+ +### 4.3 Strategic Planning + +#### 4.3.1 Add Feature + +##### Implementation + +This feature allows the user to add players to the strategy board. It is facilitated by `ModelManager` which +makes use of the method `#addPlayer()` and `#updateFilteredPlayerList()` to add a new player to the strategy board. + +Given below is an example usage scenario of how the adding player mechanism behaves at each step. + +Step 1: The user inputs `add-player Cena` to add a new player to the strategy board. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `AddPlayerCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `AddPlayerCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#addPlayer()` is called which adds the new player to an internal list and updates the GUI display with a new player named "Cena" shown in the strategy board. The command results are then generated and shown to the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![AddPlayerSequenceDiagram](images/AddPlayerSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be an abstraction for players:** + +* **Alternative 1:** A player is a String of player name. + * Pros: Easy to implement. + * Cons: Hard to extend. +* **Alternative 2 (current choice):** A player is an object of class `Player`. + * Pros: Easy to extend and manipulate attributes of a player. + * Cons: Hard to implement. + +
+ +#### 4.3.2 Load Court Feature + +##### Implementation + +This feature allows the user to load an image to serve as the background of the strategy board. It is facilitated by `CommandResult` which +carries a representation of the image to the `MainWindow` to update the background of the strategy board accordingly. + +Given below is an example usage scenario of how the load court mechanism behaves at each step. + +Step 1: The user inputs `load-court basketball` to load the image from the filepath `courts/basketball.png` to serve as the background of strategy board. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `LoadCourtCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `LoadCourtCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `CommandResult` object is returned to the `MainWindow`, where `CommandResult#getBackgroundImage()` is called to retrieve the image representation. Then, `MainWindow#handleLoadImage(back)` is called to update the background of the strategy board with a new image. The command results are then generated and shown to the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![LoadCourtSequenceDiagram](images/LoadCourtSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be different filetypes for images:** + +* **Alternative 1 (current choice):** Only png images are allowed. + * Pros: Easy to implement. + * Cons: Hard to extend and not intuitive for the user. +* **Alternative 2:** Allow for different filetypes of images (png, jpeg, etc.). + * Pros: Easy to extend and more intuitive for the user. + * Cons: Hard to implement. - 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. +
+ +#### 4.3.3 Delete Feature - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. +##### Implementation - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. +This feature allows the user to remove players from the strategy board. It is facilitated by `ModelManager` which +makes use of the method `#deletePlayer()` and `#updateFilteredPlayerList()` to delete an existing player from the strategy board. -1. _{ more test cases …​ }_ +Given below is an example usage scenario of how the deleting player mechanism behaves at each step. -### Saving data +Step 1: The user inputs `del-player Cena` to remove a player named `Cena` from the strategy board. -1. Dealing with missing/corrupted data files +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `DelPlayerCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `DeletePlayerCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#deletePlayer()` is called which remove the player from an internal list and updates the GUI display with a new player named "Cena" shown in the strategy board. The command results are then generated and shown to the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![DeletePlayerSequenceDiagram](images/DeletePlayerSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should allow users to remove multiple players at once:** + +* **Alternative 1:** Users can remove multiple players at once. + * Pros: Possibly improve the efficiency for users. + * Cons: User inputs are more prone to errors. +* **Alternative 2 (current choice):** Users can only remove one player at a time. + * Pros: Easy to implement. + * Cons: Users need to repeat the same command multiple times to remove multiple players. + +
+ +#### 4.3.4 Move Feature + +##### Implementation + +This feature allows the user to move players on the strategy board. It is facilitated by `ModelManager` which +makes use of the method `#deletePlayer()`, `#addPlayer()`, and `#updateFilteredPlayerList()` to move an existing player to a new position on the strategy board. + +Given below is an example usage scenario of how the moving player mechanism behaves at each step. + +Step 1: The user inputs `move Cena x/200 y/100` to move a player named `Cena` to a new position with x-coordinate `200` and y-coordinate `100` on the strategy board. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable parser class to process the user inputs. This initialises the `MovePlayerCommandParser` where its method `#parse()` is called to process the user inputs. + +Step 3: It then returns a newly initialised `MovePlayerCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `ModelManager#deletePlayer()` is firstly called which removes the player named "Cena" from an internal list. +Then the `ModelManager#addPlayer()` is called which adds the player with same name but new position to an internal list and updates the GUI display with a player named "Cena" with new position `(200, 100)` shown in the strategy board. +At last, the command results are then generated and shown to the user. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![MovePlayerSequenceDiagram](images/MovePlayerSequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should allow users to move multiple players at once:** +* **Alternative 1:** Users can move multiple players at once. + * Pros: Possibly improve the efficiency for users. + * Cons: User inputs are more prone to errors. +* **Alternative 2 (current choice):** Users can only move one player at a time. + * Pros: Easy to implement. + * Cons: Users need to repeat the same command multiple times to move multiple players. + +
+ +#### 3.3.5 Export Feature + +##### Implementation + +This feature allows the user to export the strategy board as a .png image into their device. +It is facilitated by `CommandResult` which makes use of the method `#captureAndSaveStrategyBoard()` +from the `StrategyPanel` to export the strategy board. + +Given below is an example usage scenario of how the export command mechanism behaves at each step. + +Step 1: The user inputs `export` to into the command line to export the current strategy board. + +Step 2: This argument is passed into `LogicManager` which calls on `Coach2K22Parser#parseCommand()` to find a suitable +parser class to process the user inputs. + +Step 3: It then returns a newly initialised `ExportCommand` back to the `LogicManager` for command execution. + +Step 4: During the command execution, the `CommandResult` object is returned to the `MainWindow`, +where `MainWindow#captureAndSaveStrategyPanel()` is called to capture an image of the strategy board. +The user is then prompted to choose a directory from their local disk to save the image in, and hence the picture is saved locally. + +
+ +The steps above are summarised using a sequence diagram as shown below. +![ExportCommandDiagram](images/ExportStrategySequenceDiagram.png) + +##### Design Consideration + +**Aspect: Should there be a predetermined location and randomly generated file names for the images:** + +* **Alternative 1 (current choice):** User is prompted to select their local directory and filename of choice. + * Pros: Allows users to categorize and structure their filenames according to their preferences. + * Cons: Hard to guarantee execution on user end. +* **Alternative 2:** A directory is created in-app with randomly generated file names`. + * Pros: Easier for users as they have less work to do. + * Cons: Hard to organize and structure. + +-------------------------------------------------------------------------------------------------------------------- + +
+ +## **5. Documentation, logging, testing, configuration, dev-ops** + +* [Documentation guide](Documentation.md) +* [Testing guide](Testing.md) +* [Logging guide](Logging.md) +* [Configuration guide](Configuration.md) +* [DevOps guide](DevOps.md) + +-------------------------------------------------------------------------------------------------------------------- + +## **6. Appendix: Requirements** + +### 6.1 Product scope + +**Target user profile**: + +* is a coach managing a team of players +* has a need to manage a significant number of players +* prefer desktop apps over other types +* can type fast +* prefers typing to mouse interactions +* is reasonably comfortable using CLI apps +* needs to keep track of administrative tasks +* can help enhance decision-making during games instantaneously + +**Value proposition**: helps busy sports coaches organise their overwhelming lists of contacts and messy weekly +schedules, and provides them with a platform to visualise defensive and offensive plays as the game unfolds + +
+ +### 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…​ | +|----------|---------------------------------|-----------------------------------------------------------------|------------------------------------------------------------------------------| +| `* * *` | forgetful coach | enter team-specific or player note | look up these information | +| `* * *` | coach | delete team-specific or player note | keep these information relevant and up-to-date | +| `* * *` | forgetful coach | remember the names of players on my team | look up them in case I forget | +| `* * *` | disorganized | add and tag new roles/teams to a contact | easily retrieve relevant information | +| `* * *` | coach | easily retrieve contact information of relevant parties | quickly broadcast information to them | +| `* * *` | organized coach | view players by their strengths and weaknesses | make informed decision on choosing the best person | +| `* * *` | disorganized coach | add existing and upcoming tasks | keep track of my schedule easily | +| `* * *` | disorganized coach | delete wrongly added or past tasks | organize my schedule better | +| `* *` | lazy and forgetful coach | view a list of help commands and their descriptions | easily recall how to do a specific task | +| `* *` | organised coach | view players by their strengths and weaknesses | make informed decisions on choosing the best person for a specific objective | +| `**` | organized and data-driven coach | sort my players by strengths and weaknesses | get a better understanding of how players compare | +| `*` | strategic coach | change the position of players (x-y coordinate) during the game | ensure my team works together | +| `*` | coach | drag and drop a player into a calendar | plan scheduled events for them according to their needs | + +
+ +### 6.3 Use cases + +(For all use cases below, the **System** is `Coach2K22` and the **Actor** is the `user`, unless specified otherwise) + +#### 6.3.1 Contact Management + +**Use case: Add a person** + +**MSS** + +1. User requests to add a new person in the list +2. Coach2K22 shows a list with the newly added person + + Use case ends. + +**Extensions** + +* 1a. The parameters supplied by the user is invalid. + + * 1a1. Coach2K22 shows an error message. + + Use case ends. + +* 1b. Compulsory parameters not supplied by the user. + + * 1b1. Coach2K22 shows an error message. + + Use case ends. + +
+ +**Use case: Edit a person** + +**MSS** + +1. User requests to edit an existing person in the list +2. Coach2K22 shows a list with the newly edited person + + Use case ends. + +**Extensions** + +* 1a. The person list is empty. + + Use case ends. + +* 1b. The parameters supplied by the user is invalid. + + * 1b1. Coach2K22 shows an error message. + + Use case ends. + +* 1c. Compulsory parameters not supplied by the user. + + * 1c1. Coach2K22 shows an error message. + + Use case ends. + +
+ +**Use case: Delete a person** + +**MSS** + +1. User requests to list persons +2. Coach2K22 shows a list of persons +3. User requests to delete a specific person in the list +4. Coach2K22 deletes the person + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + + Use case ends. + +* 3a. The given index is invalid. + + * 3a1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +
+ +**Use case: Add a strength to a person** + +**MSS** + +1. User requests to list persons +2. Coach2K22 shows a list of persons +3. User requests to add a strength to a person +4. Coach2K22 shows the new details of the person + + Use case ends. + +**Extensions** + +* 2a. The person list is empty. + + * 2a1. Coach2K22 shows an error message. + + Use case ends. + +* 3a. The given list index cannot be found in Coach2K22. + + * 3a1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +* 3b. The strength provided is an empty string. + + * 3b1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +
+ +**Use case: Add a weakness to a person** + +* This use case describes a similar interaction between the user and Coach2K22 to that of `Add a strength to a person` + * Takes in a weakness instead of a strength + +**Use case: Add a miscellaneous note to a person** + +* This use case describes a similar interaction between the user and Coach2K22 to that of `Add a strength to a person` + * Takes in a miscellaneous note instead of a strength + +**Use case: Delete a strength from a person** + +**MSS** + +1. User requests to list persons +2. Coach2K22 shows a list of persons +3. User requests to delete a strength for a person +4. Coach2K22 shows the new details of the person + + Use case ends. + +
+ +**Extensions** + +* 2a. The person list is empty. + + * 2a1. Coach2K22 shows an error message. + + Use case ends. + +* 3a. The given list index is invalid. + + * 3a1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +* 3b. Strengths have not been assigned to the person. + + * 3b1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +* 3c. The given strength index is invalid. + + * 3c1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +
+ +**Use case: Delete a weakness from a person** + +* This use case describes a similar interaction between the user and Coach2K22 to that of `Delete a strength from a person` + * Takes in a weakness index instead of a strength index + +**Use case: Delete a miscellaneous note from a person** + +* This use case describes a similar interaction between the user and Coach2K22 to that of `Delete a strength from a person` + * Takes in a misc. index instead of a strength index + +**Use case: Find persons by name or tag** + +**MSS** + +1. User requests to list persons with a specific name or tag +2. Coach2K22 shows a list of filtered persons + + Use case ends. + +
+ +**Extensions** + +* 1a. The given name and tag keywords cannot be found in Coach2K22. + + * 1a1. Coach2K22 shows an empty list. + + Use case ends. + +* 1b. The keyword provided is not indicated by a prefix e.g. `n/` or `t/`. + + * 1b1. Coach2K22 shows an error message. + + Use case ends. + +* 1c. No keywords are provided after the `find` command. + + * 1c1. Coach2K22 shows an error message. + + Use case ends. + +* 1d. Missing keyword after a prefix is given e.g. `n/` or `t/`. + + * 1d1. Coach2K22 shows an error message. + + Use case ends. + +
+ +**Use case: Sort address book by strengths in descending order** + +**MSS** + +1. User requests to sort the list of persons by total number of strengths +2. Coach2K22 shows the new sorted list of persons + + Use case ends. + +**Extensions** + +* 1a. The person list is empty. + + * 1a1. Coach2K22 shows an error message. + + Use case ends. + +**Use case: Sort address book by weaknesses in descending order** + +* This use case describes a similar interaction between the user and Coach2K22 to that of `Sort address book by strengths in descending order` + * Sorts list of persons by total number of weaknesses in descending order instead of total number of strengths + +
+ +**Use case: Add a tag to a person** + +**MSS** + +1. User requests to list persons +2. Coach2K22 shows a list of persons +3. User requests to attach a new tag to a person +4. Coach2k22 shows the new details of the person + + Use case ends. + +**Extensions** + +* 2a. The list is empty + + Use case ends. + +
+ +**Use case: Delete a tag from a person** + +**MSS** + +1. User requests to list persons +2. Coach2K22 shows a list of persons +3. User requests to remove an existing tag from a person +4. Coach2k22 shows the updated details of the person + + Use case ends. + +**Extensions** + +* 2a. The list is empty. + + Use case ends. +@ +* 3a. The tag requested to remove does not exist for the person. + + * 3a1. Coach2K22 shows an error message. + + Use case ends. + +* 3b. No argument for tag removal is provided. + + * 3b1. Coach2K22 shows an error message. + + Use case ends. + +
+ +#### 6.3.2 Task Management + +**Use case: Add a task to the task list** + +**MSS** + +1. User requests to add a new task to the list +2. Coach2K22 shows a list with the newly added task + + Use case ends. + +**Extensions** + +* 1a. The parameters supplied by the user is invalid. + + * 1a1. Coach2K22 shows an error message. + + Use case resumes at step 1. + +* 1b. Compulsory parameters not supplied by the user. + + * 1b1. Coach2K22 shows an error message. + + Use case resumes at step 1. + +
+ +**Use case: Edit a task** + +**MSS** + +1. User requests to edit an existing task from the list +2. Coach2K22 shows a list with the newly edited task + + Use case ends. + +**Extensions** + +* 1a. The task list is empty. + + Use case ends. + +* 1b. No index supplied by the user. + + * 1b1. Coach2K22 shows an error message. + + Use case ends. + +* 1c. The parameters supplied by the user is invalid. + + * 1c1. Coach2K22 shows an error message. + + Use case ends. + +* 1d. Compulsory parameters not supplied by the user. + + * 1d1. Coach2K22 shows an error message. + + Use case ends. + +
+ +**Use case: Delete a task from the task list** + +**MSS** + +1. User requests to view the list of tasks +2. Coach2K22 shows a list of tasks +3. User requests to delete a specific task from the list +4. Coach2K22 deletes the task + + Use case ends. + +**Extensions** + +* 1a. The task list is empty. + + Use case ends. + +* 3a. The index requested does not exist. + + * 3a1. Coach2K22 shows an error message. + + Use case resumes at step 2 + +
+ +**Use case: Clear all tasks from task list** + +**MSS** + +1. User requests to list tasks +2. Coach2K22 shows a list of tasks +3. User requests to clear the task list +4. Coach2k22 shows the updated details of the task list + + Use case ends. + +**Extensions** + +* 2a. The task list is empty. + + Use case ends. + +
+ +**Use case: Clear all tasks for a specified date from task list** + +**MSS** + +1. User requests to list tasks +2. Coach2K22 shows a list of tasks +3. User requests to clear all tasks of a specified date from the task list +4. Coach2k22 shows the updated details of the task list + + Use case ends. + +**Extensions** + +* 2a. The task list is empty. + + Use case ends. + +* 3a. The provided date is not in the correct format. + + Use case resumes at step 2. + +
+ +**Use case: Add a tag to a task from task list** + +**MSS** + +1. User requests to list tasks +2. Coach2K22 shows a list of tasks +3. User requests to add a tag to a task of a specified index in the task list +4. Coach2k22 shows the updated details of the task list + + Use case ends. + +**Extensions** + +* 2a. The task list is empty. + + Use case ends. + +
+ +**Use case: Remove a tag from a task in task list** + +**MSS** + +1. User requests to list tasks +2. Coach2K22 shows a list of tasks +3. User requests to remove a tag from a task of a specified index in the task list +4. Coach2K22 shows the updated details of the task list + + Use case ends. + +**Extensions** + +* 1a. The task list is empty. + + Use case ends. + +* 3a. The task does not have that specified tag. + + Use case resumes at step 2. + +* 3b. The index provided is invalid. + + Use case resumes at step 2. + +
+ +**Use case: Find tasks by name or tag** + +**MSS** + +1. User requests to view the list of tasks +2. Coach2K22 shows a list of filtered tasks + + Use case ends. + +**Extensions** + +* 1a. The given name and tag keywords cannot be found in Coach2K22. + + * 1a1. Coach2K22 shows an empty list. + + Use case ends. + +* 1b. The keyword provided is not indicated by a prefix e.g. `n/` or `t/`. + + * 1b1. Coach2K22 shows an error message. + + Use case ends. + +* 1c. No keywords are provided after the `find` command. + + * 1c1. Coach2K22 shows an error message. + + Use case ends. + +* 1d. Missing keyword after a prefix is given e.g. `n/` or `t/`. + + * 1d1. Coach2K22 shows an error message. + + Use case ends. + +
+ +**Use case: Find persons tagged to a task** + +**MSS** + +1. User requests to view the list of tasks +2. Coach2K22 shows a list of tasks +3. User requests to find persons tagged to a specified task from the list +4. Coach2K22 shows a filtered list containing the specified persons + + Use case ends. + +**Extensions** + +* 1a. The task list is empty. + + Use case ends. + +* 3a. The index requested does not exist. + + * 3a1. Coach2K22 shows an error message. + + Use case resumes at step 2 + +* 3b. No persons are tagged to the specified task. + + * 3b1. Coach2K22 shows an empty contact list. + + Use case ends + +
+ +**Use case: Sort task list by date** + +**MSS** + +1. User requests to sort the task list by date. +2. Coach2k22 shows the updated task list, sorted with the tasks with the earliest deadline at the top. + + Use case ends. + +**Extensions** + +* 1a. The task list is empty. + + Use case ends. + +
+ +#### 6.3.3 Strategic Planning + +**Use case: Add a new player to the strategy board** + +**MSS** + +1. User requests to show all players on the strategy board +2. Coach2K22 shows players +3. User requests to add a new player to the strategy board +4. Coach2K22 shows the updated strategy board with the new player + + Use case ends. + +**Extensions** +* 3a. The player name is invalid. + * 3a1. Coach2K22 shows an error message. + + Use case resumes at step 2. +* 3b. The player name is already on the strategy board. + * 3b1. Coach2K22 shows an error message. + + Use case resumes at step 2. +* 3c. The player name is not provided. + * 3c1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +
+ +**Use case: Remove a player from the strategy board** + +**MSS** + +1. User requests to show all players on the strategy board +2. Coach2K22 shows players +3. User requests to remove a player from the strategy board +4. Coach2K22 shows the updated strategy board without the player + + Use case ends. + +**Extensions** +* 3a. The player name is invalid. + * 3a1. Coach2K22 shows an error message. + + Use case resumes at step 2. +* 3b. The player name is not on the strategy board. + * 3b1. Coach2K22 shows an error message. + + Use case resumes at step 2. +* 3c. The player name is not provided. + * 3c1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +
+ +**Use case: Move a player on the strategy board** + +**MSS** + +1. User requests to show all players on the strategy board +2. Coach2K22 shows players +3. User requests to move a player on the strategy board +4. Coach2K22 shows the updated strategy board with the player moved to the new position + + Use case ends. + +**Extension** +* 3a. The player name is invalid. + * 3a1. Coach2K22 shows an error message. + + Use case resumes at step 2. +* 3b. The player name is not on the strategy board. + * 3b1. Coach2K22 shows an error message. + + Use case resumes at step 2. +* 3c. The position is invalid. + * 3c1. Coach2K22 shows an error message. + + Use case resumes at step 2. + +
+ +**Use case: Load new background image for strategy tab** + +**MSS** + +1. User requests load a new background image. +2. Coach2k22 shows the updated strategy tab with the new background image. + + Use case ends. + +**Extensions** + +* 1a. Provided name of image is invalid. + + Use case ends. + +* 1b. Image does not exist. + + Use case ends. + +* 1c. Image is not in `png` format. + + Use case ends. + +**Use case: Export strategy tab** + +**MSS** + +1. User requests to export the current strategy tab. +2. Coach2k22 creates the image and prompts user to save it locally. + + Use case ends. + +
+ +### 6.4 Non-Functional Requirements + +1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. +2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. +3. Should be able to hold up to 100 teams without a noticeable sluggishness in performance for typical usage. +4. Should be able to hold up to 50 tags without a noticeable sluggishness in performance for typical usage. +5. Should be portable so moving from one OS to another OS will not create problems. +6. Should warn the user when attempting to delete a contact. +7. 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. + + +### 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 +* **Time-clash**: An error where the user attempts to schedule an event at the same time as another +* **Switchover**: The action of switching a player for another on a given field +* **Liability-Potential** The statistics of a player's overall penalties and injuries across games +* **DRY Principle** The *Don't Repeat Yourself (DRY)* is a Software Engineering principle of reducing repetition in the code + +-------------------------------------------------------------------------------------------------------------------- + +
+ +## **7. Appendix: Instructions for manual testing** + +Given below are instructions to test the app manually. + +
:information_source: **Note:** These instructions only provide a starting point for testers to work on; +testers are expected to do more *exploratory* testing. + +
+ +### 7.1 Launch and shutdown + +1. Initial launch + +2. Download the jar file and copy into an empty folder + +3. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. + +4. Saving window preferences + +5. Resize the window to an optimum size. Move the window to a different location. Close the window. + +6. Re-launch the app by double-clicking the jar file.
+ Expected: The most recent window size and location is retained. + + +
+ +### 7.3 Saving data + +1. Dealing with corrupted data files + +1. Stop the program. + +2. Delete all files in the folder `data`; This folder should be found in the same directory as your JAR file. + +3. Restart the program. + +### 7.2 Deleting a person + +1. Deleting a person while all persons are being shown + + 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. + + 2. 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. + + 3. Test case: `delete 0`
+ Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + + 4. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous. + +
+ +### 7.4 Adding a Strength/Weakness/Miscellaneous note to a person + +1. Adding a strength/weakness/miscellaneous (using `strength-add`, `weakness-add`, and `misc-add` respectively) note to a person while currently viewable person list has people. + +2. Prerequisites: Currently viewable Contacts list has to contain some people. + +3. Test case: `strength-add 1 good stamina`
+ Expected: The note `good stamina` is added to the strength list of the first person in the current Contacts list. Details of the modified contact shown in the status message. + +4. Test case: `weakness-add 1 bad stamina`
+ Expected: The note `bad stamina` is added to the weakness list of the first person in the current Contacts list. Details of the modified contact shown in the status message. + +5. Test case: `misc-add 1 likes ice cream`
+ Expected: The note `likes ice cream` is added to the misc list of the first person in the current Contacts list. Details of the modified contact shown in the status message. + +6. Test case: `strength-add 0 good stamina`
+ Expected: No note is added. Error details shown in the status message. Status bar remains the same. + +7. Other incorrect commands to try: `strength-add`, `misc-add x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous. + +
+ +### 7.5 Deleting a Strength/Weakness/Miscellaneous note to a person + +1. Deleting a strength/weakness/miscellaneous note (using `strength-del`, `weakness-del`, and `misc-del` respectively) to a person while currently viewable person list has people. + +2. Prerequisites: Currently viewable Contacts list has to contain some people. + +3. Test case: `strength-del 1 1`
+ Expected: The first note is deleted from the strength list of the first person in the current Contacts list. Details of the modified contact shown in the status message. + +4. Test case: `weakness-del 1 1`
+ Expected: Similar to previous, except the note is deleted from the weakness list. + +5. Test case: `misc-del 1 1`
+ Expected: Similar to previous, except the note is deleted from the misc list. + +6. Test case: `strength-del 0 1`
+ Expected: No note is deleted. Error details shown in the status message. Status bar remains the same. + +7. Test case: `strength-del 1 0`
+ Expected: Similar to previous. + +8. Other incorrect commands to try: `strength-del`, `misc-del x`, `...` (where x is larger than the list size)
+ Expected: Similar to previous. + + +
+ +### 7.6 Clearing all Tasks from the task list or only those on a particular day + +1. Empty the task list or remove only those that correspond with a given day. + +2. Prerequisites: Unfiltered Schedule must contain some tasks. + +3. Test case: `clear-t`
+ Expected: The entire task list in contacts will be cleared. + +4. Test case: `clear-t d/03-03-2022`
+ Expected: If there are tasks that are allocated on `03-03-2022`, remove them. Otherwise, no task is deleted, and error details are shown in the status message. + +5. Test case: `clear-t d/30-02-2022`
+ Expected: No task is deleted. Error details shown in the status message. Status bar remains the same. + +6. Test case: `clear-t d/2022-03-03`
+ Expected: Similar to previous. + +7. Other incorrect commands to try: `clear-t d/abc`, `clear-t d`, `clear-t d/`, `...`
+ Expected: Similar to previous. + +
+ +### 7.7 Sort the Contacts list by strength or weakness in descending order + +1. Sort the entire Contact list by strength (for `sort-strength`) or weakness (for `sort-weakness`) in descending order + +2. Prerequisites: Unfiltered Contacts list must contain some people. + +3. Test case: `sort-strength`
+ Expected: The entire contact list is sorted by total number of strengths in descending order. + +4. Test case: `sort-weakness`
+ Expected: The entire contact list is sorted by total number of weaknesses in descending order. + +5. Test case: `sort-strength abc`
+ Expected: The entire contact list is sorted by total number of strengths in descending order (anything after the command word is ignored). + +
+ +### 7.8 Load new image to serve as court in Strategy Tab + +1. Loads the given image by the provided name to serve as the court in Strategy Tab. + +2. Prerequisites: Image must be a png file and stored in the automatically generated `courts` directory which exists in the same folder as the JAR. + +3. Test case: `load-court basketball`
+ Expected: The image `basketball.png` will be loaded from the `courts` directory and serve as the court in the Strategy tab. If it does not exist, no image is loaded, and error details are shown in the status message. + +4. Test case: `load-court`
+ Expected: No image is loaded, and error details are shown in the status message. + +5. Test case: `load-court test/`
+ Expected: Similar to previous. Even if `test.png` exists, as `/` are not allowed in command argument. + +
+ +### 7.9 Finding persons by name or tag + +1. Find persons matching any of the given keywords from our contact list. You can choose to find by `NAME(s)`, `TAG(s)`, or both. + +2. Prerequisites: Contact List should not be empty. + +3. Test case: `find-p n/Alan t/team1`
+ Expected: All persons whose name matches the keyword `Alan` (case-insensitive) or tag matches the keyword `team1`(case-insensitive). + +4. Test case: `find-p n/Alan n/John`
+ Expected: All persons whose name matches at least one of the keywords `Alan` or `John` (case-insensitive). + +5. Test case: `find-p t/team1 n/team2` + Expected: All persons whose tag matches at least one of the keyword `team1` or `team2` (case-insensitive). + +6. Test case: `find-p` + Expected: Error message shown in the status message denoting the arguments `find-p` takes in. + +
+ +### 7.10 Finding tasks by name or tag + +1. Find tasks matching any of the given keywords from our contact list. You can choose to find by `NAME(s)`, `TAG(s)`, or both. + +2. Prerequisites: Task List should not be empty. + +3. Test case: `find-t n/Meeting t/team1`
+ Expected: All persons whose name matches the keyword `Meeting` (case-insensitive) or tag matches the keyword `team1`(case-insensitive). + +4. Test case: `find-t n/Meeting n/Talk`
+ Expected: All persons whose name matches at least one of the keywords `Meeting` or `Talk` (case-insensitive). + +5. Test case: `find-t t/team1 n/team2`
+ Expected: All persons whose tag matches at least one of the keyword `team1` or `team2` (case-insensitive). + +6. Test case: `find-t`
+ Expected: Error message shown in the status message denoting the arguments `find-t` takes in. + +
+ +### 7.11 Finding persons tagged to a task + +1. Pull out the contact information of persons tagged to a task. + +2. Prerequisites: Task List should not be empty. + +3. Assumption: All tasks have persons tagged to it. + +3.1 Test case: `get-person 1`
+Expected: Switch to the contacts tab and show contact details of all persons tagged to the first task in the task list. + +3.2 Test case: `get-person`
+Expected: Error message shown in the status message denoting the arguments `get-person` takes in. + +4. Assumption: All tasks do not have persons tagged to it. + +4.1 Test case: `get-person 1`
+Expected: Switch to the contacts tab and show an empty contact list. + +4.2 Test case: `get-person`
+Expected: Same as point 3.2. + +
+ +### 7.12 Adding a person +1. Add a person into the contact list. + +2. Test case: `add-p n/Johnson p/83918273 a/Woodlands Avenue 4 e/johnson@gmail.com t/Hustlers`
+ Expected: Add a person named `Johnson` with his relevant details into the contact list + +3. Test case: `add-p`
+ Expected: Error message shown in the status message denoting the arguments `add-p` takes in. + +### 7.13 Editing a person +1. Edit a person in the contact list. + +2. Prerequisites: Contact list should not be empty. + +3. Assumption: The first person in the contact list has the name `Johnson`. + + 3.1 Test case: `edit-p 1 n/John`
+ Expected: The first person in the contact list will have his name changed to `John`. + Additionally, all tasks with person `Johnson` tagged to it will have the specific participant tag changed from `Johnson` to `John`. + + 3.2 Test case: `edit-p 2 a/Woodlands`
+ Expected: The second person in the contact list will have his address details changed to `Woodlands`. + + 3.3 Test case: `edit-p 1`
+ Expected: Error message shown in the status message denoting the arguments `edit-p` takes in. + + 3.4 Test case: `edit-p n/hello`
+ Expected: Same as previous. + + 3.5 Test case: `edit-p`
+ Expected: Same as previous. + +
+ +### 7.14 Clearing all contact entries +1. Clears all entries from the contact list. + +2. Test case: `clear-p`
+ Expected: All entries in the contact list will be cleared. It also clears the participant field of all tasks. + +-------------------------------------------------------------------------------------------------------------------- - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +## **8. Appendix: Effort** -1. _{ more test cases …​ }_ +1. The team has extended the program to deal with three different entity types: `Person`, `Task`, `Strategy`. These entities are interconnected where features specific to `Person` may directly affect the `Task` entity. Deliberate and extensive checks are implemented across features to ensure functional correctness. + +2. The team has implemented a JavaFX strategy board with draggable nodes. In addition, a `move` feature is implemented to cater to existing requirements. It was tough to implement this as nodes placements are determined by its relative position. Our team also tried to get these placements as accurate as possible to ensure functional correctness - increasing the implementation difficulty levels. diff --git a/docs/Gemfile b/docs/Gemfile index 999a7099d8d..f3648431048 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } +git_source(:github) {"https://github.com/AY2122S2-CS2103T-W14-2/tp"} gem 'jekyll' gem 'github-pages', group: :jekyll_plugins diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..fffebdf0067 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -2,50 +2,151 @@ 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. +[![codecov](https://codecov.io/gh/AY2122S2-CS2103T-W14-2/tp/branch/master/graph/badge.svg?token=N3IGRH3TN0)](https://codecov.io/gh/AY2122S2-CS2103T-W14-2/tp) * Table of Contents -{:toc} + {:toc} -------------------------------------------------------------------------------------------------------------------- -## Quick start +## **1. Introduction** +Welcome to the Coach2k22 User guide! +Coach2K22 is a desktop app to helps busy sports coaches **organise their overwhelming lists of contacts and messy weekly schedules.** in addition to providing a **platform to visualise defensive and offensive plays** as the game unfolds. + +## **2. Quick Start** + +This section answers all your question on how to get started! This application is **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, Coach2K22 can get your administrative tasks done faster than it takes to learn the interface of traditional GUI apps! + +### 2.1 Installation Instructions + +Installation is fairly simple, just follow these steps. Do ensure you have Java `11` or above installed in your computer +before starting. + +1.Download the latest '.jar' file from [here](https://github.com/AY2122S2-CS2103T-W14-2/tp/releases). + +2.Copy the file to the folder you want to use as the _home folder_ for your copy of Coach2k22. + +3.Double-click the file to start the app, and you're done! + +A GUI similar to the image below should appear in a few seconds. +Note how the app contains some sample data.
+![Ui](images/Ui.png) + +### 2.2 Using the application + +The GUI is designed to ensure all necessary information is present at first glance. Follow along to find out what each section is and does. + +For reference, below is a labelled diagram of what each section is. +![Ui-labeled](images/Ui-Labeled.png) + +1. Type your commands in the Command Line Interface box (CLI) labeled above. + +2. You can switch between the three tabs (Contact, Schedule, Strategy tabs). + +3. Scroll through each list in the Contact and Schedule tabs to view other items in the list. + +4. Refer to the [Features](#4-features) below for details of each command, as well as their formats.
+ +Contacts and Schedule related tabs have automatic saving features and changes to it will be saved without any user action. +Strategy tabs have the option to be explicitly saved using the `export` command. +For more details head to the [Features](#4-features) section! + +
+ +:bulb: **Note:** Coach2K22 can run on computers with Windows and MacOS (_Requires at least 1GB RAM and 500 MB of storage_). +
-1. Ensure you have Java `11` or above installed in your Computer. +### 2.2 CLI tutorial -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +For those of you that may be encountering a Command Line Interface (CLI) based application for the first time – fret not – it is incredibly simple. +Here's a quick tutorial to get you started! -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +1. Follow the instructions above in section 2.1 to download the Coach2k22 application to a directory of your choice. -1. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png) +2. Double-click the file to launch the application. -1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try: +3. Upon launch, the GUI like in section 2.1 will be visible to you! - * **`list`** : Lists all contacts. +4. Hover your cursor over the CLI input box and click on it once. - * **`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. +5. You can now use their keyboard to type commands into the CLI. - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. +6. If the command entered is valid, it should disappear from the CLI box upon pressing enter. +A message indicating successful completion will pop up. - * **`clear`** : Deletes all contacts. +6.1 A visual successful example for the command [add-task](#422-adding-a-task-add-t) is shown below! - * **`exit`** : Exits the app. +![add-task-true](images/add-task-true.png) -1. Refer to the [Features](#features) below for details of each command. +7. If the command entered is invalid it will turn red and remain in the CLI box upon pressing enter. An error message +detailing what might have gone wrong will pop up instead! + +7.1 A visual example for the command [add-task](#422-adding-a-task-add-t) is shown below! + +![add-task-false](images/add-task-false.png) + +8.For more information on the commands Refer to the [Features](#4-features) section below for more information and details on the command formats Coach2k22 accepts! -------------------------------------------------------------------------------------------------------------------- -## Features -
+## **3. About the User Guide** + +### 3.1 Purpose +This User Guide is meant to help you get started in your journey with Coach2k22! Along with providing an in-depth +documentation of our product to help familarise you with the features, it is also a one stop for any difficulties +you might face as well! + +Refer to [Structure](#32-structure) to get a birds eye view on what's included in this document. + +### 3.2 Structure +This User Guide consist of five sections: [Introduction](#1-introduction), [Quick Start](#2-quick-start), [About the User Guide](#3-about-the-user-guide), [Features](#4-features), [FAQ](#5-faq), and [Command Summary](#6-command-summary). + +* The **Introduction** section provides a general overview of what our product does! + +* The **Quick Start** section provides the basics, including installation instructions, and a CLI quick tutorial to get you started. + +* The **About the User Guide** section introduces you to icons and unfamiliar terms used throughout this document. + +* The **Features** section provides you with step-by-step instructions for every feature. + +* The **FAQ** section provides you with the answer to commonly asked questions. + +* The **Command Summary** section provides a quick summary to every feature's format! It includes usage examples to get started as well. + +A table of content is also provided at the start of this document to allow you to navigate to each section and subsection easily. + +### 3.3 User Guide Icons +The table below shows the icons used in this document with its associated meaning. + +| Icon | Meaning | +|---------------------------------------------------------------------------|---------------------------------------------------------------| +|
:information_source:
| This icon indicates important information to be taken note of | +|
:bulb:
| This icon indicates useful tips for the users | + +### 3.4 Glossary +The table below describes the terms used in this document with its accompanying definitions. + +| Term | Definition | +|--------------------|------------------------------------------------------------------------------------------------| +|**GUI** | The *Graphical User Interface (GUI)* allows program interaction through graphics | +|**CLI** | The *Command Line Interface (CLI)* allows program interaction through commands | +|**Command** | An instruction to the program to perform a specific task or operation
e.g., `list-p` | +|**Prefix** | An indicator used to mark the start of a *Parameter*
e.g., `n/` `st/` | +|**Parameter** | A value supplied by the user in a command
e.g., `NAME` `START_TIME` | +|**Command Format** | The *Command Format* describes the arrangement of the *Command*, *Prefix*, and *Parameter*
e.g., `add-p n/NAME p/PHONE_NUMBER a/ADDRESS e/EMAIL` | + +### 3.5 Notes about the Command Format -**:information_source: Notes about the command format:**
+The Command format includes two major sections, as detailed in this image below: + +![CommandFormatSections](images/CommandFormatSections.png) + +All feature command formats minimally include a 'Command' section. Each feature has its own specifications on what needs to be included in the 'Prefix and Parameter' section, however, they all follow some general principles as detailed below! +The tips in this section include noteworthy information about the command format that will be useful in guiding you through the [Features](#4-features) section. * 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-p n/NAME`, `NAME` is a parameter which can be used as `add-p n/John Doe`. * 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`. @@ -62,131 +163,678 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo * Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`. -
+-------------------------------------------------------------------------------------------------------------------- + +## **4. Features** + +Now for the good part! This section contains an exhaustive list of commands supported by Coach2K22. +They are further classified into the following categories based on their functions: + +* [Contact Management](#41-contact-management) are commands that allow you to manage your contacts efficiently. + +* [Task Management](#42-task-management) are commands that allow you to manage your ongoing tasks. + +* [Strategic Planning](#43-strategic-planning) are commands that allow you to manipulate the strategy board. + +* [General](#44-general) are commands that do not fit into any of the aforementioned categories. -### Viewing help : `help` +### 4.1 Contact Management -Shows a message explaning how to access the help page. +#### 4.1.1 Listing all contacts : `list-p` -![help message](images/helpMessage.png) +Shows a list of all persons in our contact list. -Format: `help` +**Format:** `list-p` +#### 4.1.2 Adding a person: `add-p` -### Adding a person: `add` +Adds a person to our contact list. -Adds a person to the address book. +**Format:** `add-p n/NAME p/PHONE_NUMBER a/ADDRESS e/EMAIL [t/TAG_NAME]…​` -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +**Examples:** +* `add-p n/Johnson p/83918273 a/Woodlands Avenue 4 e/johnson@gmail.com t/Hustlers` -
:bulb: **Tip:** +
+ +:bulb: **Tip:** A person can have any number of tags (including 0)
-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` +#### 4.1.3 Deleting a person: `del-p` -### Listing all persons : `list` +Delete a person from our contact list. -Shows a list of all persons in the address book. +**Format:** `del-p INDEX` -Format: `list` +**Examples:** +* `del-p 1` deletes the first person in the contact list. -### Editing a person : `edit` +#### 4.1.4 Editing a person: `edit-p` -Edits an existing person in the address book. +Edit a person from our contact list. -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +**Format:** `edit-p INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [t/TAG_NAME]…​` -* 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. +**Examples:** +* `edit-p 1 p/98273712 e/johndoe@example.com` edits the phone number and email addresses of the 1st person into `98273712` and `johndoe@example.com` respectively. +* `edit-p 2 n/Alan Walker t/` edits the name of the 2nd person and clear all existing tags. -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. +#### 4.1.5 Clearing all contact entries: `clear-p` -### Locating persons by name: `find` +Clear all entries from our contact list. -Finds persons whose names contain any of the given keywords. +**Format:** `clear-p` -Format: `find KEYWORD [MORE_KEYWORDS]` +#### 4.1.6 Adding a tag : `tag-add-p` -* 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` +Add tags to a selected person from our contact list. -Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +**Format:** `tag-add-p INDEX TAG_NAME` + +**Examples:** +* `tag-add-p 1 public` adds the tag `public` to the first person in the contact list. + +
+ +**:information_source: Additional Notes:**
+ +* The tag name should not contain spaces and should be alphanumeric. + +
+ + +#### 4.1.7 Deleting a tag : `tag-del-p` + +Add tags to a selected person from our contact list. + +**Format:** `tag-del-p INDEX TAG_NAME` + +**Examples:** +* `tag-del-p 1 team` deletes the tag `team` from the first person in the contact list. + +
+ +**:information_source: Additional Notes:**
+ +* The tag name should not contain spaces and should be alphanumeric. +* The tag name is case-sensitive and must be an exact match for it to be recognised. + +
+ +#### 4.1.8 Locating persons by keyword : `find-p` + +Find persons matching any of the given keywords from our contact list. +Users can choose to find by `NAME`(s), `TAG`(s), or both. + +**Format:** `find-p [n/NAME]…​ [t/TAG]…​` + +**Examples:** +* `find-p n/Alan t/team1` +* `find-p n/Alan` +* `find-p t/team1` +* `find-p n/Alex n/Charlotte t/team1 t/team2` + +
-### Deleting a person : `delete` +**:information_source: Additional Notes:**
-Deletes the specified person from the address book. +* One of the optional items e.g. `[n/NAME]` must be present for the command to work. +* The search is case-insensitive e.g. `hans` will match `Hans`. +* The order of the keywords does not matter e.g. `n/hans n/bo` will return the same result as `n/bo n/hans`. +* Only full words will be matched e.g. `Han` will not match `Hans`. +* Persons matching at least one keyword will be returned e.g. `n/Hans n/Bo` will return the persons `Hans Gruber` and `Bo Yang`. +* Keywords should not contain spaces and should be alphanumeric. -Format: `delete INDEX` +
+ + +#### 4.1.9 Adding a strength : `strength-add` + +Add a strength to a selected person from our contact list. + +**Format:** `strength-add INDEX STRENGTH_DESCRIPTION` + +**Examples:** +* `strength-add 1 Great stamina` adds the strength "Great stamina" to the 1st person in the contact list. + +
+ +**:information_source: Additional Notes:**
+ +* Adds a strength at the specified `INDEX`. +* `INDEX` refers to the index number shown in the displayed person list. +* The provided `INDEX` must be a valid (must be an unsigned non-zero integer and exists in the list). +* The `STRENGTH_DESCRIPTION` must not be greater than 50 characters. + +
+ +#### 4.1.10 Adding a weakness : `weakness-add` + +Add a weakness to a selected person from our contact list. + +**Format:** `weakness-add INDEX WEAKNESS_DESCRIPTION` + +**Examples:** +* `weakness-add 1 Poor defensive abilities` adds the weakness "Poor defensive abilities" to the 1st person in the contact list. + +
+ +**:information_source: Additional Notes:**
+ +* Adds a weakness at the specified `INDEX`. +* `INDEX` refers to the index number shown in the displayed person list. +* The provided `INDEX` must be a valid (must be an unsigned non-zero integer and exists in the list). +* The `WEAKNESS_DESCRIPTION` must not be greater than 50 characters. + +
+ + +#### 4.1.11 Adding a miscellaneous note : `misc-add` + +Add a miscellaneous note to a selected person from our contact list. + +**Format:** `misc-add INDEX NOTE_DESCRIPTION` + +**Examples:** +* `misc-add 1 Likes ice cream` adds the miscellaneous note "Likes ice cream" to the 1st person in the contact list. + +
+ +**:information_source: Additional Notes:**
+ +* Adds a miscellaneous note at the specified `INDEX`. +* `INDEX` refers to the index number shown in the displayed person list. +* The provided `INDEX` must be a valid (must be an unsigned non-zero integer and exists in the list). +* The `NOTE_DESCRIPTION` must not be greater than 50 characters. + +
+ +#### 4.1.12 Deleting a strength : `strength-del` + +Delete a strength from a selected person from our contact list. + +**Format:** `strength-del INDEX STRENGTH_INDEX` + +**Examples:** +* `strength-del 1 1` deletes the first strength from the first person in the contact list. + +
-* Deletes the person at the specified `INDEX`. +**:information_source: Additional Notes:**
+ +* Deletes the strength at the specified `INDEX`. +* The index refers to the index number shown in the displayed person list. +* The strength index refers to the index number shown in the strength list of the respective person. +* Both indices **must be a positive integer** 1, 2, 3, …​ +* Both indices must be valid (existing in their respective lists). + +
+ + +#### 4.1.13 Deleting a weakness : `weakness-del` + +Delete the weakness from a selected person from our contact list. + +**Format:** `weakness-del INDEX WEAKNESS_INDEX` + +**Examples:** +* `weakness-del 1 1` deletes the first weakness from the first person in the contact list. + +
+ +**:information_source: Additional Notes:**
+ +* Deletes the weakness at the specified `INDEX`. +* The index refers to the index number shown in the displayed person list. +* The weakness index refers to the index number shown in the weakness list of the respective person. +* Both indices **must be a positive integer** 1, 2, 3, …​ +* Both indices must be valid (existing in their respective lists). + +
+ +#### 4.1.14 Deleting a miscellaneous note : `misc-del` + +Delete the miscellaneous note from a selected person from our contact list. + +**Format:** `misc-del INDEX NOTE_INDEX` + +**Examples:** +* `misc-del 1 1` deletes the first miscellaneous note from the first person in the contact list. + +
+ +**:information_source: Additional Notes:**
+ +* Deletes the miscellaneous note 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, …​ +* The note index refers to the index number shown in the misc. list of the respective person. +* Both indices **must be a positive integer** 1, 2, 3, …​ +* Both indices must be valid (existing in their respective lists) + +
+ + +#### 4.1.15 Sorting list of persons by strengths : `sort-strength` + +Sorts the list of persons by total strengths in descending order. + +**Format:** `sort-strength` + +#### 4.1.16 Sorting list of persons by weaknesses : `sort-weakness` + +Sorts the list of persons by total weaknesses in descending order. + +**Format:** `sort-weaknesses` + +### 4.2 Task Management + +#### 4.2.1 Listing all tasks : `list-t` + +Shows a list of all tasks in our task list. + +**Format:** `list-t` + +#### 4.2.2 Adding a task: `add-t` + +Adds a task to our task list. + +**Format:** `add-t n/NAME d/DATE st/STARTTIME et/ENDTIME [t/TAG_NAME]…​ [c/PERSON_NAME]…​` + +**Examples:** +* `add-t n/Welcome Tea d/24-04-2022 st/09:00 et/12:00 t/Socials c/Alex Yeoh` + + +
+ +**:information_source: Additional Notes:**
+ +* `PERSON_NAME` has to be present in the contact list for them to be tagged to a task. + +
+ +
+ +**:bulb: Tips:**
+ +* A task can have any number of tags (including 0)
+ +* A task can also be assigned to multiple persons (including 0) + +
+ + +#### 4.2.3 Deleting a task: `del-t` + +Delete a task from our task list. + +**Format:** `del-t INDEX` + +**Examples:** +* `del-t 2` deletes the second task in the task list. + +
+ +**:information_source: Additional Notes:**
+ +* Deletes the task at the specified `INDEX`. +* The index can be obtained by referring to the indicated task index on the displayed task list. +* Index values start from 1 and are always positive integers. + +
+ +#### 4.2.4 Editing a task: `edit-t` + +Edit a task from our task list. + +**Format:** `edit-t INDEX [n/NAME] [d/DATE] [st/STARTTIME] [et/ENDTIME] [t/TAG_NAME]…​ [c/PERSON_NAME]…​` + +**Examples:** +* `edit-t 2 d/29-04-2022 et/10:00` edits the date and end time of the second task into `29-04-2022` and `10:00` respectively. +* `edit-t 1 n/PR Event t/` edits the name of the first task to `PR Event` and clears all existing tags. + +#### 4.2.5 Clear all task entries : `clear-t` + +Clear all tasks or tasks on a selected date from our task list. + +**Format:** `clear-t [d/DATE]` + +**Format:** +* `clear-t d/10-10-2022` clears all tasks on the date given. + + +
+ +**:information_source: Additional Notes:**
+ +* Clears all tasks on the specified `DATE`. +* Date must be in the format `dd-mm-yyyy`. + +
+ +#### 4.2.6 Adding a tag : `tag-add-t` + +Add a tag to a selected task from our task list. + +**Format:** `tag-add-t INDEX TAG_NAME` + +**Examples:** +* `tag-add-t 1 important` adds the tag "important" to the first task in the list. + +
+ +**:information_source: Additional Notes:**
+ +* The tag name should not contain spaces and should be alphanumeric. + +
+ +#### 4.2.7 Deleting a tag : `tag-del-t` + +Delete a tag from a selected task in our task list. + +**Format:** `tag-del-t INDEX TAG_NAME` + +**Examples:** +* `tag-del-t 1 important` removes the tag "important" from the first task in the list. + +
+ +**:information_source: Additional Notes:**
+ +* The tag name should not contain spaces and should be alphanumeric. +* The tag name is case-sensitive and must be an exact match for it to be recognised. + +
+ + +#### 4.2.8 Locating tasks by keyword : `find-t` + +Find tasks matching any of the given keywords from our task list. +Users can choose to find by `NAME`(s), `TAG`(s), or both. + +**Format:** `find-t [n/NAME]…​ [t/TAG]…​` + +**Examples:** +* `find-t n/Meeting t/team1` +* `find-t n/Meeting` +* `find-t t/team1` +* `find-t n/Meeting n/Training t/team1 t/team2` + +
+ +**:information_source: Additional Notes:**
+ +* One of the optional items e.g. `[n/NAME]` must be present for the command to work. +* The search is case-insensitive e.g. `meeting` will match `Meeting`. +* The order of the keywords does not matter e.g. `n/meeting n/shareholder` will return the same result as `n/shareholder n/meeting`. +* Only full words will be matched e.g. `Meeting` will not match `Meetings`. +* Tasks matching at least one keyword will be returned e.g. `n/Meeting n/Shareholders` will return the persons `Engagement with Shareholders` and `Annual Meeting`. +* Keywords should not contain spaces and should be alphanumeric. + +
+ +#### 4.2.9 Locating contacts tagged to a task : `get-person` + +Pull out the contact information of participants/persons tagged to a task. + +**Format:** `get-person INDEX` + +**Examples:** + +To get the contact information of all participants in Team Training (Task 2), you can enter the following command: + +* `get-person 2` + +This will bring you to the contact tab - displaying only the contact information of participants in Task 2. + +**Your GUI display before entering the command:** + +![getperson-ex1](images/getperson-ex1.png) + +**Your GUI display after entering the command:** + +![getperson-ex2](images/getperson-ex2.png) + + +#### 4.2.10 Sorting tasks by date : `sort-date` + +Sort the task list by date, in order of the task whose deadline is earlier. + +**Format:** `sort-date` + +### 4.3 Strategic Planning + +#### 4.3.1 Load new background image : `load-court` + +Load a new background image in the strategy tab. + +**Format:** `load-court IMAGE_NAME` 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. -### Clearing all entries : `clear` +`load-court basketball` loads would set the image from the filepath `courts/basketball.png` as the background image of strategy tab (if it exists). Visually it would look as follows: -Clears all entries from the address book. +Upon typing `load-court basketball`, without pressing enter, the GUI should look as follows: -Format: `clear` +![load-court-ex1](images/load-court-ex1.png) -### Exiting the program : `exit` +Upon pressing enter however, the image will be loaded from `courts/basketball.png` and set as the background for the Strategy board, as seen below! -Exits the program. +![load-court-ex2](images/load-court-ex2.png) + +Then just select your desired directory and filename and you're done! + +
+ +**:information_source: Additional Notes:**
+ +* A folder named `courts` will be automatically generated in the same directory as the JAR file +* Image must be in `png` format and be contained in the aforementioned `courts` directory. +* Loads the given image from the filepath `courts/IMAGE_NAME.png`. + +
+ +#### 4.3.2 Adding new players: `add-player` + +Add a new player to the strategy board. + +**Format:** `add-player PLAYER_NAME` + +**Examples:** +* `add-player Messi` adds a new player named `Messi` in the strategy board. + + +
+ +**:information_source: Additional Notes:**
+ +* `PLAYER_NAME` is case-sensitive e.g. `John Cena` will NOT match `john Cena`. +* `PLAYER_NAME` can not be empty and its length must be less or equal to `50` characters. +* `PLAYER_NAME` must be unique and can NOT contain the character `/`. + +
+ + +#### 4.3.3 Removing players: `del-player` + +Remove a player from the strategy board. + +**Format:** `del-player PLAYER_NAME` -Format: `exit` +
+ +**:information_source: Additional Notes:**
+ +* `PLAYER_NAME` is case-sensitive e.g. `John Cena` will NOT match `john Cena`. +* `PLAYER_NAME` can not be empty and its length must be less or equal to `50` characters. +* `PLAYER_NAME` must be unique and can NOT contain the character `/`. +* `PLAYER_NAME` must be present in the strategy board + +
+ +**Examples:** +* `del-player Messi` removes the player named `Messi` from the strategy board if such player exists on the strategy board. + +#### 4.3.4 Moving a player to a coordinate: `move` + +Move a player to a coordinate on the strategy board. + +**Format:** `move PLAYER_NAME x/X_COORDINATE y/Y_COORDINATE` + + +
+ +**:information_source: Additional Notes:**
+ +* `PLAYER_NAME` is case-sensitive e.g. `John Cena` will NOT match `john Cena`. +* `PLAYER_NAME` can not be empty and its length must be less or equal to `50` characters. +* `PLAYER_NAME` must be unique and can NOT contain the character `/`. +* `PLAYER_NAME` must be present in the strategy board. +* `X_COORDINATE` and `Y_COORDINATE` must be integers. +* `X_COORDINATE` must be non-negative and less than or equal to `1000`. +* `Y_COORDINATE` must be non-negative and less than or equal to `600`. + +
+ +**Examples:** + +Suppose you have added a player named `John` to the strategy board. And suppose this is the current position of John: +It is roughly located at the coordinate `(50, 550)` since the circle center is around that position. + +![move-ex1](images/move-ex1.png) -### Saving the data +Then you can move John to the coordinate `(150, 500)` by typing: +* `move John x/150 y/500`. -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +The image below shows the new position of John: You can see that the circle center has moved to the new coordinate. -### Editing the data file +![move-ex2](images/move-ex2.png) -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.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, AddressBook will discard all data and start with an empty data file at the next run. +#### 4.3.5 Export strategy board as image file: `export` + +Exports current view of the strategy board as an image to the users local device. + +**Format:** `export` + +**Examples:** + +Upon typing `export` for a pre-designed strategy board, the GUI should look as follows: + +![export-ex1](images/export-ex1.png) + +Once pressing enter however, you should be directed to your directory filechooser, similar to the diagram shown below. + +![export-ex2](images/export-ex2.png) + +Then just select your desired directory and filename and you're done! + +
+:bulb: Note: The above GUI may look slightly different depending on your operating system.
-### Archiving data files `[coming in v2.0]` +### 4.4 General + +#### 4.4.1 Viewing help: `help` -_Details coming soon ..._ +Shows a message explaining how to access the help page. + +**Format:** `help` + +#### 4.4.2 Exiting the program : `exit` + +Exits the program. + +**Format:** `exit` -------------------------------------------------------------------------------------------------------------------- -## FAQ + +## **5. FAQ** **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. +**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 Coach2k22 home folder. + +**Q**: What is the purpose of having a separate command for listing player strengths and weaknesses
+**A**: These functions allow coach to judge players based on their respective strengths/weaknesses for improved judgement of abilities, analyzing their liabilities, and strategically choosing the right player for the right purpose. +**Q**: Why can't I load a court using the `load-court` command despite using the right command format?
+**A**: Check to see if the /courts directory in the application has the aforementioned court file! If not, do remember to upload that in. + +**Q**: I saved an image using `export` before but after saving another image the first one disappeared?
+**A**: Make sure to change the file name when you download the image to your local directory. Oftentimes if you have two images with the same name in the same place the new file replaces the older one. -------------------------------------------------------------------------------------------------------------------- -## 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` +## **6. Command summary** + +This section gives you a summary of all the available commands in the application. +They are classified into the following categories by command usage: + +* [Contact Management](#61-contact-management) +* [Task Management](#62-task-management) +* [Strategic Planning](#63-strategic-planning) +* [General](#64-general) + +You can find the corresponding command format and an example for the actions you are able to perform on the application. + +### 6.1 Contact Management +This section lists all the commands available for contact management in the application. + +| Action | Format, Examples | +|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------| +| **List** | `list-p` | +| **Add** | `add-p n/NAME p/PHONE_NUMBER a/ADDRESS e/EMAIL [t/TAG_NAME]…​`
e.g., `add-p n/Johnson p/83918273 a/Woodlands Avenue 4 e/johnson@gmail.com t/Hustlers` | +| **Delete** | `del-p INDEX`
e.g., `del-p 1` | +| **Edit** | `edit-p INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [t/TAG_NAME]…​`
e.g., `edit-p 1 p/98273712 e/johndoe@example.com` | +| **Clear** | `clear-p` | +| **Add Tag** | `tag-add-p INDEX TAG_NAME`
e.g., `tag-add-p 1 public` | +| **Delete Tag** | `tag-del-p INDEX TAG_NAME`
e.g., `tag-del-p 1 team` | +| **Find** | `find-p [n/NAME]…​ [t/TAG]…​`
e.g., `find-p n/Alex n/Charlotte t/team1 t/team2` | +| **Add Strength** | `strength-add INDEX STRENGTH_DESCRIPTION`
e.g., `strength-add 1 Great stamina` | +| **Add Weakness** | `weakness-add INDEX WEAKNESS_DESCRIPTION`
e.g., `weakness-add 1 Poor defensive abilities` | +| **Add Miscellaneous** | `misc-add INDEX NOTE_DESCRIPTION`
e.g., `misc-add 1 Likes ice cream` | +| **Delete Strength** | `strength-del INDEX STRENGTH_INDEX`
e.g., `strength-del 1 1` | +| **Delete Weakness** | `weakness-del INDEX WEAKNESS_INDEX`
e.g., `weakness-del 1 1` | +| **Delete Miscellaneous** | `misc-del INDEX NOTE_INDEX`
e.g., `misc-del 1 1` | +| **Sort by Strengths** | `sort-strength` | +| **Sort by Weaknesses** | `sort-weakness`
| + + +### 6.2 Task Management +This section lists all the commands available for task management in the application. + +| Action | Format, Examples | +|------------------|-----------------------------------------------------------------------------------| +| **List** | `list-t` | +| **Add** | `add-t n/NAME d/DATE st/STARTTIME et/ENDTIME [t/TAG_NAME]…​ [c/PERSON_NAME]…​`
e.g., `add-t n/Welcome Tea d/24-04-2022 st/09:00 et/12:00 t/Socials c/Alex Yeoh`| +| **Delete** | `del-t INDEX`
e.g., `del-p 1` | +| **Edit** | `edit-t INDEX [n/NAME] [d/DATE] [st/STARTTIME] [et/ENDTIME] [t/TAG_NAME]…​ [c/PERSON_NAME]…​`
e.g., `edit-t 2 d/29-04-2022 et/10:00`| +| **Clear** | `clear-t [d/DATE]`
e.g., `clear-t 2022-10-10` | +| **Add Tag** | `tag-add-t INDEX TAG_NAME`
e.g., `tag-add-t 1 important` | +| **Delete Tag** | `tag-del-t INDEX TAG_NAME`
e.g., `tag-del-t 1 important` | +| **Find** | `find-t [n/NAME]…​ [t/TAG]…​`
e.g., `find-p n/Meeting n/Training t/team1 t/team2` | +| **Get Person** | `get-person INDEX`
e.g., `get-person 2` | +| **Sort By Date** | `sort-date` | + + +### 6.3 Strategic Planning +This section lists all the commands available for performing strategic planning in the application. + +| Action | Format, Examples | +|-------------------|-------------------------------------------------------------------------| +| **Load BG Image** | `load-court IMAGE_NAME`
e.g., `load-court basketball` | +| **Add** | `add-player PLAYER_NAME`
e.g., `add-player Messi` | +| **Delete** | `del-player PLAYER_NAME`
e.g., `del-player Messi` | +| **Move** | `move PLAYER_NAME x/X_COORDINATE y/Y_COORDINATE`
e.g., `move Messi x/0 y/0` | +| **Export** | `export` | + + +### 6.4 General +This section lists all the commands available for general usage. + +| Action | Format, Examples | +|--------------|-------------------------| +| **Help** | `help` | +| **Exit** | `exit` | + diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..a25da783525 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "Coach2K22" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2122S2-CS2103T-W14-2/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..93eada6d494 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: "Coach2K22"; font-size: 32px; } } diff --git a/docs/_sass/minima/_layout.scss b/docs/_sass/minima/_layout.scss index ca99f981701..f370d3f40a5 100644 --- a/docs/_sass/minima/_layout.scss +++ b/docs/_sass/minima/_layout.scss @@ -176,7 +176,7 @@ .post-content { margin-bottom: $spacing-unit; - h1, h2, h3 { margin-top: $spacing-unit * 2 } + h1, h2, h3 { margin-top: $spacing-unit } h4, h5, h6 { margin-top: $spacing-unit } h2 { diff --git a/docs/diagrams/AddNoteSequenceDiagram.puml b/docs/diagrams/AddNoteSequenceDiagram.puml new file mode 100644 index 00000000000..7997f3ffa90 --- /dev/null +++ b/docs/diagrams/AddNoteSequenceDiagram.puml @@ -0,0 +1,67 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":AddNoteCommandParser" as AddNoteCommandParser LOGIC_COLOR +participant "p:AddNoteCommand" as AddNoteCommand 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("note-add 1 has...") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("note-add 1 has...") +activate Coach2K22Parser + +create AddNoteCommandParser +Coach2K22Parser -> AddNoteCommandParser +activate AddNoteCommandParser + +AddNoteCommandParser --> Coach2K22Parser +deactivate AddNoteCommandParser + +Coach2K22Parser -> AddNoteCommandParser : parse("note-add 1 has...") +activate AddNoteCommandParser + +create AddNoteCommand +AddNoteCommandParser -> AddNoteCommand +activate AddNoteCommand + +AddNoteCommand --> AddNoteCommandParser : p +deactivate AddNoteCommand +AddNoteCommandParser --> Coach2K22Parser : p + +AddNoteCommandParser -[hidden]-> Coach2K22Parser +destroy AddNoteCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> AddNoteCommand : execute() +activate AddNoteCommand + +AddNoteCommand -> Model : setPerson(originalPerson, editedPerson) +activate Model + +Model --> AddNoteCommand +deactivate Model + +create CommandResult +AddNoteCommand -> CommandResult +activate CommandResult + +CommandResult --> AddNoteCommand +deactivate CommandResult + +AddNoteCommand --> LogicManager : result +deactivate AddNoteCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddPersonSequenceDiagram.puml b/docs/diagrams/AddPersonSequenceDiagram.puml new file mode 100644 index 00000000000..10960ffe667 --- /dev/null +++ b/docs/diagrams/AddPersonSequenceDiagram.puml @@ -0,0 +1,67 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":AddPersonCommandParser" as AddPersonCommandParser LOGIC_COLOR +participant "p:AddPersonCommand" as AddPersonCommand 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("add-p n/Johnson ...") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("n/Johnson ...") +activate Coach2K22Parser + +create AddPersonCommandParser +Coach2K22Parser -> AddPersonCommandParser +activate AddPersonCommandParser + +AddPersonCommandParser --> Coach2K22Parser +deactivate AddPersonCommandParser + +Coach2K22Parser -> AddPersonCommandParser : parse("n/Johnson ...") +activate AddPersonCommandParser + +create AddPersonCommand +AddPersonCommandParser -> AddPersonCommand +activate AddPersonCommand + +AddPersonCommand --> AddPersonCommandParser : p +deactivate AddPersonCommand +AddPersonCommandParser --> Coach2K22Parser : p + +AddPersonCommandParser -[hidden]-> Coach2K22Parser +destroy AddPersonCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> AddPersonCommand : execute() +activate AddPersonCommand + +AddPersonCommand -> Model : addPerson("Johnson ...") +activate Model + +Model --> AddPersonCommand +deactivate Model + +create CommandResult +AddPersonCommand -> CommandResult +activate CommandResult + +CommandResult --> AddPersonCommand +deactivate CommandResult + +AddPersonCommand --> LogicManager : result +deactivate AddPersonCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddPersonTagSequenceDiagram.puml b/docs/diagrams/AddPersonTagSequenceDiagram.puml new file mode 100644 index 00000000000..84b343d9b52 --- /dev/null +++ b/docs/diagrams/AddPersonTagSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":AddPersonTagCommandParser" as AddPersonTagCommandParser LOGIC_COLOR +participant "p:AddPersonTagCommand" as AddTagCommand 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("tag-add-p 1 friend") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("tag-add-p 1 friend") +activate Coach2K22Parser + +create AddPersonTagCommandParser +Coach2K22Parser -> AddPersonTagCommandParser +activate AddPersonTagCommandParser + +AddPersonTagCommandParser --> Coach2K22Parser +deactivate AddPersonTagCommandParser + +Coach2K22Parser -> AddPersonTagCommandParser : parse(" 1 friend") +activate AddPersonTagCommandParser + +create AddTagCommand +AddPersonTagCommandParser -> AddTagCommand +activate AddTagCommand + +AddTagCommand --> AddPersonTagCommandParser : p +deactivate AddTagCommand + +AddPersonTagCommandParser --> Coach2K22Parser : p +deactivate AddPersonTagCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +AddPersonTagCommandParser -[hidden]-> Coach2K22Parser +destroy AddPersonTagCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> AddTagCommand : execute() +activate AddTagCommand + +AddTagCommand -> Model : setPerson(personToEdit, editedPerson) +activate Model + +Model --> AddTagCommand +deactivate Model + +create CommandResult +AddTagCommand -> CommandResult +activate CommandResult + +CommandResult --> AddTagCommand +deactivate CommandResult + +AddTagCommand --> LogicManager : result +deactivate AddTagCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddPlayerSequenceDiagram.puml b/docs/diagrams/AddPlayerSequenceDiagram.puml new file mode 100644 index 00000000000..6d8a73c4dd4 --- /dev/null +++ b/docs/diagrams/AddPlayerSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":AddPlayerCommandParser" as AddPlayerCommandParser LOGIC_COLOR +participant "p:AddPlayerCommand" as AddPlayerCommand 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("add-player Cena") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("add-player Cena") +activate Coach2K22Parser + +create AddPlayerCommandParser +Coach2K22Parser -> AddPlayerCommandParser +activate AddPlayerCommandParser + +AddPlayerCommandParser --> Coach2K22Parser +deactivate AddPlayerCommandParser + +Coach2K22Parser -> AddPlayerCommandParser : parse("Cena") +activate AddPlayerCommandParser + +create AddPlayerCommand +AddPlayerCommandParser -> AddPlayerCommand +activate AddPlayerCommand + +AddPlayerCommand --> AddPlayerCommandParser : p +deactivate AddPlayerCommand + +AddPlayerCommandParser --> Coach2K22Parser : p +deactivate AddPlayerCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +AddPlayerCommandParser -[hidden]-> Coach2K22Parser +destroy AddPlayerCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> AddPlayerCommand : execute() +activate AddPlayerCommand + +AddPlayerCommand -> Model : addPlayer(Player) +activate Model + +Model --> AddPlayerCommand +deactivate Model + +create CommandResult +AddPlayerCommand -> CommandResult +activate CommandResult + +CommandResult --> AddPlayerCommand +deactivate CommandResult + +AddPlayerCommand --> LogicManager : result +deactivate AddPlayerCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddTaskSequenceDiagram.puml b/docs/diagrams/AddTaskSequenceDiagram.puml new file mode 100644 index 00000000000..8eec52a4e82 --- /dev/null +++ b/docs/diagrams/AddTaskSequenceDiagram.puml @@ -0,0 +1,84 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":AddTaskCommandParser" as AddTaskCommandParser LOGIC_COLOR +participant "t:AddTaskCommand" as AddTaskCommand 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("add-t n/Meet ...") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("add-t n/Meet ...") +activate Coach2K22Parser + +create AddTaskCommandParser +Coach2K22Parser -> AddTaskCommandParser +activate AddTaskCommandParser + +AddTaskCommandParser --> Coach2K22Parser +deactivate AddTaskCommandParser + +Coach2K22Parser -> AddTaskCommandParser : parse("n/Meet ...") +activate AddTaskCommandParser + +create AddTaskCommand +AddTaskCommandParser -> AddTaskCommand +activate AddTaskCommand + +AddTaskCommand --> AddTaskCommandParser : t +deactivate AddTaskCommand +AddTaskCommandParser --> Coach2K22Parser : t + +AddTaskCommandParser -[hidden]-> Coach2K22Parser +destroy AddTaskCommandParser + +Coach2K22Parser --> LogicManager : t +deactivate Coach2K22Parser + +LogicManager -> AddTaskCommand : execute() +activate AddTaskCommand + +AddTaskCommand -> Model : addTask("Meet ...") +activate Model + +Model --> AddTaskCommand +deactivate Model + +AddTaskCommand -> Model : hasStartEndTimeConflict() +activate Model + +Model --> AddTaskCommand +deactivate Model + +loop size of Task ContactList + loop size of TaskList + AddTaskCommand -> Model : hasDateTimeConflict("Meet ...") + activate Model + Model --> AddTaskCommand + deactivate Model + end +end + + + +create CommandResult +AddTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> AddTaskCommand +deactivate CommandResult + +AddTaskCommand --> LogicManager : result +deactivate AddTaskCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/AddTaskTagSequenceDiagram.puml b/docs/diagrams/AddTaskTagSequenceDiagram.puml new file mode 100644 index 00000000000..1534b98a2f1 --- /dev/null +++ b/docs/diagrams/AddTaskTagSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":AddTaskTagCommandParser" as AddTaskTagCommandParser LOGIC_COLOR +participant "p:AddTaskTagCommand" as AddTaskTagCommand 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("tag-add-t 1 important") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("tag-add-t 1 important") +activate Coach2K22Parser + +create AddTaskTagCommandParser +Coach2K22Parser -> AddTaskTagCommandParser +activate AddTaskTagCommandParser + +AddTaskTagCommandParser --> Coach2K22Parser +deactivate AddTaskTagCommandParser + +Coach2K22Parser -> AddTaskTagCommandParser : parse(" 1 important") +activate AddTaskTagCommandParser + +create AddTaskTagCommand +AddTaskTagCommandParser -> AddTaskTagCommand +activate AddTaskTagCommand + +AddTaskTagCommand --> AddTaskTagCommandParser : p +deactivate AddTaskTagCommand + +AddTaskTagCommandParser --> Coach2K22Parser : p +deactivate AddTaskTagCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +AddTaskTagCommandParser -[hidden]-> Coach2K22Parser +destroy AddTaskTagCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> AddTaskTagCommand : execute() +activate AddTaskTagCommand + +AddTaskTagCommand -> Model : setTask(taskToEdit, editedTask) +activate Model + +Model --> AddTaskTagCommand +deactivate Model + +create CommandResult +AddTaskTagCommand -> CommandResult +activate CommandResult + +CommandResult --> AddTaskTagCommand +deactivate CommandResult + +AddTaskTagCommand --> LogicManager : result +deactivate AddTaskTagCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ClearPersonSequenceDiagram.puml b/docs/diagrams/ClearPersonSequenceDiagram.puml new file mode 100644 index 00000000000..2d7c61bb3b4 --- /dev/null +++ b/docs/diagrams/ClearPersonSequenceDiagram.puml @@ -0,0 +1,65 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant "p:ClearPersonCommand" as ClearPersonCommand 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("clear-p") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("clear-p") +activate Coach2K22Parser + +create ClearPersonCommand +Coach2K22Parser -> ClearPersonCommand +activate ClearPersonCommand + +ClearPersonCommand --> Coach2K22Parser : p +deactivate ClearPersonCommand + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> ClearPersonCommand : execute() +activate ClearPersonCommand + +ClearPersonCommand -> Model : getFilteredTaskList() +activate Model + +Model --> ClearPersonCommand +deactivate Model + +loop size of TaskList + ClearPersonCommand -> Model : setTask(editedTask) + activate Model + Model --> ClearPersonCommand + deactivate Model +end + +ClearPersonCommand -> Model : setAddressBook(new AddressBook) +activate Model + +Model --> ClearPersonCommand +deactivate Model + +create CommandResult +ClearPersonCommand -> CommandResult +activate CommandResult + +CommandResult --> ClearPersonCommand +deactivate CommandResult + +ClearPersonCommand --> LogicManager : result +deactivate ClearPersonCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ClearTaskSequenceDiagram.png b/docs/diagrams/ClearTaskSequenceDiagram.png new file mode 100644 index 00000000000..fc0f5542c50 Binary files /dev/null and b/docs/diagrams/ClearTaskSequenceDiagram.png differ diff --git a/docs/diagrams/ClearTaskSequenceDiagram.puml b/docs/diagrams/ClearTaskSequenceDiagram.puml new file mode 100644 index 00000000000..c385addace2 --- /dev/null +++ b/docs/diagrams/ClearTaskSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":ClearTaskCommandParser" as ClearTaskCommandParser LOGIC_COLOR +participant "p:ClearTaskCommand" as ClearTaskCommand 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("clear-t 2020-10-10") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("clear-t 2020-10-10") +activate Coach2K22Parser + +create ClearTaskCommandParser +Coach2K22Parser -> ClearTaskCommandParser +activate ClearTaskCommandParser + +ClearTaskCommandParser --> Coach2K22Parser +deactivate ClearTaskCommandParser + +Coach2K22Parser -> ClearTaskCommandParser : parse("2020-10-10") +activate ClearTaskCommandParser + +create ClearTaskCommand +ClearTaskCommandParser -> ClearTaskCommand +activate ClearTaskCommand + +ClearTaskCommand --> ClearTaskCommandParser : p +deactivate ClearTaskCommand + +ClearTaskCommandParser --> Coach2K22Parser : p +deactivate ClearTaskCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +ClearTaskCommandParser -[hidden]-> Coach2K22Parser +destroy ClearTaskCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> ClearTaskCommand : execute() +activate ClearTaskCommand + +Model -> ClearTaskCommand :getFilteredTaskList() + +loop size of TaskList +alt task.getDate() == date + ClearTaskCommand -> Model : deleteTask(task) + end +end +activate Model + +Model --> ClearTaskCommand +deactivate Model + +create CommandResult +ClearTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> ClearTaskCommand +deactivate CommandResult + +ClearTaskCommand --> LogicManager : result +deactivate ClearTaskCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteNoteSequenceDiagram.puml b/docs/diagrams/DeleteNoteSequenceDiagram.puml new file mode 100644 index 00000000000..57f99db8db6 --- /dev/null +++ b/docs/diagrams/DeleteNoteSequenceDiagram.puml @@ -0,0 +1,67 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":DeleteNoteCommandParser" as DeleteNoteCommandParser LOGIC_COLOR +participant "p:DeleteNoteCommand" as DeleteNoteCommand 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("note-del 1 1...") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("note-del 1 1...") +activate Coach2K22Parser + +create DeleteNoteCommandParser +Coach2K22Parser -> DeleteNoteCommandParser +activate DeleteNoteCommandParser + +DeleteNoteCommandParser --> Coach2K22Parser +deactivate DeleteNoteCommandParser + +Coach2K22Parser -> DeleteNoteCommandParser : parse("note-del 1 1...") +activate DeleteNoteCommandParser + +create DeleteNoteCommand +DeleteNoteCommandParser -> DeleteNoteCommand +activate DeleteNoteCommand + +DeleteNoteCommand --> DeleteNoteCommandParser : p +deactivate DeleteNoteCommand +DeleteNoteCommandParser --> Coach2K22Parser : p + +DeleteNoteCommandParser -[hidden]-> Coach2K22Parser +destroy DeleteNoteCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> DeleteNoteCommand : execute() +activate DeleteNoteCommand + +DeleteNoteCommand -> Model : setPerson(originalPerson, editedPerson) +activate Model + +Model --> DeleteNoteCommand +deactivate Model + +create CommandResult +DeleteNoteCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteNoteCommand +deactivate CommandResult + +DeleteNoteCommand --> LogicManager : result +deactivate DeleteNoteCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeletePersonSequenceDiagram.puml b/docs/diagrams/DeletePersonSequenceDiagram.puml new file mode 100644 index 00000000000..f6d5c4ae5ae --- /dev/null +++ b/docs/diagrams/DeletePersonSequenceDiagram.puml @@ -0,0 +1,82 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":DeletePersonCommandParser" as DeletePersonCommandParser LOGIC_COLOR +participant "p:DeletePersonCommand" as DeletePersonCommand 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("del-p 1") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("del-p 1") +activate Coach2K22Parser + +create DeletePersonCommandParser +Coach2K22Parser -> DeletePersonCommandParser +activate DeletePersonCommandParser + +DeletePersonCommandParser --> Coach2K22Parser +deactivate DeletePersonCommandParser + +Coach2K22Parser -> DeletePersonCommandParser : parse("1") +activate DeletePersonCommandParser + +create DeletePersonCommand +DeletePersonCommandParser -> DeletePersonCommand +activate DeletePersonCommand + +DeletePersonCommand --> DeletePersonCommandParser : p +deactivate DeletePersonCommand +DeletePersonCommandParser --> Coach2K22Parser : p + +DeletePersonCommandParser -[hidden]-> Coach2K22Parser +destroy DeletePersonCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> DeletePersonCommand : execute() +activate DeletePersonCommand + +DeletePersonCommand -> Model : getFilteredTaskList() +activate Model + +Model --> DeletePersonCommand +deactivate Model + +loop size of TaskList + alt task.contains(personToDelete) + DeletePersonCommand -> Model : setTask(editedTask) + activate Model + Model --> DeletePersonCommand + deactivate Model + end +end + +DeletePersonCommand -> Model : deletePerson(person) +activate Model + +Model --> DeletePersonCommand +deactivate Model + +create CommandResult +DeletePersonCommand -> CommandResult +activate CommandResult + +CommandResult --> DeletePersonCommand +deactivate CommandResult + +DeletePersonCommand --> LogicManager : result +deactivate DeletePersonCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeletePersonTagSequenceDiagram.puml b/docs/diagrams/DeletePersonTagSequenceDiagram.puml new file mode 100644 index 00000000000..7d9f2554f33 --- /dev/null +++ b/docs/diagrams/DeletePersonTagSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":DeletePersonTagCommandParser" as DeletePersonTagCommandParser LOGIC_COLOR +participant "p:DeletePersonTagCommand" as DeletePersonTagCommand 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("tag-del-p 1 friend") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("tag-del-p 1 friend") +activate Coach2K22Parser + +create DeletePersonTagCommandParser +Coach2K22Parser -> DeletePersonTagCommandParser +activate DeletePersonTagCommandParser + +DeletePersonTagCommandParser --> Coach2K22Parser +deactivate DeletePersonTagCommandParser + +Coach2K22Parser -> DeletePersonTagCommandParser : parse(" 1 friend") +activate DeletePersonTagCommandParser + +create DeletePersonTagCommand +DeletePersonTagCommandParser -> DeletePersonTagCommand +activate DeletePersonTagCommand + +DeletePersonTagCommand --> DeletePersonTagCommandParser : p +deactivate DeletePersonTagCommand + +DeletePersonTagCommandParser --> Coach2K22Parser : p +deactivate DeletePersonTagCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeletePersonTagCommandParser -[hidden]-> Coach2K22Parser +destroy DeletePersonTagCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> DeletePersonTagCommand : execute() +activate DeletePersonTagCommand + +DeletePersonTagCommand -> Model : setPerson(personToEdit, editedPerson) +activate Model + +Model --> DeletePersonTagCommand +deactivate Model + +create CommandResult +DeletePersonTagCommand -> CommandResult +activate CommandResult + +CommandResult --> DeletePersonTagCommand +deactivate CommandResult + +DeletePersonTagCommand --> LogicManager : result +deactivate DeletePersonTagCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeletePlayerSequenceDiagram.puml b/docs/diagrams/DeletePlayerSequenceDiagram.puml new file mode 100644 index 00000000000..7699f4e6313 --- /dev/null +++ b/docs/diagrams/DeletePlayerSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":DeletePlayerCommandParser" as DeletePlayerCommandParser LOGIC_COLOR +participant "p:DeletePlayerCommand" as DeletePlayerCommand 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("del-player Cena") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("del-player Cena") +activate Coach2K22Parser + +create DeletePlayerCommandParser +Coach2K22Parser -> DeletePlayerCommandParser +activate DeletePlayerCommandParser + +DeletePlayerCommandParser --> Coach2K22Parser +deactivate DeletePlayerCommandParser + +Coach2K22Parser -> DeletePlayerCommandParser : parse("Cena") +activate DeletePlayerCommandParser + +create DeletePlayerCommand +DeletePlayerCommandParser -> DeletePlayerCommand +activate DeletePlayerCommand + +DeletePlayerCommand --> DeletePlayerCommandParser : p +deactivate DeletePlayerCommand + +DeletePlayerCommandParser --> Coach2K22Parser : p +deactivate DeletePlayerCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeletePlayerCommandParser -[hidden]-> Coach2K22Parser +destroy DeletePlayerCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> DeletePlayerCommand : execute() +activate DeletePlayerCommand + +DeletePlayerCommand -> Model : deletePlayer(Player) +activate Model + +Model --> DeletePlayerCommand +deactivate Model + +create CommandResult +DeletePlayerCommand -> CommandResult +activate CommandResult + +CommandResult --> DeletePlayerCommand +deactivate CommandResult + +DeletePlayerCommand --> LogicManager : result +deactivate DeletePlayerCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..1184437fd46 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,7 +3,7 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR @@ -16,17 +16,17 @@ end box [-> LogicManager : execute("delete 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> Coach2K22Parser : parseCommand("delete 1") +activate Coach2K22Parser create DeleteCommandParser -AddressBookParser -> DeleteCommandParser +Coach2K22Parser -> DeleteCommandParser activate DeleteCommandParser -DeleteCommandParser --> AddressBookParser +DeleteCommandParser --> Coach2K22Parser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +Coach2K22Parser -> DeleteCommandParser : parse("1") activate DeleteCommandParser create DeleteCommand @@ -36,14 +36,14 @@ activate DeleteCommand DeleteCommand --> DeleteCommandParser : d deactivate DeleteCommand -DeleteCommandParser --> AddressBookParser : d +DeleteCommandParser --> Coach2K22Parser : d deactivate DeleteCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser +DeleteCommandParser -[hidden]-> Coach2K22Parser destroy DeleteCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +Coach2K22Parser --> LogicManager : d +deactivate Coach2K22Parser LogicManager -> DeleteCommand : execute() activate DeleteCommand diff --git a/docs/diagrams/DeleteTaskSequenceDiagram.puml b/docs/diagrams/DeleteTaskSequenceDiagram.puml new file mode 100644 index 00000000000..50d52f0240a --- /dev/null +++ b/docs/diagrams/DeleteTaskSequenceDiagram.puml @@ -0,0 +1,67 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":DeleteTaskCommandParser" as DeleteTaskCommandParser LOGIC_COLOR +participant "t:DeleteTaskCommand" as DeleteTaskCommand 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("del-t 1") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("del-t 1") +activate Coach2K22Parser + +create DeleteTaskCommandParser +Coach2K22Parser -> DeleteTaskCommandParser +activate DeleteTaskCommandParser + +DeleteTaskCommandParser --> Coach2K22Parser +deactivate DeleteTaskCommandParser + +Coach2K22Parser -> DeleteTaskCommandParser : parse("1") +activate DeleteTaskCommandParser + +create DeleteTaskCommand +DeleteTaskCommandParser -> DeleteTaskCommand +activate DeleteTaskCommand + +DeleteTaskCommand --> DeleteTaskCommandParser : t +deactivate DeleteTaskCommand +DeleteTaskCommandParser --> Coach2K22Parser : t + +DeleteTaskCommandParser -[hidden]-> Coach2K22Parser +destroy DeleteTaskCommandParser + +Coach2K22Parser --> LogicManager : t +deactivate Coach2K22Parser + +LogicManager -> DeleteTaskCommand : execute() +activate DeleteTaskCommand + +DeleteTaskCommand -> Model : deleteTask(task) +activate Model + +Model --> DeleteTaskCommand +deactivate Model + +create CommandResult +DeleteTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteTaskCommand +deactivate CommandResult + +DeleteTaskCommand --> LogicManager : result +deactivate DeleteTaskCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteTaskTagSequenceDiagram.puml b/docs/diagrams/DeleteTaskTagSequenceDiagram.puml new file mode 100644 index 00000000000..1d8729ab8f6 --- /dev/null +++ b/docs/diagrams/DeleteTaskTagSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":DeleteTaskTagCommandParser" as DeleteTaskTagCommandParser LOGIC_COLOR +participant "p:DeleteTaskTagCommand" as DeleteTaskTagCommand 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("tag-del-t 1 important") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("tag-del-t 1 important") +activate Coach2K22Parser + +create DeleteTaskTagCommandParser +Coach2K22Parser -> DeleteTaskTagCommandParser +activate DeleteTaskTagCommandParser + +DeleteTaskTagCommandParser --> Coach2K22Parser +deactivate DeleteTaskTagCommandParser + +Coach2K22Parser -> DeleteTaskTagCommandParser : parse(" 1 important") +activate DeleteTaskTagCommandParser + +create DeleteTaskTagCommand +DeleteTaskTagCommandParser -> DeleteTaskTagCommand +activate DeleteTaskTagCommand + +DeleteTaskTagCommand --> DeleteTaskTagCommandParser : p +deactivate DeleteTaskTagCommand + +DeleteTaskTagCommandParser --> Coach2K22Parser : p +deactivate DeleteTaskTagCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteTaskTagCommandParser -[hidden]-> Coach2K22Parser +destroy DeleteTaskTagCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> DeleteTaskTagCommand : execute() +activate DeleteTaskTagCommand + +DeleteTaskTagCommand -> Model : setTask(taskToEdit, editedTask) +activate Model + +Model --> DeleteTaskTagCommand +deactivate Model + +create CommandResult +DeleteTaskTagCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteTaskTagCommand +deactivate CommandResult + +DeleteTaskTagCommand --> LogicManager : result +deactivate DeleteTaskTagCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditPersonSequenceDiagram.puml b/docs/diagrams/EditPersonSequenceDiagram.puml new file mode 100644 index 00000000000..61f8a914d1c --- /dev/null +++ b/docs/diagrams/EditPersonSequenceDiagram.puml @@ -0,0 +1,82 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":EditPersonCommandParser" as EditPersonCommandParser LOGIC_COLOR +participant "p:EditPersonCommand" as EditPersonCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("edit-p 1 n/Johnson") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("edit-p 1 n/Johnson") +activate Coach2K22Parser + +create EditPersonCommandParser +Coach2K22Parser -> EditPersonCommandParser +activate EditPersonCommandParser + +EditPersonCommandParser --> Coach2K22Parser +deactivate EditPersonCommandParser + +Coach2K22Parser -> EditPersonCommandParser : parse("1 n/Johnson") +activate EditPersonCommandParser + +create EditPersonCommand +EditPersonCommandParser -> EditPersonCommand +activate EditPersonCommand + +EditPersonCommand --> EditPersonCommandParser : p +deactivate EditPersonCommand +EditPersonCommandParser --> Coach2K22Parser : p + +EditPersonCommandParser -[hidden]-> Coach2K22Parser +destroy EditPersonCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> EditPersonCommand : execute() +activate EditPersonCommand + +EditPersonCommand -> Model : getFilteredTaskList() +activate Model + +Model --> EditPersonCommand +deactivate Model + +loop size of TaskList + alt newName != prevName && task.contains(prevName) + EditPersonCommand -> Model : setTask(editedTask) + activate Model + Model --> EditPersonCommand + deactivate Model + end +end + +EditPersonCommand -> Model : setPerson(editedPerson) +activate Model + +Model --> EditPersonCommand +deactivate Model + +create CommandResult +EditPersonCommand -> CommandResult +activate CommandResult + +CommandResult --> EditPersonCommand +deactivate CommandResult + +EditPersonCommand --> LogicManager : result +deactivate EditPersonCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/EditTaskSequenceDiagram.puml b/docs/diagrams/EditTaskSequenceDiagram.puml new file mode 100644 index 00000000000..fbb7cb4c64b --- /dev/null +++ b/docs/diagrams/EditTaskSequenceDiagram.puml @@ -0,0 +1,84 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":EditTaskCommandParser" as EditTaskCommandParser LOGIC_COLOR +participant "t:EditTaskCommand" as EditTaskCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("edit-t 1 n/Meeting") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("edit-t 1 n/Meeting") +activate Coach2K22Parser + +create EditTaskCommandParser +Coach2K22Parser -> EditTaskCommandParser +activate EditTaskCommandParser + +EditTaskCommandParser --> Coach2K22Parser +deactivate EditTaskCommandParser + +Coach2K22Parser -> EditTaskCommandParser : parse("1 n/Meeting") +activate EditTaskCommandParser + +create EditTaskCommand +EditTaskCommandParser -> EditTaskCommand +activate EditTaskCommand + +EditTaskCommand --> EditTaskCommandParser : t +deactivate EditTaskCommand +EditTaskCommandParser --> Coach2K22Parser : t + +EditTaskCommandParser -[hidden]-> Coach2K22Parser +destroy EditTaskCommandParser + +Coach2K22Parser --> LogicManager : t +deactivate Coach2K22Parser + +LogicManager -> EditTaskCommand : execute() +activate EditTaskCommand + +EditTaskCommand -> Model : setTask(editedTask) +activate Model + +Model --> EditTaskCommand +deactivate Model + +EditTaskCommand -> Model : hasStartEndTimeConflict() +activate Model + +Model --> EditTaskCommand +deactivate Model + +loop size of Task ContactList + loop size of TaskList + EditTaskCommand -> Model : hasDateTimeConflict(editedTask) + activate Model + Model --> EditTaskCommand + deactivate Model + end +end + + + +create CommandResult +EditTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> EditTaskCommand +deactivate CommandResult + +EditTaskCommand --> LogicManager : result +deactivate EditTaskCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ExportStrategySequenceDiagram.puml b/docs/diagrams/ExportStrategySequenceDiagram.puml new file mode 100644 index 00000000000..7da1bf9f1b6 --- /dev/null +++ b/docs/diagrams/ExportStrategySequenceDiagram.puml @@ -0,0 +1,49 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":ExportCommand" as ExportCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box MainWindow MODEL_COLOR_T1 +participant ":MainWindow" as MainWindow MODEL_COLOR +end box + +[-> LogicManager : execute() +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand(args) +activate Coach2K22Parser + +create ExportCommand +Coach2K22Parser -> ExportCommand : e +activate ExportCommand + +ExportCommand --> Coach2K22Parser +deactivate ExportCommand + +Coach2K22Parser --> LogicManager : e +deactivate Coach2K22Parser + +LogicManager -> ExportCommand : execute() +activate ExportCommand + +create CommandResult +ExportCommand -> CommandResult +activate CommandResult + +CommandResult --> ExportCommand + +MainWindow --> CommandResult : captureAndSaveStrategyPanel() +CommandResult --> MainWindow +deactivate CommandResult + +ExportCommand --> LogicManager : result +deactivate ExportCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FindPersonSequenceDiagram.puml b/docs/diagrams/FindPersonSequenceDiagram.puml new file mode 100644 index 00000000000..94bdd59065e --- /dev/null +++ b/docs/diagrams/FindPersonSequenceDiagram.puml @@ -0,0 +1,66 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":FindPersonCommandParser" as FindPersonCommandParser LOGIC_COLOR +participant "f:FindPersonCommand" as FindPersonCommand 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(arg) +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand(arg) +activate Coach2K22Parser + +create FindPersonCommandParser +Coach2K22Parser -> FindPersonCommandParser +activate FindPersonCommandParser + +FindPersonCommandParser --> Coach2K22Parser +deactivate FindPersonCommandParser + +Coach2K22Parser -> FindPersonCommandParser : parse(arg) +activate FindPersonCommandParser + +create FindPersonCommand +FindPersonCommandParser -> FindPersonCommand +activate FindPersonCommand + +FindPersonCommand --> FindPersonCommandParser : f +deactivate FindPersonCommand + +FindPersonCommandParser --> Coach2K22Parser : f +deactivate FindPersonCommandParser + +Coach2K22Parser --> LogicManager : f +deactivate Coach2K22Parser +destroy FindPersonCommandParser + +LogicManager -> FindPersonCommand : execute() +activate FindPersonCommand + +FindPersonCommand -> Model: updateFilteredPersonList(predicate) +activate Model + +Model --> FindPersonCommand +deactivate Model + +create CommandResult +FindPersonCommand -> CommandResult +activate CommandResult + +CommandResult --> FindPersonCommand +deactivate CommandResult + +FindPersonCommand -->LogicManager : result +deactivate FindPersonCommand + +[<--LogicManager : result +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FindTaskSequenceDiagram.puml b/docs/diagrams/FindTaskSequenceDiagram.puml new file mode 100644 index 00000000000..2d76eaf9fb2 --- /dev/null +++ b/docs/diagrams/FindTaskSequenceDiagram.puml @@ -0,0 +1,66 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":FindTaskCommandParser" as FindTaskCommandParser LOGIC_COLOR +participant "f:FindTaskCommand" as FindTaskCommand 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(arg) +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand(arg) +activate Coach2K22Parser + +create FindTaskCommandParser +Coach2K22Parser -> FindTaskCommandParser +activate FindTaskCommandParser + +FindTaskCommandParser --> Coach2K22Parser +deactivate FindTaskCommandParser + +Coach2K22Parser -> FindTaskCommandParser : parse(arg) +activate FindTaskCommandParser + +create FindTaskCommand +FindTaskCommandParser -> FindTaskCommand +activate FindTaskCommand + +FindTaskCommand --> FindTaskCommandParser : f +deactivate FindTaskCommand + +FindTaskCommandParser --> Coach2K22Parser : f +deactivate FindTaskCommandParser + +Coach2K22Parser --> LogicManager : f +deactivate Coach2K22Parser +destroy FindTaskCommandParser + +LogicManager -> FindTaskCommand : execute() +activate FindTaskCommand + +FindTaskCommand -> Model: updateFilteredTaskList(predicate) +activate Model + +Model --> FindTaskCommand +deactivate Model + +create CommandResult +FindTaskCommand -> CommandResult +activate CommandResult + +CommandResult --> FindTaskCommand +deactivate CommandResult + +FindTaskCommand --> LogicManager : result +deactivate FindTaskCommand + +[<--LogicManager : result +deactivate LogicManager +@enduml diff --git a/docs/diagrams/GetPersonSequenceDiagram.puml b/docs/diagrams/GetPersonSequenceDiagram.puml new file mode 100644 index 00000000000..0f699162755 --- /dev/null +++ b/docs/diagrams/GetPersonSequenceDiagram.puml @@ -0,0 +1,66 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":GetPersonCommandParser" as GetPersonCommandParser LOGIC_COLOR +participant "f:GetPersonCommand" as GetPersonCommand 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(arg) +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand(arg) +activate Coach2K22Parser + +create GetPersonCommandParser +Coach2K22Parser -> GetPersonCommandParser +activate GetPersonCommandParser + +GetPersonCommandParser --> Coach2K22Parser +deactivate GetPersonCommandParser + +Coach2K22Parser -> GetPersonCommandParser : parse(arg) +activate GetPersonCommandParser + +create GetPersonCommand +GetPersonCommandParser -> GetPersonCommand +activate GetPersonCommand + +GetPersonCommand --> GetPersonCommandParser : f +deactivate GetPersonCommand + +GetPersonCommandParser --> Coach2K22Parser : f +deactivate GetPersonCommandParser + +Coach2K22Parser --> LogicManager : f +deactivate Coach2K22Parser +destroy GetPersonCommandParser + +LogicManager -> GetPersonCommand : execute() +activate GetPersonCommand + +GetPersonCommand -> Model: updateFilteredPersonList(predicate) +activate Model + +Model --> GetPersonCommand +deactivate Model + +create CommandResult +GetPersonCommand -> CommandResult +activate CommandResult + +CommandResult --> GetPersonCommand +deactivate CommandResult + +GetPersonCommand -->LogicManager : result +deactivate GetPersonCommand + +[<--LogicManager : result +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LoadCourtSequenceDiagram.puml b/docs/diagrams/LoadCourtSequenceDiagram.puml new file mode 100644 index 00000000000..c8a31fbaced --- /dev/null +++ b/docs/diagrams/LoadCourtSequenceDiagram.puml @@ -0,0 +1,65 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":LoadCourtCommandParser" as LoadCourtCommandParser LOGIC_COLOR +participant "l:LoadCourtCommand" as LoadCourtCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box MainWindow MODEL_COLOR_T1 +participant ":MainWindow" as MainWindow MODEL_COLOR +end box + +[-> LogicManager : execute() +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand(args) +activate Coach2K22Parser + +create LoadCourtCommandParser +Coach2K22Parser -> LoadCourtCommandParser +activate LoadCourtCommandParser + +LoadCourtCommandParser --> Coach2K22Parser +deactivate LoadCourtCommandParser + +Coach2K22Parser -> LoadCourtCommandParser : parse(args) +activate LoadCourtCommandParser + +create LoadCourtCommand +LoadCourtCommandParser -> LoadCourtCommand +activate LoadCourtCommand + +LoadCourtCommand --> LoadCourtCommandParser +deactivate LoadCourtCommand + +LoadCourtCommandParser --> Coach2K22Parser + +LoadCourtCommandParser -[hidden]-> Coach2K22Parser +destroy LoadCourtCommandParser + +Coach2K22Parser --> LogicManager +deactivate Coach2K22Parser + +LogicManager -> LoadCourtCommand : execute() +activate LoadCourtCommand + +create CommandResult +LoadCourtCommand -> CommandResult +activate CommandResult + +CommandResult --> LoadCourtCommand + +MainWindow --> CommandResult : getBackgroundImage() +CommandResult --> MainWindow +deactivate CommandResult + +LoadCourtCommand --> LogicManager : result +deactivate LoadCourtCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..aca2f458245 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -6,7 +6,7 @@ skinparam classBackgroundColor LOGIC_COLOR package Logic { -Class AddressBookParser +Class Coach2K22Parser 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" Coach2K22Parser +Coach2K22Parser ..> XYZCommand : creates > XYZCommand -up-|> Command LogicManager .left.> Command : executes > diff --git a/docs/diagrams/ModelClassDiagram.png b/docs/diagrams/ModelClassDiagram.png new file mode 100644 index 00000000000..ed7f36f85b8 Binary files /dev/null and b/docs/diagrams/ModelClassDiagram.png differ diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..568b8fa3a11 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -7,9 +7,14 @@ skinparam classBackgroundColor MODEL_COLOR Package Model <>{ Class "<>\nReadOnlyAddressBook" as ReadOnlyAddressBook Class "<>\nReadOnlyUserPrefs" as ReadOnlyUserPrefs +Class "<>\nReadOnlyStrategyBoard" as ReadOnlyStrategyBoard Class "<>\nModel" as Model Class AddressBook Class ModelManager +Class StrategyBoard +Class TaskBook +Class UniqueTaskList +Class UniquePlayerList Class UserPrefs Class UniquePersonList @@ -19,6 +24,7 @@ Class Email Class Name Class Phone Class Tag +Class Task } @@ -27,9 +33,15 @@ HiddenOutside ..> Model AddressBook .up.|> ReadOnlyAddressBook +StrategyBoard .up.|> ReadOnlyStrategyBoard + +StrategyBoard *--> "1" UniquePlayerList + + ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs Model .left.> ReadOnlyAddressBook +Model .left.> ReadOnlyStrategyBoard ModelManager -left-> "1" AddressBook ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs @@ -46,5 +58,10 @@ Name -[hidden]right-> Phone Phone -[hidden]right-> Address Address -[hidden]right-> Email +ModelManager --> "1" TaskBook +ModelManager --> "1" StrategyBoard +TaskBook *-->"1" UniqueTaskList +UniqueTaskList --> "*" Task ModelManager -->"~* filtered" Person + @enduml diff --git a/docs/diagrams/MovePlayerSequenceDiagram.puml b/docs/diagrams/MovePlayerSequenceDiagram.puml new file mode 100644 index 00000000000..8fa293f4a75 --- /dev/null +++ b/docs/diagrams/MovePlayerSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant ":MovePlayerCommandParser" as MovePlayerCommandParser LOGIC_COLOR +participant "p:MovePlayerCommand" as MovePlayerCommand 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("move Cena ...") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("move Cena ...") +activate Coach2K22Parser + +create MovePlayerCommandParser +Coach2K22Parser -> MovePlayerCommandParser +activate MovePlayerCommandParser + +MovePlayerCommandParser --> Coach2K22Parser +deactivate MovePlayerCommandParser + +Coach2K22Parser -> MovePlayerCommandParser : parse("Cena ...") +activate MovePlayerCommandParser + +create MovePlayerCommand +MovePlayerCommandParser -> MovePlayerCommand +activate MovePlayerCommand + +MovePlayerCommand --> MovePlayerCommandParser : p +deactivate MovePlayerCommand + +MovePlayerCommandParser --> Coach2K22Parser : p +deactivate MovePlayerCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +MovePlayerCommandParser -[hidden]-> Coach2K22Parser +destroy MovePlayerCommandParser + +Coach2K22Parser --> LogicManager : p +deactivate Coach2K22Parser + +LogicManager -> MovePlayerCommand : execute() +activate MovePlayerCommand + +MovePlayerCommand -> Model : deletePlayer(Player) +activate Model + +Model --> MovePlayerCommand +deactivate Model + +MovePlayerCommand -> Model : addPlayer(Player) +activate Model + +Model --> MovePlayerCommand +deactivate Model + +create CommandResult +MovePlayerCommand -> CommandResult +activate CommandResult + +CommandResult --> MovePlayerCommand +deactivate CommandResult + +MovePlayerCommand --> LogicManager : result +deactivate MovePlayerCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..cc69f4a62c1 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 Coach2K22Parser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -19,12 +19,12 @@ Class Prefix } Class HiddenOutside #FFFFFF -HiddenOutside ..> AddressBookParser +HiddenOutside ..> Coach2K22Parser -AddressBookParser .down.> XYZCommandParser: creates > +Coach2K22Parser .down.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > -AddressBookParser ..> Command : returns > +Coach2K22Parser ..> Command : returns > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer diff --git a/docs/diagrams/SortNoteSequenceDiagram.puml b/docs/diagrams/SortNoteSequenceDiagram.puml new file mode 100644 index 00000000000..e0d94df6709 --- /dev/null +++ b/docs/diagrams/SortNoteSequenceDiagram.puml @@ -0,0 +1,52 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant "s:SortNoteCommand" as SortNoteCommand 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() +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("sort-note") +activate Coach2K22Parser + +create SortNoteCommand +Coach2K22Parser -> SortNoteCommand : s +activate SortNoteCommand + +SortNoteCommand --> Coach2K22Parser +deactivate SortNoteCommand + +Coach2K22Parser --> LogicManager : s +deactivate Coach2K22Parser + +LogicManager -> SortNoteCommand : execute() +activate SortNoteCommand + +SortNoteCommand -> Model : setAddressBook(sortedAddressBook) +activate Model + +Model --> SortNoteCommand +deactivate Model + +create CommandResult +SortNoteCommand -> CommandResult +activate CommandResult + +CommandResult --> SortNoteCommand +deactivate CommandResult + +SortNoteCommand --> LogicManager : result +deactivate SortNoteCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/SortTaskByDateSequenceDiagram.puml b/docs/diagrams/SortTaskByDateSequenceDiagram.puml new file mode 100644 index 00000000000..07154097efb --- /dev/null +++ b/docs/diagrams/SortTaskByDateSequenceDiagram.puml @@ -0,0 +1,53 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":Coach2K22Parser" as Coach2K22Parser LOGIC_COLOR +participant "p:SortTaskByDateCommand" as SortTaskByDateCommand 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("sort-date") +activate LogicManager + +LogicManager -> Coach2K22Parser : parseCommand("sort-date") +activate Coach2K22Parser + +create SortTaskByDateCommand +Coach2K22Parser -> SortTaskByDateCommand +activate SortTaskByDateCommand + +SortTaskByDateCommand --> Coach2K22Parser +deactivate SortTaskByDateCommand + +Coach2K22Parser --> LogicManager +deactivate Coach2K22Parser + +LogicManager -> SortTaskByDateCommand : execute() +activate SortTaskByDateCommand + +activate SortTaskByDateCommand +SortTaskByDateCommand -> Model : setTaskBook(sortedTaskBook) +activate Model + +Model --> SortTaskByDateCommand +deactivate Model + +create CommandResult +SortTaskByDateCommand -> CommandResult +activate CommandResult + +CommandResult --> SortTaskByDateCommand +deactivate CommandResult + +SortTaskByDateCommand --> LogicManager : result +deactivate SortTaskByDateCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/images/AddNoteSequenceDiagram.png b/docs/images/AddNoteSequenceDiagram.png new file mode 100644 index 00000000000..88a7dcd1c14 Binary files /dev/null and b/docs/images/AddNoteSequenceDiagram.png differ diff --git a/docs/images/AddPersonSequenceDiagram.png b/docs/images/AddPersonSequenceDiagram.png new file mode 100644 index 00000000000..70d272f4bf3 Binary files /dev/null and b/docs/images/AddPersonSequenceDiagram.png differ diff --git a/docs/images/AddPersonTagSequenceDiagram.png b/docs/images/AddPersonTagSequenceDiagram.png new file mode 100644 index 00000000000..94989b7afff Binary files /dev/null and b/docs/images/AddPersonTagSequenceDiagram.png differ diff --git a/docs/images/AddPlayerSequenceDiagram.png b/docs/images/AddPlayerSequenceDiagram.png new file mode 100644 index 00000000000..ed9ec1e62b4 Binary files /dev/null and b/docs/images/AddPlayerSequenceDiagram.png differ diff --git a/docs/images/AddTagSequenceDiagram.png b/docs/images/AddTagSequenceDiagram.png new file mode 100644 index 00000000000..73562c23e05 Binary files /dev/null and b/docs/images/AddTagSequenceDiagram.png differ diff --git a/docs/images/AddTaskSequenceDiagram.png b/docs/images/AddTaskSequenceDiagram.png new file mode 100644 index 00000000000..5109744fde7 Binary files /dev/null and b/docs/images/AddTaskSequenceDiagram.png differ diff --git a/docs/images/AddTaskTagSequenceDiagram.png b/docs/images/AddTaskTagSequenceDiagram.png new file mode 100644 index 00000000000..7de38060de0 Binary files /dev/null and b/docs/images/AddTaskTagSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png deleted file mode 100644 index 1ec62caa2a5..00000000000 Binary files a/docs/images/BetterModelClassDiagram.png and /dev/null differ diff --git a/docs/images/ClearPersonSequenceDiagram.png b/docs/images/ClearPersonSequenceDiagram.png new file mode 100644 index 00000000000..3f99eb2bd01 Binary files /dev/null and b/docs/images/ClearPersonSequenceDiagram.png differ diff --git a/docs/images/ClearTaskSequenceDiagram.png b/docs/images/ClearTaskSequenceDiagram.png new file mode 100644 index 00000000000..fc0f5542c50 Binary files /dev/null and b/docs/images/ClearTaskSequenceDiagram.png differ diff --git a/docs/images/CommandFormatSections.png b/docs/images/CommandFormatSections.png new file mode 100644 index 00000000000..bcbb791fef7 Binary files /dev/null and b/docs/images/CommandFormatSections.png differ diff --git a/docs/images/DeleteNoteSequenceDiagram.png b/docs/images/DeleteNoteSequenceDiagram.png new file mode 100644 index 00000000000..1f56d439fd5 Binary files /dev/null and b/docs/images/DeleteNoteSequenceDiagram.png differ diff --git a/docs/images/DeletePersonSequenceDiagram.png b/docs/images/DeletePersonSequenceDiagram.png new file mode 100644 index 00000000000..cc2f86b19cc Binary files /dev/null and b/docs/images/DeletePersonSequenceDiagram.png differ diff --git a/docs/images/DeletePersonTagSequenceDiagram.png b/docs/images/DeletePersonTagSequenceDiagram.png new file mode 100644 index 00000000000..356352a64e8 Binary files /dev/null and b/docs/images/DeletePersonTagSequenceDiagram.png differ diff --git a/docs/images/DeletePlayerSequenceDiagram.png b/docs/images/DeletePlayerSequenceDiagram.png new file mode 100644 index 00000000000..105c22f758a Binary files /dev/null and b/docs/images/DeletePlayerSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..c077edd712d 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/DeleteTaskSequenceDiagram.png b/docs/images/DeleteTaskSequenceDiagram.png new file mode 100644 index 00000000000..fbab3a01b85 Binary files /dev/null and b/docs/images/DeleteTaskSequenceDiagram.png differ diff --git a/docs/images/DeleteTaskTagSequenceDiagram.png b/docs/images/DeleteTaskTagSequenceDiagram.png new file mode 100644 index 00000000000..c23fbc9f244 Binary files /dev/null and b/docs/images/DeleteTaskTagSequenceDiagram.png differ diff --git a/docs/images/EditPersonSequenceDiagram.png b/docs/images/EditPersonSequenceDiagram.png new file mode 100644 index 00000000000..bc6ac5bd48f Binary files /dev/null and b/docs/images/EditPersonSequenceDiagram.png differ diff --git a/docs/images/EditTaskSequenceDiagram.png b/docs/images/EditTaskSequenceDiagram.png new file mode 100644 index 00000000000..90ba359542d Binary files /dev/null and b/docs/images/EditTaskSequenceDiagram.png differ diff --git a/docs/images/ExportStrategySequenceDiagram.png b/docs/images/ExportStrategySequenceDiagram.png new file mode 100644 index 00000000000..9b361b633ae Binary files /dev/null and b/docs/images/ExportStrategySequenceDiagram.png differ diff --git a/docs/images/FindPersonSequenceDiagram.png b/docs/images/FindPersonSequenceDiagram.png new file mode 100644 index 00000000000..726a03aeaab Binary files /dev/null and b/docs/images/FindPersonSequenceDiagram.png differ diff --git a/docs/images/FindTaskSequenceDiagram.png b/docs/images/FindTaskSequenceDiagram.png new file mode 100644 index 00000000000..80375542665 Binary files /dev/null and b/docs/images/FindTaskSequenceDiagram.png differ diff --git a/docs/images/GetPersonSequenceDiagram.png b/docs/images/GetPersonSequenceDiagram.png new file mode 100644 index 00000000000..1d9e6305afa Binary files /dev/null and b/docs/images/GetPersonSequenceDiagram.png differ diff --git a/docs/images/LoadCourtSequenceDiagram.png b/docs/images/LoadCourtSequenceDiagram.png new file mode 100644 index 00000000000..cea7e20c1dd Binary files /dev/null and b/docs/images/LoadCourtSequenceDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index 9e9ba9f79e5..5ddf19327a2 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 04070af60d8..ed7f36f85b8 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/MovePlayerSequenceDiagram.png b/docs/images/MovePlayerSequenceDiagram.png new file mode 100644 index 00000000000..4615fc7fb60 Binary files /dev/null and b/docs/images/MovePlayerSequenceDiagram.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index e7b4c8880cd..ea91d103dc7 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/SortNoteSequenceDiagram.png b/docs/images/SortNoteSequenceDiagram.png new file mode 100644 index 00000000000..4573022d12d Binary files /dev/null and b/docs/images/SortNoteSequenceDiagram.png differ diff --git a/docs/images/SortTaskByDateSequenceDiagram.png b/docs/images/SortTaskByDateSequenceDiagram.png new file mode 100644 index 00000000000..9b0f3c3f403 Binary files /dev/null and b/docs/images/SortTaskByDateSequenceDiagram.png differ diff --git a/docs/images/Ui-Labeled.png b/docs/images/Ui-Labeled.png new file mode 100644 index 00000000000..4104457f5e6 Binary files /dev/null and b/docs/images/Ui-Labeled.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..6e237ce466a 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 785e04dbab4..8b927451dcd 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/aalghrairy.png b/docs/images/aalghrairy.png new file mode 100644 index 00000000000..9af2b291a67 Binary files /dev/null and b/docs/images/aalghrairy.png differ diff --git a/docs/images/add-task-false.png b/docs/images/add-task-false.png new file mode 100644 index 00000000000..44dff7054a2 Binary files /dev/null and b/docs/images/add-task-false.png differ diff --git a/docs/images/add-task-true.png b/docs/images/add-task-true.png new file mode 100644 index 00000000000..33e42a59dd4 Binary files /dev/null and b/docs/images/add-task-true.png differ diff --git a/docs/images/export-ex1.png b/docs/images/export-ex1.png new file mode 100644 index 00000000000..943ad455aef Binary files /dev/null and b/docs/images/export-ex1.png differ diff --git a/docs/images/export-ex2.png b/docs/images/export-ex2.png new file mode 100644 index 00000000000..19786b218ac Binary files /dev/null and b/docs/images/export-ex2.png differ diff --git a/docs/images/getperson-ex1.png b/docs/images/getperson-ex1.png new file mode 100644 index 00000000000..09a2e8174a9 Binary files /dev/null and b/docs/images/getperson-ex1.png differ diff --git a/docs/images/getperson-ex2.png b/docs/images/getperson-ex2.png new file mode 100644 index 00000000000..98f2ecac4bd Binary files /dev/null and b/docs/images/getperson-ex2.png differ diff --git a/docs/images/juayhee.png b/docs/images/juayhee.png new file mode 100644 index 00000000000..43d58ffb25e Binary files /dev/null and b/docs/images/juayhee.png differ diff --git a/docs/images/load-court-ex1.png b/docs/images/load-court-ex1.png new file mode 100644 index 00000000000..e71aef5f538 Binary files /dev/null and b/docs/images/load-court-ex1.png differ diff --git a/docs/images/load-court-ex2.png b/docs/images/load-court-ex2.png new file mode 100644 index 00000000000..0f61d0b8398 Binary files /dev/null and b/docs/images/load-court-ex2.png differ diff --git a/docs/images/move-ex1.png b/docs/images/move-ex1.png new file mode 100644 index 00000000000..6cee0e874cc Binary files /dev/null and b/docs/images/move-ex1.png differ diff --git a/docs/images/move-ex2.png b/docs/images/move-ex2.png new file mode 100644 index 00000000000..d221c981c57 Binary files /dev/null and b/docs/images/move-ex2.png differ diff --git a/docs/images/prgj.png b/docs/images/prgj.png new file mode 100644 index 00000000000..96acb3f20c3 Binary files /dev/null and b/docs/images/prgj.png differ diff --git a/docs/images/rye-catcher.png b/docs/images/rye-catcher.png new file mode 100644 index 00000000000..02335f237b2 Binary files /dev/null and b/docs/images/rye-catcher.png differ diff --git a/docs/images/simjunheng.png b/docs/images/simjunheng.png new file mode 100644 index 00000000000..346bcffddd3 Binary files /dev/null and b/docs/images/simjunheng.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..aaa2e4a25cf 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,23 @@ --- layout: page -title: AddressBook Level-3 +title: Coach2K22 --- -[![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/AY2122S2-CS2103T-W14-2/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2122S2-CS2103T-W14-2/tp/actions) +[![codecov](https://codecov.io/gh/AY2122S2-CS2103T-W14-2/tp/branch/master/graph/badge.svg)](https://app.codecov.io/gh/AY2122S2-CS2103T-W14-2/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). +**Coach2K22 is a contact management application that helps busy sports coaches organise their overwhelming lists of contacts and messy weekly schedules. It also provides them with a platform to visualise defensive and offensive plays as the game unfolds.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). -* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). -* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. +Example usages: +* as a contact management utility for his players and relevant contacts +* as a visualiser for real-time play to aid decision making on the fly +* as an organisation tool for teams and organisations + +Guide: +* If you are interested in using Coach2K22, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing Coach2K22, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/aalghrairy.md b/docs/team/aalghrairy.md new file mode 100644 index 00000000000..7514a1cdbff --- /dev/null +++ b/docs/team/aalghrairy.md @@ -0,0 +1,62 @@ +### Abdulelah Faisal S Al Ghrairy’s Project Portfolio Page + +#### Overview: +Our product helps busy sports coaches organise their overwhelming lists of contacts and messy weekly +schedules. It also provides them with a platform to visualise defensive and offensive plays as the game unfolds. + +#### Summary of Contributions: + +* **Code contributed**: [RepoSense link]( +https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=aalghrairy&breakdown=true) +* **Enhancements Implemented:** + * Implemented `note-del` command to delete notes from list for each contact + * Implemented GUI of TaskCard and TaskListPanel + * Enhanced Note feature to include three different lists of notes for each contact + * _Expanded `note-add` command to_`strength-add`, `weakness-add`, and `misc-add` + * _Expanded `note-del` command to_`strength-del`, `weakness-del`, and `misc-del` + * Improved GUI of PersonCard to better display the three note lists + * Implemented `clear-t` command to clear all tasks or tasks for a given date + * Implemented `load-court` command to load background image for strategy tab + * Required many instances of defensive programming due to reliance on external directories and files + * Implemented `sort-strength` and `sort-weakness` commands to sort the address book by total strengths/weaknesses in descending order respectively +* **Contributions to the UG:** + * Added feature definitions for the following functions (and included in command summary): + * (`strength-add`, `weakness-add`, `misc-add`, `strength-del`, `weakness-del`, `misc-del`, `clear-t`, `load-court`, `sort-strength`, `sort-weakness`) + * Add detailed installation instructions, including labelled GUI example, operating system, and system requirements. +* **Contributions to the DG:** + * Added the Product Scope and Glossary sections + * Implemented the Model Component, including the UML Diagram + * Added the following implementation descriptions of functions, as well as their respective UML Diagrams + * Clear Tasks + * Load Court + * Add and Delete Strength/Weakness/Misc + * Sort by Strength/Weakness + * Added the following use cases: + * Add a strength, weakness, and miscellaneous note to a person + * Delete a strength, weakness, and miscellaneous note from a person + * Sort address book by strengths/weaknesses in descending order + * Clear all tasks from the task list + * Clear all tasks for a specified date from task list + * Load new background image for strategy tab +* **Contributions to team-based tasks**: + * Replaced references to `AddressBook` with `Coach2K22` in Developer Guide + * Maintained some issues to be implemented by all team members + * Maintained issue tracking for some individual tasks of respective team members + * Including ensuring milestones, assignees, and labels were properly selected + * Modified the Index.md file +* **Review/mentoring contributions**: + * [#26](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/26) + * [#38](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/38) + * [#76](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/76) + * [#97](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/97) + * [#106](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/106) + * [#109](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/109) + * [#124](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/124) + * [#140](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/140) + * [#143](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/143) +* **Contributions beyond the project team:** + * Evidence of helping others: + * [#197](https://github.com/nus-cs2103-AY2122S2/forum/issues/197) + * [#222](https://github.com/nus-cs2103-AY2122S2/forum/issues/222) + * Evidence of technical leadership + * _n/a_ 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/juayhee.md b/docs/team/juayhee.md new file mode 100644 index 00000000000..e0b6bf0df12 --- /dev/null +++ b/docs/team/juayhee.md @@ -0,0 +1,21 @@ +### Project: Coach2K22 + +#### Overview +Our product helps busy sports coaches organise their overwhelming lists of contacts and messy weekly +schedules. It also provides them with a platform to visualise defensive and offensive plays as the game unfolds. + +#### Summary of Contributions +* **Code contributed**: [RepoSense link]( + https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=juayhee&breakdown=true) +* **Enhancement implemented** + * _to be added soon_ +* **Contributions to the UG** + * _to be added soon_ +* **Contributions to the DG** + * _to be added soon_ +* **Contributions to team-based tasks** + * _to be added soon_ +* **Review/mentoring contributions** + * _to be added soon_ +* **Contributions beyond the project team** + * _to be added soon_ diff --git a/docs/team/prgj.md b/docs/team/prgj.md new file mode 100644 index 00000000000..2c56c6d6740 --- /dev/null +++ b/docs/team/prgj.md @@ -0,0 +1,30 @@ +### Project: Coach2K22 + +#### Overview +Our product helps busy sports coaches organise their overwhelming lists of contacts and messy weekly +schedules. It also provides them with a platform to visualise defensive and offensive plays as the game unfolds. + +#### Summary of Contributions +* **Code contributed**: [RepoSense link]( + https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=prgj&breakdown=true) +* **Enhancement implemented** + * Implemented `add-t` command and relevant test cases to add a new task to the TaskList (PR: [#56](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/56), [#65](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/65)) + * Implemented `del-t` command and relevant test cases to delete an existing task from the TaskList (PR: [#56](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/56), [#65](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/65)) + * Implemented `edit-t` command and relevant test cases to edit an existing task in the TaskList (PR: [#135](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/135), [#136](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/136)) + * Implemented `export` command and relevant test cases to export an image from the Strategy Panel (PR: [#137](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/137), [#231](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/231)) +* **Contributions to the UG** + * Added documentation for `add-t`, `del-t`,`edit-t`and `export` (PR: [#94](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/94)) + * Added CLI tutorial ([#252](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/252)) +* **Contributions to the DG** + * Added use cases for `add-t`, `del-t`, and `export` (PR: [#94](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/94)) + * Added user stories `add-t`, `del-t`, and `export` (PR: [#94](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/94)) + * Added implementation details for `add-t`and `export`, modified `edit-t`(PR: [#263](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/263)) + * Added use cases for `edit-t`, `edit-p` and `export` (PR: [#263](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/263)) + * Added acknowledgements (PR: [#263](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/263)) +* **Contributions to team-based tasks** + * Update UserGuide with contents of draft user guide (PR: [#21](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/21)) +* **Review/mentoring contributions** + * PR: [#86](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/86) + * PR: [#139](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/139) +* **Contributions beyond the project team** + * _to be added soon_ diff --git a/docs/team/rye-catcher.md b/docs/team/rye-catcher.md new file mode 100644 index 00000000000..942bb48fa5e --- /dev/null +++ b/docs/team/rye-catcher.md @@ -0,0 +1,46 @@ +### Lyu Xiaoteng's Project Portfolio Page + +#### Overview +Our product helps busy sports coaches organise their overwhelming lists of contacts and messy weekly +schedules. It also provides them with a platform to visualise defensive and offensive plays as the game unfolds. + +#### Summary of Contributions +* **Code contributed**: [RepoSense link]( + https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=rye-catcher&breakdown=true&sort=groupTitle&sortWithin=title&since=2022-02-18&timeframe=commit&mergegroup=&groupSelect=groupByRepos&checkedFileTypes=docs~functional-code~test-code~other) +* **Enhancement implemented** + * Implemented `add-player` command to add a new player to the strategy board (PR: [#85](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/85)) + * Implemented `del-player` command to delete a player from the strategy board (PR: [#85](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/85)) + * Implemented `move` command to move a player on the strategy board (PR: [#122](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/122)) + * Implemented drag and move features on the strategy board (PR: [#61](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/61)) + * Partially implemented note features to allow users to add notes to the contacts (PR: [#38](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/38)) +* **Contributions to the UG** + * Added the documentation for the following features `add-player`, `del-player` (PR: [#92](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/92)) + * Added the documentation for the following features `move` (PR: [#253](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/253)) + * Added the supplemental information for the command summery information (PR: [#237](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/237)) +* **Contributions to the DG** + * Added the following use cases to the documentation: + * Add a new player to the strategy board (PR: [#92](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/92)) + * Remove a player from the strategy board (PR: [#92](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/92)) + * Move a player on the strategy board (PR: [#240](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/240)) + * Added the implementation and design consideration for **Add Players** feature (PR: [#92](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/92)) + * Added the implementation and design consideration for **Remove Players** and **Move Players** features (PR: [#240](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/240)) +* **Contributions to team-based tasks** + * Maintained some issues tracking and fix some minor bugs in others' codes. +* **Review/mentoring contributions** + * PR: [#16](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/16) + * PR: [#20](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/20) + * PR: [#21](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/21) + * PR: [#24](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/24) + * PR: [#48](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/48) + * PR: [#72](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/72) + * PR: [#83](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/83) + * PR: [#86](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/86) + * PR: [#91](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/91) + * PR: [#93](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/93) + * PR: [#94](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/94) + * PR: [#95](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/95) + * PR: [#96](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/96) + * PR: [#100](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/100) + * PR: [#115](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/115) +* **Contributions beyond the project team** + * _to be added soon_ diff --git a/docs/team/simjunheng.md b/docs/team/simjunheng.md new file mode 100644 index 00000000000..8d5f38c5926 --- /dev/null +++ b/docs/team/simjunheng.md @@ -0,0 +1,48 @@ +### Sim Jun Heng's Project Portfolio Page + +#### Overview +Our product helps busy sports coaches organise their overwhelming lists of contacts and messy weekly +schedules. It also provides them with a platform to visualise defensive and offensive plays as the game unfolds. + +#### Summary of Contributions +* **Code contributed**: [RepoSense link]( + https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=simjunheng&breakdown=true) +* **Enhancement implemented** + * Implemented `find-p` command to find persons by tags and names (PR: [#24](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/24)) + * Implemented `find-t` command to find tasks by tags and names (PR: [#79](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/79)) + * Implemented `get-person` command to retrieve the contact information of persons tagged to a specific task (PR: [#100](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/100)) + * Added JavaFX Sliders to complement the `move` command (PR: [#106](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/106)) + * Partially implemented the `add-t` command to allow persons to be tagged to task (PR: [#76](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/76)) + * Implemented checks in the `clear-p`, `delete-p`, and `edit-p` commands to ensure that changes in the person list are also reflected in the task list (PR: [#76](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/76), [#97](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/97)) + * Added JavaFX Tab Panes to accommodate task list and strategies (PR: [#37](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/37)) +* **Contributions to the UG** + * Added the documentation for the following features `find-p`, `find-t`, `get-person` (PR: [#86](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/86), [#139](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/139)) + * Added **About the User Guide** section to improve front matter of the user guide (PR: [#214](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/214)) + * Added section numberings throughout the user guide (PR: [#86](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/86)) +* **Contributions to the DG** + * Update UI component diagram (PR: [#223](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/223)) + * Added the following use cases: + * Find tasks by name or tag (PR: [#139](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/139)) + * Find persons by name or tag (PR: [#24](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/24) + * Find persons tagged to a task (PR: [#139](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/139)) + * Added the implementation and design consideration for: + * **Find Persons** feature (PR: [#86](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/86)) + * **Add Persons** feature (PR: [#223](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/223)) + * **Edit Persons** feature (PR: [#223](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/223)) + * **Delete Persons** feature (PR: [#223](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/223)) + * **Delete Tasks** feature (PR: [#223](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/223)) + * **Find Tasks** feature (PR: [#223](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/223)) + * **Get Persons** feature (PR: [#223](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/223)) +* **Contributions to team-based tasks** + * Released JAR file for v1.2 and v1.3 + * Update collaborative project notes for v1.2 and v1.3 features demo + * Categorised PE-D issues with labels to make bug triage easier + * Enabled assertions in build.gradle +* **Review/mentoring contributions** + * PR: [#65](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/65) + * PR: [#93](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/93) + * PR: [#96](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/96) + * PR: [#99](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/99) + * PR: [#137](https://github.com/AY2122S2-CS2103T-W14-2/tp/pull/137) +* **Contributions beyond the project team** + * _to be added soon_ diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index 4133aaa0151..b36d3d8a437 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -1,5 +1,6 @@ package seedu.address; +import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; @@ -19,14 +20,19 @@ import seedu.address.model.Model; import seedu.address.model.ModelManager; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyTaskBook; import seedu.address.model.ReadOnlyUserPrefs; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; import seedu.address.model.UserPrefs; import seedu.address.model.util.SampleDataUtil; import seedu.address.storage.AddressBookStorage; import seedu.address.storage.JsonAddressBookStorage; +import seedu.address.storage.JsonTaskBookStorage; import seedu.address.storage.JsonUserPrefsStorage; import seedu.address.storage.Storage; import seedu.address.storage.StorageManager; +import seedu.address.storage.TaskBookStorage; import seedu.address.storage.UserPrefsStorage; import seedu.address.ui.Ui; import seedu.address.ui.UiManager; @@ -48,7 +54,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing Coach2K22 ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -57,7 +63,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); + TaskBookStorage taskBookStorage = new JsonTaskBookStorage(userPrefs.getTaskBookFilePath()); + storage = new StorageManager(addressBookStorage, taskBookStorage, userPrefsStorage); initLogging(config); @@ -66,6 +73,8 @@ public void init() throws Exception { logic = new LogicManager(model, storage); ui = new UiManager(logic); + + initCourtsDirectory(); } /** @@ -75,22 +84,37 @@ public void init() throws Exception { */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional taskBookOptional; + ReadOnlyAddressBook initialAddressData; + ReadOnlyTaskBook initialTaskData; try { addressBookOptional = storage.readAddressBook(); if (!addressBookOptional.isPresent()) { logger.info("Data file not found. Will be starting with a sample AddressBook"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialAddressData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); } catch (DataConversionException e) { logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + initialAddressData = new AddressBook(); } catch (IOException e) { logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + initialAddressData = new AddressBook(); + } + try { + taskBookOptional = storage.readTaskBook(); + if (!taskBookOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample TaskBook"); + } + initialTaskData = taskBookOptional.orElseGet(SampleDataUtil::getSampleTaskBook); + } catch (DataConversionException e) { + logger.warning("Data file not in the correct format. Will be starting with an empty TaskBook"); + initialTaskData = new TaskBook(); + } catch (IOException e) { + logger.warning("Problem while reading from the file. Will be starting with an empty TaskBook"); + initialTaskData = new TaskBook(); } + return new ModelManager(initialAddressData, initialTaskData, new StrategyBoard(), userPrefs); - return new ModelManager(initialData, userPrefs); } private void initLogging(Config config) { @@ -165,15 +189,30 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { return initializedPrefs; } + /** + * Creates the "/courts" directory if it does not already exist. + */ + protected void initCourtsDirectory() { + File courtDirectory = new File("courts"); + + //Ensures that the "courts" directory exists. If it does, ensure it is not being mistaken for + // a similarly named file + if (!courtDirectory.exists()) { + courtDirectory.mkdir(); + logger.info( + "Courts directory cannot be found. Will be creating a new courts directory."); + } + } + @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting Coach2K22 " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping Coach2K22 ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e469..feeee611f54 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -8,6 +8,14 @@ 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_INVALID_TASK_DISPLAYED_INDEX = "The task index provided is invalid"; + public static final String MESSAGE_INVALID_NOTE_DISPLAYED_INDEX = "The note index provided is invalid"; public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - + public static final String MESSAGE_TASKS_LISTED_OVERVIEW = "%1$d tasks listed!"; + public static final String MESSAGE_INVALID_TAG = "Tags must be alphanumeric only!"; + public static final String MESSAGE_EMPTY_PERSON_LIST = "Person list is empty!"; + public static final String MESSAGE_INVALID_PLAYER = "%1$s is not in the strategy board!"; + public static final String MESSAGE_DUPLICATE_STRENGTH = "This strength already exists for this person!"; + public static final String MESSAGE_DUPLICATE_WEAKNESS = "This weakness already exists for this person!"; + public static final String MESSAGE_DUPLICATE_MISC = "This note already exists for this person!"; } diff --git a/src/main/java/seedu/address/commons/core/Tabs.java b/src/main/java/seedu/address/commons/core/Tabs.java new file mode 100644 index 00000000000..bce0d2f0b00 --- /dev/null +++ b/src/main/java/seedu/address/commons/core/Tabs.java @@ -0,0 +1,11 @@ +package seedu.address.commons.core; + +/** + * Container for Tab Index. + */ +public class Tabs { + public static final int DEFAULT = -1; + public static final int CONTACT_TAB = 0; + public static final int TASK_TAB = 1; + public static final int STRATEGY_TAB = 2; +} diff --git a/src/main/java/seedu/address/logic/EditPersonDescriptor.java b/src/main/java/seedu/address/logic/EditPersonDescriptor.java new file mode 100644 index 00000000000..4ea06cdd50b --- /dev/null +++ b/src/main/java/seedu/address/logic/EditPersonDescriptor.java @@ -0,0 +1,181 @@ +package seedu.address.logic; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.util.CollectionUtil; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; +import seedu.address.model.person.Address; +import seedu.address.model.person.Email; +import seedu.address.model.person.Phone; +import seedu.address.model.tag.Tag; + + +/** + * Stores the details to edit the person with. Each non-empty field value will replace the + * corresponding field value of the person. + */ +public class EditPersonDescriptor { + private Name name; + private Phone phone; + private Email email; + private Address address; + private Set tags; + private List strengths; + private List weaknesses; + private List miscellaneous; + + public EditPersonDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditPersonDescriptor(EditPersonDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.phone); + setEmail(toCopy.email); + setAddress(toCopy.address); + setTags(toCopy.tags); + setStrengths(toCopy.strengths); + setWeaknesses(toCopy.weaknesses); + setMiscellaneous(toCopy.miscellaneous); + } + + /** + * 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); + } + + public void setPhone(Phone phone) { + this.phone = phone; + } + + 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; + } + + 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(); + } + + /** + * Sets {@code strengths} to this object's {@code strengths}. + * A defensive copy of {@code strengths} is used internally. + */ + public void setStrengths(List strengths) { + this.strengths = (strengths != null) ? new ArrayList<>(strengths) : null; + } + + /** + * Returns an unmodifiable note list, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code strengths} is null. + */ + public Optional> getStrengths() { + return (strengths != null) ? Optional.of(Collections.unmodifiableList(strengths)) : Optional.empty(); + } + + /** + * Sets {@code weaknesses} to this object's {@code weaknesses}. + * A defensive copy of {@code weaknesses} is used internally. + */ + public void setWeaknesses(List weaknesses) { + this.weaknesses = (weaknesses != null) ? new ArrayList<>(weaknesses) : null; + } + + /** + * Returns an unmodifiable note list, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code weaknesses} is null. + */ + public Optional> getWeaknesses() { + return (weaknesses != null) ? Optional.of(Collections.unmodifiableList(weaknesses)) : Optional.empty(); + } + + /** + * Sets {@code miscellaneous} to this object's {@code miscellaneous}. + * A defensive copy of {@code miscellaneous} is used internally. + */ + public void setMiscellaneous(List miscellaneous) { + this.miscellaneous = (miscellaneous != null) ? new ArrayList<>(miscellaneous) : null; + } + + /** + * Returns an unmodifiable note list, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code notes} is null. + */ + public Optional> getMiscellaneous() { + return (miscellaneous != null) + ? Optional.of(Collections.unmodifiableList(miscellaneous)) : Optional.empty(); + } + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditPersonDescriptor)) { + return false; + } + + // state check + EditPersonDescriptor e = (EditPersonDescriptor) other; + + return getName().equals(e.getName()) + && getPhone().equals(e.getPhone()) + && getEmail().equals(e.getEmail()) + && getAddress().equals(e.getAddress()) + && getTags().equals(e.getTags()) + && getStrengths().equals(e.getStrengths()) + && getWeaknesses().equals(e.getWeaknesses()) + && getMiscellaneous().equals(e.getMiscellaneous()); + } +} diff --git a/src/main/java/seedu/address/logic/EditTaskDescriptor.java b/src/main/java/seedu/address/logic/EditTaskDescriptor.java new file mode 100644 index 00000000000..05dfd50bb3d --- /dev/null +++ b/src/main/java/seedu/address/logic/EditTaskDescriptor.java @@ -0,0 +1,137 @@ +package seedu.address.logic; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import seedu.address.commons.util.CollectionUtil; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; + +/** + * Stores the details to edit the task with. Each non-empty field value will replace the + * corresponding field value of the task. + */ +public class EditTaskDescriptor { + private Name name; + private Date date; + private StartTime startTime; + private EndTime endTime; + private Set tags; + private Set persons; + + public EditTaskDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditTaskDescriptor(EditTaskDescriptor toCopy) { + setName(toCopy.name); + setDate(toCopy.date); + setStartTime(toCopy.startTime); + setEndTime(toCopy.endTime); + setTags(toCopy.tags); + setPersons(toCopy.persons); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, date, startTime, endTime, tags, persons); + } + + public void setName(Name name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setDate(Date date) { + this.date = date; + } + + public Optional getDate() { + return Optional.ofNullable(date); + } + + public void setStartTime(StartTime startTime) { + this.startTime = startTime; + } + + public Optional getStartTime() { + return Optional.ofNullable(startTime); + } + + public void setEndTime(EndTime endTime) { + this.endTime = endTime; + } + + public Optional getEndTime() { + return Optional.ofNullable(endTime); + } + + /** + * 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(); + } + + /** + * Sets {@code persons} to this object's {@code persons}. + * A defensive copy of {@code persons} is used internally. + */ + public void setPersons(Set persons) { + this.persons = (persons != null) ? new HashSet<>(persons) : null; + } + + /** + * Returns an unmodifiable person set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + * Returns {@code Optional#empty()} if {@code persons} is null. + */ + public Optional> getPersons() { + return (persons != null) ? Optional.of(Collections.unmodifiableSet(persons)) : Optional.empty(); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditTaskDescriptor)) { + return false; + } + + // state check + EditTaskDescriptor e = (EditTaskDescriptor) other; + + return getName().equals(e.getName()) + && getDate().equals(e.getDate()) + && getStartTime().equals(e.getStartTime()) + && getEndTime().equals(e.getEndTime()) + && getTags().equals(e.getTags()) + && getPersons().equals(e.getPersons()); + } +} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..5aaf87bdfc8 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -5,10 +5,12 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.commands.task.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.ReadOnlyAddressBook; import seedu.address.model.person.Person; +import seedu.address.model.strategy.Player; +import seedu.address.model.task.Task; /** * API of the Logic component @@ -33,6 +35,12 @@ public interface Logic { /** Returns an unmodifiable view of the filtered list of persons */ ObservableList getFilteredPersonList(); + /** Returns an unmodifiable view of the filtered list of tasks */ + ObservableList getFilteredTaskList(); + + /** Returns an unmodifiable view of the filtered list of players */ + ObservableList getFilteredPlayerList(); + /** * Returns the user prefs' address book file path. */ diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9d9c6d15bdc..b161babbc9c 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -9,12 +9,14 @@ import seedu.address.commons.core.LogsCenter; 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.commands.task.exceptions.CommandException; +import seedu.address.logic.parser.Coach2K22Parser; 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.strategy.Player; +import seedu.address.model.task.Task; import seedu.address.storage.Storage; /** @@ -26,7 +28,7 @@ public class LogicManager implements Logic { private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final Coach2K22Parser parser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,7 +36,7 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + parser = new Coach2K22Parser(); } @Override @@ -42,11 +44,12 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = parser.parseCommand(commandText); commandResult = command.execute(model); try { storage.saveAddressBook(model.getAddressBook()); + storage.saveTaskBook(model.getTaskBook()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -64,6 +67,16 @@ public ObservableList getFilteredPersonList() { return model.getFilteredPersonList(); } + @Override + public ObservableList getFilteredTaskList() { + return model.getFilteredTaskList(); + } + + @Override + public ObservableList getFilteredPlayerList() { + return model.getFilteredPlayerList(); + } + @Override public Path getAddressBookFilePath() { return model.getAddressBookFilePath(); diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java deleted file mode 100644 index 9c86b1fa6e4..00000000000 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ /dev/null @@ -1,23 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.model.AddressBook; -import seedu.address.model.Model; - -/** - * Clears the address book. - */ -public class ClearCommand extends Command { - - public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java index 64f18992160..dcdcf996a3e 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/address/logic/commands/Command.java @@ -1,6 +1,6 @@ package seedu.address.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.commands.task.exceptions.CommandException; import seedu.address.model.Model; /** diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 92f900b7916..30792f5aa8a 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -1,9 +1,13 @@ package seedu.address.logic.commands; import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Tabs.DEFAULT; import java.util.Objects; +import seedu.address.model.image.Image; + + /** * Represents the result of a command execution. */ @@ -11,19 +15,47 @@ public class CommandResult { private final String feedbackToUser; - /** Help information should be shown to the user. */ + /** + * Help information should be shown to the user. + */ private final boolean showHelp; - /** The application should exit. */ + /** + * The application should exit. + */ private final boolean exit; + private final int tabPane; + + /** + * The background image of StrategyBoard should be changed + */ + private final boolean isLoadImage; + + /** + * The background image of StrategyBoard + */ + private final Image image; + + /** + * The application should export image in user-selected directory. + */ + private final boolean isExportCommand; + /** * Constructs a {@code CommandResult} with the specified fields. + *

+ * Specifically used for load-court and export commands. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, boolean showHelp, boolean exit, int tabPane, + boolean isLoadImage, Image image, boolean isExportCommand) { this.feedbackToUser = requireNonNull(feedbackToUser); this.showHelp = showHelp; + this.isLoadImage = isLoadImage; + this.image = image; this.exit = exit; + this.tabPane = tabPane; + this.isExportCommand = isExportCommand; } /** @@ -31,7 +63,7 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * and other fields set to their default value. */ public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + this(feedbackToUser, false, false, DEFAULT, false, null, false); } public String getFeedbackToUser() { @@ -46,6 +78,22 @@ public boolean isExit() { return exit; } + public int getTabPane() { + return tabPane; + } + + public boolean isLoadImageCommand() { + return isLoadImage; + } + + public Image getBackgroundImage() { + return image; + } + + public boolean isExportCommand() { + return isExportCommand; + } + @Override public boolean equals(Object other) { if (other == this) { @@ -60,12 +108,27 @@ public boolean equals(Object other) { CommandResult otherCommandResult = (CommandResult) other; return feedbackToUser.equals(otherCommandResult.feedbackToUser) && showHelp == otherCommandResult.showHelp - && exit == otherCommandResult.exit; + && exit == otherCommandResult.exit + && tabPane == otherCommandResult.tabPane + && isLoadImage == otherCommandResult.isLoadImage + && image == otherCommandResult.image; } @Override public int hashCode() { - return Objects.hash(feedbackToUser, showHelp, exit); + return Objects.hash(feedbackToUser, showHelp, exit, tabPane, isLoadImage, image); } + @Override + public String toString() { + return "CommandResult{" + + "feedbackToUser='" + feedbackToUser + '\'' + + ", showHelp=" + showHelp + + ", exit=" + exit + + ", tabPane=" + tabPane + + ", isLoadImage=" + isLoadImage + + ", image=" + image + + ", isExportCommand=" + isExportCommand + + '}'; + } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java deleted file mode 100644 index 02fd256acba..00000000000 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ /dev/null @@ -1,53 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; -import seedu.address.model.person.Person; - -/** - * Deletes a person identified using it's displayed index from the address book. - */ -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" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - private final Index targetIndex; - - public DeleteCommand(Index targetIndex) { - this.targetIndex = targetIndex; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java index 3dd85a8ba90..3c4d3cde3e3 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java @@ -1,5 +1,7 @@ package seedu.address.logic.commands; +import static seedu.address.commons.core.Tabs.DEFAULT; + import seedu.address.model.Model; /** @@ -13,7 +15,7 @@ public class ExitCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, DEFAULT, false, null, false); } } diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java deleted file mode 100644 index d6b19b0a0de..00000000000 --- a/src/main/java/seedu/address/logic/commands/FindCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; - -import seedu.address.commons.core.Messages; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; - -/** - * Finds and lists all persons in address book 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 " - + "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"; - - private final NameContainsKeywordsPredicate predicate; - - public FindCommand(NameContainsKeywordsPredicate predicate) { - this.predicate = predicate; - } - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(predicate); - return new CommandResult( - String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof FindCommand // instanceof handles nulls - && predicate.equals(((FindCommand) other).predicate)); // state check - } -} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/address/logic/commands/HelpCommand.java index bf824f91bd0..f8269c9c759 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/address/logic/commands/HelpCommand.java @@ -1,5 +1,7 @@ package seedu.address.logic.commands; +import static seedu.address.commons.core.Tabs.DEFAULT; + import seedu.address.model.Model; /** @@ -16,6 +18,6 @@ public class HelpCommand extends Command { @Override public CommandResult execute(Model model) { - return new CommandResult(SHOWING_HELP_MESSAGE, true, false); + return new CommandResult(SHOWING_HELP_MESSAGE, true, false, DEFAULT, false, null, false); } } diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/person/AddPersonCommand.java similarity index 79% rename from src/main/java/seedu/address/logic/commands/AddCommand.java rename to src/main/java/seedu/address/logic/commands/person/AddPersonCommand.java index 71656d7c5c8..ec6580ea81a 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/person/AddPersonCommand.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; import static java.util.Objects.requireNonNull; import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; @@ -7,16 +7,18 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; import seedu.address.model.Model; import seedu.address.model.person.Person; /** * Adds a person to the address book. */ -public class AddCommand extends Command { +public class AddPersonCommand extends Command { - public static final String COMMAND_WORD = "add"; + public static final String COMMAND_WORD = "add-p"; public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " + "Parameters: " @@ -41,7 +43,7 @@ public class AddCommand extends Command { /** * Creates an AddCommand to add the specified {@code Person} */ - public AddCommand(Person person) { + public AddPersonCommand(Person person) { requireNonNull(person); toAdd = person; } @@ -61,7 +63,7 @@ public CommandResult execute(Model model) throws CommandException { @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); + || (other instanceof AddPersonCommand // instanceof handles nulls + && toAdd.equals(((AddPersonCommand) other).toAdd)); } } diff --git a/src/main/java/seedu/address/logic/commands/person/AddPersonTagCommand.java b/src/main/java/seedu/address/logic/commands/person/AddPersonTagCommand.java new file mode 100644 index 00000000000..d0e29e52e41 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/AddPersonTagCommand.java @@ -0,0 +1,121 @@ +package seedu.address.logic.commands.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; +import seedu.address.model.person.Address; +import seedu.address.model.person.Email; +import seedu.address.model.person.Person; +import seedu.address.model.person.Phone; +import seedu.address.model.tag.Tag; + +public class AddPersonTagCommand extends Command { + public static final String COMMAND_WORD = "tag-add-p"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Add a tag to a person from our contact list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "TAG NAME (must be non-empty)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "friend"; + public static final String MESSAGE_ADD_TAG_SUCCESS = "Added tag: %1$s"; + public static final String MESSAGE_DUPLICATE_TAG = "This person already has this tag!"; + + public final Index index; + public final String tagName; + + /** + * Public constructor for AddTagCommand + * + * @param index Index of person to add tag to + * @param tagName Name of tag + */ + public AddPersonTagCommand(Index index, String tagName) { + requireAllNonNull(index, tagName); + + this.index = index; + this.tagName = tagName; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + // Exception when index out of bounds + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + + Person personToEdit = lastShownList.get(index.getZeroBased()); + Person editedPerson = addTagToPerson(personToEdit); + + + // Exception when a duplicate tag is added + Tag testTag = new Tag(this.tagName); + if (personToEdit.getTags().contains(testTag)) { + throw new CommandException(MESSAGE_DUPLICATE_TAG); + } + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + return new CommandResult(String.format(MESSAGE_ADD_TAG_SUCCESS, this.tagName)); + } + + /** + * Creates and returns a {@code Person} with a new tag {@code tagName} added to + * {@code personToEdit} + * + * @param personToEdit Person to be edited + * @return New Person object with the tag added (tag list updated) + */ + private Person addTagToPerson(Person personToEdit) throws CommandException { + // Keep all other fields the same + Name updatedName = personToEdit.getName(); + Phone updatedPhone = personToEdit.getPhone(); + Email updatedEmail = personToEdit.getEmail(); + Address updatedAddress = personToEdit.getAddress(); + List updatedStrengths = personToEdit.getStrengths(); + List updatedWeaknesses = personToEdit.getWeaknesses(); + List updatedMisc = personToEdit.getMiscellaneous(); + + // Changing tags + // Make modifiable copy since Person#getTags returns an unmodifiable Set + Set tagList = new HashSet<>(personToEdit.getTags()); + try { + tagList.add(new Tag(this.tagName)); + } catch (Exception e) { + throw new CommandException(Messages.MESSAGE_INVALID_TAG); + } + + + return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, tagList, + updatedStrengths, updatedWeaknesses, updatedMisc); + } + + @Override + public boolean equals(Object other) { + + return other == this // short circuit if same object + || (other instanceof AddPersonTagCommand // instanceof handles nulls + && (index.equals(((AddPersonTagCommand) other).index) + && tagName.equals((((AddPersonTagCommand) other).tagName)))); + } + +} + diff --git a/src/main/java/seedu/address/logic/commands/person/ClearPersonCommand.java b/src/main/java/seedu/address/logic/commands/person/ClearPersonCommand.java new file mode 100644 index 00000000000..14ac2e9ee9d --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/ClearPersonCommand.java @@ -0,0 +1,63 @@ +package seedu.address.logic.commands.person; + +import static java.util.Objects.requireNonNull; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; + +/** + * Clears the address book. + */ +public class ClearPersonCommand extends Command { + + public static final String COMMAND_WORD = "clear-p"; + public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + List unfilteredTaskList = model.getUnfilteredTaskList(); + + // update tasks after clearing the address book + for (Task task: unfilteredTaskList) { + EditTaskDescriptor editTaskDescriptor = new EditTaskDescriptor(); + Set persons = new HashSet<>(); + editTaskDescriptor.setPersons(persons); + Task editedTask = createEditedTask(task, editTaskDescriptor); + model.setTask(task, editedTask); + } + model.setAddressBook(new AddressBook()); + return new CommandResult(MESSAGE_SUCCESS); + } + + /** + * Creates and returns a {@code Task} with the details of {@code taskToEdit} + * edited with {@code editTaskDescriptor}. + */ + public static Task createEditedTask(Task taskToEdit, EditTaskDescriptor editTaskDescriptor) { + requireNonNull(taskToEdit); + + Name updatedName = editTaskDescriptor.getName().orElse(taskToEdit.getName()); + Date updatedDate = editTaskDescriptor.getDate().orElse(taskToEdit.getDate()); + StartTime updatedStartTime = editTaskDescriptor.getStartTime().orElse(taskToEdit.getStartTime()); + EndTime updatedEndTime = editTaskDescriptor.getEndTime().orElse(taskToEdit.getEndTime()); + Set updatedTags = editTaskDescriptor.getTags().orElse(taskToEdit.getTags()); + Set updatedPersons = editTaskDescriptor.getPersons().orElse(taskToEdit.getPersons()); + return new Task(updatedName, updatedDate, + updatedStartTime, updatedEndTime, updatedTags, updatedPersons); + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/DeletePersonCommand.java b/src/main/java/seedu/address/logic/commands/person/DeletePersonCommand.java new file mode 100644 index 00000000000..762e6336458 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/DeletePersonCommand.java @@ -0,0 +1,94 @@ +package seedu.address.logic.commands.person; + +import static java.util.Objects.requireNonNull; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.person.Person; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; + +/** + * Deletes a person identified using it's displayed index from the address book. + */ +public class DeletePersonCommand extends Command { + + public static final String COMMAND_WORD = "del-p"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the person identified by the index number used in the displayed person 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"; + + private final Index targetIndex; + + public DeletePersonCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownPersonList = model.getFilteredPersonList(); + List unfilteredTaskList = model.getUnfilteredTaskList(); + + if (targetIndex.getZeroBased() >= lastShownPersonList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToDelete = lastShownPersonList.get(targetIndex.getZeroBased()); + + // update tasks after the deletion of person + for (Task task: unfilteredTaskList) { + if (task.getPersons().contains(personToDelete.getName())) { + EditTaskDescriptor editTaskDescriptor = new EditTaskDescriptor(); + Set persons = new HashSet<>(task.getPersons()); + persons.remove(personToDelete.getName()); + editTaskDescriptor.setPersons(persons); + Task editedTask = createEditedTask(task, editTaskDescriptor); + model.setTask(task, editedTask); + } + } + model.deletePerson(personToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); + } + + /** + * Creates and returns a {@code Task} with the details of {@code taskToEdit} + * edited with {@code editTaskDescriptor}. + */ + public static Task createEditedTask(Task taskToEdit, EditTaskDescriptor editTaskDescriptor) { + assert taskToEdit != null; + + Name updatedName = editTaskDescriptor.getName().orElse(taskToEdit.getName()); + Date updatedDate = editTaskDescriptor.getDate().orElse(taskToEdit.getDate()); + StartTime updatedStartTime = editTaskDescriptor.getStartTime().orElse(taskToEdit.getStartTime()); + EndTime updatedEndTime = editTaskDescriptor.getEndTime().orElse(taskToEdit.getEndTime()); + Set updatedTags = editTaskDescriptor.getTags().orElse(taskToEdit.getTags()); + Set updatedPersons = editTaskDescriptor.getPersons().orElse(taskToEdit.getPersons()); + return new Task(updatedName, updatedDate, + updatedStartTime, updatedEndTime, updatedTags, updatedPersons); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeletePersonCommand // instanceof handles nulls + && targetIndex.equals(((DeletePersonCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/DeletePersonTagCommand.java b/src/main/java/seedu/address/logic/commands/person/DeletePersonTagCommand.java new file mode 100644 index 00000000000..c2f437daa27 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/DeletePersonTagCommand.java @@ -0,0 +1,113 @@ +package seedu.address.logic.commands.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; +import seedu.address.model.person.Address; +import seedu.address.model.person.Email; +import seedu.address.model.person.Person; +import seedu.address.model.person.Phone; +import seedu.address.model.tag.Tag; + +public class DeletePersonTagCommand extends Command { + public static final String COMMAND_WORD = "tag-del-p"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Delete a tag from a person in our contact list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "TAG NAME (must be non-empty)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "friend"; + public static final String MESSAGE_DELETE_TAG_SUCCESS = "Deleted tag: %1$s"; + public static final String MESSAGE_INVALID_TAG = "This person does not have this tag!"; + + private final Index index; + private final String tagName; + + /** + * Public constructor for DeleteTagCommand + * @param index Index of person to delete tag from + * @param tagName Name of tag + */ + + public DeletePersonTagCommand(Index index, String tagName) { + requireAllNonNull(index, tagName); + + this.index = index; + this.tagName = tagName; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + // Exception when index out of bounds + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + + Person personToEdit = lastShownList.get(index.getZeroBased()); + Person editedPerson = deleteTagFromPerson(personToEdit); + + // Exception when a non-existent tag is indicated for deletion + Tag testTag = new Tag(this.tagName); + if (!personToEdit.getTags().contains(testTag)) { + throw new CommandException(MESSAGE_INVALID_TAG); + } + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + return new CommandResult(String.format(MESSAGE_DELETE_TAG_SUCCESS, this.tagName)); + } + + /** + * Creates and returns a {@code Person} with a new tag {@code tagName} added to + * {@code personToEdit} + * + * @param personToEdit Person to be edited + * @return New Person object with the tag added (tag list updated) + */ + private Person deleteTagFromPerson(Person personToEdit) { + // Keep all other fields the same + Name updatedName = personToEdit.getName(); + Phone updatedPhone = personToEdit.getPhone(); + Email updatedEmail = personToEdit.getEmail(); + Address updatedAddress = personToEdit.getAddress(); + List updatedStrengths = personToEdit.getStrengths(); + List updatedWeaknesses = personToEdit.getWeaknesses(); + List updatedMisc = personToEdit.getMiscellaneous(); + + // Changing tags + // Make modifiable copy since Person#getTags returns an unmodifiable Set + Set tagList = new HashSet<>(personToEdit.getTags()); + tagList.remove(new Tag(this.tagName)); + + return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, tagList, + updatedStrengths, updatedWeaknesses, updatedMisc); + } + + @Override + public boolean equals(Object other) { + + return other == this // short circuit if same object + || (other instanceof DeletePersonTagCommand // instanceof handles nulls + && (index.equals(((DeletePersonTagCommand) other).index) + && tagName.equals((((DeletePersonTagCommand) other).tagName)))); + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/person/EditPersonCommand.java similarity index 55% rename from src/main/java/seedu/address/logic/commands/EditCommand.java rename to src/main/java/seedu/address/logic/commands/person/EditPersonCommand.java index 7e36114902f..c3b8cb98319 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/person/EditPersonCommand.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; import static java.util.Objects.requireNonNull; import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; @@ -8,30 +8,36 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Optional; import java.util.Set; import seedu.address.commons.core.Messages; import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; -import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.EditPersonDescriptor; +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; 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.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; /** * Edits the details of an existing person in the address book. */ -public class EditCommand extends Command { +public class EditPersonCommand extends Command { - public static final String COMMAND_WORD = "edit"; + public static final String COMMAND_WORD = "edit-p"; 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. " @@ -57,7 +63,7 @@ public class EditCommand extends Command { * @param index of the person in the filtered person list to edit * @param editPersonDescriptor details to edit the person with */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { + public EditPersonCommand(Index index, EditPersonDescriptor editPersonDescriptor) { requireNonNull(index); requireNonNull(editPersonDescriptor); @@ -69,6 +75,7 @@ public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { public CommandResult execute(Model model) throws CommandException { requireNonNull(model); List lastShownList = model.getFilteredPersonList(); + List unfilteredTaskList = model.getUnfilteredTaskList(); if (index.getZeroBased() >= lastShownList.size()) { throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); @@ -81,6 +88,21 @@ public CommandResult execute(Model model) throws CommandException { throw new CommandException(MESSAGE_DUPLICATE_PERSON); } + // update tasks if a person's name is edited + for (Task task: unfilteredTaskList) { + if (personToEdit.getName().equals(editedPerson.getName())) { + break; + } else if (task.getPersons().contains(personToEdit.getName())) { + EditTaskDescriptor editTaskDescriptor = new EditTaskDescriptor(); + Set persons = new HashSet<>(task.getPersons()); + persons.remove(personToEdit.getName()); + persons.add(editedPerson.getName()); + editTaskDescriptor.setPersons(persons); + Task editedTask = createEditedTask(task, editTaskDescriptor); + model.setTask(task, editedTask); + } + } + model.setPerson(personToEdit, editedPerson); model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); @@ -98,8 +120,28 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); + List updatedStrengthNotes = editPersonDescriptor.getStrengths().orElse(personToEdit.getStrengths()); + List updatedWeaknessNotes = editPersonDescriptor.getWeaknesses().orElse(personToEdit.getWeaknesses()); + List updatedMisc = editPersonDescriptor.getMiscellaneous().orElse(personToEdit.getMiscellaneous()); + return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags, + updatedStrengthNotes, updatedWeaknessNotes, updatedMisc); + } - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); + /** + * Creates and returns a {@code Task} with the details of {@code taskToEdit} + * edited with {@code editTaskDescriptor}. + */ + public static Task createEditedTask(Task taskToEdit, EditTaskDescriptor editTaskDescriptor) { + assert taskToEdit != null; + + Name updatedName = editTaskDescriptor.getName().orElse(taskToEdit.getName()); + Date updatedDate = editTaskDescriptor.getDate().orElse(taskToEdit.getDate()); + StartTime updatedStartTime = editTaskDescriptor.getStartTime().orElse(taskToEdit.getStartTime()); + EndTime updatedEndTime = editTaskDescriptor.getEndTime().orElse(taskToEdit.getEndTime()); + Set updatedTags = editTaskDescriptor.getTags().orElse(taskToEdit.getTags()); + Set updatedPersons = editTaskDescriptor.getPersons().orElse(taskToEdit.getPersons()); + return new Task(updatedName, updatedDate, + updatedStartTime, updatedEndTime, updatedTags, updatedPersons); } @Override @@ -110,117 +152,13 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof EditCommand)) { + if (!(other instanceof EditPersonCommand)) { return false; } // state check - EditCommand e = (EditCommand) other; + EditPersonCommand e = (EditPersonCommand) other; return index.equals(e.index) && editPersonDescriptor.equals(e.editPersonDescriptor); } - - /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. - */ - public static class EditPersonDescriptor { - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} - - /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. - */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); - 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); - } - - public void setPhone(Phone phone) { - this.phone = phone; - } - - 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; - } - - 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) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { - return false; - } - - // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; - - return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); - } - } } diff --git a/src/main/java/seedu/address/logic/commands/person/FindPersonCommand.java b/src/main/java/seedu/address/logic/commands/person/FindPersonCommand.java new file mode 100644 index 00000000000..30948a6c67d --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/FindPersonCommand.java @@ -0,0 +1,58 @@ +package seedu.address.logic.commands.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.name.PersonNameContainsKeywordsPredicate; +import seedu.address.model.tag.PersonTagContainsKeywordsPredicate; + +/** + * Finds and lists all persons in address book whose name contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindPersonCommand extends Command { + + public static final String COMMAND_WORD = "find-p"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all persons whose names or tags contain any of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: " + + "[" + PREFIX_NAME + "NAME]... " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "Alex " + PREFIX_NAME + "Charlotte " + + PREFIX_TAG + "friends"; + + private final PersonNameContainsKeywordsPredicate namePredicate; + private final PersonTagContainsKeywordsPredicate tagPredicate; + + /** + * Initialises new object using {@code namePredicate} and {@code tagPredicate} + */ + public FindPersonCommand(PersonNameContainsKeywordsPredicate namePredicate, + PersonTagContainsKeywordsPredicate tagPredicate) { + this.namePredicate = namePredicate; + this.tagPredicate = tagPredicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredPersonList(namePredicate.or(tagPredicate)); + return new CommandResult( + String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindPersonCommand // instanceof handles nulls + && namePredicate.equals(((FindPersonCommand) other).namePredicate) + && tagPredicate.equals(((FindPersonCommand) other).tagPredicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/person/ListPersonCommand.java similarity index 67% rename from src/main/java/seedu/address/logic/commands/ListCommand.java rename to src/main/java/seedu/address/logic/commands/person/ListPersonCommand.java index 84be6ad2596..342fc2f0d5a 100644 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ b/src/main/java/seedu/address/logic/commands/person/ListPersonCommand.java @@ -1,16 +1,18 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; import static java.util.Objects.requireNonNull; import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; import seedu.address.model.Model; /** * Lists all persons in the address book to the user. */ -public class ListCommand extends Command { +public class ListPersonCommand extends Command { - public static final String COMMAND_WORD = "list"; + public static final String COMMAND_WORD = "list-p"; public static final String MESSAGE_SUCCESS = "Listed all persons"; diff --git a/src/main/java/seedu/address/logic/commands/person/SortStrengthCommand.java b/src/main/java/seedu/address/logic/commands/person/SortStrengthCommand.java new file mode 100644 index 00000000000..26b94300e74 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/SortStrengthCommand.java @@ -0,0 +1,62 @@ +package seedu.address.logic.commands.person; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.person.Person; + +/** + * Sort the list of persons in the address book by total strengths in descending order and display to the user. + */ +public class SortStrengthCommand extends Command { + + public static final String COMMAND_WORD = "sort-strength"; + + public static final String MESSAGE_SUCCESS = "Sorted all persons by most strengths"; + + /** + * Comparator that compares two Person objects by their total strengths. + * If total strengths of two Person objects are equal, then compare by least total weaknesses + */ + protected static final Comparator BY_STRENGTHS = new Comparator() { + @Override + public int compare(Person o1, Person o2) { + //if total strengths are equal, order by least weaknesses + if (o1.getStrengths().size() == o2.getStrengths().size()) { + return o1.getWeaknesses().size() - o2.getWeaknesses().size(); + } else if (o1.getStrengths().size() > o2.getStrengths().size()) { + return -1; + } else { + return 1; + } + } + }; + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownPersonList = model.getUnfilteredPersonList(); + List newPersonList = new ArrayList<>(lastShownPersonList); + AddressBook newAddressBook = new AddressBook(); + + if (newPersonList.isEmpty()) { + throw new CommandException(Messages.MESSAGE_EMPTY_PERSON_LIST); + } + + newPersonList.sort(BY_STRENGTHS); + for (Person p : newPersonList) { + newAddressBook.addPerson(p); + } + model.setAddressBook(new AddressBook(newAddressBook)); //update address book with new sorted book + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/SortWeaknessCommand.java b/src/main/java/seedu/address/logic/commands/person/SortWeaknessCommand.java new file mode 100644 index 00000000000..6a92ace65a0 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/SortWeaknessCommand.java @@ -0,0 +1,62 @@ +package seedu.address.logic.commands.person; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.person.Person; + +/** + * Sort the list of persons in the address book by total weaknesses in descending order and display to the user. + */ +public class SortWeaknessCommand extends Command { + + public static final String COMMAND_WORD = "sort-weakness"; + + public static final String MESSAGE_SUCCESS = "Sorted all persons by most weaknesses"; + + /** + * Comparator that compares two Person objects by their total weaknesses. + * If total weaknesses of two Person objects are equal, then compare by least total strengths + */ + protected static final Comparator BY_WEAKNESSES = new Comparator() { + @Override + public int compare(Person o1, Person o2) { + //if total weaknesses are equal, order by least strengths + if (o1.getWeaknesses().size() == o2.getWeaknesses().size()) { + return o1.getStrengths().size() - o2.getStrengths().size(); + } else if (o1.getWeaknesses().size() > o2.getWeaknesses().size()) { + return -1; + } else { + return 1; + } + } + }; + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownPersonList = model.getUnfilteredPersonList(); + List newPersonList = new ArrayList<>(lastShownPersonList); + AddressBook newAddressBook = new AddressBook(); + + if (newPersonList.isEmpty()) { + throw new CommandException(Messages.MESSAGE_EMPTY_PERSON_LIST); + } + + newPersonList.sort(BY_WEAKNESSES); + for (Person p : newPersonList) { + newAddressBook.addPerson(p); + } + model.setAddressBook(new AddressBook(newAddressBook)); //update address book with new sorted book + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/notecommands/AddMiscCommand.java b/src/main/java/seedu/address/logic/commands/person/notecommands/AddMiscCommand.java new file mode 100644 index 00000000000..bf23b9898c7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/notecommands/AddMiscCommand.java @@ -0,0 +1,94 @@ +package seedu.address.logic.commands.person.notecommands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Adds a miscellaneous note to a person in the address book. + */ +public class AddMiscCommand extends Command { + public static final String COMMAND_WORD = "misc-add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Add a miscellaneous note to a selected person from our contact list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "NOTE_DESCRIPTION (must be non-empty and not more than 50 characters)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "Surgery Scheduled for tomorrow"; + + public static final String MESSAGE_SUCCESS = "New miscellaneous note added: %1$s"; + + private final Index index; + private final Note misc; + + /** + * @param index of the person in the filtered person list to add the misc + * @param misc note of the person to be added + */ + public AddMiscCommand(Index index, Note misc) { + requireAllNonNull(index, misc); + + this.index = index; + this.misc = misc; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + List newMiscList = new ArrayList<>(personToEdit.getMiscellaneous()); + + if (newMiscList.contains(misc)) { //makes sure note does not already exist for given person + throw new CommandException(Messages.MESSAGE_DUPLICATE_MISC); + } + + newMiscList.add(misc); + + Person editedPerson = new Person( + personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getAddress(), personToEdit.getTags(), personToEdit.getStrengths(), + personToEdit.getWeaknesses(), newMiscList); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddMiscCommand // instanceof handles nulls + && (index.equals(((AddMiscCommand) other).index) + && misc.equals(((AddMiscCommand) other).misc))); + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/notecommands/AddStrengthCommand.java b/src/main/java/seedu/address/logic/commands/person/notecommands/AddStrengthCommand.java new file mode 100644 index 00000000000..1746b1abc5e --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/notecommands/AddStrengthCommand.java @@ -0,0 +1,94 @@ +package seedu.address.logic.commands.person.notecommands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Adds a strength note to a person in the address book. + */ +public class AddStrengthCommand extends Command { + public static final String COMMAND_WORD = "strength-add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Add a strength to a selected person from our contact list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "NOTE_DESCRIPTION (must be non-empty and not more than 50 characters)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "Good at defense"; + + public static final String MESSAGE_SUCCESS = "New strength added: %1$s"; + + private final Index index; + private final Note strength; + + /** + * @param index of the person in the filtered person list to add the strength + * @param strength of the person to be added + */ + public AddStrengthCommand(Index index, Note strength) { + requireAllNonNull(index, strength); + + this.index = index; + this.strength = strength; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + List newStrengthList = new ArrayList<>(personToEdit.getStrengths()); + + if (newStrengthList.contains(strength)) { //makes sure note does not already exist for given person + throw new CommandException(Messages.MESSAGE_DUPLICATE_STRENGTH); + } + + newStrengthList.add(strength); + + Person editedPerson = new Person( + personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getAddress(), personToEdit.getTags(), newStrengthList, personToEdit.getWeaknesses(), + personToEdit.getMiscellaneous()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddStrengthCommand // instanceof handles nulls + && (index.equals(((AddStrengthCommand) other).index) + && strength.equals(((AddStrengthCommand) other).strength))); + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/notecommands/AddWeaknessCommand.java b/src/main/java/seedu/address/logic/commands/person/notecommands/AddWeaknessCommand.java new file mode 100644 index 00000000000..7b09cc08913 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/notecommands/AddWeaknessCommand.java @@ -0,0 +1,94 @@ +package seedu.address.logic.commands.person.notecommands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Adds a weakness note to a person in the address book. + */ +public class AddWeaknessCommand extends Command { + public static final String COMMAND_WORD = "weakness-add"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Add a weakness to a selected person from our contact list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "NOTE_DESCRIPTION (must be non-empty and not more than 50 characters)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "Poor endurance"; + + public static final String MESSAGE_SUCCESS = "New weakness added: %1$s"; + + private final Index index; + private final Note weakness; + + /** + * @param index of the person in the filtered person list to add the weakness + * @param weakness of the person to be added + */ + public AddWeaknessCommand(Index index, Note weakness) { + requireAllNonNull(index, weakness); + + this.index = index; + this.weakness = weakness; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + List newWeaknessList = new ArrayList<>(personToEdit.getWeaknesses()); + + if (newWeaknessList.contains(weakness)) { //makes sure note does not already exist for given person + throw new CommandException(Messages.MESSAGE_DUPLICATE_WEAKNESS); + } + + newWeaknessList.add(weakness); + + Person editedPerson = new Person( + personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getAddress(), personToEdit.getTags(), personToEdit.getStrengths(), newWeaknessList, + personToEdit.getMiscellaneous()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddWeaknessCommand // instanceof handles nulls + && (index.equals(((AddWeaknessCommand) other).index) + && weakness.equals(((AddWeaknessCommand) other).weakness))); + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteMiscCommand.java b/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteMiscCommand.java new file mode 100644 index 00000000000..2cc43209149 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteMiscCommand.java @@ -0,0 +1,94 @@ +package seedu.address.logic.commands.person.notecommands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Delete a miscellaneous note from a person in the address book + */ +public class DeleteMiscCommand extends Command { + public static final String COMMAND_WORD = "misc-del"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Delete a miscellaneous note from the note-list of selected person from our contact list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "NOTE-INDEX (must be a positive)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "2"; + + public static final String MESSAGE_SUCCESS = "Miscellaneous note has been deleted: %1$s"; + + + private final Index index; + private final Index noteIndex; + + /** + * Constructor of DeleteMiscCommand class + * @param index index of the person in the filtered person list + * @param noteIndex index of the misc. note from the person's misc-list to be deleted + */ + public DeleteMiscCommand(Index index, Index noteIndex) { + requireAllNonNull(index, noteIndex); + this.index = index; + this.noteIndex = noteIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + List newMisc = new ArrayList<>(personToEdit.getMiscellaneous()); + + if (noteIndex.getZeroBased() >= newMisc.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_NOTE_DISPLAYED_INDEX); + } + newMisc.remove(noteIndex.getZeroBased()); + + Person editedPerson = new Person( + personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getAddress(), personToEdit.getTags(), + personToEdit.getStrengths(), personToEdit.getWeaknesses(), newMisc); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof DeleteMiscCommand + && index.equals(((DeleteMiscCommand) other).index) + && noteIndex.equals(((DeleteMiscCommand) other).noteIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteStrengthCommand.java b/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteStrengthCommand.java new file mode 100644 index 00000000000..cdbbdcb02e7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteStrengthCommand.java @@ -0,0 +1,93 @@ +package seedu.address.logic.commands.person.notecommands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Delete a strength from a person in the address book + */ +public class DeleteStrengthCommand extends Command { + public static final String COMMAND_WORD = "strength-del"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Delete a strength from the strength-list of a selected person from our contact list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "NOTE-INDEX (must be a positive)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "2"; + + public static final String MESSAGE_SUCCESS = "Strength has been deleted: %1$s"; + + private final Index index; + private final Index noteIndex; + + /** + * Constructor of DeleteStrengthCommand class + * @param index index of the person in the filtered person list + * @param noteIndex index of the strength from the person's strength-list to be deleted + */ + public DeleteStrengthCommand(Index index, Index noteIndex) { + requireAllNonNull(index, noteIndex); + this.index = index; + this.noteIndex = noteIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + List newStrengths = new ArrayList<>(personToEdit.getStrengths()); + + if (noteIndex.getZeroBased() >= newStrengths.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_NOTE_DISPLAYED_INDEX); + } + newStrengths.remove(noteIndex.getZeroBased()); + + Person editedPerson = new Person( + personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getAddress(), personToEdit.getTags(), + newStrengths, personToEdit.getWeaknesses(), personToEdit.getMiscellaneous()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof DeleteStrengthCommand + && index.equals(((DeleteStrengthCommand) other).index) + && noteIndex.equals(((DeleteStrengthCommand) other).noteIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteWeaknessCommand.java b/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteWeaknessCommand.java new file mode 100644 index 00000000000..e54b530006f --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/person/notecommands/DeleteWeaknessCommand.java @@ -0,0 +1,93 @@ +package seedu.address.logic.commands.person.notecommands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.ArrayList; +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Delete a weakness from a person in the address book + */ +public class DeleteWeaknessCommand extends Command { + public static final String COMMAND_WORD = "weakness-del"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Delete a weakness from the weakness-list of a selected person from our contact list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "NOTE-INDEX (must be a positive)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "2"; + + public static final String MESSAGE_SUCCESS = "Weakness has been deleted: %1$s"; + + private final Index index; + private final Index noteIndex; + + /** + * Constructor of DeleteWeaknessCommand class + * @param index index of the person in the filtered person list + * @param noteIndex index of the weakness from the person's weakness-list to be deleted + */ + public DeleteWeaknessCommand(Index index, Index noteIndex) { + requireAllNonNull(index, noteIndex); + this.index = index; + this.noteIndex = noteIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredPersonList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToEdit = lastShownList.get(index.getZeroBased()); + List newWeaknesses = new ArrayList<>(personToEdit.getWeaknesses()); + + if (noteIndex.getZeroBased() >= newWeaknesses.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_NOTE_DISPLAYED_INDEX); + } + newWeaknesses.remove(noteIndex.getZeroBased()); + + Person editedPerson = new Person( + personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(), + personToEdit.getAddress(), personToEdit.getTags(), + personToEdit.getStrengths(), newWeaknesses, personToEdit.getMiscellaneous()); + + model.setPerson(personToEdit, editedPerson); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + + return new CommandResult(generateSuccessMessage(editedPerson)); + } + + /** + * Generates a command execution success message + * {@code personToEdit}. + */ + private String generateSuccessMessage(Person personToEdit) { + return String.format(MESSAGE_SUCCESS, personToEdit); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof DeleteWeaknessCommand + && index.equals(((DeleteWeaknessCommand) other).index) + && noteIndex.equals(((DeleteWeaknessCommand) other).noteIndex)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/strategy/AddPlayerCommand.java b/src/main/java/seedu/address/logic/commands/strategy/AddPlayerCommand.java new file mode 100644 index 00000000000..49a32e225c1 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/strategy/AddPlayerCommand.java @@ -0,0 +1,62 @@ +package seedu.address.logic.commands.strategy; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.strategy.Player; + +/** + * Adds a player in the strategy panel + */ +public class AddPlayerCommand extends Command { + public static final String COMMAND_WORD = "add-player"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Add a player in the strategy panel. " + + "Parameters: " + + "PLAYER_NAME" + + "(must be non-empty, not more than 50 characters and does not contain \"/\")\n" + + "Example: " + COMMAND_WORD + " " + + "Lionel Messi"; + + public static final String MESSAGE_NOT_IMPLEMENTED_YET = + "AddPlayer command not implemented yet"; + + public static final String MESSAGE_SUCCESS = "New player added: %1$s"; + public static final String MESSAGE_DUPLICATE_PLAYER = "This player already exists in the PlayerList"; + + + private final Player toAdd; + + /** + * Creates an AddPlayerCommand to add the specified {@code Player}. + */ + public AddPlayerCommand(Player player) { + requireAllNonNull(player); + this.toAdd = player; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasPlayer(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_PLAYER); + } + + model.addPlayer(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd.getName())); + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddPlayerCommand // instanceof handles nulls + && (toAdd.equals(((AddPlayerCommand) other).toAdd))); + } +} diff --git a/src/main/java/seedu/address/logic/commands/strategy/DeletePlayerCommand.java b/src/main/java/seedu/address/logic/commands/strategy/DeletePlayerCommand.java new file mode 100644 index 00000000000..c10a8812283 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/strategy/DeletePlayerCommand.java @@ -0,0 +1,66 @@ +package seedu.address.logic.commands.strategy; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.strategy.Player; + +/** + * Deletes a player in the strategy panel + */ +public class DeletePlayerCommand extends Command { + public static final String COMMAND_WORD = "del-player"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Delete a player in the strategy panel. " + + "Parameters: " + + "PLAYER_NAME (must be non-empty and not more than 50 characters)\n" + + "Example: " + COMMAND_WORD + " " + + "Lionel Messi"; + + public static final String MESSAGE_NOT_IMPLEMENTED_YET = + "DeletePlayer command not implemented yet"; + + public static final String MESSAGE_SUCCESS = "Player deleted: %1$s"; + public static final String MESSAGE_PLAYER_NOT_FOUND = "Player not found in the list: %1$s"; + + private final String playerName; + + /** + * @param playerName the name of the player to be added + */ + public DeletePlayerCommand(String playerName) { + requireAllNonNull(playerName); + this.playerName = playerName; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + if (playerName.replace("/s", "").isEmpty()) { + throw new CommandException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPlayerCommand.MESSAGE_USAGE)); + } + + Player player = new Player(playerName); + if (!model.hasPlayer(player)) { + throw new CommandException( + String.format(MESSAGE_PLAYER_NOT_FOUND, playerName)); + } + requireNonNull(model); + model.deletePlayer(player); + return new CommandResult(String.format(MESSAGE_SUCCESS, playerName)); + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeletePlayerCommand // instanceof handles nulls + && (playerName.equals(((DeletePlayerCommand) other).playerName))); + } +} diff --git a/src/main/java/seedu/address/logic/commands/strategy/ExportCommand.java b/src/main/java/seedu/address/logic/commands/strategy/ExportCommand.java new file mode 100644 index 00000000000..a8e361d1418 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/strategy/ExportCommand.java @@ -0,0 +1,41 @@ +package seedu.address.logic.commands.strategy; + +import static seedu.address.commons.core.Tabs.DEFAULT; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.UUID; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; + +public class ExportCommand extends Command { + + public static final String COMMAND_WORD = "export"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Exports current Strategy Panel as a .png image into user selected directory. " + + "Example: " + COMMAND_WORD; + + public static final String MESSAGE_EXPORT_ACKNOWLEDGEMENT = "Strategy Panel is being exported."; + + private String fileName; + + /** + * Constructor for ExportCommand class. + */ + + @Override + public CommandResult execute(Model model) { + requireAllNonNull(model); + return new CommandResult(MESSAGE_EXPORT_ACKNOWLEDGEMENT, false, false, DEFAULT, false, null, true); + } + + + /** + * Randomized file names + */ + private static String imageRandomFileString() { + return UUID.randomUUID().toString(); + } +} diff --git a/src/main/java/seedu/address/logic/commands/strategy/LoadCourtCommand.java b/src/main/java/seedu/address/logic/commands/strategy/LoadCourtCommand.java new file mode 100644 index 00000000000..29ff3a4c3da --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/strategy/LoadCourtCommand.java @@ -0,0 +1,78 @@ +package seedu.address.logic.commands.strategy; + +import static seedu.address.commons.core.Tabs.DEFAULT; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.io.File; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.image.Image; + +/** + * Loads an image as a background image for use in StrategyPanel. + */ +public class LoadCourtCommand extends Command { + public static final String COMMAND_WORD = "load-court"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Load an image from '/courts/' directory and sets as background image for the Strategy Panel. " + + "Parameters: " + + "IMAGE_NAME (" + Image.MESSAGE_CONSTRAINTS + ") " + + "Example: " + COMMAND_WORD + " " + + "basketball"; + + public static final String MESSAGE_SUCCESS = "Image has been loaded: %1$s"; + + public static final String MESSAGE_IMAGE_INVALID = "Provided image cannot be loaded!"; + + private final Image image; + + /** + * Constructor for LoadCourtCommand class. + * @param image The image file. + */ + public LoadCourtCommand(Image image) { + requireAllNonNull(image); + this.image = image; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireAllNonNull(model); + if (checkIfThrowsException()) { //throws if the image file cannot be instantiated + throw new CommandException(MESSAGE_IMAGE_INVALID); + + } + + return new CommandResult(generateSuccessMessage(image), false, false, + DEFAULT, true, this.image, false); + } + + /** + * Creates a test javafx.scene.image object and checks if it throws an Exception. + * @return false if the image file cannot be instantiated + */ + public boolean checkIfThrowsException() { + File imagePath = image.getImagePath(); + javafx.scene.image.Image testImage = new javafx.scene.image.Image((imagePath.toURI().toString())); + return testImage.isError(); + } + + /** + * Generates a command execution success message + * {@code image}. + */ + private String generateSuccessMessage(Image image) { + return String.format(MESSAGE_SUCCESS, image); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof LoadCourtCommand + && (image.equals(((LoadCourtCommand) other).image))); + } +} diff --git a/src/main/java/seedu/address/logic/commands/strategy/MovePlayerCommand.java b/src/main/java/seedu/address/logic/commands/strategy/MovePlayerCommand.java new file mode 100644 index 00000000000..e451b2de7ff --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/strategy/MovePlayerCommand.java @@ -0,0 +1,78 @@ +package seedu.address.logic.commands.strategy; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PLAYERS; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.strategy.Player; + +/** + * Moves the player to the specified location in the strategy board. + */ +public class MovePlayerCommand extends Command { + public static final String COMMAND_WORD = "move"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Moves the player to the specified location.\n" + + "Parameters: " + + "PLAYER_NAME " + + "x/X_COORDINATE (must be non-negative integers no greater than 1000) " + + "y/Y_COORDINATE (must be non-negative integers no greater than 600)\n" + + "Example: " + COMMAND_WORD + " " + "John" + " " + "x/100" + " " + "y/200"; + + public static final String MESSAGE_SUCCESS = "%1$s has been moved to the specified location: (%2$d, %3$d)."; + + private final String playerName; + private final int xCoordinate; + private final int yCoordinate; + + /** + * @param playerName the name of the player to move + * @param xCoordinate the x coordinate of the location to move to + * @param yCoordinate the y coordinate of the location to move to + */ + public MovePlayerCommand(String playerName, int xCoordinate, int yCoordinate) { + requireAllNonNull(playerName, xCoordinate, yCoordinate); + + this.playerName = playerName; + this.xCoordinate = xCoordinate; + this.yCoordinate = yCoordinate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireAllNonNull(model); + + Player player = new Player(playerName); + + if (!model.hasPlayer(player)) { + throw new CommandException(String.format(Messages.MESSAGE_INVALID_PLAYER, playerName)); + } + + Player editedPlayer = player.toCopy(); + + editedPlayer.setXCoord(xCoordinate); + editedPlayer.setYCoord(yCoordinate); + + //model.setPlayer(player, editedPlayer); + model.deletePlayer(player); + model.addPlayer(editedPlayer); + model.updateFilteredPlayerList(PREDICATE_SHOW_ALL_PLAYERS); + + return new CommandResult(String.format(MESSAGE_SUCCESS, playerName, xCoordinate, yCoordinate)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof MovePlayerCommand // instanceof handles nulls + && (playerName.equals(((MovePlayerCommand) other).playerName)) + && (xCoordinate == ((MovePlayerCommand) other).xCoordinate) + && (yCoordinate == ((MovePlayerCommand) other).yCoordinate)); + } +} + diff --git a/src/main/java/seedu/address/logic/commands/task/AddTaskCommand.java b/src/main/java/seedu/address/logic/commands/task/AddTaskCommand.java new file mode 100644 index 00000000000..01accf8b04f --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/AddTaskCommand.java @@ -0,0 +1,116 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CONTACT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.List; +import java.util.Set; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.person.Person; +import seedu.address.model.task.Task; + + +/** + * Adds a task to the task list. + */ +public class AddTaskCommand extends Command { + + public static final String COMMAND_WORD = "add-t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a task to the TaskList. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_DATE + "DATE " + + PREFIX_STARTTIME + "START TIME " + + PREFIX_ENDTIME + "END TIME " + + "[" + PREFIX_TAG + "TAG]..." + + "[" + PREFIX_CONTACT + "PERSON]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Investor introduction " + + PREFIX_DATE + "08-04-2022 " + + PREFIX_STARTTIME + "13:00 " + + PREFIX_ENDTIME + "15:00 " + + PREFIX_TAG + "funding " + + PREFIX_TAG + "meeting " + + PREFIX_CONTACT + "Alex Yeoh "; + + public static final String MESSAGE_SUCCESS = "New task added: %1$s"; + public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task book"; + public static final String MESSAGE_CONTACT_NOT_FOUND = + "The person %1$s cannot be found in the current address book"; + public static final String MESSAGE_SCHEDULE_CONFLICT = + "The person %1$s is already involved in a task at this date and time"; + public static final String MESSAGE_SCHEDULE_CONFLICT_START_END_TIME = + "This task ends before or at its specified start time!"; + private final Task toAdd; + + /** + * Creates an AddTaskCommand to add the specified {@code Task} + */ + public AddTaskCommand(Task task) { + requireNonNull(task); + toAdd = task; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List unfilteredPersonList = model.getUnfilteredPersonList(); + List unfilteredTaskList = model.getUnfilteredTaskList(); + Set persons = toAdd.getPersons(); + + if (toAdd.hasStartEndTimeConflict()) { + throw new CommandException(String.format(MESSAGE_SCHEDULE_CONFLICT_START_END_TIME)); + } + + //checks if persons exist in the current list + for (Name name: persons) { + boolean notFound = true; + for (Person person: unfilteredPersonList) { + if (person.getName().equals(name)) { + notFound = false; + } + } + if (notFound) { + throw new CommandException(String.format(MESSAGE_CONTACT_NOT_FOUND, name)); + } + } + + //checks if persons are already involved in tasks with conflicting time ranges to the newly added task + for (Name name: persons) { + for (Task task: unfilteredTaskList) { + Set nameList = task.getPersons(); + if (nameList.contains(name)) { + boolean conflictExist = task.hasDateTimeConflict(toAdd); + if (conflictExist) { + throw new CommandException(String.format(MESSAGE_SCHEDULE_CONFLICT, name)); + } + } + } + } + + if (model.hasTask(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_TASK); + } + + model.addTask(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddTaskCommand // instanceof handles nulls + && toAdd.equals(((AddTaskCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/AddTaskTagCommand.java b/src/main/java/seedu/address/logic/commands/task/AddTaskTagCommand.java new file mode 100644 index 00000000000..915380a1d4b --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/AddTaskTagCommand.java @@ -0,0 +1,116 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TASKS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; + +public class AddTaskTagCommand extends Command { + public static final String COMMAND_WORD = "tag-add-t"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Add a tag to a task from our task list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "TAG NAME (must be non-empty)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "important"; + public static final String MESSAGE_DUPLICATE_TAG_T = "This task already has this tag!"; + public static final String MESSAGE_ADD_TAG_SUCCESS = "Added tag: %1$s"; + + public final Index index; + public final String tagName; + + /** + * Public contructor for AddTagToTaskCommand + * @param index Index of target task + * @param tagName Tag to be added to the target task + */ + public AddTaskTagCommand(Index index, String tagName) { + requireAllNonNull(index, tagName); + + this.index = index; + this.tagName = tagName; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTaskList(); + + // Exception when index out of bounds + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + Task taskToEdit = lastShownList.get(index.getZeroBased()); + Task editedTask = addTagToTask(taskToEdit); + + // Exception when a duplicate tag is added + Tag testTag = new Tag(this.tagName); + if (taskToEdit.getTags().contains(testTag)) { + throw new CommandException(MESSAGE_DUPLICATE_TAG_T); + } + + model.setTask(taskToEdit, editedTask); + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(String.format(MESSAGE_ADD_TAG_SUCCESS, this.tagName)); + + } + + + /** + * Creates and returns a {@code Task} with a new tag {@code tagName} added to + * {@code taskToEdit} + * + * @param taskToEdit Person to be edited + * @return New Task object with the tag added (tag list updated) + */ + private Task addTagToTask(Task taskToEdit) throws CommandException { + // Keep all other fields the same + Name updatedName = taskToEdit.getName(); + Date updatedDate = taskToEdit.getDate(); + StartTime updatedStartTime = taskToEdit.getStartTime(); + EndTime updatedEndTime = taskToEdit.getEndTime(); + Set updatedPersons = taskToEdit.getPersons(); + + // Changing tags + // Make modifiable copy since Task#getTags returns an unmodifiable Set + Set tagList = new HashSet<>(taskToEdit.getTags()); + try { + tagList.add(new Tag(this.tagName)); + } catch (Exception e) { + throw new CommandException(Messages.MESSAGE_INVALID_TAG); + } + + return new Task(updatedName, updatedDate, updatedStartTime, updatedEndTime, tagList, updatedPersons); + + } + + @Override + public boolean equals(Object other) { + + return other == this // short circuit if same object + || (other instanceof AddTaskTagCommand // instanceof handles nulls + && (index.equals(((AddTaskTagCommand) other).index) + && tagName.equals((((AddTaskTagCommand) other).tagName)))); + } + +} + + diff --git a/src/main/java/seedu/address/logic/commands/task/ClearTaskCommand.java b/src/main/java/seedu/address/logic/commands/task/ClearTaskCommand.java new file mode 100644 index 00000000000..5ea4264d35f --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/ClearTaskCommand.java @@ -0,0 +1,100 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; + +import java.util.List; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.TaskBook; +import seedu.address.model.task.Date; +import seedu.address.model.task.Task; + +/** + * Clears the tasks from the task list (either all tasks or only for date specified). + */ +public class ClearTaskCommand extends Command { + + public static final String COMMAND_WORD = "clear-t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the task identified by the date used in the TaskList, or clears all tasks if date is not " + + "provided.\n" + + "Parameters: " + + "[" + PREFIX_DATE + "DATE]\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_DATE + "08-04-2022"; + + public static final String MESSAGE_SUCCESS = "Task list has been cleared!"; + public static final String MESSAGE_SUCCESS_ALT = "Tasks for date specified have been cleared: %1$s"; + public static final String MESSAGE_INVALID_DATE = + "There are no tasks in the list that match the provided date: %1$s"; + public static final String MESSAGE_EMPTY_LIST = "Task list is empty!"; + + private final Date date; + private final boolean hasDate; + + /** + * Creates a ClearTaskCommand to add the specified {@code Date} + */ + public ClearTaskCommand(Date date) { + requireNonNull(date); + this.date = date; + this.hasDate = true; + } + + /** + * Creates a ClearTaskCommand to clear all tasks in task list + */ + public ClearTaskCommand() { + this.date = null; + this.hasDate = false; + } + + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getUnfilteredTaskList(); + if (lastShownList.isEmpty()) { + throw new CommandException(MESSAGE_EMPTY_LIST); + } + + //check if task list contains given date + if (hasDate && !lastShownList + .stream() + .anyMatch((task) -> task.getDate() + .equals(date))) { + throw new CommandException(String.format(MESSAGE_INVALID_DATE, date)); + } + + if (!hasDate) { + model.setTaskBook(new TaskBook()); + return new CommandResult(MESSAGE_SUCCESS); + } else { + for (int i = 0; i < lastShownList.size(); i++) { + if (lastShownList.get(i).getDate().equals(date)) { + Task currentTask = lastShownList.get(i); + model.deleteTask(currentTask); + --i; //decrement the list to avoid skipping elements + } + } + return new CommandResult(String.format(MESSAGE_SUCCESS_ALT, date)); + } + } + + @Override + public boolean equals(Object other) { + //check to make sure boolean variable is correctly representing date object value + assert (hasDate && date != null) || (!hasDate && date == null) + : "Boolean variable does not represent date object"; + return other == this //check if both are the same object reference + || (other instanceof ClearTaskCommand + && hasDate == (((ClearTaskCommand) other).hasDate) //check if both have dates + && (!hasDate || date.equals(((ClearTaskCommand) other).date))); //if they both don't have dates, + //return true. Otherwise, check if dates are equivalent + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/DeleteTaskCommand.java b/src/main/java/seedu/address/logic/commands/task/DeleteTaskCommand.java new file mode 100644 index 00000000000..50f0fdc68d6 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/DeleteTaskCommand.java @@ -0,0 +1,55 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.task.Task; + +/** + * Deletes a task identified using its index from the TaskList. + */ +public class DeleteTaskCommand extends Command { + + public static final String COMMAND_WORD = "del-t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the task identified by the index number used in the TaskList.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s"; + + private final Index targetIndex; + + public DeleteTaskCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTaskList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + Task taskToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteTask(taskToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteTaskCommand // instanceof handles nulls + && targetIndex.equals(((DeleteTaskCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/DeleteTaskTagCommand.java b/src/main/java/seedu/address/logic/commands/task/DeleteTaskTagCommand.java new file mode 100644 index 00000000000..c4e42908aba --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/DeleteTaskTagCommand.java @@ -0,0 +1,120 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TASKS; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; + +public class DeleteTaskTagCommand extends Command { + public static final String COMMAND_WORD = "tag-del-t"; + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Delete a tag to a task from our task list. " + + "Parameters: " + + "INDEX (must be a positive integer) " + + "TAG NAME (must be non-empty)\n" + + "Example: " + COMMAND_WORD + " " + + "1 " + + "important"; + + public static final String MESSAGE_NO_SUCH_TAG_T = "This task does not have this tag!"; + public static final String MESSAGE_DEL_TAG_SUCCESS = "Deleted tag: %1$s"; + + public final Index index; + public final String tagName; + + /** + * Public contructor for AddTagToTaskCommand + * @param index Index of target task + * @param tagName Tag to be added to the target task + */ + public DeleteTaskTagCommand(Index index, String tagName) { + requireAllNonNull(index, tagName); + + this.index = index; + this.tagName = tagName; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTaskList(); + + // Exception when index out of bounds + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + + Task taskToEdit = lastShownList.get(index.getZeroBased()); + Task editedTask = deleteTagFromTask(taskToEdit); + + // Exception when a non-existent tag is indicated + Tag testTag = new Tag(this.tagName); + if (!taskToEdit.getTags().contains(testTag)) { + throw new CommandException(MESSAGE_NO_SUCH_TAG_T); + } + + + model.setTask(taskToEdit, editedTask); + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(String.format(MESSAGE_DEL_TAG_SUCCESS, this.tagName)); + + } + + + /** + * Creates and returns a {@code Task} with a new tag {@code tagName} added to + * {@code taskToEdit} + * + * @param taskToEdit Person to be edited + * @return New Task object with the tag added (tag list updated) + */ + private Task deleteTagFromTask(Task taskToEdit) throws CommandException { + // Keep all other fields the same + Name updatedName = taskToEdit.getName(); + Date updatedDate = taskToEdit.getDate(); + StartTime updatedStartTime = taskToEdit.getStartTime(); + EndTime updatedEndTime = taskToEdit.getEndTime(); + Set updatedPersons = taskToEdit.getPersons(); + + // Changing tags + // Make modifiable copy since Task#getTags returns an unmodifiable Set + Set tagList = new HashSet<>(taskToEdit.getTags()); + try { + tagList.remove(new Tag(this.tagName)); + } catch (Exception e) { + throw new CommandException(Messages.MESSAGE_INVALID_TAG); + } + + return new Task(updatedName, updatedDate, updatedStartTime, updatedEndTime, tagList, updatedPersons); + + } + + @Override + public boolean equals(Object other) { + + return other == this // short circuit if same object + || (other instanceof DeleteTaskTagCommand // instanceof handles nulls + && (index.equals(((DeleteTaskTagCommand) other).index) + && tagName.equals((((DeleteTaskTagCommand) other).tagName)))); + } + +} + + diff --git a/src/main/java/seedu/address/logic/commands/task/EditTaskCommand.java b/src/main/java/seedu/address/logic/commands/task/EditTaskCommand.java new file mode 100644 index 00000000000..840bab35366 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/EditTaskCommand.java @@ -0,0 +1,167 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CONTACT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TASKS; + +import java.util.List; +import java.util.Set; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.person.Person; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; + +/** + * Edits the details of an existing person in the address book. + */ +public class EditTaskCommand extends Command { + + public static final String COMMAND_WORD = "edit-t"; + + 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. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_DATE + "DATE] " + + "[" + PREFIX_STARTTIME + "START TIME] " + + "[" + PREFIX_ENDTIME + "END TIME] " + + "[" + PREFIX_TAG + "TAG]..." + + "[" + PREFIX_CONTACT + "PERSON]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_DATE + "22-12-2022 " + + PREFIX_ENDTIME + "23:59"; + + public static final String MESSAGE_EDIT_TASK_SUCCESS = "Edited Task: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the address book."; + public static final String MESSAGE_CONTACT_NOT_FOUND_IN_LIST = + "Unable to edit task as the person %1$s cannot be found in the current address book"; + public static final String MESSAGE_SCHEDULE_CONFLICT = + "The person %1$s is already involved in a task at this date and time"; + public static final String MESSAGE_SCHEDULE_CONFLICT_START_END_TIME = + "This task ends before or at its specified start time!"; + + private final Index index; + private final EditTaskDescriptor editTaskDescriptor; + + /** + * @param index of the task in the filtered task list to edit + * @param editTaskDescriptor details to edit the task with + */ + public EditTaskCommand(Index index, EditTaskDescriptor editTaskDescriptor) { + requireNonNull(index); + requireNonNull(editTaskDescriptor); + + this.index = index; + this.editTaskDescriptor = new EditTaskDescriptor(editTaskDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTaskList(); + List unfilteredPersonList = model.getUnfilteredPersonList(); + List unfilteredTaskList = model.getUnfilteredTaskList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + Task taskToEdit = lastShownList.get(index.getZeroBased()); + Task editedTask = createEditedTask(taskToEdit, editTaskDescriptor); + + if (!taskToEdit.isSameTask(editedTask) && model.hasTask(editedTask)) { + throw new CommandException(MESSAGE_DUPLICATE_TASK); + } + + if (editedTask.hasStartEndTimeConflict()) { + throw new CommandException(String.format(MESSAGE_SCHEDULE_CONFLICT_START_END_TIME)); + } + + Set originalPersons = taskToEdit.getPersons(); + Set editedPersons = editedTask.getPersons(); + + for (Name name: editedPersons) { + boolean notFound = true; + for (Person person: unfilteredPersonList) { + if (person.getName().equals(name)) { + notFound = false; + } + } + + if (!originalPersons.containsAll(editedPersons)) { + for (Name names : editedPersons) { + for (Task task : unfilteredTaskList) { + Set nameList = task.getPersons(); + if (nameList.contains(names)) { + boolean conflictExist = task.hasDateTimeConflict(editedTask); + if (conflictExist) { + throw new CommandException(String.format(MESSAGE_SCHEDULE_CONFLICT, name)); + } + } + } + } + } + + + if (notFound) { + throw new CommandException(String.format(MESSAGE_CONTACT_NOT_FOUND_IN_LIST, name)); + } + } + + model.setTask(taskToEdit, editedTask); + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(String.format(MESSAGE_EDIT_TASK_SUCCESS, editedTask)); + } + + /** + * Creates and returns a {@code Task} with the details of {@code taskToEdit} + * edited with {@code editTaskDescriptor}. + */ + private static Task createEditedTask(Task taskToEdit, EditTaskDescriptor editTaskDescriptor) { + assert taskToEdit != null; + + Name updatedName = editTaskDescriptor.getName().orElse(taskToEdit.getName()); + Date updatedDate = editTaskDescriptor.getDate().orElse(taskToEdit.getDate()); + StartTime updatedStartTime = editTaskDescriptor.getStartTime().orElse(taskToEdit.getStartTime()); + EndTime updatedEndTime = editTaskDescriptor.getEndTime().orElse(taskToEdit.getEndTime()); + Set updatedTags = editTaskDescriptor.getTags().orElse(taskToEdit.getTags()); + Set updatedPersons = editTaskDescriptor.getPersons().orElse(taskToEdit.getPersons()); + return new Task(updatedName, updatedDate, updatedStartTime, updatedEndTime, updatedTags, updatedPersons); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditTaskCommand)) { + return false; + } + + // state check + EditTaskCommand e = (EditTaskCommand) other; + return index.equals(e.index) + && editTaskDescriptor.equals(e.editTaskDescriptor); + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/FindTaskCommand.java b/src/main/java/seedu/address/logic/commands/task/FindTaskCommand.java new file mode 100644 index 00000000000..b9e64890edb --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/FindTaskCommand.java @@ -0,0 +1,58 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.name.TaskNameContainsKeywordsPredicate; +import seedu.address.model.tag.TaskTagContainsKeywordsPredicate; + +/** + * Finds and lists all tasks in task book whose name or tag contains any of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindTaskCommand extends Command { + + public static final String COMMAND_WORD = "find-t"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds all tasks whose names or tags contain any of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: " + + "[" + PREFIX_NAME + "NAME]... " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + PREFIX_NAME + "Meeting " + PREFIX_NAME + "Dinner " + + PREFIX_TAG + "friends"; + + private final TaskNameContainsKeywordsPredicate namePredicate; + private final TaskTagContainsKeywordsPredicate tagPredicate; + + /** + * Initialises new object using {@code namePredicate} and {@code tagPredicate} + */ + public FindTaskCommand(TaskNameContainsKeywordsPredicate namePredicate, + TaskTagContainsKeywordsPredicate tagPredicate) { + this.namePredicate = namePredicate; + this.tagPredicate = tagPredicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredTaskList(namePredicate.or(tagPredicate)); + return new CommandResult( + String.format(Messages.MESSAGE_TASKS_LISTED_OVERVIEW, model.getFilteredTaskList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindTaskCommand // instanceof handles nulls + && namePredicate.equals(((FindTaskCommand) other).namePredicate) + && tagPredicate.equals(((FindTaskCommand) other).tagPredicate)); // state check + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/GetPersonCommand.java b/src/main/java/seedu/address/logic/commands/task/GetPersonCommand.java new file mode 100644 index 00000000000..7abc06b878f --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/GetPersonCommand.java @@ -0,0 +1,97 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Tabs.CONTACT_TAB; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.name.Name; +import seedu.address.model.person.Person; +import seedu.address.model.task.Task; + +/** + * Finds and lists all persons in address book whose name are in the specific task. + */ +public class GetPersonCommand extends Command { + + public static final String COMMAND_WORD = "get-person"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Finds and lists all persons tagged to a specific task. " + + "The task is identified by the index number used in the TaskList.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + private final Index targetIndex; + + public GetPersonCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + List lastShownList = model.getFilteredTaskList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + Task indicatedTask = lastShownList.get(targetIndex.getZeroBased()); + List keywords = new ArrayList<>(); + for (Name name: indicatedTask.getPersons()) { + keywords.add(name.fullName); + } + CustomPersonNameContainsKeywordsPredicate predicate = + new CustomPersonNameContainsKeywordsPredicate(keywords); + model.updateFilteredPersonList(predicate); + + return new CommandResult( + String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()), + false, false, CONTACT_TAB, false, null, false); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof GetPersonCommand // instanceof handles nulls + && targetIndex.equals(((GetPersonCommand) other).targetIndex)); // state check + } + + /** + * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. + * This class differs from {@code PersonNameContainsKeywordPredicate} as it takes in keywords that has + * more than one word + */ + public static class CustomPersonNameContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public CustomPersonNameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> person.getName().fullName.equals(keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof CustomPersonNameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((CustomPersonNameContainsKeywordsPredicate) other).keywords)); // state check + } + } + +} diff --git a/src/main/java/seedu/address/logic/commands/task/ListTaskCommand.java b/src/main/java/seedu/address/logic/commands/task/ListTaskCommand.java new file mode 100644 index 00000000000..eb8e6c4d5c7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/ListTaskCommand.java @@ -0,0 +1,26 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_TASKS; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; + +/** + * Lists all tasks in the task book to the user. + */ +public class ListTaskCommand extends Command { + + public static final String COMMAND_WORD = "list-t"; + + public static final String MESSAGE_SUCCESS = "Listed all tasks"; + + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/task/SortTaskByDateCommand.java b/src/main/java/seedu/address/logic/commands/task/SortTaskByDateCommand.java new file mode 100644 index 00000000000..f2cdbf6b3ad --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/task/SortTaskByDateCommand.java @@ -0,0 +1,61 @@ +package seedu.address.logic.commands.task; + +import static java.util.Objects.requireNonNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.TaskBook; +import seedu.address.model.task.Task; + +/** + * Deletes a task identified using its index from the TaskList. + */ +public class SortTaskByDateCommand extends Command { + + public static final String COMMAND_WORD = "sort-date"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Sorts the task list by date, earlier dates first"; + + public static final String MESSAGE_SORT_TASKS_SUCCESS = "Sorted tasks by date"; + + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownTaskList = new ArrayList<>(model.getUnfilteredTaskList()); + List listSortedByDate = new ArrayList<>(sortTaskListByDate(lastShownTaskList)); + TaskBook newTaskBook = new TaskBook(); + + for (Task t : listSortedByDate) { + newTaskBook.addTask(t); + } + model.setTaskBook(newTaskBook); + + return new CommandResult(String.format(MESSAGE_SORT_TASKS_SUCCESS)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof SortTaskByDateCommand); // instanceof handles nulls + } + + /** + * Sorts a task list by date + * + * @param oldList List to be sorted + * @return List with tasks sorted by date + */ + private List sortTaskListByDate(List oldList) { + Collections.sort(oldList, (t1, t2) -> t1.compareTo(t2)); + + return oldList; + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/address/logic/commands/task/exceptions/CommandException.java similarity index 89% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/address/logic/commands/task/exceptions/CommandException.java index a16bd14f2cd..c54b72efb02 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/address/logic/commands/task/exceptions/CommandException.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands.exceptions; +package seedu.address.logic.commands.task.exceptions; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java deleted file mode 100644 index 1e466792b46..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import seedu.address.logic.commands.AddCommand; -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.FindCommand; -import seedu.address.logic.commands.HelpCommand; -import seedu.address.logic.commands.ListCommand; -import seedu.address.logic.parser.exceptions.ParseException; - -/** - * Parses user input. - */ -public class AddressBookParser { - - /** - * Used for initial separation of command word and args. - */ - private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); - - /** - * Parses user input into command for execution. - * - * @param userInput full user input string - * @return the command based on the user input - * @throws ParseException if the user input does not conform the expected format - */ - public Command parseCommand(String userInput) throws ParseException { - final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); - if (!matcher.matches()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); - } - - final String commandWord = matcher.group("commandWord"); - final String arguments = matcher.group("arguments"); - switch (commandWord) { - - case AddCommand.COMMAND_WORD: - return new AddCommandParser().parse(arguments); - - case EditCommand.COMMAND_WORD: - return new EditCommandParser().parse(arguments); - - case DeleteCommand.COMMAND_WORD: - return new DeleteCommandParser().parse(arguments); - - case ClearCommand.COMMAND_WORD: - return new ClearCommand(); - - 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(); - - default: - throw new ParseException(MESSAGE_UNKNOWN_COMMAND); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index 75b1a9bf119..f2e5871315c 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -11,5 +11,10 @@ public class CliSyntax { public static final Prefix PREFIX_EMAIL = new Prefix("e/"); public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); - + public static final Prefix PREFIX_CONTACT = new Prefix("c/"); + public static final Prefix PREFIX_DATE = new Prefix("d/"); + public static final Prefix PREFIX_STARTTIME = new Prefix("st/"); + public static final Prefix PREFIX_ENDTIME = new Prefix("et/"); + public static final Prefix PREFIX_XCOORD = new Prefix("x/"); + public static final Prefix PREFIX_YCOORD = new Prefix("y/"); } diff --git a/src/main/java/seedu/address/logic/parser/Coach2K22Parser.java b/src/main/java/seedu/address/logic/parser/Coach2K22Parser.java new file mode 100644 index 00000000000..cf1342edd5d --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/Coach2K22Parser.java @@ -0,0 +1,200 @@ +package seedu.address.logic.parser; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.address.logic.commands.Command; +import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.commands.person.AddPersonCommand; +import seedu.address.logic.commands.person.AddPersonTagCommand; +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.logic.commands.person.DeletePersonCommand; +import seedu.address.logic.commands.person.DeletePersonTagCommand; +import seedu.address.logic.commands.person.EditPersonCommand; +import seedu.address.logic.commands.person.FindPersonCommand; +import seedu.address.logic.commands.person.ListPersonCommand; +import seedu.address.logic.commands.person.SortStrengthCommand; +import seedu.address.logic.commands.person.SortWeaknessCommand; +import seedu.address.logic.commands.person.notecommands.AddMiscCommand; +import seedu.address.logic.commands.person.notecommands.AddStrengthCommand; +import seedu.address.logic.commands.person.notecommands.AddWeaknessCommand; +import seedu.address.logic.commands.person.notecommands.DeleteMiscCommand; +import seedu.address.logic.commands.person.notecommands.DeleteStrengthCommand; +import seedu.address.logic.commands.person.notecommands.DeleteWeaknessCommand; +import seedu.address.logic.commands.strategy.AddPlayerCommand; +import seedu.address.logic.commands.strategy.DeletePlayerCommand; +import seedu.address.logic.commands.strategy.ExportCommand; +import seedu.address.logic.commands.strategy.LoadCourtCommand; +import seedu.address.logic.commands.strategy.MovePlayerCommand; +import seedu.address.logic.commands.task.AddTaskCommand; +import seedu.address.logic.commands.task.AddTaskTagCommand; +import seedu.address.logic.commands.task.ClearTaskCommand; +import seedu.address.logic.commands.task.DeleteTaskCommand; +import seedu.address.logic.commands.task.DeleteTaskTagCommand; +import seedu.address.logic.commands.task.EditTaskCommand; +import seedu.address.logic.commands.task.FindTaskCommand; +import seedu.address.logic.commands.task.GetPersonCommand; +import seedu.address.logic.commands.task.ListTaskCommand; +import seedu.address.logic.commands.task.SortTaskByDateCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.logic.parser.person.AddPersonCommandParser; +import seedu.address.logic.parser.person.AddPersonTagCommandParser; +import seedu.address.logic.parser.person.DeletePersonCommandParser; +import seedu.address.logic.parser.person.DeletePersonTagCommandParser; +import seedu.address.logic.parser.person.EditPersonCommandParser; +import seedu.address.logic.parser.person.FindPersonCommandParser; +import seedu.address.logic.parser.person.notecommands.AddMiscCommandParser; +import seedu.address.logic.parser.person.notecommands.AddStrengthCommandParser; +import seedu.address.logic.parser.person.notecommands.AddWeaknessCommandParser; +import seedu.address.logic.parser.person.notecommands.DeleteMiscCommandParser; +import seedu.address.logic.parser.person.notecommands.DeleteStrengthCommandParser; +import seedu.address.logic.parser.person.notecommands.DeleteWeaknessCommandParser; +import seedu.address.logic.parser.strategy.AddPlayerCommandParser; +import seedu.address.logic.parser.strategy.DeletePlayerCommandParser; +import seedu.address.logic.parser.strategy.LoadCourtCommandParser; +import seedu.address.logic.parser.strategy.MovePlayerCommandParser; +import seedu.address.logic.parser.task.AddTaskCommandParser; +import seedu.address.logic.parser.task.AddTaskTagCommandParser; +import seedu.address.logic.parser.task.ClearTaskCommandParser; +import seedu.address.logic.parser.task.DeleteTaskCommandParser; +import seedu.address.logic.parser.task.DeleteTaskTagCommandParser; +import seedu.address.logic.parser.task.EditTaskCommandParser; +import seedu.address.logic.parser.task.FindTaskCommandParser; +import seedu.address.logic.parser.task.GetPersonCommandParser; + +/** + * Parses user input. + */ +public class Coach2K22Parser { + + /** + * Used for initial separation of command word and args. + */ + private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)"); + + /** + * Parses user input into command for execution. + * + * @param userInput full user input string + * @return the command based on the user input + * @throws ParseException if the user input does not conform the expected format + */ + public Command parseCommand(String userInput) throws ParseException { + final Matcher matcher = BASIC_COMMAND_FORMAT.matcher(userInput.trim()); + if (!matcher.matches()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE)); + } + + final String commandWord = matcher.group("commandWord"); + final String arguments = matcher.group("arguments"); + switch (commandWord) { + + case AddPersonCommand.COMMAND_WORD: + return new AddPersonCommandParser().parse(arguments); + + case AddTaskCommand.COMMAND_WORD: + return new AddTaskCommandParser().parse(arguments); + + case EditPersonCommand.COMMAND_WORD: + return new EditPersonCommandParser().parse(arguments); + + case EditTaskCommand.COMMAND_WORD: + return new EditTaskCommandParser().parse(arguments); + + case DeletePersonCommand.COMMAND_WORD: + return new DeletePersonCommandParser().parse(arguments); + + case DeleteTaskCommand.COMMAND_WORD: + return new DeleteTaskCommandParser().parse(arguments); + + case ClearPersonCommand.COMMAND_WORD: + return new ClearPersonCommand(); + + case ClearTaskCommand.COMMAND_WORD: + return new ClearTaskCommandParser().parse(arguments); + + case FindPersonCommand.COMMAND_WORD: + return new FindPersonCommandParser().parse(arguments); + + case FindTaskCommand.COMMAND_WORD: + return new FindTaskCommandParser().parse(arguments); + + case GetPersonCommand.COMMAND_WORD: + return new GetPersonCommandParser().parse(arguments); + + case ListPersonCommand.COMMAND_WORD: + return new ListPersonCommand(); + + case ListTaskCommand.COMMAND_WORD: + return new ListTaskCommand(); + + case SortStrengthCommand.COMMAND_WORD: + return new SortStrengthCommand(); + + case SortWeaknessCommand.COMMAND_WORD: + return new SortWeaknessCommand(); + + case AddStrengthCommand.COMMAND_WORD: + return new AddStrengthCommandParser().parse(arguments); + + case AddWeaknessCommand.COMMAND_WORD: + return new AddWeaknessCommandParser().parse(arguments); + + case AddMiscCommand.COMMAND_WORD: + return new AddMiscCommandParser().parse(arguments); + + case DeleteStrengthCommand.COMMAND_WORD: + return new DeleteStrengthCommandParser().parse(arguments); + + case DeleteWeaknessCommand.COMMAND_WORD: + return new DeleteWeaknessCommandParser().parse(arguments); + + case DeleteMiscCommand.COMMAND_WORD: + return new DeleteMiscCommandParser().parse(arguments); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case AddPersonTagCommand.COMMAND_WORD: + return new AddPersonTagCommandParser().parse(arguments); + + case DeletePersonTagCommand.COMMAND_WORD: + return new DeletePersonTagCommandParser().parse(arguments); + + case AddPlayerCommand.COMMAND_WORD: + return new AddPlayerCommandParser().parse(arguments); + + case DeletePlayerCommand.COMMAND_WORD: + return new DeletePlayerCommandParser().parse(arguments); + + case LoadCourtCommand.COMMAND_WORD: + return new LoadCourtCommandParser().parse(arguments); + + case AddTaskTagCommand.COMMAND_WORD: + return new AddTaskTagCommandParser().parse(arguments); + + case DeleteTaskTagCommand.COMMAND_WORD: + return new DeleteTaskTagCommandParser().parse(arguments); + + case MovePlayerCommand.COMMAND_WORD: + return new MovePlayerCommandParser().parse(arguments); + + case SortTaskByDateCommand.COMMAND_WORD: + return new SortTaskByDateCommand(); + + case ExportCommand.COMMAND_WORD: + return new ExportCommand(); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindCommandParser.java deleted file mode 100644 index 4fb71f23103..00000000000 --- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java +++ /dev/null @@ -1,33 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.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 - */ -public class FindCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the FindCommand - * and returns a FindCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public FindCommand parse(String args) throws ParseException { - String trimmedArgs = args.trim(); - if (trimmedArgs.isEmpty()) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - String[] nameKeywords = trimmedArgs.split("\\s+"); - - return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); - } - -} diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index b117acb9c55..ca56e2cf99d 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -2,6 +2,7 @@ import static java.util.Objects.requireNonNull; +import java.io.File; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -9,11 +10,17 @@ import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.image.Image; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; 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.strategy.Player; import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; /** * Contains utility methods used for parsing strings in the various *Parser classes. @@ -22,6 +29,15 @@ public class ParserUtil { public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + public static final String MESSAGE_INVALID_NOTE_INDEX = "Note index is not a non-zero unsigned integer."; + + public static final String MESSAGE_INVALID_IMAGE = "Image does not exit."; + + public static final String MESSAGE_INVALID_IMAGE_NAME = "Image should not contain \'/\' characters."; + + public static final String MESSAGE_DIRECTORY_NOT_EXIST = "The " + Image.FILE_PATH + " directory does not exist!"; + + public static final String MESSAGE_INVALID_KEYWORD = "Keywords should be alphanumeric and should not be blank."; /** * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be * trimmed. @@ -35,6 +51,40 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException { return Index.fromOneBased(Integer.parseInt(trimmedIndex)); } + /** + * Parses {@code imageName} into a {@code Image} and returns it. Leading and trailing whitespaces will be + * trimmed. + * @throws ParseException if the specified imageName is invalid. + */ + public static Image parseImage(String imageName) throws ParseException { + requireNonNull(imageName); + String trimmedName = imageName.trim(); + File folder = new File(Image.FILE_PATH); + if (!folder.exists()) { + throw new ParseException(MESSAGE_DIRECTORY_NOT_EXIST); + } + if (trimmedName.contains("/")) { + throw new ParseException(MESSAGE_INVALID_IMAGE_NAME); + } + if (!Image.isValidFile(trimmedName)) { + throw new ParseException(MESSAGE_INVALID_IMAGE); + } + return new Image(trimmedName); + } + + /** + * Parses {@code oneBasedNoteIndex} into an {@code NoteIndex} and returns it. Leading and trailing whitespaces + * will be trimmed. + * @throws ParseException if the specified index is invalid (not non-zero unsigned integer). + */ + public static Index parseNoteIndex(String oneBasedNoteIndex) throws ParseException { + String trimmedNoteIndex = oneBasedNoteIndex.trim(); + if (!StringUtil.isNonZeroUnsignedInteger(trimmedNoteIndex)) { + throw new ParseException(MESSAGE_INVALID_NOTE_INDEX); + } + return Index.fromOneBased(Integer.parseInt(trimmedNoteIndex)); + } + /** * Parses a {@code String name} into a {@code Name}. * Leading and trailing whitespaces will be trimmed. @@ -50,6 +100,36 @@ public static Name parseName(String name) throws ParseException { return new Name(trimmedName); } + /** + * Parses a {@code String nameKeyword} into a {@code Name}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code nameKeyword} is invalid or contain spaces. + */ + public static Name parseNameKeyword(String nameKeyword) throws ParseException { + requireNonNull(nameKeyword); + String trimmedName = nameKeyword.trim(); + if (!Name.isValidName(trimmedName) || trimmedName.split("\\s+").length != 1) { + throw new ParseException(MESSAGE_INVALID_KEYWORD); + } + return new Name(trimmedName); + } + + /** + * Parses {@code Collection names} into a {@code Set}. + */ + public static Set parseNames(Collection names) throws ParseException { + requireNonNull(names); + final Set nameSet = new HashSet<>(); + for (String name : names) { + String trimmedName = name.trim(); + if (!Name.isValidName(trimmedName)) { + throw new ParseException(Name.MESSAGE_CONSTRAINTS); + } + nameSet.add(new Name(name)); + } + return nameSet; + } /** * Parses a {@code String phone} into a {@code Phone}. * Leading and trailing whitespaces will be trimmed. @@ -95,6 +175,51 @@ public static Email parseEmail(String email) throws ParseException { return new Email(trimmedEmail); } + /** + * Parses a {@code String date} into an {@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 startTime} into an {@code StartTime}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code startTime} is invalid. + */ + public static StartTime parseStartTime(String startTime) throws ParseException { + requireNonNull(startTime); + String trimmedStartTime = startTime.trim(); + if (!StartTime.isValidStartTime(trimmedStartTime)) { + throw new ParseException(StartTime.MESSAGE_CONSTRAINTS); + } + return new StartTime(trimmedStartTime); + } + + /** + * Parses a {@code String endTime} into an {@code EndTime}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code endTime} is invalid. + */ + public static EndTime parseEndTime(String endTime) throws ParseException { + requireNonNull(endTime); + String trimmedEndTime = endTime.trim(); + if (!EndTime.isValidEndTime(trimmedEndTime)) { + throw new ParseException(EndTime.MESSAGE_CONSTRAINTS); + } + return new EndTime(trimmedEndTime); + } + /** * Parses a {@code String tag} into a {@code Tag}. * Leading and trailing whitespaces will be trimmed. @@ -110,6 +235,21 @@ public static Tag parseTag(String tag) throws ParseException { return new Tag(trimmedTag); } + /** + * Parses a {@code String tagKeyword} into a {@code Tag}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code tagKeyword} is invalid. + */ + public static Tag parseTagKeyword(String tagKeyword) throws ParseException { + requireNonNull(tagKeyword); + String trimmedTag = tagKeyword.trim(); + if (!Tag.isValidTagName(trimmedTag) || trimmedTag.split("\\s+").length != 1) { + throw new ParseException(MESSAGE_INVALID_KEYWORD); + } + return new Tag(trimmedTag); + } + /** * Parses {@code Collection tags} into a {@code Set}. */ @@ -121,4 +261,34 @@ public static Set parseTags(Collection tags) throws ParseException } 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); + } + + /** + * Parses a {@code String playerName} into a {@code Player}. + * Leading and trailing whitespaces will be trimmed. + * + * @throws ParseException if the given {@code playerName} is invalid. + */ + public static Player paresPlayer(String playerName) throws ParseException { + requireNonNull(playerName); + String trimmedPlayerName = playerName.trim(); + if (!Player.isValidPlayer(trimmedPlayerName)) { + throw new ParseException(Player.MESSAGE_NAME_CONSTRAINTS); + } + return new Player(trimmedPlayerName); + } } diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/person/AddPersonCommandParser.java similarity index 71% rename from src/main/java/seedu/address/logic/parser/AddCommandParser.java rename to src/main/java/seedu/address/logic/parser/person/AddPersonCommandParser.java index 3b8bfa035e8..fea09d9af54 100644 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/person/AddPersonCommandParser.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.address.logic.parser.person; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; @@ -7,14 +7,22 @@ import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import java.util.ArrayList; +import java.util.List; import java.util.Set; import java.util.stream.Stream; -import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.person.AddPersonCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; 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; @@ -22,20 +30,20 @@ /** * Parses input arguments and creates a new AddCommand object */ -public class AddCommandParser implements Parser { +public class AddPersonCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the AddCommand * and returns an AddCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ - public AddCommand parse(String args) throws ParseException { + public AddPersonCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonCommand.MESSAGE_USAGE)); } Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); @@ -43,10 +51,13 @@ public AddCommand parse(String args) throws ParseException { 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)); + List strengthList = new ArrayList<>(); + List weaknessList = new ArrayList<>(); + List miscList = new ArrayList<>(); - Person person = new Person(name, phone, email, address, tagList); + Person person = new Person(name, phone, email, address, tagList, strengthList, weaknessList, miscList); - return new AddCommand(person); + return new AddPersonCommand(person); } /** diff --git a/src/main/java/seedu/address/logic/parser/person/AddPersonTagCommandParser.java b/src/main/java/seedu/address/logic/parser/person/AddPersonTagCommandParser.java new file mode 100644 index 00000000000..d1880a53aa6 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/AddPersonTagCommandParser.java @@ -0,0 +1,73 @@ +package seedu.address.logic.parser.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.ArrayList; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.AddPersonTagCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +public class AddPersonTagCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the + * {@code AddTagCommand} + * and returns a {@code AddTagCommand} object for execution + * * @param args Input string by user EXCEPT COMMAND WORD + * + * @return DeleteTagCommand object with arguments loaded in + * @throws ParseException If the user input does not conform to the expected + * format + */ + public AddPersonTagCommand parse(String args) throws ParseException { + requireNonNull(args); + + // Tokenize all arguments + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, new Prefix("")); + + // Convert the argMultimap into an ArrayList<> for easier access + // The @ArgumentTokenizer produces a map with 3 elements: + // Element 1: Whitespace + // Element 2: Index + // Element 3: tagName string + ArrayList values = new ArrayList<>(argMultimap.getAllValues(new Prefix(""))); + + // Get the index element in the ArrayList + int indexInt; + try { + indexInt = Integer.parseInt(values.get(1)); + } catch (Exception e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonTagCommand.MESSAGE_USAGE)); + + } + + // Checking if index is a positive integer + if (!(indexInt >= 1)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonTagCommand.MESSAGE_USAGE)); + } + Index index = Index.fromOneBased(indexInt); // Convert to fromOneBased index since contact list starts from 1 + + // Get the tagName element in the ArrayList + String tagName; + try { + tagName = values.get(2); + } catch (IndexOutOfBoundsException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonTagCommand.MESSAGE_USAGE)); + } + try { + new Tag(tagName); + } catch (Exception e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddPersonTagCommand.MESSAGE_USAGE)); + } + + return new AddPersonTagCommand(index, tagName); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/person/DeletePersonCommandParser.java similarity index 62% rename from src/main/java/seedu/address/logic/parser/DeleteCommandParser.java rename to src/main/java/seedu/address/logic/parser/person/DeletePersonCommandParser.java index 522b93081cc..87b2427b15d 100644 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/person/DeletePersonCommandParser.java @@ -1,28 +1,30 @@ -package seedu.address.logic.parser; +package seedu.address.logic.parser.person; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.person.DeletePersonCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; import seedu.address.logic.parser.exceptions.ParseException; /** * Parses input arguments and creates a new DeleteCommand object */ -public class DeleteCommandParser implements Parser { +public class DeletePersonCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the DeleteCommand * and returns a DeleteCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ - public DeleteCommand parse(String args) throws ParseException { + public DeletePersonCommand parse(String args) throws ParseException { try { Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); + return new DeletePersonCommand(index); } catch (ParseException pe) { throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeletePersonCommand.MESSAGE_USAGE), pe); } } diff --git a/src/main/java/seedu/address/logic/parser/person/DeletePersonTagCommandParser.java b/src/main/java/seedu/address/logic/parser/person/DeletePersonTagCommandParser.java new file mode 100644 index 00000000000..3c1ca3298ab --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/DeletePersonTagCommandParser.java @@ -0,0 +1,75 @@ +package seedu.address.logic.parser.person; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.ArrayList; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.AddPersonTagCommand; +import seedu.address.logic.commands.person.DeletePersonTagCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +public class DeletePersonTagCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the + * {@code DeleteTagCommand} + * and returns a {@code DeleteTagCommand} object for execution + * * @param args Input string by user EXCEPT COMMAND WORD + * + * @return DeleteTagCommand object with arguments loaded in + * @throws ParseException If the user input does not conform to the expected + * format + */ + public DeletePersonTagCommand parse(String args) throws ParseException { + requireNonNull(args); + + // Tokenize all arguments + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, new Prefix("")); + + // Convert the argMultimap into an ArrayList<> for easier access + // The @ArgumentTokenizer produces a map with 3 elements: + // Element 1: Whitespace + // Element 2: Index + // Element 3: tagName string + ArrayList values = new ArrayList<>(argMultimap.getAllValues(new Prefix(""))); + + // Get the index element in the ArrayList + int indexInt; + try { + indexInt = Integer.parseInt(values.get(1)); + } catch (Exception e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeletePersonTagCommand.MESSAGE_USAGE)); + } + // Checking if index is a positive integer + if (!(indexInt >= 1)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonTagCommand.MESSAGE_USAGE)); + } + + Index index = Index.fromOneBased(indexInt); // Convert to fromOneBased index since contact list starts from 1 + + // Get the tagName element in the ArrayList + String tagName; + try { + tagName = values.get(2); + } catch (IndexOutOfBoundsException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeletePersonTagCommand.MESSAGE_USAGE)); + } + try { + new Tag(tagName); + } catch (Exception e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeletePersonTagCommand.MESSAGE_USAGE)); + } + + return new DeletePersonTagCommand(index, tagName); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/person/EditPersonCommandParser.java similarity index 81% rename from src/main/java/seedu/address/logic/parser/EditCommandParser.java rename to src/main/java/seedu/address/logic/parser/person/EditPersonCommandParser.java index 845644b7dea..605028c5d4c 100644 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/person/EditPersonCommandParser.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.address.logic.parser.person; import static java.util.Objects.requireNonNull; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; @@ -14,22 +14,26 @@ 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.EditPersonDescriptor; +import seedu.address.logic.commands.person.EditPersonCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.tag.Tag; /** * Parses input arguments and creates a new EditCommand object */ -public class EditCommandParser implements Parser { +public class EditPersonCommandParser implements Parser { /** * Parses the given {@code String} of arguments in the context of the EditCommand * and returns an EditCommand object for execution. * @throws ParseException if the user input does not conform the expected format */ - public EditCommand parse(String args) throws ParseException { + public EditPersonCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); @@ -39,7 +43,8 @@ public EditCommand parse(String args) throws ParseException { try { index = ParserUtil.parseIndex(argMultimap.getPreamble()); } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + EditPersonCommand.MESSAGE_USAGE), pe); } EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); @@ -58,10 +63,10 @@ public EditCommand parse(String args) throws ParseException { parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); + throw new ParseException(EditPersonCommand.MESSAGE_NOT_EDITED); } - return new EditCommand(index, editPersonDescriptor); + return new EditPersonCommand(index, editPersonDescriptor); } /** diff --git a/src/main/java/seedu/address/logic/parser/person/FindPersonCommandParser.java b/src/main/java/seedu/address/logic/parser/person/FindPersonCommandParser.java new file mode 100644 index 00000000000..c2e4650854f --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/FindPersonCommandParser.java @@ -0,0 +1,65 @@ +package seedu.address.logic.parser.person; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.List; +import java.util.stream.Stream; + +import seedu.address.logic.commands.person.FindPersonCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.name.PersonNameContainsKeywordsPredicate; +import seedu.address.model.tag.PersonTagContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindCommand object + */ +public class FindPersonCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindCommand + * and returns a FindCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindPersonCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_TAG); + + if ((!arePrefixesPresent(argMultimap, PREFIX_NAME) && !arePrefixesPresent(argMultimap, PREFIX_TAG)) + || trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindPersonCommand.MESSAGE_USAGE)); + } + + List nameKeywords = argMultimap.getAllValues(PREFIX_NAME); + List tagKeywords = argMultimap.getAllValues(PREFIX_TAG); + + // checks if names are valid + for (String name: nameKeywords) { + ParserUtil.parseNameKeyword(name); + } + + // check if tags are valid + for (String tag: tagKeywords) { + ParserUtil.parseTagKeyword(tag); + } + + return new FindPersonCommand(new PersonNameContainsKeywordsPredicate(nameKeywords), + new PersonTagContainsKeywordsPredicate(tagKeywords)); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/person/notecommands/AddMiscCommandParser.java b/src/main/java/seedu/address/logic/parser/person/notecommands/AddMiscCommandParser.java new file mode 100644 index 00000000000..457ed2292cf --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/notecommands/AddMiscCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser.person.notecommands; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.AddMiscCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.note.Note; + +/** + * Parses input arguments and creates a new {@code AddMiscCommand} object + */ +public class AddMiscCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code AddMiscCommand} + * and returns a {@code AddMiscCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddMiscCommand parse(String args) throws ParseException { + try { + String[] splitArgs = args.trim().split("\\s+", 2); + Index index = ParserUtil.parseIndex(splitArgs[0]); + Note note = ParserUtil.parseNote(splitArgs[1]); + return new AddMiscCommand(index, note); + } catch (ParseException | IndexOutOfBoundsException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMiscCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/person/notecommands/AddStrengthCommandParser.java b/src/main/java/seedu/address/logic/parser/person/notecommands/AddStrengthCommandParser.java new file mode 100644 index 00000000000..fdf889f84f1 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/notecommands/AddStrengthCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser.person.notecommands; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.AddStrengthCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.note.Note; + +/** + * Parses input arguments and creates a new {@code AddStrengthCommand} object + */ +public class AddStrengthCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code AddStrengthCommand} + * and returns a {@code AddStrengthCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddStrengthCommand parse(String args) throws ParseException { + try { + String[] splitArgs = args.trim().split("\\s+", 2); + Index index = ParserUtil.parseIndex(splitArgs[0]); + Note note = ParserUtil.parseNote(splitArgs[1]); + return new AddStrengthCommand(index, note); + } catch (ParseException | IndexOutOfBoundsException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStrengthCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/person/notecommands/AddWeaknessCommandParser.java b/src/main/java/seedu/address/logic/parser/person/notecommands/AddWeaknessCommandParser.java new file mode 100644 index 00000000000..e6bdc803709 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/notecommands/AddWeaknessCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser.person.notecommands; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.AddWeaknessCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.note.Note; + +/** + * Parses input arguments and creates a new {@code AddWeaknessCommand} object + */ +public class AddWeaknessCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code AddWeaknessCommand} + * and returns a {@code AddWeaknessCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddWeaknessCommand parse(String args) throws ParseException { + try { + String[] splitArgs = args.trim().split("\\s+", 2); + Index index = ParserUtil.parseIndex(splitArgs[0]); + Note note = ParserUtil.parseNote(splitArgs[1]); + return new AddWeaknessCommand(index, note); + } catch (ParseException | IndexOutOfBoundsException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddWeaknessCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteMiscCommandParser.java b/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteMiscCommandParser.java new file mode 100644 index 00000000000..f8ea98c3044 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteMiscCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser.person.notecommands; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.DeleteMiscCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new {@code DeleteMiscCommand} object + */ +public class DeleteMiscCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code DeleteMiscCommand} + * and returns a {@code DeleteMiscCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + @Override + public DeleteMiscCommand parse(String args) throws ParseException { + try { + String[] splitArgs = args.trim().split("\\s+", 2); + Index index = ParserUtil.parseIndex(splitArgs[0]); + Index noteIndex = ParserUtil.parseNoteIndex(splitArgs[1]); + return new DeleteMiscCommand(index, noteIndex); + } catch (ParseException | IndexOutOfBoundsException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteMiscCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteStrengthCommandParser.java b/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteStrengthCommandParser.java new file mode 100644 index 00000000000..96131d360e8 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteStrengthCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser.person.notecommands; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.DeleteStrengthCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new {@code DeleteStrengthCommand} object + */ +public class DeleteStrengthCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code DeleteStrengthCommand} + * and returns a {@code DeleteStrengthCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + @Override + public DeleteStrengthCommand parse(String args) throws ParseException { + try { + String[] splitArgs = args.trim().split("\\s+", 2); + Index index = ParserUtil.parseIndex(splitArgs[0]); + Index noteIndex = ParserUtil.parseNoteIndex(splitArgs[1]); + return new DeleteStrengthCommand(index, noteIndex); + } catch (ParseException | IndexOutOfBoundsException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteStrengthCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteWeaknessCommandParser.java b/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteWeaknessCommandParser.java new file mode 100644 index 00000000000..85b77926d55 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/person/notecommands/DeleteWeaknessCommandParser.java @@ -0,0 +1,32 @@ +package seedu.address.logic.parser.person.notecommands; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.DeleteWeaknessCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new {@code DeleteWeaknessCommand} object + */ +public class DeleteWeaknessCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code DeleteWeaknessCommand} + * and returns a {@code DeleteWeaknessCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + @Override + public DeleteWeaknessCommand parse(String args) throws ParseException { + try { + String[] splitArgs = args.trim().split("\\s+", 2); + Index index = ParserUtil.parseIndex(splitArgs[0]); + Index noteIndex = ParserUtil.parseNoteIndex(splitArgs[1]); + return new DeleteWeaknessCommand(index, noteIndex); + } catch (ParseException | IndexOutOfBoundsException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteWeaknessCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/strategy/AddPlayerCommandParser.java b/src/main/java/seedu/address/logic/parser/strategy/AddPlayerCommandParser.java new file mode 100644 index 00000000000..88b214a59d6 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/strategy/AddPlayerCommandParser.java @@ -0,0 +1,31 @@ +package seedu.address.logic.parser.strategy; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.strategy.AddPlayerCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.strategy.Player; + +/** + * Parses input arguments and creates a new AddPlayerCommand object + */ + +public class AddPlayerCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code AddPlayerCommand} + * and returns an {@code AddPlayerCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddPlayerCommand parse(String args) throws ParseException { + try { + String[] splitArgs = args.trim().split("\\s+", 1); + Player player = ParserUtil.paresPlayer(splitArgs[0]); + return new AddPlayerCommand(player); + } catch (IndexOutOfBoundsException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPlayerCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/strategy/DeletePlayerCommandParser.java b/src/main/java/seedu/address/logic/parser/strategy/DeletePlayerCommandParser.java new file mode 100644 index 00000000000..a3748c29871 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/strategy/DeletePlayerCommandParser.java @@ -0,0 +1,28 @@ +package seedu.address.logic.parser.strategy; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.strategy.DeletePlayerCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeletePlayerCommand object + */ + +public class DeletePlayerCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code DeletePlayerCommand} + * and returns an {@code DELETEPlayerCommand} object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeletePlayerCommand parse(String args) throws ParseException { + try { + String[] splitArgs = args.trim().split("\\s+", 1); + return new DeletePlayerCommand(splitArgs[0]); + } catch (IndexOutOfBoundsException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeletePlayerCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/strategy/LoadCourtCommandParser.java b/src/main/java/seedu/address/logic/parser/strategy/LoadCourtCommandParser.java new file mode 100644 index 00000000000..1deb770380a --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/strategy/LoadCourtCommandParser.java @@ -0,0 +1,30 @@ +package seedu.address.logic.parser.strategy; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.logic.commands.strategy.LoadCourtCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.image.Image; + +/** + * Parses input arguments and creates a new LoadCourtCommand object + */ +public class LoadCourtCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the LoadCourtCommand + * and returns a LoadCourtCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public LoadCourtCommand parse(String args) throws ParseException { + try { + Image image = ParserUtil.parseImage(args); + return new LoadCourtCommand(image); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, LoadCourtCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/strategy/MovePlayerCommandParser.java b/src/main/java/seedu/address/logic/parser/strategy/MovePlayerCommandParser.java new file mode 100644 index 00000000000..3b364e180bc --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/strategy/MovePlayerCommandParser.java @@ -0,0 +1,69 @@ +package seedu.address.logic.parser.strategy; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_XCOORD; +import static seedu.address.logic.parser.CliSyntax.PREFIX_YCOORD; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.strategy.MovePlayerCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.strategy.Player; + +/** + * Parses input arguments and creates a new MovePlayerCommand object. + */ +public class MovePlayerCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the MovePlayerCommand + * and returns an MovePlayerCommand object for execution. + * + * @return an MovePlayerCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + @Override + public MovePlayerCommand parse(String args) throws ParseException { + requireNonNull(args); + + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_XCOORD, PREFIX_YCOORD); + if (!arePrefixesPresent(argMultimap, PREFIX_XCOORD, PREFIX_YCOORD) + || argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MovePlayerCommand.MESSAGE_USAGE)); + } + + try { + int xCoord = Integer.parseInt(argMultimap.getValue(PREFIX_XCOORD).get()); + int yCoord = Integer.parseInt(argMultimap.getValue(PREFIX_YCOORD).get()); + String playerName = argMultimap.getPreamble(); + + if (!Player.isValidXCoord(xCoord)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MovePlayerCommand.MESSAGE_USAGE)); + } + + if (!Player.isValidYCoord(yCoord)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + MovePlayerCommand.MESSAGE_USAGE)); + } + + return new MovePlayerCommand(playerName, xCoord, yCoord); + } catch (NumberFormatException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, MovePlayerCommand.MESSAGE_USAGE)); + } + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/task/AddTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/task/AddTaskCommandParser.java new file mode 100644 index 00000000000..2726e519677 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/AddTaskCommandParser.java @@ -0,0 +1,69 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CONTACT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.address.logic.commands.task.AddTaskCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; + + +/** + * Parses input arguments and creates a new AddTaskCommand object + */ +public class AddTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddTaskCommand + * and returns an AddTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public AddTaskCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DATE, + PREFIX_STARTTIME, PREFIX_ENDTIME, PREFIX_TAG, PREFIX_CONTACT); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_DATE, PREFIX_STARTTIME, PREFIX_ENDTIME) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE)); + } + + Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); + Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + StartTime startTime = ParserUtil.parseStartTime(argMultimap.getValue(PREFIX_STARTTIME).get()); + EndTime endTime = ParserUtil.parseEndTime(argMultimap.getValue(PREFIX_ENDTIME).get()); + Set tagSet = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + Set nameSet = ParserUtil.parseNames(argMultimap.getAllValues(PREFIX_CONTACT)); + + Task task = new Task(name, date, startTime, endTime, tagSet, nameSet); + + return new AddTaskCommand(task); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/AddTaskTagCommandParser.java b/src/main/java/seedu/address/logic/parser/task/AddTaskTagCommandParser.java new file mode 100644 index 00000000000..2cb12583038 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/AddTaskTagCommandParser.java @@ -0,0 +1,75 @@ +package seedu.address.logic.parser.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.ArrayList; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.AddPersonTagCommand; +import seedu.address.logic.commands.task.AddTaskTagCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddTaskCommand object + */ +public class AddTaskTagCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the {@code AddTagToTaskCommandParser} + * and returns a {@code AddTagToTaskCommand} object for execution + * * @param args Input string by user EXCEPT COMMAND WORD + * + * @return AddTagToTaskCommand object with arguments loaded in + * @throws ParseException If the user input does not conform to the expected format + */ + + public AddTaskTagCommand parse(String args) throws ParseException { + requireNonNull(args); + + // Tokenize all arguments + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, new Prefix("")); + + // Convert the argMultimap into an ArrayList<> for easier access + // The @ArgumentTokenizer produces a map with 3 elements: + // Element 1: Whitespace + // Element 2: Index + // Element 3: tagName string + ArrayList values = new ArrayList<>(argMultimap.getAllValues(new Prefix(""))); + + // Get the index element in the ArrayList + int indexInt; + try { + indexInt = Integer.parseInt(values.get(1)); + } catch (Exception e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskTagCommand.MESSAGE_USAGE)); + } + // Checking if index is a positive integer + if (!(indexInt >= 1)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonTagCommand.MESSAGE_USAGE)); + } + + Index index = Index.fromOneBased(indexInt); // Convert to fromOneBased index since contact list starts from 1 + + // Get the tagName element in the ArrayList + String tagName; + try { + tagName = values.get(2); + } catch (IndexOutOfBoundsException e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskTagCommand.MESSAGE_USAGE)); + } + try { + new Tag(tagName); + } catch (Exception e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + AddTaskTagCommand.MESSAGE_USAGE)); + } + + return new AddTaskTagCommand(index, tagName); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/ClearTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/task/ClearTaskCommandParser.java new file mode 100644 index 00000000000..5c7128f4771 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/ClearTaskCommandParser.java @@ -0,0 +1,58 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; + +import java.util.stream.Stream; + +import seedu.address.logic.commands.task.ClearTaskCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.task.Date; + +/** + * Parses input arguments and creates a new ClearTaskCommand object + */ +public class ClearTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the ClearTaskCommand + * and returns a ClearTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public ClearTaskCommand parse(String args) throws ParseException { + try { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_DATE); + + //check if the given string is empty (or contains only whitespace) + if (args.isBlank()) { + return new ClearTaskCommand(); + } + + if (!arePrefixesPresent(argMultimap, PREFIX_DATE) || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClearTaskCommand.MESSAGE_USAGE)); + } + + Date date = ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get()); + + return new ClearTaskCommand(date); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ClearTaskCommand.MESSAGE_USAGE), pe); + } + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/DeleteTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/task/DeleteTaskCommandParser.java new file mode 100644 index 00000000000..ffd72a2586b --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/DeleteTaskCommandParser.java @@ -0,0 +1,31 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.task.DeleteTaskCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteTaskCommand object + */ +public class DeleteTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteTaskCommand + * and returns a DeleteTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public DeleteTaskCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteTaskCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/DeleteTaskTagCommandParser.java b/src/main/java/seedu/address/logic/parser/task/DeleteTaskTagCommandParser.java new file mode 100644 index 00000000000..c1dff3663f6 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/DeleteTaskTagCommandParser.java @@ -0,0 +1,80 @@ +package seedu.address.logic.parser.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX; + +import java.util.ArrayList; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.AddPersonTagCommand; +import seedu.address.logic.commands.task.DeleteTaskTagCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddTaskCommand object + */ +public class DeleteTaskTagCommandParser implements Parser { + /** + * Parses the given {@code String} of arguments in the context of the + * {@code DeleteTagFromTaskCommandParser} + * and returns a {@code DeleteTagFromTaskCommand} object for execution + * * @param args Input string by user EXCEPT COMMAND WORD + * + * @return DeleteTagFromTaskCommand object with arguments loaded in + * @throws ParseException If the user input does not conform to the expected + * format + */ + + public DeleteTaskTagCommand parse(String args) throws ParseException { + requireNonNull(args); + + // Tokenize all arguments + ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, new Prefix("")); + + // Convert the argMultimap into an ArrayList<> for easier access + // The @ArgumentTokenizer produces a map with 3 elements: + // Element 1: Whitespace + // Element 2: Index + // Element 3: tagName string + ArrayList values = new ArrayList<>(argMultimap.getAllValues(new Prefix(""))); + + // Get the index element in the ArrayList + int indexInt; + try { + indexInt = Integer.parseInt(values.get(1)); + } catch (Exception e) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskTagCommand.MESSAGE_USAGE)); + + } + // Checking if index is a positive integer + if (!(indexInt >= 1)) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonTagCommand.MESSAGE_USAGE)); + } + + Index index = Index.fromOneBased(indexInt); // Convert to fromOneBased index since contact list starts from 1 + + // Get the tagName element in the ArrayList + String tagName; + try { + tagName = values.get(2); + } catch (IndexOutOfBoundsException e) { + throw new ParseException(String.format(MESSAGE_INVALID_TASK_DISPLAYED_INDEX, + DeleteTaskTagCommand.MESSAGE_USAGE)); + } + try { + new Tag(tagName); + } catch (Exception e) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteTaskTagCommand.MESSAGE_USAGE)); + } + + return new DeleteTaskTagCommand(index, tagName); + } +} diff --git a/src/main/java/seedu/address/logic/parser/task/EditTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/task/EditTaskCommandParser.java new file mode 100644 index 00000000000..ae69f433dfa --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/EditTaskCommandParser.java @@ -0,0 +1,105 @@ +package seedu.address.logic.parser.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_CONTACT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_STARTTIME; +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.EditTaskDescriptor; +import seedu.address.logic.commands.task.EditTaskCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditCommand object + */ +public class EditTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditTaskCommand + * and returns an EditTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public EditTaskCommand parse(String args) throws ParseException { + requireNonNull(args); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_DATE, PREFIX_STARTTIME, + PREFIX_ENDTIME, PREFIX_TAG, PREFIX_CONTACT); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTaskCommand.MESSAGE_USAGE), pe); + } + + EditTaskDescriptor editTaskDescriptor = new EditTaskDescriptor(); + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editTaskDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_DATE).isPresent()) { + editTaskDescriptor.setDate(ParserUtil.parseDate(argMultimap.getValue(PREFIX_DATE).get())); + } + if (argMultimap.getValue(PREFIX_STARTTIME).isPresent()) { + editTaskDescriptor.setStartTime(ParserUtil.parseStartTime(argMultimap.getValue(PREFIX_STARTTIME).get())); + } + if (argMultimap.getValue(PREFIX_ENDTIME).isPresent()) { + editTaskDescriptor.setEndTime(ParserUtil.parseEndTime(argMultimap.getValue(PREFIX_ENDTIME).get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editTaskDescriptor::setTags); + parsePersonsForEdit(argMultimap.getAllValues(PREFIX_CONTACT)).ifPresent(editTaskDescriptor::setPersons); + + if (!editTaskDescriptor.isAnyFieldEdited()) { + throw new ParseException(EditTaskCommand.MESSAGE_NOT_EDITED); + } + + return new EditTaskCommand(index, editTaskDescriptor); + } + + /** + * 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)); + } + + /** + * Parses {@code Collection persons} into a {@code Set} if {@code persons} is non-empty. + * If {@code persons} contain only one element which is an empty string, it will be parsed into a + * {@code Set} containing zero persons]. + */ + private Optional> parsePersonsForEdit(Collection persons) throws ParseException { + assert persons != null; + + if (persons.isEmpty()) { + return Optional.empty(); + } + Collection nameSet = persons.size() == 1 && persons.contains("") ? Collections.emptySet() : persons; + return Optional.of(ParserUtil.parseNames(nameSet)); + } + +} diff --git a/src/main/java/seedu/address/logic/parser/task/FindTaskCommandParser.java b/src/main/java/seedu/address/logic/parser/task/FindTaskCommandParser.java new file mode 100644 index 00000000000..c89061fa2a2 --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/FindTaskCommandParser.java @@ -0,0 +1,65 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.List; +import java.util.stream.Stream; + +import seedu.address.logic.commands.task.FindTaskCommand; +import seedu.address.logic.parser.ArgumentMultimap; +import seedu.address.logic.parser.ArgumentTokenizer; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.Prefix; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.name.TaskNameContainsKeywordsPredicate; +import seedu.address.model.tag.TaskTagContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindTaskCommand object + */ +public class FindTaskCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindTaskCommand + * and returns a FindTaskCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public FindTaskCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_TAG); + + if ((!arePrefixesPresent(argMultimap, PREFIX_NAME) && !arePrefixesPresent(argMultimap, PREFIX_TAG)) + || trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTaskCommand.MESSAGE_USAGE)); + } + + List nameKeywords = argMultimap.getAllValues(PREFIX_NAME); + List tagKeywords = argMultimap.getAllValues(PREFIX_TAG); + + // checks if names are valid + for (String name: nameKeywords) { + ParserUtil.parseNameKeyword(name); + } + + // check if tags are valid + for (String tag: tagKeywords) { + ParserUtil.parseTagKeyword(tag); + } + + return new FindTaskCommand(new TaskNameContainsKeywordsPredicate(nameKeywords), + new TaskTagContainsKeywordsPredicate(tagKeywords)); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) { + return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent()); + } +} diff --git a/src/main/java/seedu/address/logic/parser/task/GetPersonCommandParser.java b/src/main/java/seedu/address/logic/parser/task/GetPersonCommandParser.java new file mode 100644 index 00000000000..0e49680b3ad --- /dev/null +++ b/src/main/java/seedu/address/logic/parser/task/GetPersonCommandParser.java @@ -0,0 +1,31 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.task.GetPersonCommand; +import seedu.address.logic.parser.Parser; +import seedu.address.logic.parser.ParserUtil; +import seedu.address.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new GetPersonCommand object + */ +public class GetPersonCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the GetPersonCommand + * and returns a GetPersonCommand object for execution. + * @throws ParseException if the user input does not conform the expected format + */ + public GetPersonCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new GetPersonCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, GetPersonCommand.MESSAGE_USAGE), pe); + } + } + +} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java index d54df471c1f..e2ad197c92e 100644 --- a/src/main/java/seedu/address/model/Model.java +++ b/src/main/java/seedu/address/model/Model.java @@ -6,6 +6,8 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; import seedu.address.model.person.Person; +import seedu.address.model.strategy.Player; +import seedu.address.model.task.Task; /** * The API of the Model component. @@ -13,6 +15,8 @@ public interface Model { /** {@code Predicate} that always evaluate to true */ Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; + Predicate PREDICATE_SHOW_ALL_TASKS = unused -> true; + Predicate PREDICATE_SHOW_ALL_PLAYERS = unused -> true; /** * Replaces user prefs data with the data in {@code userPrefs}. @@ -44,6 +48,16 @@ public interface Model { */ void setAddressBookFilePath(Path addressBookFilePath); + /** + * Returns the user prefs' task book file path. + */ + Path getTaskBookFilePath(); + + /** + * Sets the user prefs' task book file path. + */ + void setTaskBookFilePath(Path taskBookFilePath); + /** * Replaces address book data with the data in {@code addressBook}. */ @@ -76,6 +90,9 @@ public interface Model { */ void setPerson(Person target, Person editedPerson); + /** Returns an unmodifiable view of the unfiltered person list */ + ObservableList getUnfilteredPersonList(); + /** Returns an unmodifiable view of the filtered person list */ ObservableList getFilteredPersonList(); @@ -84,4 +101,87 @@ public interface Model { * @throws NullPointerException if {@code predicate} is null. */ void updateFilteredPersonList(Predicate predicate); + + /** + * Replaces task book data with the data in {@code taskBook}. + */ + void setTaskBook(ReadOnlyTaskBook taskBook); + + /** Returns the TaskBook */ + ReadOnlyTaskBook getTaskBook(); + + /** + * Returns true if a task with the same identity as {@code task} exists in the task book. + */ + boolean hasTask(Task task); + + /** + * Deletes the given task. + * The task must exist in the task book. + */ + void deleteTask(Task target); + + /** + * Adds the given task. + * {@code task} must not already exist in the task book. + */ + void addTask(Task task); + + /** + * Replaces the given task {@code target} with {@code editedTask}. + * {@code target} must exist in the task book. + * The task identity of {@code editedTask} must not be the same as another existing task in the task book. + */ + void setTask(Task target, Task editedTask); + + /** Returns an unmodifiable view of the unfiltered task list */ + ObservableList getUnfilteredTaskList(); + + /** Returns an unmodifiable view of the filtered task list */ + ObservableList getFilteredTaskList(); + + /** + * Updates the filter of the filtered task list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredTaskList(Predicate predicate); + + /** + * Replaces strategy board data with the data in {@code strategy board}. + */ + void setStrategyBoard(ReadOnlyStrategyBoard strategyBoard); + + /** Returns the Strategy Board. **/ + ReadOnlyStrategyBoard getStrategyBoard(); + + /** + * Returns true if a player with the same identity as {@code player} exists in the strategy board. + */ + boolean hasPlayer(Player player); + + /** + * Deletes the given player. + * The task must exist in the strategy board. + */ + void deletePlayer(Player target); + + /** + * Adds the given player. + * {@code player} must not already exist in the strategy board. + */ + void addPlayer(Player player); + + /** + * Replaces the given player {@code target} with {@code editedPlayer}. + */ + void setPlayer(Player target, Player editedPlayer); + + /** Returns an unmodifiable view of the filtered player list */ + ObservableList getFilteredPlayerList(); + + /** + * Updates the filter of the filtered player list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredPlayerList(Predicate predicate); } diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index 86c1df298d7..56746527fad 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -12,6 +12,8 @@ import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; import seedu.address.model.person.Person; +import seedu.address.model.strategy.Player; +import seedu.address.model.task.Task; /** * Represents the in-memory model of the address book data. @@ -20,24 +22,33 @@ public class ModelManager implements Model { private static final Logger logger = LogsCenter.getLogger(ModelManager.class); private final AddressBook addressBook; + private final TaskBook taskBook; + private final StrategyBoard strategyBoard; private final UserPrefs userPrefs; private final FilteredList filteredPersons; + private final FilteredList filteredTasks; + private final FilteredList filteredPlayers; /** * Initializes a ModelManager with the given addressBook and userPrefs. */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - requireAllNonNull(addressBook, userPrefs); + public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyTaskBook taskBook, + ReadOnlyStrategyBoard strategyBoard, ReadOnlyUserPrefs userPrefs) { + requireAllNonNull(addressBook, taskBook, userPrefs); logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); this.addressBook = new AddressBook(addressBook); + this.taskBook = new TaskBook(taskBook); + this.strategyBoard = new StrategyBoard(strategyBoard); this.userPrefs = new UserPrefs(userPrefs); filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); + filteredTasks = new FilteredList<>(this.taskBook.getTaskList()); + filteredPlayers = new FilteredList<>(this.strategyBoard.getPlayerList()); } public ModelManager() { - this(new AddressBook(), new UserPrefs()); + this(new AddressBook(), new TaskBook(), new StrategyBoard(), new UserPrefs()); } //=========== UserPrefs ================================================================================== @@ -75,6 +86,17 @@ public void setAddressBookFilePath(Path addressBookFilePath) { userPrefs.setAddressBookFilePath(addressBookFilePath); } + @Override + public Path getTaskBookFilePath() { + return userPrefs.getTaskBookFilePath(); + } + + @Override + public void setTaskBookFilePath(Path taskBookFilePath) { + requireNonNull(taskBookFilePath); + userPrefs.setTaskBookFilePath(taskBookFilePath); + } + //=========== AddressBook ================================================================================ @Override @@ -111,6 +133,11 @@ public void setPerson(Person target, Person editedPerson) { addressBook.setPerson(target, editedPerson); } + @Override + public ObservableList getUnfilteredPersonList() { + return addressBook.getPersonList(); + } + //=========== Filtered Person List Accessors ============================================================= /** @@ -128,6 +155,117 @@ public void updateFilteredPersonList(Predicate predicate) { filteredPersons.setPredicate(predicate); } + //=========== Task Book ================================================================================ + + @Override + public void setTaskBook(ReadOnlyTaskBook taskBook) { + this.taskBook.resetData(taskBook); + } + + @Override + public ReadOnlyTaskBook getTaskBook() { + return taskBook; + } + + @Override + public boolean hasTask(Task task) { + requireNonNull(task); + return taskBook.hasTask(task); + } + + @Override + public void deleteTask(Task target) { + taskBook.removeTask(target); + } + + @Override + public void addTask(Task task) { + taskBook.addTask(task); + updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + } + + @Override + public void setTask(Task target, Task editedTask) { + requireAllNonNull(target, editedTask); + + taskBook.setTask(target, editedTask); + } + + @Override + public ObservableList getUnfilteredTaskList() { + return taskBook.getTaskList(); + } + + //=========== Filtered Task List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Task} backed by the internal list of + * {@code versionedTaskBook} + */ + @Override + public ObservableList getFilteredTaskList() { + return filteredTasks; + } + + @Override + public void updateFilteredTaskList(Predicate predicate) { + requireNonNull(predicate); + filteredTasks.setPredicate(predicate); + } + + //=========== Strategy Board ================================================================================ + + @Override + public void setStrategyBoard(ReadOnlyStrategyBoard strategyBoard) { + this.strategyBoard.resetData(strategyBoard); + } + + @Override + public ReadOnlyStrategyBoard getStrategyBoard() { + return strategyBoard; + } + + @Override + public boolean hasPlayer(Player player) { + requireNonNull(player); + return strategyBoard.hasPlayer(player); + } + + @Override + public void deletePlayer(Player target) { + strategyBoard.removePlayer(target); + } + + @Override + public void addPlayer(Player player) { + strategyBoard.addPlayer(player); + updateFilteredPlayerList(PREDICATE_SHOW_ALL_PLAYERS); + } + + @Override + public void setPlayer(Player target, Player editedPlayer) { + requireAllNonNull(target, editedPlayer); + + strategyBoard.setPlayer(target, editedPlayer); + } + + //=========== Filtered Player List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Player} backed by the internal list of + * {@code versionedStrategyBoard} + */ + @Override + public ObservableList getFilteredPlayerList() { + return filteredPlayers; + } + + @Override + public void updateFilteredPlayerList(Predicate predicate) { + requireNonNull(predicate); + filteredPlayers.setPredicate(predicate); + } + @Override public boolean equals(Object obj) { // short circuit if same object @@ -144,7 +282,11 @@ public boolean equals(Object obj) { ModelManager other = (ModelManager) obj; return addressBook.equals(other.addressBook) && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); + && filteredPersons.equals(other.filteredPersons) + && taskBook.equals(other.taskBook) + && filteredTasks.equals(other.filteredTasks) + && strategyBoard.equals(other.strategyBoard) + && filteredPlayers.equals(other.filteredPlayers); } } diff --git a/src/main/java/seedu/address/model/ReadOnlyStrategyBoard.java b/src/main/java/seedu/address/model/ReadOnlyStrategyBoard.java new file mode 100644 index 00000000000..e0600a8eeb3 --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyStrategyBoard.java @@ -0,0 +1,15 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; +import seedu.address.model.strategy.Player; + +/** + * Unmodifiable view of a strategy board + */ +public interface ReadOnlyStrategyBoard { + /** + * Returns an unmodifiable view of the player list. + * This list will not contain any duplicate players. + */ + ObservableList getPlayerList(); +} diff --git a/src/main/java/seedu/address/model/ReadOnlyTaskBook.java b/src/main/java/seedu/address/model/ReadOnlyTaskBook.java new file mode 100644 index 00000000000..22b6380fe38 --- /dev/null +++ b/src/main/java/seedu/address/model/ReadOnlyTaskBook.java @@ -0,0 +1,15 @@ +package seedu.address.model; + +import javafx.collections.ObservableList; +import seedu.address.model.task.Task; + +/** + * Unmodifiable view of an task book + */ +public interface ReadOnlyTaskBook { + /** + * Returns an unmodifiable view of the persons list. + * This list will not contain any duplicate tasks. + */ + ObservableList getTaskList(); +} diff --git a/src/main/java/seedu/address/model/StrategyBoard.java b/src/main/java/seedu/address/model/StrategyBoard.java new file mode 100644 index 00000000000..507d2595329 --- /dev/null +++ b/src/main/java/seedu/address/model/StrategyBoard.java @@ -0,0 +1,112 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.model.strategy.Player; +import seedu.address.model.strategy.UniquePlayerList; + +/** + * Wraps all data at the strategy-board level + * Duplicates are not allowed (by .equals comparison) + */ +public class StrategyBoard implements ReadOnlyStrategyBoard { + private final UniquePlayerList players; + /* + * 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. + */ + { + players = new UniquePlayerList(); + } + + public StrategyBoard() {} + + /** + * Creates an StrategyBoard using the Players in the {@code toBeCopied} + */ + public StrategyBoard(ReadOnlyStrategyBoard toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the player list with {@code players}. + */ + public void setPlayers(List players) { + this.players.setPlayers(players); + } + + /** + * Resets the existing data of this {@code StrategyBoard} with {@code newData}. + */ + public void resetData(ReadOnlyStrategyBoard newData) { + requireNonNull(newData); + + setPlayers(newData.getPlayerList()); + } + + //// player-level operations + + /** + * Returns true if a player with the same identity as {@code player} exists in the address book. + */ + public boolean hasPlayer(Player player) { + requireNonNull(player); + return players.contains(player); + } + + /** + * Adds a player to the Strategy Board. + * The player must not already exist in the strategy board. + */ + public void addPlayer(Player t) { + players.add(t); + } + + /** + * Removes {@code key} from this {@code StrategyBoard}. + * {@code key} must exist in the strategy board. + */ + public void removePlayer(Player key) { + players.remove(key); + } + + public void setPlayer(Player target, Player editedPlayer) { + requireNonNull(editedPlayer); + + players.setPlayer(target, editedPlayer); + } + //// util methods + + @Override + public String toString() { + return players.asUnmodifiableObservableList().size() + " players"; + } + + @Override + public ObservableList getPlayerList() { + return players.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StrategyBoard // instanceof handles nulls + && players.equals(((StrategyBoard) other).players)); + } + + @Override + public int hashCode() { + return players.hashCode(); + } + + +} diff --git a/src/main/java/seedu/address/model/TaskBook.java b/src/main/java/seedu/address/model/TaskBook.java new file mode 100644 index 00000000000..9cd5a286517 --- /dev/null +++ b/src/main/java/seedu/address/model/TaskBook.java @@ -0,0 +1,122 @@ +package seedu.address.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.address.model.task.Task; +import seedu.address.model.task.UniqueTaskList; + + +/** + * Wraps all data at the task-book level + * Duplicates are not allowed (by .isSameTask comparison) + */ +public class TaskBook implements ReadOnlyTaskBook { + + + private final UniqueTaskList tasks; + + /* + * 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. + */ + { + tasks = new UniqueTaskList(); + } + + public TaskBook() {} + + /** + * Creates an TaskBook using the tasks in the {@code toBeCopied} + */ + public TaskBook(ReadOnlyTaskBook toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// list overwrite operations + + /** + * Replaces the contents of the task list with {@code tasks}. + * {@code tasks} must not contain duplicate tasks. + */ + public void setTasks(List tasks) { + this.tasks.setTasks(tasks); + } + + /** + * Resets the existing data of this {@code TaskBook} with {@code newData}. + */ + public void resetData(ReadOnlyTaskBook newData) { + requireNonNull(newData); + + setTasks(newData.getTaskList()); + } + + //// task-level operations + + /** + * Returns true if a task with the same identity as {@code task} exists in the task book. + */ + public boolean hasTask(Task task) { + requireNonNull(task); + return tasks.contains(task); + } + + /** + * Adds a task to the Task book. + * The task must not already exist in the task book. + */ + public void addTask(Task t) { + tasks.add(t); + } + + /** + * Replaces the given task {@code target} in the list with {@code editedTask}. + * {@code target} must exist in the task book. + * The task identity of {@code editedTask} must not be the same as another existing task in the task book. + */ + public void setTask(Task target, Task editedTask) { + requireNonNull(editedTask); + + tasks.setTask(target, editedTask); + } + + /** + * Removes {@code key} from this {@code TaskBook}. + * {@code key} must exist in the task book. + */ + public void removeTask(Task key) { + tasks.remove(key); + } + + //// util methods + + @Override + public String toString() { + return tasks.asUnmodifiableObservableList().size() + " tasks"; + // TODO: refine later + } + + @Override + public ObservableList getTaskList() { + return tasks.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskBook // instanceof handles nulls + && tasks.equals(((TaskBook) other).tasks)); + } + + @Override + public int hashCode() { + return tasks.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 25a5fd6eab9..93b00616016 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -15,6 +15,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path taskBookFilePath = Paths.get("data", "taskbook.json"); /** * Creates a {@code UserPrefs} with default values. @@ -56,6 +57,15 @@ public void setAddressBookFilePath(Path addressBookFilePath) { this.addressBookFilePath = addressBookFilePath; } + public Path getTaskBookFilePath() { + return taskBookFilePath; + } + + public void setTaskBookFilePath(Path taskBookFilePath) { + requireNonNull(taskBookFilePath); + this.taskBookFilePath = taskBookFilePath; + } + @Override public boolean equals(Object other) { if (other == this) { diff --git a/src/main/java/seedu/address/model/image/Image.java b/src/main/java/seedu/address/model/image/Image.java new file mode 100644 index 00000000000..057a6e8f1a8 --- /dev/null +++ b/src/main/java/seedu/address/model/image/Image.java @@ -0,0 +1,59 @@ +package seedu.address.model.image; + +import static java.util.Objects.requireNonNull; + +import java.io.File; +/** + * Represents an Image in Coach2K22. + */ +public class Image { + public static final String FILE_PATH = "courts/"; + + public static final String MESSAGE_CONSTRAINTS = + "Image must be a valid .png image and contained in \'" + FILE_PATH + "\'"; + + public final String imageName; + + /** + * Constructor for Image class. + * @param imageName The name of an image + */ + public Image(String imageName) { + requireNonNull(imageName); + this.imageName = imageName; + } + + /** + * Checks if given image name is a valid image. + * @param imageName The name of an image + * @return true if the image exists + */ + public static Boolean isValidFile(String imageName) { + String filePath = FILE_PATH + imageName + ".png"; + File file = new File(filePath); + return file.exists(); + } + + + /** + * Returns the File format of the image. + * @return File object of the given image + */ + public File getImagePath() { + String filePath = FILE_PATH + this.imageName + ".png"; + File file = new File(filePath); + return file; + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof Image + && imageName.equals(((Image) other).imageName)); + } + + @Override + public String toString() { + return this.imageName; + } +} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/name/Name.java similarity index 97% rename from src/main/java/seedu/address/model/person/Name.java rename to src/main/java/seedu/address/model/name/Name.java index 79244d71cf7..30deefd4303 100644 --- a/src/main/java/seedu/address/model/person/Name.java +++ b/src/main/java/seedu/address/model/name/Name.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.name; import static java.util.Objects.requireNonNull; import static seedu.address.commons.util.AppUtil.checkArgument; diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/name/PersonNameContainsKeywordsPredicate.java similarity index 58% rename from src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java rename to src/main/java/seedu/address/model/name/PersonNameContainsKeywordsPredicate.java index c9b5868427c..2578e0dcbd8 100644 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ b/src/main/java/seedu/address/model/name/PersonNameContainsKeywordsPredicate.java @@ -1,17 +1,18 @@ -package seedu.address.model.person; +package seedu.address.model.name; import java.util.List; import java.util.function.Predicate; import seedu.address.commons.util.StringUtil; +import seedu.address.model.person.Person; /** * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. */ -public class NameContainsKeywordsPredicate implements Predicate { +public class PersonNameContainsKeywordsPredicate implements Predicate { private final List keywords; - public NameContainsKeywordsPredicate(List keywords) { + public PersonNameContainsKeywordsPredicate(List keywords) { this.keywords = keywords; } @@ -24,8 +25,7 @@ public boolean test(Person person) { @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof NameContainsKeywordsPredicate // instanceof handles nulls - && keywords.equals(((NameContainsKeywordsPredicate) other).keywords)); // state check + || (other instanceof PersonNameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((PersonNameContainsKeywordsPredicate) other).keywords)); // state check } - } diff --git a/src/main/java/seedu/address/model/name/TaskNameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/name/TaskNameContainsKeywordsPredicate.java new file mode 100644 index 00000000000..3019779dcf2 --- /dev/null +++ b/src/main/java/seedu/address/model/name/TaskNameContainsKeywordsPredicate.java @@ -0,0 +1,32 @@ +package seedu.address.model.name; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; +import seedu.address.model.task.Task; + +/** + * Tests that a {@code Task}'s {@code Name} matches any of the keywords given. + */ +public class TaskNameContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public TaskNameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Task task) { + return keywords.stream() + .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(task.getName().fullName, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskNameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((TaskNameContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/note/Note.java b/src/main/java/seedu/address/model/note/Note.java new file mode 100644 index 00000000000..ec0791b10a0 --- /dev/null +++ b/src/main/java/seedu/address/model/note/Note.java @@ -0,0 +1,55 @@ +package seedu.address.model.note; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Note in the address book. + * Guarantees: immutable; name is valid as declared in {@link #isValidNote(String)} + */ +public class Note { + + public static final String MESSAGE_CONSTRAINTS = "Notes should be a String which is no longer than 50 characters"; + public static final String VALIDATION_REGEX = "\\p{Print}+"; // printable + public static final int MAX_LENGTH = 50; + + public final String note; + + /** + * Constructs a {@code Note}. + * + * @param note A valid note string. + */ + public Note(String note) { + requireNonNull(note); + checkArgument(isValidNote(note), MESSAGE_CONSTRAINTS); + this.note = note; + } + + /** + * Returns true if a given string is a valid note. + */ + public static boolean isValidNote(String test) { + return test.matches(VALIDATION_REGEX) && test.length() <= MAX_LENGTH; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Note // instanceof handles nulls + && note.equalsIgnoreCase(((Note) other).note)); // state check + } + + @Override + public int hashCode() { + return note.hashCode(); + } + + /** + * Format state as text for viewing. + */ + public String toString() { + return note; + } + +} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index 8ff1d83fe89..6ad7754a457 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -2,12 +2,17 @@ import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; import seedu.address.model.tag.Tag; +import seedu.address.model.util.ListUtil; /** * Represents a Person in the address book. @@ -23,17 +28,24 @@ public class Person { // Data fields private final Address address; private final Set tags = new HashSet<>(); + private final List strengths = new ArrayList<>(); + private final List weaknesses = new ArrayList<>(); + private final List miscellaneous = new ArrayList<>(); /** * 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); + public Person(Name name, Phone phone, Email email, Address address, Set tags, + List strength, List weaknesses, List misc) { + requireAllNonNull(name, phone, email, address, tags, strength, weaknesses, misc); this.name = name; this.phone = phone; this.email = email; this.address = address; this.tags.addAll(tags); + this.strengths.addAll(strength); + this.weaknesses.addAll(weaknesses); + this.miscellaneous.addAll(misc); } public Name getName() { @@ -60,6 +72,28 @@ public Set getTags() { return Collections.unmodifiableSet(tags); } + /** + * Returns an immutable note list, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public List getStrengths() { + return Collections.unmodifiableList(strengths); + } + /** + * Returns an immutable note list, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public List getWeaknesses() { + return Collections.unmodifiableList(weaknesses); + } + /** + * Returns an immutable note list, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public List getMiscellaneous() { + return Collections.unmodifiableList(miscellaneous); + } + /** * Returns true if both persons have the same name. * This defines a weaker notion of equality between two persons. @@ -92,13 +126,16 @@ public boolean equals(Object other) { && otherPerson.getPhone().equals(getPhone()) && otherPerson.getEmail().equals(getEmail()) && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); + && otherPerson.getTags().equals(getTags()) + && otherPerson.getStrengths().equals(getStrengths()) + && otherPerson.getWeaknesses().equals(getWeaknesses()) + && otherPerson.getMiscellaneous().equals(getMiscellaneous()); } @Override public int hashCode() { // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); + return Objects.hash(name, phone, email, address, tags, strengths, weaknesses, miscellaneous); } @Override @@ -117,7 +154,27 @@ public String toString() { builder.append("; Tags: "); tags.forEach(builder::append); } + + List strengths = getStrengths(); + if (!strengths.isEmpty()) { + builder.append("; Strengths: "); + ListUtil.toIndexedStringList(strengths) + .forEach(builder::append); + } + + List weaknesses = getWeaknesses(); + if (!weaknesses.isEmpty()) { + builder.append("; Weaknesses: "); + ListUtil.toIndexedStringList(weaknesses) + .forEach(builder::append); + } + + List miscellaneous = getMiscellaneous(); + if (!miscellaneous.isEmpty()) { + builder.append("; Misc: "); + ListUtil.toIndexedStringList(miscellaneous) + .forEach(builder::append); + } return builder.toString(); } - } diff --git a/src/main/java/seedu/address/model/strategy/Player.java b/src/main/java/seedu/address/model/strategy/Player.java new file mode 100644 index 00000000000..4f813f21fb4 --- /dev/null +++ b/src/main/java/seedu/address/model/strategy/Player.java @@ -0,0 +1,115 @@ +package seedu.address.model.strategy; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.util.Objects; + +public class Player { + public static final String MESSAGE_NAME_CONSTRAINTS = "Player name cannot be empty" + + " and must be not more then 24 characters" + + " and must not contain \"/\". \n"; + public static final String MESSAGE_X_CONSTRAINTS = "X coordinate must be an integer between 0 and 1000"; + public static final String MESSAGE_Y_CONSTRAINTS = "Y coordinate must be an integer between 0 and 600"; + + private static final int DEF_XCOORD = 50; + private static final int DEF_YCOORD = 500; + private static final int X_LIMIT = 1000; + private static final int Y_LIMIT = 600; + + private final String name; + private int xCoord; + private int yCoord; + + /** + * Creates a new player without specifying the coordinates + */ + public Player(String name) { + requireNonNull(name); + checkArgument(isValidPlayer(name), MESSAGE_NAME_CONSTRAINTS); + this.name = name; + this.xCoord = DEF_XCOORD; + this.yCoord = DEF_YCOORD; + } + + /** + * Creates a new player with specified coordinates + */ + public Player(String name, int xCoord, int yCoord) { + requireNonNull(name); + checkArgument(isValidPlayer(name), MESSAGE_NAME_CONSTRAINTS); + checkArgument(isValidXCoord(xCoord), MESSAGE_X_CONSTRAINTS); + checkArgument(isValidYCoord(yCoord), MESSAGE_Y_CONSTRAINTS); + this.name = name; + this.xCoord = xCoord; + this.yCoord = yCoord; + } + + /** + * Returns true if the given player name is a valid player name. + */ + public static boolean isValidPlayer(String playerName) { + return !playerName.isEmpty() && playerName.length() <= 50 && !playerName.contains("/"); + } + + public static boolean isValidXCoord(int xCoord) { + return xCoord >= 0 && xCoord <= X_LIMIT; + } + + public static boolean isValidYCoord(int yCoord) { + return yCoord >= 0 && yCoord <= Y_LIMIT; + } + + //Getters + public String getName() { + return name; + } + + public int getXCoord() { + return xCoord; + } + + public int getYCoord() { + return yCoord; + } + + //Setters + public void setXCoord(int xCoord) { + checkArgument(isValidXCoord(xCoord), MESSAGE_X_CONSTRAINTS); + this.xCoord = xCoord; + } + + public void setYCoord(int yCoord) { + checkArgument(isValidYCoord(yCoord), MESSAGE_Y_CONSTRAINTS); + this.yCoord = yCoord; + } + + //uti methods + public Player toCopy() { + return new Player(name, xCoord, yCoord); + } + + /** + * Returns true if both players have the same name + * This defines a stronger notion of equality between two tasks. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Player)) { + return false; + } + + Player otherPlayer = (Player) other; + return otherPlayer.getName().equals(getName()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, xCoord, yCoord); + } +} diff --git a/src/main/java/seedu/address/model/strategy/UniquePlayerList.java b/src/main/java/seedu/address/model/strategy/UniquePlayerList.java new file mode 100644 index 00000000000..7de747dbd02 --- /dev/null +++ b/src/main/java/seedu/address/model/strategy/UniquePlayerList.java @@ -0,0 +1,121 @@ +package seedu.address.model.strategy; + +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.strategy.exceptions.DuplicatePlayerException; +import seedu.address.model.strategy.exceptions.PlayerNotFoundException; + +/** + * A list of players that enforces uniqueness between its elements and does not allow nulls. + * + * Supports a minimal set of list operations. + */ +public class UniquePlayerList implements Iterable { + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent player as the given argument. + */ + public boolean contains(Player toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::equals); + } + + /** + * Adds a player to the list. + * The player must not already exist in the list. + */ + public void add(Player toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicatePlayerException(); + } + internalList.add(toAdd); + } + + /** + * Removes the equivalent player from the list. + * The player must exist in the list. + */ + public void remove(Player toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new PlayerNotFoundException(); + } + } + + /** + * Replaces the player {@code target} in the list with {@code editedPlayer}. + * {@code target} must exist in the list. + */ + public void setPlayer(Player target, Player editedPlayer) { + requireNonNull(target); + requireNonNull(editedPlayer); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new PlayerNotFoundException(); + } + + internalList.set(index, editedPlayer); + } + + /** + * Replaces the contents of this list with {@code players}. + * {@code players} must not contain duplicate players. + */ + public void setPlayers(List players) { + requireAllNonNull(players); + if (!playersAreUnique(players)) { + throw new DuplicatePlayerException(); + } + + internalList.setAll(players); + } + + /** + * 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) { + return other == this // short circuit if same object + || (other instanceof UniquePlayerList // instanceof handles nulls + && internalList.equals(((UniquePlayerList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code players} contains only unique players. + */ + private boolean playersAreUnique(List players) { + for (int i = 0; i < players.size() - 1; i++) { + for (int j = i + 1; j < players.size(); j++) { + if (players.get(i).equals(players.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/address/model/strategy/exceptions/DuplicatePlayerException.java b/src/main/java/seedu/address/model/strategy/exceptions/DuplicatePlayerException.java new file mode 100644 index 00000000000..fd7aa695560 --- /dev/null +++ b/src/main/java/seedu/address/model/strategy/exceptions/DuplicatePlayerException.java @@ -0,0 +1,10 @@ +package seedu.address.model.strategy.exceptions; + +/** + * Indicates that the operation will result in duplicate Players + */ +public class DuplicatePlayerException extends RuntimeException { + public DuplicatePlayerException() { + super("Operation would result in duplicate persons"); + } +} diff --git a/src/main/java/seedu/address/model/strategy/exceptions/PlayerNotFoundException.java b/src/main/java/seedu/address/model/strategy/exceptions/PlayerNotFoundException.java new file mode 100644 index 00000000000..55a9a605f51 --- /dev/null +++ b/src/main/java/seedu/address/model/strategy/exceptions/PlayerNotFoundException.java @@ -0,0 +1,7 @@ +package seedu.address.model.strategy.exceptions; + +/** + * Signals that the operation is unable to find the specified player. + */ +public class PlayerNotFoundException extends RuntimeException { +} diff --git a/src/main/java/seedu/address/model/tag/PersonTagContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/tag/PersonTagContainsKeywordsPredicate.java new file mode 100644 index 00000000000..ddd99515f03 --- /dev/null +++ b/src/main/java/seedu/address/model/tag/PersonTagContainsKeywordsPredicate.java @@ -0,0 +1,33 @@ +package seedu.address.model.tag; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; +import seedu.address.model.person.Person; + +/** + * Tests that a {@code Person}'s {@code Tag} matches any of the keywords given. + */ +public class PersonTagContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public PersonTagContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Person person) { + return keywords.stream() + .anyMatch(keyword -> person.getTags().stream() + .anyMatch(tag -> StringUtil.containsWordIgnoreCase(tag.tagName, keyword))); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof PersonTagContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((PersonTagContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/tag/TaskTagContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/tag/TaskTagContainsKeywordsPredicate.java new file mode 100644 index 00000000000..32f76c07930 --- /dev/null +++ b/src/main/java/seedu/address/model/tag/TaskTagContainsKeywordsPredicate.java @@ -0,0 +1,33 @@ +package seedu.address.model.tag; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.address.commons.util.StringUtil; +import seedu.address.model.task.Task; + +/** + * Tests that a {@code Task}'s {@code Tag} matches any of the keywords given. + */ +public class TaskTagContainsKeywordsPredicate implements Predicate { + private final List keywords; + + public TaskTagContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Task task) { + return keywords.stream() + .anyMatch(keyword -> task.getTags().stream() + .anyMatch(tag -> StringUtil.containsWordIgnoreCase(tag.tagName, keyword))); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TaskTagContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((TaskTagContainsKeywordsPredicate) other).keywords)); // state check + } + +} diff --git a/src/main/java/seedu/address/model/task/Date.java b/src/main/java/seedu/address/model/task/Date.java new file mode 100644 index 00000000000..611c8b7212b --- /dev/null +++ b/src/main/java/seedu/address/model/task/Date.java @@ -0,0 +1,71 @@ +package seedu.address.model.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; + + +/** + * Represents a Task's date in the schedule book. + * Guarantees: immutable; is valid as declared in {@link #isValidDate(String)} + */ +public class Date { + public static final String MESSAGE_CONSTRAINTS = "Date should be in the following format DD-MM-YYYY " + + "and adhere to the following constraints: \n" + + "1. YYYY should be a 4-digit numeric \n" + + "2. MM should be a 2-digit numeric ranging from 01 to 12 \n" + + "3. DD should be a 2-digit numeric, " + + "however the range of this numeric depends on the month and year " + + "e.g. 2020-02-29 is valid as it is a leap year while 2021-02-29 is invalid"; + + public final String value; + + /** + * Constructs a {@code Date}. + * + * @param date A valid date. + */ + public Date(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), MESSAGE_CONSTRAINTS); + value = date; + } + + /** + * Returns true if the given date is valid. + */ + public static boolean isValidDate(String date) { + try { + DateTimeFormatter customFormat = DateTimeFormatter + .ofPattern("dd-MM-uuuu").withResolverStyle(ResolverStyle.STRICT); + LocalDate.parse(date, customFormat); + } catch (DateTimeParseException ex) { + return false; + } + return true; + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof Date // instanceof handles nulls + && value.equals(((Date) other).value)); // state check + + } + + @Override + public int hashCode() { + return value.hashCode(); + } + +} + diff --git a/src/main/java/seedu/address/model/task/EndTime.java b/src/main/java/seedu/address/model/task/EndTime.java new file mode 100644 index 00000000000..7b58b738392 --- /dev/null +++ b/src/main/java/seedu/address/model/task/EndTime.java @@ -0,0 +1,65 @@ +package seedu.address.model.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; + +/** + * Represents a Task's end time in the schedule book. + * Guarantees: immutable; is valid as declared in {@link #isValidEndTime(String)} + */ +public class EndTime { + public static final String MESSAGE_CONSTRAINTS = "The end time should be in the following format HH:MM " + + "and adhere to the following constraints: \n" + + "1. HH should be a 2-digit numeric in the range of 00 to 23 \n" + + "2. MM should be a 2-digit numeric in the range of 00 to 59"; + + public final String value; + + /** + * Constructs a {@code EndTime}. + * + * @param time A valid EndTime. + */ + public EndTime(String time) { + requireNonNull(time); + checkArgument(isValidEndTime(time), MESSAGE_CONSTRAINTS); + value = time; + } + + /** + * Returns true if the given time is valid. + */ + public static boolean isValidEndTime(String time) { + try { + DateTimeFormatter customFormat = DateTimeFormatter + .ofPattern("HH:mm").withResolverStyle(ResolverStyle.STRICT); + LocalTime.parse(time, customFormat); + } catch (DateTimeParseException ex) { + return false; + } + return true; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EndTime // instanceof handles nulls + && value.equals(((EndTime) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value; + } +} + diff --git a/src/main/java/seedu/address/model/task/StartTime.java b/src/main/java/seedu/address/model/task/StartTime.java new file mode 100644 index 00000000000..b691ab161a8 --- /dev/null +++ b/src/main/java/seedu/address/model/task/StartTime.java @@ -0,0 +1,65 @@ +package seedu.address.model.task; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; + +/** + * Represents a Task's start time in the schedule book. + * Guarantees: immutable; is valid as declared in {@link #isValidStartTime(String)} + */ +public class StartTime { + public static final String MESSAGE_CONSTRAINTS = "The start time should be in the following format HH:MM " + + "and adhere to the following constraints: \n" + + "1. HH should be a 2-digit numeric in the range of 00 to 23 \n" + + "2. MM should be a 2-digit numeric in the range of 00 to 59"; + + public final String value; + + /** + * Constructs a {@code StartTime}. + * + * @param time A valid StartTime. + */ + public StartTime(String time) { + requireNonNull(time); + checkArgument(isValidStartTime(time), MESSAGE_CONSTRAINTS); + value = time; + } + + /** + * Returns true if the given time is valid. + */ + public static boolean isValidStartTime(String time) { + try { + DateTimeFormatter customFormat = DateTimeFormatter + .ofPattern("HH:mm").withResolverStyle(ResolverStyle.STRICT); + LocalTime.parse(time, customFormat); + } catch (DateTimeParseException ex) { + return false; + } + return true; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof StartTime // instanceof handles nulls + && value.equals(((StartTime) other).value)); // state check + + } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public String toString() { + return value; + } +} diff --git a/src/main/java/seedu/address/model/task/Task.java b/src/main/java/seedu/address/model/task/Task.java new file mode 100644 index 00000000000..aa1a4e7508a --- /dev/null +++ b/src/main/java/seedu/address/model/task/Task.java @@ -0,0 +1,203 @@ +package seedu.address.model.task; + +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.ResolverStyle; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; + +public class Task implements Comparable { + + //Identity fields + private final Name name; + private final Date date; + private final StartTime startTime; + private final EndTime endTime; + private String time; // Date + startTime for sorting comparisons in SortTaskByDateCommand + + //Data fields + private final Set tags = new HashSet<>(); + private final Set persons = new HashSet<>(); //persons are represented by their names + + /** + * Every field must be present and not null. + */ + public Task(Name name, Date date, StartTime startTime, EndTime endTime, Set tags, Set persons) { + requireAllNonNull(name, date, startTime, endTime, tags, persons); + this.name = name; + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + this.tags.addAll(tags); + this.persons.addAll(persons); + this.time = this.date.toString() + " " + this.startTime.toString(); + } + + //Getters + public Name getName() { + return name; + } + + public Date getDate() { + return date; + } + + public StartTime getStartTime() { + return startTime; + } + + public EndTime getEndTime() { + return endTime; + } + + public String getTime() { + return time; + } + + /** + * Returns the start and end time values joined together for Task Card label + * + * @return appended values of start and end time + */ + public String appendStartAndEndTime() { + return startTime.value + " - " + endTime.value; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns an immutable persons set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getPersons() { + return Collections.unmodifiableSet(persons); + } + + /** + * Returns true if both tasks have the same name. + * This defines a weaker notion of equality between two tasks. + */ + public boolean isSameTask(Task otherTask) { + if (otherTask == this) { + return true; + } + + return otherTask != null + && otherTask.getName().equals(getName()); + } + + /** + * Returns true if task has an end time equal to or before its start time. + */ + public boolean hasStartEndTimeConflict() { + LocalTime thisTaskStart = LocalTime.parse(startTime.value); + LocalTime thisTaskEnd = LocalTime.parse(endTime.value); + return thisTaskEnd.compareTo(thisTaskStart) <= 0; + } + /** + * Returns true if both tasks have the same date and conflicting time ranges. + */ + public boolean hasDateTimeConflict(Task otherTask) { + if (otherTask == this) { + return true; + } + + LocalTime thisTaskStart = LocalTime.parse(startTime.value); + LocalTime thisTaskEnd = LocalTime.parse(endTime.value); + LocalTime otherTaskStart = LocalTime.parse(otherTask.startTime.value); + LocalTime otherTaskEnd = LocalTime.parse(otherTask.endTime.value); + + // Solution below adapted from https://stackoverflow.com/q/325933 + // checks if time ranges overlap (exclusive) + boolean timeConflict = + thisTaskStart.isBefore(otherTaskEnd) + && otherTaskStart.isBefore(thisTaskEnd); + + return otherTask != null + && otherTask.getDate().equals(getDate()) //test for same dates + && timeConflict; + } + + /** + * Returns true if both tasks have the same identity and data fields. + * This defines a stronger notion of equality between two tasks. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Task)) { + return false; + } + + Task otherTask = (Task) other; + return otherTask.getName().equals(getName()) + && otherTask.getDate().equals(getDate()) + && otherTask.getStartTime().equals(getStartTime()) + && otherTask.getEndTime().equals(getEndTime()) + && otherTask.getTags().equals(getTags()) + && otherTask.getPersons().equals(getPersons()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, date, startTime, endTime, tags, persons); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append("; Date: ") + .append(getDate()) + .append("; Start Time: ") + .append(getStartTime()) + .append("; End Time: ") + .append(getEndTime()); + + Set tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + Set persons = getPersons(); + if (!persons.isEmpty()) { + builder.append("; Persons: "); + int count = 0; + for (Name name : persons) { + builder.append(name); + builder.append(" "); + } + } + + return builder.toString(); + } + + @Override + public int compareTo(Task otherTask) { + DateTimeFormatter customFormat = DateTimeFormatter + .ofPattern("dd-MM-uuuu HH:mm").withResolverStyle(ResolverStyle.STRICT); + + LocalDateTime date1 = LocalDateTime.parse(this.getTime(), customFormat); + LocalDateTime date2 = LocalDateTime.parse(otherTask.getTime(), customFormat); + + return date1.compareTo(date2); + } +} diff --git a/src/main/java/seedu/address/model/task/UniqueTaskList.java b/src/main/java/seedu/address/model/task/UniqueTaskList.java new file mode 100644 index 00000000000..046738ce724 --- /dev/null +++ b/src/main/java/seedu/address/model/task/UniqueTaskList.java @@ -0,0 +1,138 @@ +package seedu.address.model.task; + +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.task.exceptions.DuplicateTaskException; +import seedu.address.model.task.exceptions.TaskNotFoundException; + +/** + * A list of tasks that enforces uniqueness between its elements and does not allow nulls. + * A task is considered unique by comparing using {@code Task#isSameTask(Task)}. As such, adding and updating of + * tasks uses Task#isSameTask(Task) for equality so as to ensure that the task being added or updated is + * unique in terms of identity in the UniqueTaskList. However, the removal of a task uses Task#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 Task#isSameTask(Task) + */ +public class UniqueTaskList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent task as the given argument. + */ + public boolean contains(Task toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameTask); + } + + /** + * Adds a task to the list. + * The task must not already exist in the list. + */ + public void add(Task toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateTaskException(); + } + internalList.add(toAdd); + } + + /** + * Replaces the task {@code target} in the list with {@code editedTask}. + * {@code target} must exist in the list. + * The person identity of {@code editedTask} must not be the same as another existing task in the list. + */ + public void setTask(Task target, Task editedTask) { + requireAllNonNull(target, editedTask); + + int index = internalList.indexOf(target); + if (index == -1) { + throw new TaskNotFoundException(); + } + + if (!target.isSameTask(editedTask) && contains(editedTask)) { + throw new DuplicateTaskException(); + } + + internalList.set(index, editedTask); + } + + /** + * Removes the equivalent task from the list. + * The task must exist in the list. + */ + public void remove(Task toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new TaskNotFoundException(); + } + } + + public void setTasks(UniqueTaskList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code tasks}. + * {@code tasks} must not contain duplicate tasks. + */ + public void setTasks(List tasks) { + requireAllNonNull(tasks); + if (!tasksAreUnique(tasks)) { + throw new DuplicateTaskException(); + } + + internalList.setAll(tasks); + } + + /** + * 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) { + return other == this // short circuit if same object + || (other instanceof UniqueTaskList // instanceof handles nulls + && internalList.equals(((UniqueTaskList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code tasks} contains only unique tasks. + */ + private boolean tasksAreUnique(List tasks) { + for (int i = 0; i < tasks.size() - 1; i++) { + for (int j = i + 1; j < tasks.size(); j++) { + if (tasks.get(i).isSameTask(tasks.get(j))) { + return false; + } + } + } + return true; + } + +} diff --git a/src/main/java/seedu/address/model/task/exceptions/DuplicateTaskException.java b/src/main/java/seedu/address/model/task/exceptions/DuplicateTaskException.java new file mode 100644 index 00000000000..00dd4a63c4b --- /dev/null +++ b/src/main/java/seedu/address/model/task/exceptions/DuplicateTaskException.java @@ -0,0 +1,12 @@ +package seedu.address.model.task.exceptions; + +/** + * Indicates that the operation will result in duplicate Tasks (Tasks are consider duplicates if they + * have the same identity). + */ +public class DuplicateTaskException extends RuntimeException { + public DuplicateTaskException() { + super("Operation would result in duplicate persons"); + } +} + diff --git a/src/main/java/seedu/address/model/task/exceptions/TaskNotFoundException.java b/src/main/java/seedu/address/model/task/exceptions/TaskNotFoundException.java new file mode 100644 index 00000000000..305f190f79e --- /dev/null +++ b/src/main/java/seedu/address/model/task/exceptions/TaskNotFoundException.java @@ -0,0 +1,8 @@ +package seedu.address.model.task.exceptions; + +/** + * Indicates that the operation is unable to find the specified task. + */ +public class TaskNotFoundException extends RuntimeException { +} + diff --git a/src/main/java/seedu/address/model/util/ListUtil.java b/src/main/java/seedu/address/model/util/ListUtil.java new file mode 100644 index 00000000000..4e486890ce0 --- /dev/null +++ b/src/main/java/seedu/address/model/util/ListUtil.java @@ -0,0 +1,21 @@ +package seedu.address.model.util; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * Contains utility methods for converting a list of Object to a list of String. + */ +public class ListUtil { + /** + * Convert a list of Object to a list of indexed String + * @param lst the list of Object + * @param the Object type, must have a meaningful @code{toString} method + * @return the list of String with each element indexed + */ + public static List toIndexedStringList(List lst) { + return lst.stream() + .map(obj -> (lst.indexOf(obj) + 1) + ". " + obj.toString() + " ") + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java index 1806da4facf..d4d49de8d95 100644 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java @@ -1,17 +1,25 @@ package seedu.address.model.util; import java.util.Arrays; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; import seedu.address.model.AddressBook; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyTaskBook; +import seedu.address.model.TaskBook; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; 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.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; /** * Contains utility methods for populating {@code AddressBook} with sample data. @@ -21,22 +29,41 @@ 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")), + getTagSet("friends"), getNoteList("good offence"), + getNoteList("bad defense"), getNoteList("likes bing chilling")), 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")), + getTagSet("colleagues", "friends"), getNoteList("good offence"), + getNoteList("bad defense"), getNoteList("surgery scheduled today")), 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")), + getTagSet("neighbours"), getNoteList("good offence"), + getNoteList("bad defense"), getNoteList("birthday next Monday")), 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")), + getTagSet("family"), getNoteList("good offence"), + getNoteList("bad defense"), getNoteList("buy a new phone")), 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")), + getTagSet("classmates"), getNoteList("good offence"), + getNoteList("bad defense"), getNoteList("play games together tonight")), 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")) + getTagSet("colleagues"), getNoteList("good offence"), + getNoteList("bad defense"), getNoteList("meeting with him this Sunday")) + }; + } + + public static Task[] getSampleTasks() { + return new Task[] { + new Task(new Name("Shareholders Meeting"), new Date("10-04-2022"), new StartTime("09:00"), + new EndTime("12:00"), getTagSet("team1"), getPersonSet("Alex Yeoh")), + new Task(new Name("Team Training"), new Date("11-04-2022"), new StartTime("09:00"), + new EndTime("12:00"), getTagSet("team1"), getPersonSet("Alex Yeoh", "Bernice Yu")), + new Task(new Name("Annual Dinner"), new Date("11-04-2022"), new StartTime("18:00"), + new EndTime("20:00"), getTagSet("family", "friends"), getPersonSet("Irfan Ibrahim")), + new Task(new Name("Recruitment Talk"), new Date("12-04-2022"), new StartTime("15:00"), + new EndTime("18:00"), getTagSet("school"), getPersonSet("Irfan Ibrahim")) }; } @@ -48,6 +75,14 @@ public static ReadOnlyAddressBook getSampleAddressBook() { return sampleAb; } + public static ReadOnlyTaskBook getSampleTaskBook() { + TaskBook sampleTb = new TaskBook(); + for (Task sampleTask : getSampleTasks()) { + sampleTb.addTask(sampleTask); + } + return sampleTb; + } + /** * Returns a tag set containing the list of strings given. */ @@ -57,4 +92,22 @@ public static Set getTagSet(String... strings) { .collect(Collectors.toSet()); } + /** + * Returns a person set containing the list of strings given. + */ + public static Set getPersonSet(String... strings) { + return Arrays.stream(strings) + .map(Name::new) + .collect(Collectors.toSet()); + } + + /** + * Returns a note list containing the list of strings given. + */ + public static List getNoteList(String... strings) { + return Arrays.stream(strings) + .map(Note::new) + .collect(Collectors.toList()); + } + } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedName.java b/src/main/java/seedu/address/storage/JsonAdaptedName.java new file mode 100644 index 00000000000..3e67659b2f2 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedName.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.name.Name; + +/** + * Jackson-friendly version of {@link Name}. + */ +class JsonAdaptedName { + + private final String name; + + /** + * Constructs a {@code JsonAdaptedName} with the given {@code name}. + */ + @JsonCreator + public JsonAdaptedName(String name) { + this.name = name; + } + + /** + * Converts a given {@code Name} into this class for Jackson use. + */ + public JsonAdaptedName(Name source) { + name = source.fullName; + } + + @JsonValue + public String getPersonName() { + return name; + } + + /** + * Converts this Jackson-friendly adapted tag object into the model's {@code Name} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted name. + */ + public Name toModelType() throws IllegalValueException { + if (!Name.isValidName(name)) { + throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); + } + return new Name(name); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedNote.java b/src/main/java/seedu/address/storage/JsonAdaptedNote.java new file mode 100644 index 00000000000..113a67f81ca --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedNote.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.note.Note; + +/** + * Jackson-friendly version of {@link Note}. + */ +class JsonAdaptedNote { + + private final String note; + + /** + * Constructs a {@code JsonAdaptedNote} with the given {@code note}. + */ + @JsonCreator + public JsonAdaptedNote(String note) { + this.note = note; + } + + /** + * Converts a given {@code Note} into this class for Jackson use. + */ + public JsonAdaptedNote(Note source) { + note = source.note; + } + + @JsonValue + public String getNote() { + return note; + } + + /** + * Converts this Jackson-friendly adapted note object into the model's {@code Note} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted note. + */ + public Note toModelType() throws IllegalValueException { + if (!Note.isValidNote(note)) { + throw new IllegalValueException(Note.MESSAGE_CONSTRAINTS); + } + return new Note(note); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java index a6321cec2ea..8c54ae8e224 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java @@ -10,9 +10,10 @@ import com.fasterxml.jackson.annotation.JsonProperty; import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; 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; @@ -29,14 +30,19 @@ class JsonAdaptedPerson { private final String email; private final String address; private final List tagged = new ArrayList<>(); - + private final List strengths = new ArrayList<>(); + private final List weaknesses = new ArrayList<>(); + private final List misc = 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("tagged") List tagged) { + @JsonProperty("tagged") List tagged, + @JsonProperty("strengths") List strengths, + @JsonProperty("weaknesses") List weaknesses, + @JsonProperty("misc") List misc) { this.name = name; this.phone = phone; this.email = email; @@ -44,6 +50,15 @@ public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone if (tagged != null) { this.tagged.addAll(tagged); } + if (strengths != null) { + this.strengths.addAll(strengths); + } + if (weaknesses != null) { + this.weaknesses.addAll(weaknesses); + } + if (misc != null) { + this.misc.addAll(misc); + } } /** @@ -57,6 +72,15 @@ public JsonAdaptedPerson(Person source) { tagged.addAll(source.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList())); + strengths.addAll(source.getStrengths().stream() + .map(JsonAdaptedNote::new) + .collect(Collectors.toList())); + weaknesses.addAll(source.getWeaknesses().stream() + .map(JsonAdaptedNote::new) + .collect(Collectors.toList())); + misc.addAll(source.getMiscellaneous().stream() + .map(JsonAdaptedNote::new) + .collect(Collectors.toList())); } /** @@ -66,9 +90,21 @@ public JsonAdaptedPerson(Person source) { */ public Person toModelType() throws IllegalValueException { final List personTags = new ArrayList<>(); + final List personStrengths = new ArrayList<>(); + final List personWeaknesses = new ArrayList<>(); + final List personMisc = new ArrayList<>(); for (JsonAdaptedTag tag : tagged) { personTags.add(tag.toModelType()); } + for (JsonAdaptedNote note : strengths) { + personStrengths.add(note.toModelType()); + } + for (JsonAdaptedNote note : weaknesses) { + personWeaknesses.add(note.toModelType()); + } + for (JsonAdaptedNote note : misc) { + personMisc.add(note.toModelType()); + } if (name == null) { throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); @@ -103,7 +139,11 @@ public Person toModelType() throws IllegalValueException { final Address modelAddress = new Address(address); final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); + final List modelStrengths = new ArrayList<>(personStrengths); + final List modelWeaknesses = new ArrayList<>(personWeaknesses); + final List modelMisc = new ArrayList<>(personMisc); + return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags, modelStrengths, modelWeaknesses, + modelMisc); } } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTask.java b/src/main/java/seedu/address/storage/JsonAdaptedTask.java new file mode 100644 index 00000000000..3602070bd32 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedTask.java @@ -0,0 +1,120 @@ +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.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; + +public class JsonAdaptedTask { + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Task's %s field is missing!"; + + private final String name; + private final String date; + private final String startTime; + private final String endTime; + private final List tagged = new ArrayList<>(); + private final List persons = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedTask} with the given task details. + */ + @JsonCreator + public JsonAdaptedTask(@JsonProperty("name") String name, @JsonProperty("date") String date, + @JsonProperty("startTime") String startTime, @JsonProperty("endTime") String endTime, + @JsonProperty("tagged") List tagged, + @JsonProperty("persons") List persons) { + this.name = name; + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + if (tagged != null) { + this.tagged.addAll(tagged); + } + if (persons != null) { + this.persons.addAll(persons); + } + } + + /** + * Converts a given {@code Task} into this class for Jackson use. + */ + public JsonAdaptedTask(Task source) { + name = source.getName().fullName; + date = source.getDate().value; + startTime = source.getStartTime().value; + endTime = source.getEndTime().value; + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + persons.addAll(source.getPersons().stream() + .map(JsonAdaptedName::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted task object into the model's {@code Task} object. + * Does not check if names in {@code persons} are in the address book. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted task. + */ + public Task toModelType() throws IllegalValueException { + final List taskTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + taskTags.add(tag.toModelType()); + } + final List taskPersons = new ArrayList<>(); + for (JsonAdaptedName person : persons) { + taskPersons.add(person.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 (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, + StartTime.class.getSimpleName())); + } + if (!StartTime.isValidStartTime(startTime)) { + throw new IllegalValueException(StartTime.MESSAGE_CONSTRAINTS); + } + final StartTime modelStartTime = new StartTime(startTime); + + if (endTime == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, EndTime.class.getSimpleName())); + } + if (!EndTime.isValidEndTime(endTime)) { + throw new IllegalValueException(EndTime.MESSAGE_CONSTRAINTS); + } + final EndTime modelEndTime = new EndTime(endTime); + + final Set modelTags = new HashSet<>(taskTags); + final Set modelPersons = new HashSet<>(taskPersons); + return new Task(modelName, modelDate, modelStartTime, modelEndTime, modelTags, modelPersons); + } +} diff --git a/src/main/java/seedu/address/storage/JsonSerializableTaskBook.java b/src/main/java/seedu/address/storage/JsonSerializableTaskBook.java new file mode 100644 index 00000000000..7a50156b88d --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonSerializableTaskBook.java @@ -0,0 +1,59 @@ +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.ReadOnlyTaskBook; +import seedu.address.model.TaskBook; +import seedu.address.model.task.Task; + +/** + * An Immutable TaskBook that is serializable to JSON format. + */ +@JsonRootName(value = "taskbook") +class JsonSerializableTaskBook { + + public static final String MESSAGE_DUPLICATE_TASK = "Tasks list contains duplicate task(s)."; + + private final List tasks = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableTaskBook} with the given tasks. + */ + @JsonCreator + public JsonSerializableTaskBook(@JsonProperty("tasks") List tasks) { + this.tasks.addAll(tasks); + } + + /** + * Converts a given {@code ReadOnlyTaskBook} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableTaskBook}. + */ + public JsonSerializableTaskBook(ReadOnlyTaskBook source) { + tasks.addAll(source.getTaskList().stream().map(JsonAdaptedTask::new).collect(Collectors.toList())); + } + + /** + * Converts this task book into the model's {@code TaskBook} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public TaskBook toModelType() throws IllegalValueException { + TaskBook taskBook = new TaskBook(); + for (JsonAdaptedTask jsonAdaptedTask : tasks) { + Task task = jsonAdaptedTask.toModelType(); + if (taskBook.hasTask(task)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_TASK); + } + taskBook.addTask(task); + } + return taskBook; + } +} diff --git a/src/main/java/seedu/address/storage/JsonTaskBookStorage.java b/src/main/java/seedu/address/storage/JsonTaskBookStorage.java new file mode 100644 index 00000000000..af8e7106f85 --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonTaskBookStorage.java @@ -0,0 +1,76 @@ +package seedu.address.storage; + +import static java.util.Objects.requireNonNull; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.address.commons.core.LogsCenter; +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.commons.util.FileUtil; +import seedu.address.commons.util.JsonUtil; +import seedu.address.model.ReadOnlyTaskBook; + +public class JsonTaskBookStorage implements TaskBookStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonTaskBookStorage.class); + + private Path filePath; + + public JsonTaskBookStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getTaskBookFilePath() { + return filePath; + } + + @Override + public Optional readTaskBook() throws DataConversionException { + return readTaskBook(filePath); + } + + /** + * Similar to {@link #readTaskBook()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readTaskBook(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonTaskBook = JsonUtil.readJsonFile( + filePath, JsonSerializableTaskBook.class); + if (!jsonTaskBook.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTaskBook.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveTaskBook(ReadOnlyTaskBook taskBook) throws IOException { + saveTaskBook(taskBook, filePath); + } + + /** + * Similar to {@link #saveTaskBook(ReadOnlyTaskBook)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveTaskBook(ReadOnlyTaskBook taskBook, Path filePath) throws IOException { + requireNonNull(taskBook); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableTaskBook(taskBook), filePath); + } +} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java index beda8bd9f11..1bd43689e0e 100644 --- a/src/main/java/seedu/address/storage/Storage.java +++ b/src/main/java/seedu/address/storage/Storage.java @@ -6,13 +6,14 @@ import seedu.address.commons.exceptions.DataConversionException; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyTaskBook; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; /** * API of the Storage component */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { +public interface Storage extends AddressBookStorage, UserPrefsStorage, TaskBookStorage { @Override Optional readUserPrefs() throws DataConversionException, IOException; @@ -27,6 +28,15 @@ public interface Storage extends AddressBookStorage, UserPrefsStorage { Optional readAddressBook() throws DataConversionException, IOException; @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; + void saveAddressBook(ReadOnlyAddressBook taskBook) throws IOException; + + @Override + Path getTaskBookFilePath(); + + @Override + Optional readTaskBook() throws DataConversionException, IOException; + + @Override + void saveTaskBook(ReadOnlyTaskBook taskBook) throws IOException; } diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java index 6cfa0162164..2552f2a7756 100644 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ b/src/main/java/seedu/address/storage/StorageManager.java @@ -8,6 +8,7 @@ import seedu.address.commons.core.LogsCenter; import seedu.address.commons.exceptions.DataConversionException; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyTaskBook; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.UserPrefs; @@ -18,13 +19,16 @@ public class StorageManager implements Storage { private static final Logger logger = LogsCenter.getLogger(StorageManager.class); private AddressBookStorage addressBookStorage; + private TaskBookStorage taskBookStorage; private UserPrefsStorage userPrefsStorage; /** * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { + public StorageManager(AddressBookStorage addressBookStorage, TaskBookStorage taskBookStorage, + UserPrefsStorage userPrefsStorage) { this.addressBookStorage = addressBookStorage; + this.taskBookStorage = taskBookStorage; this.userPrefsStorage = userPrefsStorage; } @@ -75,4 +79,33 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) thro addressBookStorage.saveAddressBook(addressBook, filePath); } + // ================ TaskBook methods ============================== + + @Override + public Path getTaskBookFilePath() { + return taskBookStorage.getTaskBookFilePath(); + } + + @Override + public Optional readTaskBook() throws DataConversionException, IOException { + return readTaskBook(taskBookStorage.getTaskBookFilePath()); + } + + @Override + public Optional readTaskBook(Path filePath) throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return taskBookStorage.readTaskBook(filePath); + } + + @Override + public void saveTaskBook(ReadOnlyTaskBook taskBook) throws IOException { + saveTaskBook(taskBook, taskBookStorage.getTaskBookFilePath()); + } + + @Override + public void saveTaskBook(ReadOnlyTaskBook taskBook, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + taskBookStorage.saveTaskBook(taskBook, filePath); + } + } diff --git a/src/main/java/seedu/address/storage/TaskBookStorage.java b/src/main/java/seedu/address/storage/TaskBookStorage.java new file mode 100644 index 00000000000..f097b3520bb --- /dev/null +++ b/src/main/java/seedu/address/storage/TaskBookStorage.java @@ -0,0 +1,44 @@ +package seedu.address.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.address.commons.exceptions.DataConversionException; +import seedu.address.model.ReadOnlyTaskBook; + +/** + * Represents a storage for {@link seedu.address.model.TaskBook}. + */ +public interface TaskBookStorage { + + /** + * Returns the file path of the data file. + */ + Path getTaskBookFilePath(); + + /** + * Returns TaskBook data as a {@link ReadOnlyTaskBook}. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readTaskBook() throws DataConversionException, IOException; + + /** + * @see #getTaskBookFilePath() + */ + Optional readTaskBook(Path filePath) throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyTaskBook} to the storage. + * @param taskBook cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveTaskBook(ReadOnlyTaskBook taskBook) throws IOException; + + /** + * @see #saveTaskBook(ReadOnlyTaskBook) + */ + void saveTaskBook(ReadOnlyTaskBook taskBook, Path filePath) throws IOException; +} diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/address/ui/CommandBox.java index 9e75478664b..f6482a2d24d 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/address/ui/CommandBox.java @@ -5,7 +5,7 @@ import javafx.scene.control.TextField; import javafx.scene.layout.Region; import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.commands.task.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; /** diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java index 9a665915949..3f38854208b 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/address/ui/HelpWindow.java @@ -15,8 +15,8 @@ */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; - public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; + public static final String USERGUIDE_URL = "https://ay2122s2-cs2103t-w14-2.github.io/tp/UserGuide.html"; + public static final String HELP_MESSAGE = "Refer to user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); private static final String FXML = "HelpWindow.fxml"; diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 9106c3aa6e5..2989aa8591d 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -5,17 +5,20 @@ import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.MenuItem; +import javafx.scene.control.TabPane; import javafx.scene.control.TextInputControl; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; +import javafx.scene.layout.AnchorPane; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.core.LogsCenter; import seedu.address.logic.Logic; import seedu.address.logic.commands.CommandResult; -import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.commands.task.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.image.Image; /** * The Main Window. Provides the basic application layout containing @@ -32,6 +35,8 @@ public class MainWindow extends UiPart { // Independent Ui parts residing in this Ui container private PersonListPanel personListPanel; + private TaskListPanel taskListPanel; + private StrategyPanel strategyPanel; private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -44,12 +49,21 @@ public class MainWindow extends UiPart { @FXML private StackPane personListPanelPlaceholder; + @FXML + private StackPane taskListPanelPlaceholder; + + @FXML + private AnchorPane strategyPanelPlaceholder; + @FXML private StackPane resultDisplayPlaceholder; @FXML private StackPane statusbarPlaceholder; + @FXML + private TabPane tabPane; + /** * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}. */ @@ -112,6 +126,10 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { void fillInnerParts() { personListPanel = new PersonListPanel(logic.getFilteredPersonList()); personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + taskListPanel = new TaskListPanel(logic.getFilteredTaskList()); + taskListPanelPlaceholder.getChildren().add(taskListPanel.getRoot()); + strategyPanel = new StrategyPanel(logic.getFilteredPlayerList()); + strategyPanelPlaceholder.getChildren().add(strategyPanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); @@ -163,6 +181,18 @@ private void handleExit() { primaryStage.hide(); } + private void switchTab(int index) { + tabPane.getSelectionModel().select(index); + } + + private void handleLoadImage(Image image) { + strategyPanel.changeImageBackground(image.getImagePath()); + } + + private void handleExport() { + strategyPanel.captureAndSaveStrategyPanel(); + } + public PersonListPanel getPersonListPanel() { return personListPanel; } @@ -186,6 +216,17 @@ private CommandResult executeCommand(String commandText) throws CommandException handleExit(); } + switchTab(commandResult.getTabPane()); + + if (commandResult.isLoadImageCommand()) { + Image backGroundImage = commandResult.getBackgroundImage(); + handleLoadImage(backGroundImage); + } + + if (commandResult.isExportCommand()) { + handleExport(); + } + return commandResult; } catch (CommandException | ParseException e) { logger.info("Invalid command: " + commandText); diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index 7fc927bc5d9..666fd909b7e 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -1,6 +1,7 @@ package seedu.address.ui; import java.util.Comparator; +import java.util.stream.Collectors; import javafx.fxml.FXML; import javafx.scene.control.Label; @@ -8,6 +9,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Region; import seedu.address.model.person.Person; +import seedu.address.model.util.ListUtil; /** * An UI component that displays information of a {@code Person}. @@ -40,6 +42,12 @@ public class PersonCard extends UiPart { private Label email; @FXML private FlowPane tags; + @FXML + private Label strengths; + @FXML + private Label weaknesses; + @FXML + private Label misc; /** * Creates a {@code PersonCode} with the given {@code Person} and index to display. @@ -55,6 +63,15 @@ public PersonCard(Person person, int displayedIndex) { person.getTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + strengths.setText(ListUtil.toIndexedStringList(person.getStrengths()) + .stream() + .collect(Collectors.joining("\n"))); + weaknesses.setText(ListUtil.toIndexedStringList(person.getWeaknesses()) + .stream() + .collect(Collectors.joining("\n"))); + misc.setText(ListUtil.toIndexedStringList(person.getMiscellaneous()) + .stream() + .collect(Collectors.joining("\n"))); } @Override diff --git a/src/main/java/seedu/address/ui/StrategyPanel.java b/src/main/java/seedu/address/ui/StrategyPanel.java new file mode 100644 index 00000000000..4d70f1b951a --- /dev/null +++ b/src/main/java/seedu/address/ui/StrategyPanel.java @@ -0,0 +1,241 @@ +package seedu.address.ui; + +import java.awt.image.RenderedImage; +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import javax.imageio.ImageIO; + +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.embed.swing.SwingFXUtils; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.geometry.VPos; +import javafx.scene.Cursor; +import javafx.scene.SnapshotParameters; +import javafx.scene.control.Slider; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.image.WritableImage; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.paint.Color; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Circle; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import seedu.address.MainApp; +import seedu.address.commons.core.LogsCenter; +import seedu.address.model.strategy.Player; + +public class StrategyPanel extends UiPart { + private static final String FXML = "StrategyPanel.fxml"; + private static final Logger logger = LogsCenter.getLogger(MainApp.class); + private static final Map table = new HashMap<>(); + private static final int BOARD_WIDTH = 1000; + private static final int BOARD_HEIGHT = 600; + + private static double orgSceneX; + private static double orgSceneY; + private static double orgTranslateX; + private static double orgTranslateY; + + @FXML + private Pane playerView; + @FXML + private ImageView strategyImage; + @FXML + private AnchorPane strategyAnchorPane; + @FXML + private AnchorPane playerAnchorPane; + @FXML + private Slider vSlider; + @FXML + private Slider hSlider; + + + // Credit to http://java-buddy.blogspot.com/2013/07/move-node-to-front.html + private final EventHandler pressHandler = + new EventHandler() { + + @Override + public void handle(MouseEvent t) { + orgSceneX = t.getSceneX(); + orgSceneY = t.getSceneY(); + orgTranslateX = ((StackPane) (t.getSource())).getTranslateX(); + orgTranslateY = ((StackPane) (t.getSource())).getTranslateY(); + //logger.log(Level.INFO, "orgCenterX: {0}", new Object[]{orgCenterX}); + //logger.log(Level.INFO, "orgCenterY: {0}", new Object[]{orgCenterY}); + } + }; + + // Credit to http://java-buddy.blogspot.com/2013/07/move-node-to-front.html + private final EventHandler dragHandler = + new EventHandler() { + + @Override + public void handle(MouseEvent t) { + double offsetX = t.getSceneX() - orgSceneX; + double offsetY = t.getSceneY() - orgSceneY; + double newTranslateX = orgTranslateX + offsetX; + double newTranslateY = orgTranslateY + offsetY; + StackPane tmp = (StackPane) (t.getSource()); + //tmp.setLayoutX(newCenterX); + //tmp.setLayoutY(newCenterY); + double ratioX = newTranslateX / strategyAnchorPane.getWidth(); + double ratioY = newTranslateY / strategyAnchorPane.getHeight(); + tmp.translateXProperty().bind(strategyAnchorPane.widthProperty().multiply(ratioX)); + tmp.translateYProperty().bind(strategyAnchorPane.heightProperty().multiply(ratioY)); + //logger.log(Level.INFO, "newTranslateX: {0} new TranslateY: {1} ratioX: {2} ratioY: {3}", + // new Object[]{newTranslateX, newTranslateY, ratioX, ratioY}); + //logger.log(Level.INFO, "newTrueX: {0} new TrueY: {1}", + // new Object[]{tmp.getLayoutX(), tmp.getLayoutY()}); + } + }; + + /** + * Creates a {@code StrategyPanel} with draggable circles. + */ + public StrategyPanel(ObservableList playerList) { + super(FXML); + initBackgroundImage(); + playerList.addListener((ListChangeListener) change -> { + while (change.next()) { + if (change.wasAdded()) { + changeOnAdd(change.getAddedSubList()); + } else if (change.wasRemoved()) { + changeOnDelete(change.getRemoved()); + } else if (change.wasReplaced()) { + changeOnReplace(change.getRemoved(), change.getAddedSubList()); + } + } + }); + // brings slider to the back + vSlider.toBack(); + hSlider.toBack(); + } + + private void changeOnAdd(List addedSubList) { + for (Player player : addedSubList) { + String playerName = player.getName(); + if (table.containsKey(playerName)) { + continue; + } + StackPane stack = new StackPane(); + initStack(stack, playerName, player.getXCoord(), player.getYCoord(), 50, Color.BLUE); + playerView.getChildren().add(stack); + table.put(playerName, stack); + } + } + + private void changeOnDelete(List removeList) { + for (Player player : removeList) { + String playerName = player.getName(); + if (table.containsKey(playerName)) { + playerView.getChildren().remove(table.get(playerName)); + table.remove(playerName); + } + } + } + + private void changeOnReplace(List removeList, List addSubList) { + changeOnDelete(removeList); + changeOnAdd(addSubList); + } + + /** + * Changes the image contained in ImageView. + * @param file the file reference for the image to be loaded + */ + public void changeImageBackground(File file) { + strategyImage.setImage((new Image((file.toURI().toString())))); + } + + /** + * Initializes the background image to allow it to resize automatically along with the window. + */ + private void initBackgroundImage() { + strategyImage.setPreserveRatio(false); //needs to be marked false to allow image to properly resize with window + strategyImage.fitWidthProperty().bind(strategyAnchorPane.widthProperty()); + strategyImage.fitHeightProperty().bind(strategyAnchorPane.heightProperty()); + strategyImage.setManaged(false); + strategyImage.toBack(); //set image to back to avoid covering player icons + } + + private void initCircle(Circle circle, double rad, double x, double y, Paint color) { + circle.setRadius(rad); + circle.setFill(color); + circle.setCenterX(x); + circle.setCenterY(y); + circle.setCursor(Cursor.HAND); + } + + private void initText(Text text, String value, double x, double y) { + text.setX(x); + text.setY(y); + text.setText(value); + text.setFont(Font.font("Arial", 24)); + double width = text.prefWidth(-1); + text.setX(250 - width / 2); + text.setTextOrigin(VPos.CENTER); + } + + private void initStack(StackPane stack, String name, double x, double y, double rad, Paint color) { + Text text = new Text(); + initText(text, name, x, y); + Circle cr = new Circle(); + initCircle(cr, rad, x, y, color); + text.xProperty().bind(cr.centerXProperty()); + text.yProperty().bind(cr.centerYProperty()); + stack.getChildren().addAll(cr, text); + double width = Math.max(cr.getRadius() * 2, text.getLayoutBounds().getWidth()); + stack.translateXProperty() + .bind((strategyAnchorPane.widthProperty().subtract(AnchorPane.getLeftAnchor(hSlider))) + .divide(BOARD_WIDTH).multiply(x).subtract(width / 2)); + stack.translateYProperty() + .bind((strategyAnchorPane.heightProperty().subtract(AnchorPane.getTopAnchor(vSlider))) + .divide(BOARD_HEIGHT).multiply(BOARD_HEIGHT - y).subtract(40)); + stack.setOnMousePressed(pressHandler); + stack.setOnMouseDragged(dragHandler); + } + + /** + * Captures image of strategyAnchorPane and stores it in users local drive. + */ + //https://stackoverflow.com/questions/38028825/javafx-save-view-of-pane-to-image + public void captureAndSaveStrategyPanel() { + + FileChooser chooser = new FileChooser(); + + //include title name randomization + chooser.setInitialFileName("title" + ".png"); + chooser.getExtensionFilters().addAll( + new FileChooser.ExtensionFilter("PNG Files", "*.png")); + File file = chooser.showSaveDialog(null); + + if (file != null) { + try { + //parameters + SnapshotParameters sp = new SnapshotParameters(); + sp.setFill(Color.TRANSPARENT); + + //no edits to capture area + WritableImage image = strategyAnchorPane.snapshot(sp, null); + RenderedImage renderedImage = SwingFXUtils.fromFXImage(image, null); + + //Write the snapshot to the chosen file + ImageIO.write(renderedImage, "png", file); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/seedu/address/ui/TaskCard.java b/src/main/java/seedu/address/ui/TaskCard.java new file mode 100644 index 00000000000..88804634836 --- /dev/null +++ b/src/main/java/seedu/address/ui/TaskCard.java @@ -0,0 +1,71 @@ +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.task.Task; + +/** + * An UI component that displays information of a {@code Task}. + */ +public class TaskCard extends UiPart { + + private static final String FXML = "TaskCard.fxml"; + + public final Task task; + + @FXML + private HBox cardPane; + @FXML + private Label name; + @FXML + private Label id; + @FXML + private Label date; + @FXML + private Label time; + @FXML + private FlowPane tags; + @FXML + private FlowPane persons; + + /** + * Creates a {@code TaskCode} with the given {@code Task} and index to display. + */ + public TaskCard(Task task, int displayedIndex) { + super(FXML); + this.task = task; + id.setText(displayedIndex + ". "); + name.setText(task.getName().fullName); + date.setText(task.getDate().value); + time.setText(task.appendStartAndEndTime()); + task.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + task.getPersons().stream() + .sorted(Comparator.comparing(name -> name.fullName)) + .forEach(name -> persons.getChildren().add(new Label(name.fullName))); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof TaskCard)) { + return false; + } + + // state check + TaskCard card = (TaskCard) other; + return id.getText().equals(card.id.getText()) + && task.equals(card.task); + } +} diff --git a/src/main/java/seedu/address/ui/TaskListPanel.java b/src/main/java/seedu/address/ui/TaskListPanel.java new file mode 100644 index 00000000000..8cc91e33d90 --- /dev/null +++ b/src/main/java/seedu/address/ui/TaskListPanel.java @@ -0,0 +1,49 @@ +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.task.Task; + +/** + * Panel containing the list of tasks. + */ +public class TaskListPanel extends UiPart { + private static final String FXML = "TaskListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); + + @FXML + private ListView taskListView; + + /** + * Creates a {@code TaskListPanel} with the given {@code ObservableList}. + */ + public TaskListPanel(ObservableList taskList) { + super(FXML); + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Task} using a {@code TaskCard}. + */ + class TaskListViewCell extends ListCell { + @Override + protected void updateItem(Task task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new TaskCard(task, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..4752a8aec6d 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -31,6 +31,9 @@ .tab-pane { -fx-padding: 0 0 0 1; + -fx-background-color: derive(#1d1d1d, 20%); + -fx-border-color: derive(#1d1d1d, 10%); + -fx-border-top-width: 1px; } .tab-pane .tab-header-area { @@ -39,6 +42,36 @@ -fx-max-height: 0; } +.tab-pane .tab-header-area .tab-header-background { + -fx-background-color: #696969; +} + +.tab { + -fx-background-insets: 0 1 0 1; + -fx-background-color: #D3D3D3; +} + +.tab .tab-label { + -fx-alignment: center; + -fx-font-family: "Segoe UI Bold"; + -fx-font-size: 13; + -fx-text-fill: #000000; +} + +.tab:selected { + -fx-background-insets: 0 1 0 1; + -fx-background-color: derive(#1d1d1d, 20%); +} + +.tab:selected .focus-indicator { + -fx-border-color: transparent; +} + +.tab:selected .tab-label { + -fx-font-family: "Segoe UI Bold"; + -fx-text-fill: #ffffff; +} + .table-view { -fx-base: #1d1d1d; -fx-control-inner-background: #1d1d1d; @@ -350,3 +383,26 @@ -fx-background-radius: 2; -fx-font-size: 11; } + +#persons { + -fx-hgap: 7; + -fx-vgap: 3; +} + +#persons .label { + -fx-text-fill: white; + -fx-background-color: #FF6347; + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 11; +} + +#personLabel { + -fx-font-weight: bold +} + +#tagLabel { + -fx-font-weight: bold +} + diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css index bfe82a85964..d4456ff3521 100644 --- a/src/main/resources/view/Extensions.css +++ b/src/main/resources/view/Extensions.css @@ -18,3 +18,47 @@ .tooltip-text { -fx-text-fill: white; } + +/* @@author simjunheng-reused */ +/* CSS for slider taken from https://stackoverflow.com/questions/53879734 */ + +.slider > .thumb, +.slider > .thumb:hover, +.slider:focused > .thumb{ + -fx-background-color: #ff6a6a; + -fx-background-insets: 2 0 -23 0; + -fx-padding: 1 1 0 1; + -fx-background-radius: 0; +} + +.slider:vertical > .thumb, +.slider:vertical > .thumb:hover, +.slider:vertical:focused > .thumb{ + -fx-background-color: #ff6a6a; + -fx-background-insets: 0 -23 0 2; + -fx-padding: 1 0 1 1; + -fx-background-radius: 0; +} + +.slider > .track, +.slider:vertical > .track { + -fx-background-color: transparent; + -fx-background-insets: 0; + -fx-background-radius: 0; + -fx-padding: 0; +} + +.slider > .axis { + -fx-tick-mark-stroke: transparent; + -fx-tick-label-font-size: 0.833333em; + -fx-tick-label-fill: #9a9a9a; + -fx-background-color: #333; +} + +.slider.axis-top > .axis { + -fx-side: TOP; +} + +.slider.axis-left > .axis { + -fx-side: LEFT; +} diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..d562801505e 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -6,13 +6,15 @@ - + + + + - + @@ -33,27 +35,59 @@ - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + diff --git a/src/main/resources/view/NoteStyle.css b/src/main/resources/view/NoteStyle.css new file mode 100644 index 00000000000..88a5745f5db --- /dev/null +++ b/src/main/resources/view/NoteStyle.css @@ -0,0 +1,29 @@ +#strengthTitle { + -fx-font-weight: bold; +} + +#weaknessTitle { + -fx-font-weight: bold; +} + +#miscTitle { + -fx-font-weight: bold; +} + +#strengths { + -fx-max-width: 150.0; + -fx-background-color: #fff2cd; + -fx-text-fill: #000000; +} + +#weaknesses { + -fx-max-width: 150.0; + -fx-background-color: #fff2cd; + -fx-text-fill: #000000; +} + +#misc { + -fx-max-width: 150.0; + -fx-background-color: #fff2cd; + -fx-text-fill: #000000; +} diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index f08ea32ad55..040520700f0 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -1,5 +1,6 @@ + @@ -7,30 +8,65 @@ + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/StrategyPanel.fxml b/src/main/resources/view/StrategyPanel.fxml new file mode 100644 index 00000000000..d886bfd468f --- /dev/null +++ b/src/main/resources/view/StrategyPanel.fxml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/TaskCard.fxml b/src/main/resources/view/TaskCard.fxml new file mode 100644 index 00000000000..210e7fa266c --- /dev/null +++ b/src/main/resources/view/TaskCard.fxml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/TaskListPanel.fxml b/src/main/resources/view/TaskListPanel.fxml new file mode 100644 index 00000000000..096ccf83953 --- /dev/null +++ b/src/main/resources/view/TaskListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json index 48831cc7674..e41ea864489 100644 --- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json +++ b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json @@ -4,11 +4,17 @@ "phone": "94351253", "email": "alice@example.com", "address": "123, Jurong West Ave 6, #08-111", - "tagged": [ "friends" ] + "tagged": [ "friends" ], + "strengths": [ "good offence" ], + "weaknesses": [ "bad defense" ], + "misc": [ "likes bing chilling" ] }, { "name": "Alice Pauline", "phone": "94351253", "email": "pauline@example.com", - "address": "4th street" + "address": "4th street", + "strengths": [ "good offence" ], + "weaknesses": [ "bad defense" ], + "misc": [ "likes bing chilling" ] } ] } diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json index f10eddee12e..3ce52e51afd 100644 --- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json +++ b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json @@ -5,42 +5,63 @@ "phone" : "94351253", "email" : "alice@example.com", "address" : "123, Jurong West Ave 6, #08-111", - "tagged" : [ "friends" ] + "tagged" : [ "friends" ], + "strengths" : [ ], + "weaknesses" : [ ], + "misc" : [ ] }, { "name" : "Benson Meier", "phone" : "98765432", "email" : "johnd@example.com", "address" : "311, Clementi Ave 2, #02-25", - "tagged" : [ "owesMoney", "friends" ] + "tagged" : [ "owesMoney", "friends" ], + "strengths" : [ "Good Defense", "Great Stamina" ], + "weaknesses" : [ "Bad Offense", "Poor Endurance" ], + "misc" : [ "birthday tomorrow", "likes Python" ] }, { "name" : "Carl Kurz", "phone" : "95352563", "email" : "heinz@example.com", "address" : "wall street", - "tagged" : [ ] + "tagged" : [ ], + "strengths" : [ ], + "weaknesses" : [ ], + "misc" : [ ] }, { "name" : "Daniel Meier", "phone" : "87652533", "email" : "cornelia@example.com", "address" : "10th street", - "tagged" : [ "friends" ] + "tagged" : [ "friends" ], + "strengths" : [ ], + "weaknesses" : [ ], + "misc" : [ "likes bing chilling" ] }, { "name" : "Elle Meyer", "phone" : "9482224", "email" : "werner@example.com", "address" : "michegan ave", - "tagged" : [ ] + "tagged" : [ ], + "strengths" : [ ], + "weaknesses" : [ ], + "misc" : [ ] }, { "name" : "Fiona Kunz", "phone" : "9482427", "email" : "lydia@example.com", "address" : "little tokyo", - "tagged" : [ ] + "tagged" : [ ], + "strengths" : [ ], + "weaknesses" : [ ], + "misc" : [ ] }, { "name" : "George Best", "phone" : "9482442", "email" : "anna@example.com", "address" : "4th street", - "tagged" : [ ] + "tagged" : [ ], + "strengths" : [ ], + "weaknesses" : [ ], + "misc" : [ ] } ] } diff --git a/src/test/data/JsonSerializableTaskBookTest/duplicateTasksTaskBook.json b/src/test/data/JsonSerializableTaskBookTest/duplicateTasksTaskBook.json new file mode 100644 index 00000000000..4eea8adf2da --- /dev/null +++ b/src/test/data/JsonSerializableTaskBookTest/duplicateTasksTaskBook.json @@ -0,0 +1,14 @@ +{ + "tasks": [ { + "name": "Meeting", + "date": "10-02-2020", + "startTime": "09:00", + "endTime": "10:00", + "tagged": [ "friends" ] + }, { + "name": "Meeting", + "date": "10-02-2021", + "startTime": "13:00", + "endTime": "16:00" + } ] +} diff --git a/src/test/data/JsonSerializableTaskBookTest/invalidTasksTaskBook.json b/src/test/data/JsonSerializableTaskBookTest/invalidTasksTaskBook.json new file mode 100644 index 00000000000..c7f1e1bd0cd --- /dev/null +++ b/src/test/data/JsonSerializableTaskBookTest/invalidTasksTaskBook.json @@ -0,0 +1,8 @@ +{ + "tasks": [ { + "name": "Mee@ting", + "date": "29-02-2021", + "startTime": "09:00", + "endTime": "10:00" + } ] +} diff --git a/src/test/data/JsonSerializableTaskBookTest/typicalTasksTaskBook.json b/src/test/data/JsonSerializableTaskBookTest/typicalTasksTaskBook.json new file mode 100644 index 00000000000..115dbd94672 --- /dev/null +++ b/src/test/data/JsonSerializableTaskBookTest/typicalTasksTaskBook.json @@ -0,0 +1,25 @@ +{ + "_comment": "TaskBook save file which contains the same Task values as in TypicalTasks#getTypicalTaskBook()", + "tasks" : [ { + "name" : "Meetings", + "date" : "09-10-2022", + "startTime" : "09:00", + "endTime" : "10:00", + "tagged" : [ "friends", "colleagues" ], + "persons" : ["Alice Pauline", "Benson Meier"] + }, { + "name" : "Training", + "date" : "29-02-2020", + "startTime" : "14:00", + "endTime" : "16:00", + "tagged" : [ "colleagues" ], + "persons" : ["Carl Kurz"] + }, { + "name" : "Shareholder Conference", + "date" : "29-02-2020", + "startTime" : "14:00", + "endTime" : "16:00", + "tagged" : [ "colleagues" ], + "persons" : ["Benson Meier"] + }] +} diff --git a/src/test/data/JsonTaskBookStorageTest/invalidAndValidTasksTaskBook.json b/src/test/data/JsonTaskBookStorageTest/invalidAndValidTasksTaskBook.json new file mode 100644 index 00000000000..0ea45f8f3d2 --- /dev/null +++ b/src/test/data/JsonTaskBookStorageTest/invalidAndValidTasksTaskBook.json @@ -0,0 +1,13 @@ +{ + "tasks": [ { + "name": "Valid Task", + "date": "19-01-2023", + "startTime": "09:00", + "endTime": "12:00" + }, { + "name": "Task With Invalid Date Field", + "date": "29-30-2020", + "startTime": "09:00", + "endTime": "12:00" + } ] +} diff --git a/src/test/data/JsonTaskBookStorageTest/invalidTasksTaskBook.json b/src/test/data/JsonTaskBookStorageTest/invalidTasksTaskBook.json new file mode 100644 index 00000000000..eb5214f70d6 --- /dev/null +++ b/src/test/data/JsonTaskBookStorageTest/invalidTasksTaskBook.json @@ -0,0 +1,8 @@ +{ + "tasks": [ { + "name": "Task with invalid name field: Ha!ns Mu@ster", + "date": "10-02-2020", + "startTime": "09:00", + "endTime": "14:00" + } ] +} diff --git a/src/test/data/JsonTaskBookStorageTest/notJsonFormatTaskBook.json b/src/test/data/JsonTaskBookStorageTest/notJsonFormatTaskBook.json new file mode 100644 index 00000000000..a1097343b5d --- /dev/null +++ b/src/test/data/JsonTaskBookStorageTest/notJsonFormatTaskBook.json @@ -0,0 +1 @@ +not json format! diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java index ad923ac249a..74dc468888c 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/address/logic/LogicManagerTest.java @@ -17,21 +17,26 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -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.commands.person.AddPersonCommand; +import seedu.address.logic.commands.person.ListPersonCommand; +import seedu.address.logic.commands.task.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.StrategyBoard; +import seedu.address.model.TaskBook; import seedu.address.model.UserPrefs; import seedu.address.model.person.Person; import seedu.address.storage.JsonAddressBookStorage; +import seedu.address.storage.JsonTaskBookStorage; import seedu.address.storage.JsonUserPrefsStorage; import seedu.address.storage.StorageManager; +import seedu.address.storage.TaskBookStorage; import seedu.address.testutil.PersonBuilder; + public class LogicManagerTest { private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); @@ -46,7 +51,9 @@ public void setUp() { JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + TaskBookStorage taskBookStorage = + new JsonTaskBookStorage(temporaryFolder.resolve("taskBook.json")); + StorageManager storage = new StorageManager(addressBookStorage, taskBookStorage, userPrefsStorage); logic = new LogicManager(model, storage); } @@ -58,14 +65,14 @@ public void execute_invalidCommandFormat_throwsParseException() { @Test public void execute_commandExecutionError_throwsCommandException() { - String deleteCommand = "delete 9"; + String deleteCommand = "del-p 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); + String listCommand = ListPersonCommand.COMMAND_WORD; + assertCommandSuccess(listCommand, ListPersonCommand.MESSAGE_SUCCESS, model); } @Test @@ -75,11 +82,12 @@ public void execute_storageThrowsIoException_throwsCommandException() { new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + TaskBookStorage taskBookStorage = null; //to be implemented + StorageManager storage = new StorageManager(addressBookStorage, taskBookStorage, userPrefsStorage); logic = new LogicManager(model, storage); // Execute add command - String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + String addCommand = AddPersonCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY; Person expectedPerson = new PersonBuilder(AMY).withTags().build(); ModelManager expectedModel = new ModelManager(); @@ -129,7 +137,8 @@ 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.getAddressBook(), + new TaskBook(), new StrategyBoard(), new UserPrefs()); // Not fully implemented assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); } diff --git a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java b/src/test/java/seedu/address/logic/commands/ClearCommandTest.java deleted file mode 100644 index 80d9110c03a..00000000000 --- a/src/test/java/seedu/address/logic/commands/ClearCommandTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -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; - -public class ClearCommandTest { - - @Test - public void execute_emptyAddressBook_success() { - Model model = new ModelManager(); - Model expectedModel = new ModelManager(); - - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); - } - - @Test - public void execute_nonEmptyAddressBook_success() { - Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel.setAddressBook(new AddressBook()); - - assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/address/logic/commands/CommandResultTest.java index 4f3eb46e9ef..990a2bb477d 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/address/logic/commands/CommandResultTest.java @@ -4,6 +4,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.commons.core.Tabs.CONTACT_TAB; +import static seedu.address.commons.core.Tabs.DEFAULT; import org.junit.jupiter.api.Test; @@ -14,7 +16,7 @@ public void equals() { // same values -> returns true assertTrue(commandResult.equals(new CommandResult("feedback"))); - assertTrue(commandResult.equals(new CommandResult("feedback", false, false))); + assertTrue(commandResult.equals(new CommandResult("feedback", false, false, DEFAULT, false, null, false))); // same object -> returns true assertTrue(commandResult.equals(commandResult)); @@ -29,10 +31,13 @@ public void equals() { assertFalse(commandResult.equals(new CommandResult("different"))); // different showHelp value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", true, false))); + assertFalse(commandResult.equals(new CommandResult("feedback", true, false, DEFAULT, false, null, false))); // different exit value -> returns false - assertFalse(commandResult.equals(new CommandResult("feedback", false, true))); + assertFalse(commandResult.equals(new CommandResult("feedback", false, true, DEFAULT, false, null, false))); + + // different tab pane value -> returns false + assertFalse(commandResult.equals(new CommandResult("feedback", false, true, CONTACT_TAB, false, null, false))); } @Test @@ -46,9 +51,23 @@ public void hashcode() { assertNotEquals(commandResult.hashCode(), new CommandResult("different").hashCode()); // different showHelp value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", true, false, + DEFAULT, false, null, false).hashCode()); // different exit value -> returns different hashcode - assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true).hashCode()); + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, true, + DEFAULT, false, null, false).hashCode()); + + // different tab pane value -> returns different hashcode + assertNotEquals(commandResult.hashCode(), new CommandResult("feedback", false, false, + CONTACT_TAB, false, null, false).hashCode()); + } + + @Test + public void toStringTest() { + CommandResult commandResult = new CommandResult("feedback", true, false, DEFAULT, false, null, false); + // same values -> returns same String + assertEquals(commandResult.toString(), + new CommandResult("feedback", true, false, DEFAULT, false, null, false).toString()); } } diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java index 643a1d08069..5a8b18be554 100644 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java @@ -3,9 +3,12 @@ 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_DATE; import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ENDTIME; 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_STARTTIME; import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import static seedu.address.testutil.Assert.assertThrows; @@ -14,12 +17,17 @@ import java.util.List; import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.EditPersonDescriptor; +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.logic.commands.task.exceptions.CommandException; import seedu.address.model.AddressBook; import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.name.PersonNameContainsKeywordsPredicate; +import seedu.address.model.name.TaskNameContainsKeywordsPredicate; import seedu.address.model.person.Person; +import seedu.address.model.task.Task; import seedu.address.testutil.EditPersonDescriptorBuilder; +import seedu.address.testutil.EditTaskDescriptorBuilder; /** * Contains helper methods for testing commands. @@ -36,6 +44,23 @@ public class CommandTestUtil { 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_NOTE_AMY = "Amy"; + public static final String VALID_NOTE_BOB = "Bob"; + + public static final String VALID_NAME_TASK1 = "Task 1"; + public static final String VALID_NAME_TASK2 = "Task 2"; + public static final String VALID_DATE_TASK1 = "16-05-2022"; + public static final String VALID_DATE_TASK2 = "04-08-2022"; + public static final String VALID_STARTTIME_TASK1 = "09:00"; + public static final String VALID_STARTTIME_TASK2 = "10:00"; + public static final String VALID_ENDTIME_TASK1 = "12:00"; + public static final String VALID_ENDTIME_TASK2 = "13:00"; + public static final String VALID_TAG_EVENT = "Event"; + public static final String VALID_TAG_MEET = "Meet"; + public static final String VALID_PERSON1 = "Amy Bee"; + public static final String VALID_PERSON2 = "Bob Choo"; + + public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; @@ -48,34 +73,62 @@ public class CommandTestUtil { 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 NAME_DESC_TASK1 = " " + PREFIX_NAME + VALID_NAME_TASK1; + public static final String NAME_DESC_TASK2 = " " + PREFIX_NAME + VALID_NAME_TASK2; + public static final String DATE_DESC_TASK1 = " " + PREFIX_DATE + VALID_DATE_TASK1; + public static final String DATE_DESC_TASK2 = " " + PREFIX_DATE + VALID_DATE_TASK2; + public static final String STARTTIME_DESC_TASK1 = " " + PREFIX_STARTTIME + VALID_STARTTIME_TASK1; + public static final String STARTTIME_DESC_TASK2 = " " + PREFIX_STARTTIME + VALID_STARTTIME_TASK2; + public static final String ENDTIME_DESC_TASK1 = " " + PREFIX_ENDTIME + VALID_ENDTIME_TASK1; + public static final String ENDTIME_DESC_TASK2 = " " + PREFIX_ENDTIME + VALID_ENDTIME_TASK2; + public static final String TAG_DESC_EVENT = " " + PREFIX_TAG + VALID_TAG_EVENT; + public static final String TAG_DESC_MEET = " " + PREFIX_TAG + VALID_TAG_MEET; + 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_TASK_NAME_DESC = " " + PREFIX_NAME + "$hareholder"; // '$' not allowed in names + public static final String INVALID_TASK_DATE_DESC = " " + PREFIX_DATE + "24/02/2022"; // DD-MM-YYYY format required + public static final String INVALID_TASK_STARTTIME_DESC = " " + PREFIX_STARTTIME + "2pm"; // MM:HH format required + public static final String INVALID_TASK_ENDTIME_DESC = " " + PREFIX_ENDTIME + "1030"; // MM:HH format required + public static final String INVALID_TASK_TAG_DESC = " " + PREFIX_TAG + "test!"; // '!' not allowed in tags + + public static final String PREAMBLE_WHITESPACE = "\t \r \n"; public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; - public static final EditCommand.EditPersonDescriptor DESC_AMY; - public static final EditCommand.EditPersonDescriptor DESC_BOB; + public static final EditPersonDescriptor DESC_AMY; + public static final EditPersonDescriptor DESC_BOB; + + public static final EditTaskDescriptor DESC_TASK1; + public static final EditTaskDescriptor DESC_TASK2; 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(); + .withTags(VALID_TAG_FRIEND).withMiscellaneous(VALID_NOTE_AMY).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(); + .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).withMiscellaneous(VALID_NOTE_BOB).build(); + DESC_TASK1 = new EditTaskDescriptorBuilder().withName(VALID_NAME_TASK1) + .withDate(VALID_DATE_TASK1).withStartTime(VALID_STARTTIME_TASK1) + .withEndTime(VALID_ENDTIME_TASK1).withTags(VALID_TAG_EVENT).withPersons(VALID_PERSON1).build(); + DESC_TASK2 = new EditTaskDescriptorBuilder().withName(VALID_NAME_TASK2) + .withDate(VALID_DATE_TASK2).withStartTime(VALID_STARTTIME_TASK2) + .withEndTime(VALID_ENDTIME_TASK2).withTags(VALID_TAG_EVENT).withPersons(VALID_PERSON2).build(); } + /** * Executes the given {@code command}, confirms that
* - the returned {@link CommandResult} matches {@code expectedCommandResult}
* - the {@code actualModel} matches {@code expectedModel} */ public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult, - Model expectedModel) { + Model expectedModel) { try { CommandResult result = command.execute(actualModel); assertEquals(expectedCommandResult, result); @@ -90,7 +143,7 @@ public static void assertCommandSuccess(Command command, Model actualModel, Comm * that takes a string {@code expectedMessage}. */ public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, - Model expectedModel) { + Model expectedModel) { CommandResult expectedCommandResult = new CommandResult(expectedMessage); assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); } @@ -111,6 +164,7 @@ public static void assertCommandFailure(Command command, Model actualModel, Stri assertEquals(expectedAddressBook, actualModel.getAddressBook()); assertEquals(expectedFilteredList, actualModel.getFilteredPersonList()); } + /** * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the * {@code model}'s address book. @@ -120,9 +174,23 @@ public static void showPersonAtIndex(Model model, Index targetIndex) { Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased()); final String[] splitName = person.getName().fullName.split("\\s+"); - model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); + model.updateFilteredPersonList(new PersonNameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); assertEquals(1, model.getFilteredPersonList().size()); } + /** + * Updates {@code model}'s filtered list to show only the tasks with the given {@code Name} in the + * {@code model}'s task book. + */ + public static void showTaskAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getFilteredTaskList().size()); + + Task task = model.getFilteredTaskList().get(targetIndex.getZeroBased()); + final String[] splitName = task.getName().fullName.split("\\s+"); + model.updateFilteredTaskList(new TaskNameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); + + assertEquals(1, model.getFilteredTaskList().size()); // The task book contains only unique names + } + } diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java index 9533c473875..3801b68fd85 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/ExitCommandTest.java @@ -1,5 +1,6 @@ package seedu.address.logic.commands; +import static seedu.address.commons.core.Tabs.DEFAULT; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; import static seedu.address.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; @@ -14,7 +15,8 @@ public class ExitCommandTest { @Test public void execute_exit_success() { - CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); + CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true, + DEFAULT, false, null, false); assertCommandSuccess(new ExitCommand(), model, expectedCommandResult, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindCommandTest.java deleted file mode 100644 index 9b15db28bbb..00000000000 --- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java +++ /dev/null @@ -1,83 +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.commons.core.Messages.MESSAGE_PERSONS_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 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.person.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()); - - @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); - - // same object -> returns true - assertTrue(findFirstCommand.equals(findFirstCommand)); - - // same values -> returns true - FindCommand findFirstCommandCopy = new FindCommand(firstPredicate); - assertTrue(findFirstCommand.equals(findFirstCommandCopy)); - - // different types -> returns false - assertFalse(findFirstCommand.equals(1)); - - // null -> returns false - assertFalse(findFirstCommand.equals(null)); - - // different person -> returns false - assertFalse(findFirstCommand.equals(findSecondCommand)); - } - - @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); - assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Collections.emptyList(), model.getFilteredPersonList()); - } - - @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); - assertCommandSuccess(command, model, expectedMessage, expectedModel); - assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList()); - } - - /** - * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}. - */ - private NameContainsKeywordsPredicate preparePredicate(String userInput) { - return new NameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); - } -} diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java index 4904fc4352e..761453d457e 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/HelpCommandTest.java @@ -1,5 +1,6 @@ package seedu.address.logic.commands; +import static seedu.address.commons.core.Tabs.DEFAULT; import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; @@ -14,7 +15,8 @@ public class HelpCommandTest { @Test public void execute_help_success() { - CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false); + CommandResult expectedCommandResult = new CommandResult(SHOWING_HELP_MESSAGE, true, false, + DEFAULT, false, null, false); assertCommandSuccess(new HelpCommand(), model, expectedCommandResult, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/person/AddPersonCommandIntegrationTest.java similarity index 57% rename from src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java rename to src/test/java/seedu/address/logic/commands/person/AddPersonCommandIntegrationTest.java index cb8714bb055..b2e15dbc9b1 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java +++ b/src/test/java/seedu/address/logic/commands/person/AddPersonCommandIntegrationTest.java @@ -1,14 +1,16 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import seedu.address.logic.commands.CommandTestUtil; import seedu.address.model.Model; import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; import seedu.address.model.UserPrefs; import seedu.address.model.person.Person; import seedu.address.testutil.PersonBuilder; @@ -16,30 +18,31 @@ /** * Contains integration tests (interaction with the Model) for {@code AddCommand}. */ -public class AddCommandIntegrationTest { +public class AddPersonCommandIntegrationTest { private Model model; @BeforeEach public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), new StrategyBoard(), new UserPrefs()); } @Test public void execute_newPerson_success() { Person validPerson = new PersonBuilder().build(); - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + Model expectedModel = new ModelManager(model.getAddressBook(), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); expectedModel.addPerson(validPerson); - assertCommandSuccess(new AddCommand(validPerson), model, - String.format(AddCommand.MESSAGE_SUCCESS, validPerson), expectedModel); + CommandTestUtil.assertCommandSuccess(new AddPersonCommand(validPerson), model, + String.format(AddPersonCommand.MESSAGE_SUCCESS, validPerson), expectedModel); } @Test public void execute_duplicatePerson_throwsCommandException() { Person personInList = model.getAddressBook().getPersonList().get(0); - assertCommandFailure(new AddCommand(personInList), model, AddCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandFailure(new AddPersonCommand(personInList), model, AddPersonCommand.MESSAGE_DUPLICATE_PERSON); } } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/person/AddPersonCommandTest.java similarity index 56% rename from src/test/java/seedu/address/logic/commands/AddCommandTest.java rename to src/test/java/seedu/address/logic/commands/person/AddPersonCommandTest.java index 5865713d5dd..559a6a55031 100644 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/person/AddPersonCommandTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; import static java.util.Objects.requireNonNull; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -15,19 +15,24 @@ import javafx.collections.ObservableList; import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.commands.CommandResult; +import seedu.address.logic.commands.task.exceptions.CommandException; import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.ReadOnlyStrategyBoard; +import seedu.address.model.ReadOnlyTaskBook; import seedu.address.model.ReadOnlyUserPrefs; import seedu.address.model.person.Person; +import seedu.address.model.strategy.Player; +import seedu.address.model.task.Task; import seedu.address.testutil.PersonBuilder; -public class AddCommandTest { +public class AddPersonCommandTest { @Test public void constructor_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new AddCommand(null)); + assertThrows(NullPointerException.class, () -> new AddPersonCommand(null)); } @Test @@ -35,33 +40,34 @@ public void execute_personAcceptedByModel_addSuccessful() throws Exception { ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded(); Person validPerson = new PersonBuilder().build(); - CommandResult commandResult = new AddCommand(validPerson).execute(modelStub); + CommandResult commandResult = new AddPersonCommand(validPerson).execute(modelStub); - assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, validPerson), commandResult.getFeedbackToUser()); + assertEquals(String.format(AddPersonCommand.MESSAGE_SUCCESS, validPerson), commandResult.getFeedbackToUser()); assertEquals(Arrays.asList(validPerson), modelStub.personsAdded); } @Test public void execute_duplicatePerson_throwsCommandException() { Person validPerson = new PersonBuilder().build(); - AddCommand addCommand = new AddCommand(validPerson); + AddPersonCommand addCommand = new AddPersonCommand(validPerson); ModelStub modelStub = new ModelStubWithPerson(validPerson); - assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub)); + assertThrows(CommandException.class, + AddPersonCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub)); } @Test public void equals() { Person alice = new PersonBuilder().withName("Alice").build(); Person bob = new PersonBuilder().withName("Bob").build(); - AddCommand addAliceCommand = new AddCommand(alice); - AddCommand addBobCommand = new AddCommand(bob); + AddPersonCommand addAliceCommand = new AddPersonCommand(alice); + AddPersonCommand addBobCommand = new AddPersonCommand(bob); // same object -> returns true assertTrue(addAliceCommand.equals(addAliceCommand)); // same values -> returns true - AddCommand addAliceCommandCopy = new AddCommand(alice); + AddPersonCommand addAliceCommandCopy = new AddPersonCommand(alice); assertTrue(addAliceCommand.equals(addAliceCommandCopy)); // different types -> returns false @@ -108,6 +114,16 @@ public void setAddressBookFilePath(Path addressBookFilePath) { throw new AssertionError("This method should not be called."); } + @Override + public Path getTaskBookFilePath() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setTaskBookFilePath(Path taskBookFilePath) { + throw new AssertionError("This method should not be called."); + } + @Override public void addPerson(Person person) { throw new AssertionError("This method should not be called."); @@ -143,10 +159,100 @@ public ObservableList getFilteredPersonList() { throw new AssertionError("This method should not be called."); } + @Override + public ObservableList getUnfilteredPersonList() { + throw new AssertionError("This method should not be called."); + } + @Override public void updateFilteredPersonList(Predicate predicate) { throw new AssertionError("This method should not be called."); } + @Override + public void setTaskBook(ReadOnlyTaskBook taskBook) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyTaskBook getTaskBook() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasTask(Task task) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deleteTask(Task target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addTask(Task task) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setTask(Task target, Task editedTask) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredTaskList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getUnfilteredTaskList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredTaskList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setStrategyBoard(ReadOnlyStrategyBoard strategyBoard) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ReadOnlyStrategyBoard getStrategyBoard() { + throw new AssertionError("This method should not be called."); + } + + @Override + public boolean hasPlayer(Player player) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void deletePlayer(Player target) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void addPlayer(Player player) { + throw new AssertionError("This method should not be called."); + } + + @Override + public void setPlayer(Player target, Player editedPlayer) { + throw new AssertionError("This method should not be called."); + } + + @Override + public ObservableList getFilteredPlayerList() { + throw new AssertionError("This method should not be called."); + } + + @Override + public void updateFilteredPlayerList(Predicate predicate) { + throw new AssertionError("This method should not be called."); + } + } /** diff --git a/src/test/java/seedu/address/logic/commands/person/AddPersonTagCommandTest.java b/src/test/java/seedu/address/logic/commands/person/AddPersonTagCommandTest.java new file mode 100644 index 00000000000..4da13714e99 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/AddPersonTagCommandTest.java @@ -0,0 +1,88 @@ +package seedu.address.logic.commands.person; + +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.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.person.Person; +import seedu.address.model.tag.Tag; +import seedu.address.testutil.PersonBuilder; + +class AddPersonTagCommandTest { + // Test tags + // Because PersonBuilder#withTags takes String ... instead of Set + private static final String TAG1 = "TAG1"; + + // Test model + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + void execute_addTagCommandUnfilteredList_success() { + Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + + // Adding 1 more tag to the editedPerson + Set firstPersonTags = new HashSet<>(firstPerson.getTags()); // Copy of Set of ALICE (first person) + firstPersonTags.add(new Tag(TAG1)); + + // Convert Set to array for PersonBuilder#withTags + String[] firstPersonTagsStringArray = firstPersonTags + .stream() + .map(x -> x.tagName) + .toArray(String[]::new); + + Person editedPerson = new PersonBuilder(firstPerson).withTags(firstPersonTagsStringArray).build(); + + AddPersonTagCommand addTagCommand = new AddPersonTagCommand(INDEX_FIRST_PERSON, TAG1); + + String expectedMessage = String.format(AddPersonTagCommand.MESSAGE_ADD_TAG_SUCCESS, TAG1); + + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setPerson(firstPerson, editedPerson); + + assertCommandSuccess(addTagCommand, model, expectedMessage, expectedModel); + } + + @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()); + + AddPersonTagCommand addTagCommand = new AddPersonTagCommand(outOfBoundIndex, TAG1); + + assertCommandFailure(addTagCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final AddPersonTagCommand command = new AddPersonTagCommand(INDEX_FIRST_PERSON, TAG1); + + // If they are the same objects, they are equal + assertTrue(command.equals(command)); + + } +} diff --git a/src/test/java/seedu/address/logic/commands/person/ClearPersonCommandTest.java b/src/test/java/seedu/address/logic/commands/person/ClearPersonCommandTest.java new file mode 100644 index 00000000000..b413aae22a9 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/ClearPersonCommandTest.java @@ -0,0 +1,57 @@ +package seedu.address.logic.commands.person; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.SECOND_TASK; +import static seedu.address.testutil.TypicalTasks.THIRD_TASK; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.CommandTestUtil; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.Task; +import seedu.address.testutil.TaskBuilder; + +public class ClearPersonCommandTest { + + @Test + public void execute_emptyAddressBook_success() { + Model model = new ModelManager(); + Model expectedModel = new ModelManager(); + + CommandTestUtil.assertCommandSuccess(new ClearPersonCommand(), + model, ClearPersonCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nonEmptyAddressBook_success() { + Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + Model expectedModel = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + Task editedFirstTask = new TaskBuilder(FIRST_TASK).withPersons().build(); + Task editedSecondTask = new TaskBuilder(SECOND_TASK).withPersons().build(); + Task editedThirdTask = new TaskBuilder(THIRD_TASK).withPersons().build(); + List tasks = new ArrayList<>(Arrays.asList(editedFirstTask, editedSecondTask, editedThirdTask)); + TaskBook newTaskBook = new TaskBook(); + newTaskBook.setTasks(tasks); + + expectedModel.setTaskBook(newTaskBook); + expectedModel.setAddressBook(new AddressBook()); + + assertCommandSuccess(new ClearPersonCommand(), model, ClearPersonCommand.MESSAGE_SUCCESS, expectedModel); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/person/DeletePersonCommandTest.java similarity index 59% rename from src/test/java/seedu/address/logic/commands/DeleteCommandTest.java rename to src/test/java/seedu/address/logic/commands/person/DeletePersonCommandTest.java index 45a8c910ba1..63cd83ff83c 100644 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/person/DeletePersonCommandTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -7,42 +7,57 @@ 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.TypicalIndexes.TASK_FIRST_INDEX; +import static seedu.address.testutil.TypicalPersons.BENSON; import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; import org.junit.jupiter.api.Test; import seedu.address.commons.core.Messages; import seedu.address.commons.core.index.Index; +import seedu.address.logic.EditTaskDescriptor; import seedu.address.model.Model; import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; import seedu.address.model.UserPrefs; import seedu.address.model.person.Person; +import seedu.address.model.task.Task; +import seedu.address.testutil.EditTaskDescriptorBuilder; /** * Contains integration tests (interaction with the Model) and unit tests for * {@code DeleteCommand}. */ -public class DeleteCommandTest { +public class DeletePersonCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); @Test public void execute_validIndexUnfilteredList_success() { Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); + DeletePersonCommand deleteCommand = new DeletePersonCommand(INDEX_FIRST_PERSON); - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); + String expectedMessage = String.format(DeletePersonCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + ModelManager expectedModel = new ModelManager(model.getAddressBook(), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); expectedModel.deletePerson(personToDelete); + Task taskToAmend = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + EditTaskDescriptor editTaskDescriptor = + new EditTaskDescriptorBuilder().withPersons(BENSON.getName().fullName).build(); + Task editedTask = DeletePersonCommand.createEditedTask(taskToAmend, editTaskDescriptor); + expectedModel.setTask(taskToAmend, editedTask); + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); } @Test public void execute_invalidIndexUnfilteredList_throwsCommandException() { Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); + DeletePersonCommand deleteCommand = new DeletePersonCommand(outOfBoundIndex); assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } @@ -52,14 +67,21 @@ public void execute_validIndexFilteredList_success() { showPersonAtIndex(model, INDEX_FIRST_PERSON); Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); + DeletePersonCommand deleteCommand = new DeletePersonCommand(INDEX_FIRST_PERSON); - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); + String expectedMessage = String.format(DeletePersonCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + Model expectedModel = new ModelManager(model.getAddressBook(), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); expectedModel.deletePerson(personToDelete); showNoPerson(expectedModel); + Task taskToAmend = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + EditTaskDescriptor editTaskDescriptor = + new EditTaskDescriptorBuilder().withPersons(BENSON.getName().fullName).build(); + Task editedTask = DeletePersonCommand.createEditedTask(taskToAmend, editTaskDescriptor); + expectedModel.setTask(taskToAmend, editedTask); + assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); } @@ -71,21 +93,21 @@ public void execute_invalidIndexFilteredList_throwsCommandException() { // ensures that outOfBoundIndex is still in bounds of address book list assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); + DeletePersonCommand deleteCommand = new DeletePersonCommand(outOfBoundIndex); assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } @Test public void equals() { - DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON); - DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON); + DeletePersonCommand deleteFirstCommand = new DeletePersonCommand(INDEX_FIRST_PERSON); + DeletePersonCommand deleteSecondCommand = new DeletePersonCommand(INDEX_SECOND_PERSON); // same object -> returns true assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); // same values -> returns true - DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON); + DeletePersonCommand deleteFirstCommandCopy = new DeletePersonCommand(INDEX_FIRST_PERSON); assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); // different types -> returns false diff --git a/src/test/java/seedu/address/logic/commands/person/DeletePersonTagCommandTest.java b/src/test/java/seedu/address/logic/commands/person/DeletePersonTagCommandTest.java new file mode 100644 index 00000000000..ca2b54f5ad3 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/DeletePersonTagCommandTest.java @@ -0,0 +1,88 @@ +package seedu.address.logic.commands.person; + +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.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.person.Person; +import seedu.address.model.tag.Tag; +import seedu.address.testutil.PersonBuilder; + +class DeletePersonTagCommandTest { + // Test tags + // Because PersonBuilder#withTags takes String ... instead of Set + private static final String TAG_1 = "friends"; // ALICE has a tag called "friends" + + // Test model + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + void execute_deleteTagCommandUnfilteredList_success() { + Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + + // Removing 1 tag to the editedPerson + Set firstPersonTags = new HashSet<>(firstPerson.getTags()); // Copy of Set of ALICE (first person) + firstPersonTags.remove(new Tag(TAG_1)); + + // Convert Set to array for PersonBuilder#withTags + String[] firstPersonTagsStringArray = firstPersonTags + .stream() + .map(x -> x.tagName) + .toArray(String[]::new); + + Person editedPerson = new PersonBuilder(firstPerson).withTags(firstPersonTagsStringArray).build(); + + DeletePersonTagCommand deleteTagCommand = new DeletePersonTagCommand(INDEX_FIRST_PERSON, TAG_1); + + String expectedMessage = String.format(DeletePersonTagCommand.MESSAGE_DELETE_TAG_SUCCESS, TAG_1); + + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setPerson(firstPerson, editedPerson); + + assertCommandSuccess(deleteTagCommand, model, expectedMessage, expectedModel); + } + + @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()); + + AddPersonTagCommand addTagCommand = new AddPersonTagCommand(outOfBoundIndex, TAG_1); + + assertCommandFailure(addTagCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final DeletePersonTagCommand command = new DeletePersonTagCommand(INDEX_FIRST_PERSON, TAG_1); + + // If they are the same objects, they are equal + assertTrue(command.equals(command)); + + } +} diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/person/EditPersonCommandTest.java similarity index 62% rename from src/test/java/seedu/address/logic/commands/EditCommandTest.java rename to src/test/java/seedu/address/logic/commands/person/EditPersonCommandTest.java index 214c6c2507b..59bd91fafed 100644 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/person/EditPersonCommandTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -12,39 +12,55 @@ 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.TypicalIndexes.TASK_FIRST_INDEX; +import static seedu.address.testutil.TypicalPersons.BENSON; import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; import org.junit.jupiter.api.Test; import seedu.address.commons.core.Messages; import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.EditPersonDescriptor; +import seedu.address.logic.EditTaskDescriptor; import seedu.address.model.AddressBook; import seedu.address.model.Model; import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; import seedu.address.model.UserPrefs; import seedu.address.model.person.Person; +import seedu.address.model.task.Task; import seedu.address.testutil.EditPersonDescriptorBuilder; +import seedu.address.testutil.EditTaskDescriptorBuilder; import seedu.address.testutil.PersonBuilder; /** * Contains integration tests (interaction with the Model) and unit tests for EditCommand. */ -public class EditCommandTest { +public class EditPersonCommandTest { - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); @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); + EditPersonCommand editCommand = new EditPersonCommand(INDEX_FIRST_PERSON, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(EditPersonCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); + Task taskToAmend = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + EditTaskDescriptor editTaskDescriptor = + new EditTaskDescriptorBuilder() + .withPersons(editedPerson.getName().fullName, BENSON.getName().fullName).build(); + Task editedTask = DeletePersonCommand.createEditedTask(taskToAmend, editTaskDescriptor); + expectedModel.setTask(taskToAmend, editedTask); + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @@ -59,11 +75,12 @@ public void execute_someFieldsSpecifiedUnfilteredList_success() { EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build(); - EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); + EditPersonCommand editCommand = new EditPersonCommand(indexLastPerson, descriptor); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(EditPersonCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); expectedModel.setPerson(lastPerson, editedPerson); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); @@ -71,12 +88,13 @@ public void execute_someFieldsSpecifiedUnfilteredList_success() { @Test public void execute_noFieldSpecifiedUnfilteredList_success() { - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); + EditPersonCommand editCommand = new EditPersonCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(EditPersonCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @@ -87,14 +105,22 @@ public void execute_filteredList_success() { 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, + EditPersonCommand editCommand = new EditPersonCommand(INDEX_FIRST_PERSON, new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); + String expectedMessage = String.format(EditPersonCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); + Task taskToAmend = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + EditTaskDescriptor editTaskDescriptor = + new EditTaskDescriptorBuilder() + .withPersons(editedPerson.getName().fullName, BENSON.getName().fullName).build(); + Task editedTask = DeletePersonCommand.createEditedTask(taskToAmend, editTaskDescriptor); + expectedModel.setTask(taskToAmend, editedTask); + assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); } @@ -102,9 +128,9 @@ public void execute_filteredList_success() { 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); + EditPersonCommand editCommand = new EditPersonCommand(INDEX_SECOND_PERSON, descriptor); - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandFailure(editCommand, model, EditPersonCommand.MESSAGE_DUPLICATE_PERSON); } @Test @@ -113,17 +139,17 @@ public void execute_duplicatePersonFilteredList_failure() { // 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, + EditPersonCommand editCommand = new EditPersonCommand(INDEX_FIRST_PERSON, new EditPersonDescriptorBuilder(personInList).build()); - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); + assertCommandFailure(editCommand, model, EditPersonCommand.MESSAGE_DUPLICATE_PERSON); } @Test public void execute_invalidPersonIndexUnfilteredList_failure() { Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(outOfBoundIndex, descriptor); + EditPersonCommand editCommand = new EditPersonCommand(outOfBoundIndex, descriptor); assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); } @@ -139,7 +165,7 @@ public void execute_invalidPersonIndexFilteredList_failure() { // ensures that outOfBoundIndex is still in bounds of address book list assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size()); - EditCommand editCommand = new EditCommand(outOfBoundIndex, + EditPersonCommand editCommand = new EditPersonCommand(outOfBoundIndex, new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); @@ -147,11 +173,11 @@ public void execute_invalidPersonIndexFilteredList_failure() { @Test public void equals() { - final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY); + final EditPersonCommand standardCommand = new EditPersonCommand(INDEX_FIRST_PERSON, DESC_AMY); // same values -> returns true EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY); - EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, copyDescriptor); + EditPersonCommand commandWithSameValues = new EditPersonCommand(INDEX_FIRST_PERSON, copyDescriptor); assertTrue(standardCommand.equals(commandWithSameValues)); // same object -> returns true @@ -161,13 +187,13 @@ public void equals() { assertFalse(standardCommand.equals(null)); // different types -> returns false - assertFalse(standardCommand.equals(new ClearCommand())); + assertFalse(standardCommand.equals(new ClearPersonCommand())); // different index -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY))); + assertFalse(standardCommand.equals(new EditPersonCommand(INDEX_SECOND_PERSON, DESC_AMY))); // different descriptor -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_FIRST_PERSON, DESC_BOB))); + assertFalse(standardCommand.equals(new EditPersonCommand(INDEX_FIRST_PERSON, DESC_BOB))); } } diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/person/EditPersonDescriptorTest.java similarity index 86% rename from src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java rename to src/test/java/seedu/address/logic/commands/person/EditPersonDescriptorTest.java index e0288792e72..f57485a0b93 100644 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ b/src/test/java/seedu/address/logic/commands/person/EditPersonDescriptorTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -7,12 +7,13 @@ 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_NOTE_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.logic.EditPersonDescriptor; import seedu.address.testutil.EditPersonDescriptorBuilder; public class EditPersonDescriptorTest { @@ -54,5 +55,9 @@ public void equals() { // different tags -> returns false editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); assertFalse(DESC_AMY.equals(editedAmy)); + + // different notes -> returns false + editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withMiscellaneous(VALID_NOTE_BOB).build(); + assertFalse(DESC_AMY.equals(editedAmy)); } } diff --git a/src/test/java/seedu/address/logic/commands/person/FindPersonCommandTest.java b/src/test/java/seedu/address/logic/commands/person/FindPersonCommandTest.java new file mode 100644 index 00000000000..6b8530121c7 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/FindPersonCommandTest.java @@ -0,0 +1,106 @@ +package seedu.address.logic.commands.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.commons.core.Messages.MESSAGE_PERSONS_LISTED_OVERVIEW; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalPersons.ALICE; +import static seedu.address.testutil.TypicalPersons.BENSON; +import static seedu.address.testutil.TypicalPersons.DANIEL; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.name.PersonNameContainsKeywordsPredicate; +import seedu.address.model.tag.PersonTagContainsKeywordsPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FindCommand}. + */ +public class FindPersonCommandTest { + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void equals() { + + PersonNameContainsKeywordsPredicate firstPredicate = + new PersonNameContainsKeywordsPredicate(Collections.singletonList("first")); + PersonNameContainsKeywordsPredicate secondPredicate = + new PersonNameContainsKeywordsPredicate(Collections.singletonList("second")); + PersonTagContainsKeywordsPredicate thirdPredicate = + new PersonTagContainsKeywordsPredicate(Collections.singletonList("third")); + PersonTagContainsKeywordsPredicate fourthPredicate = + new PersonTagContainsKeywordsPredicate(Collections.singletonList("fourth")); + + FindPersonCommand findFirstCommand = new FindPersonCommand(firstPredicate, thirdPredicate); + FindPersonCommand findSecondCommand = new FindPersonCommand(secondPredicate, fourthPredicate); + + // same object -> returns true + assertTrue(findFirstCommand.equals(findFirstCommand)); + + // same values -> returns true + FindPersonCommand findFirstCommandCopy = new FindPersonCommand(firstPredicate, thirdPredicate); + assertTrue(findFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(findFirstCommand.equals(1)); + + // null -> returns false + assertFalse(findFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(findFirstCommand.equals(findSecondCommand)); + } + + @Test + public void execute_zeroKeywords_noPersonAndTagFound() { + List nameList = Arrays.asList(); + List tagList = Arrays.asList(); + String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0); + PersonNameContainsKeywordsPredicate namePredicate = prepareNamePredicate(nameList); + PersonTagContainsKeywordsPredicate tagPredicate = prepareTagPredicate(tagList); + FindPersonCommand command = new FindPersonCommand(namePredicate, tagPredicate); + expectedModel.updateFilteredPersonList(namePredicate.or(tagPredicate)); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredPersonList()); + } + + @Test + public void execute_multipleKeywords_multiplePersonsAndTagsFound() { + List nameList = Arrays.asList("Alice", "Bob"); + List tagList = Arrays.asList("friends", "neighbours"); + String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 3); + PersonNameContainsKeywordsPredicate namePredicate = prepareNamePredicate(nameList); + PersonTagContainsKeywordsPredicate tagPredicate = prepareTagPredicate(tagList); + FindPersonCommand command = new FindPersonCommand(namePredicate, tagPredicate); + expectedModel.updateFilteredPersonList(namePredicate.or(tagPredicate)); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(ALICE, BENSON, DANIEL), model.getFilteredPersonList()); + } + + /** + * Creates a {@code PersonNameContainsKeywordsPredicate} using {@code nameList}. + */ + private PersonNameContainsKeywordsPredicate prepareNamePredicate(List nameList) { + return new PersonNameContainsKeywordsPredicate(nameList); + } + /** + * Creates a {@code PersonTagContainsKeywordsPredicate} using {@code tagSet}. + */ + private PersonTagContainsKeywordsPredicate prepareTagPredicate(List tagList) { + return new PersonTagContainsKeywordsPredicate(tagList); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/person/ListPersonCommandTest.java similarity index 58% rename from src/test/java/seedu/address/logic/commands/ListCommandTest.java rename to src/test/java/seedu/address/logic/commands/person/ListPersonCommandTest.java index 435ff1f7275..dbd71d6fb9c 100644 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ b/src/test/java/seedu/address/logic/commands/person/ListPersonCommandTest.java @@ -1,39 +1,45 @@ -package seedu.address.logic.commands; +package seedu.address.logic.commands.person; 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 static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import seedu.address.logic.commands.CommandTestUtil; import seedu.address.model.Model; import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; import seedu.address.model.UserPrefs; /** * Contains integration tests (interaction with the Model) and unit tests for ListCommand. */ -public class ListCommandTest { +public class ListPersonCommandTest { private Model model; private Model expectedModel; @BeforeEach public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); } @Test public void execute_listIsNotFiltered_showsSameList() { - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); + CommandTestUtil.assertCommandSuccess(new ListPersonCommand(), model, + ListPersonCommand.MESSAGE_SUCCESS, expectedModel); } @Test public void execute_listIsFiltered_showsEverything() { showPersonAtIndex(model, INDEX_FIRST_PERSON); - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); + assertCommandSuccess(new ListPersonCommand(), model, ListPersonCommand.MESSAGE_SUCCESS, expectedModel); } } diff --git a/src/test/java/seedu/address/logic/commands/person/SortStrengthCommandTest.java b/src/test/java/seedu/address/logic/commands/person/SortStrengthCommandTest.java new file mode 100644 index 00000000000..f94099f40b8 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/SortStrengthCommandTest.java @@ -0,0 +1,48 @@ +package seedu.address.logic.commands.person; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.testutil.TypicalPersons.getGenericAddressBookWithSortedStrengths; +import static seedu.address.testutil.TypicalPersons.getGenericAddressBookWithUnsortedStrengths; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.CommandTestUtil; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for SortStrengthCommand. + */ +public class SortStrengthCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getGenericAddressBookWithUnsortedStrengths(), new TaskBook(), + new StrategyBoard(), new UserPrefs()); + expectedModel = new ModelManager(getGenericAddressBookWithSortedStrengths(), + new TaskBook(), new StrategyBoard(), new UserPrefs()); + } + + @Test + public void execute_unsortedList_success() { + CommandTestUtil.assertCommandSuccess(new SortStrengthCommand(), model, + SortStrengthCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_emptyList_throwsCommandException() { + Model emptyModel = new ModelManager(new AddressBook(), new TaskBook(), + new StrategyBoard(), new UserPrefs()); + + assertCommandFailure(new SortStrengthCommand(), emptyModel, Messages.MESSAGE_EMPTY_PERSON_LIST); + } +} diff --git a/src/test/java/seedu/address/logic/commands/person/SortWeaknessCommandTest.java b/src/test/java/seedu/address/logic/commands/person/SortWeaknessCommandTest.java new file mode 100644 index 00000000000..de4e08f0bc8 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/SortWeaknessCommandTest.java @@ -0,0 +1,48 @@ +package seedu.address.logic.commands.person; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.testutil.TypicalPersons.getGenericAddressBookWithSortedWeaknesses; +import static seedu.address.testutil.TypicalPersons.getGenericAddressBookWithUnsortedWeaknesses; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.CommandTestUtil; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for SortWeaknessCommand. + */ +public class SortWeaknessCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getGenericAddressBookWithUnsortedWeaknesses(), new TaskBook(), + new StrategyBoard(), new UserPrefs()); + expectedModel = new ModelManager(getGenericAddressBookWithSortedWeaknesses(), + new TaskBook(), new StrategyBoard(), new UserPrefs()); + } + + @Test + public void execute_unsortedList_success() { + CommandTestUtil.assertCommandSuccess(new SortWeaknessCommand(), model, + SortWeaknessCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_emptyList_throwsCommandException() { + Model emptyModel = new ModelManager(new AddressBook(), new TaskBook(), + new StrategyBoard(), new UserPrefs()); + + assertCommandFailure(new SortWeaknessCommand(), emptyModel, Messages.MESSAGE_EMPTY_PERSON_LIST); + } +} diff --git a/src/test/java/seedu/address/logic/commands/person/notecommand/AddMiscCommandTest.java b/src/test/java/seedu/address/logic/commands/person/notecommand/AddMiscCommandTest.java new file mode 100644 index 00000000000..8ff55845763 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/notecommand/AddMiscCommandTest.java @@ -0,0 +1,123 @@ +package seedu.address.logic.commands.person.notecommand; + +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.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.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.logic.commands.person.notecommands.AddMiscCommand; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; +import seedu.address.testutil.PersonBuilder; + +public class AddMiscCommandTest { + + private static final String NOTE_STUB_1 = "Some note 1"; + private static final String NOTE_STUB_2 = "Some note 2"; + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void execute_addMiscUnfilteredList_success() { + Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + Person editedPerson = new PersonBuilder(firstPerson).withMisc(NOTE_STUB_1).build(); + + AddMiscCommand addMiscCommand = new AddMiscCommand( + INDEX_FIRST_PERSON, + new Note(NOTE_STUB_1)); + + String expectedMessage = String.format( + AddMiscCommand.MESSAGE_SUCCESS, editedPerson); + + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setPerson(firstPerson, editedPerson); + + assertCommandSuccess(addMiscCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidPersonIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + AddMiscCommand addMiscCommand = new AddMiscCommand(outOfBoundIndex, new Note(VALID_NOTE_BOB)); + + assertCommandFailure(addMiscCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of address book + */ + @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()); + + AddMiscCommand addMiscCommand = new AddMiscCommand(outOfBoundIndex, new Note(VALID_NOTE_BOB)); + + assertCommandFailure(addMiscCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void execute_duplicateMisc_failure() { + Person person = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); + assert person.getMiscellaneous().size() > 0; + Note duplicateMisc = person.getMiscellaneous().get(0); + + AddMiscCommand addMiscCommand = new AddMiscCommand(INDEX_SECOND_PERSON, duplicateMisc); + + assertCommandFailure(addMiscCommand, model, Messages.MESSAGE_DUPLICATE_MISC); + } + + @Test + public void equals() { + final AddMiscCommand standardCommand = new AddMiscCommand(INDEX_FIRST_PERSON, + new Note(VALID_NOTE_AMY)); + + // same values -> returns true + AddMiscCommand commandWithSameValues = new AddMiscCommand(INDEX_FIRST_PERSON, + 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 ClearPersonCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new AddMiscCommand(INDEX_SECOND_PERSON, + new Note(VALID_NOTE_AMY)))); + + // different Note -> returns false + assertFalse(standardCommand.equals(new AddMiscCommand(INDEX_FIRST_PERSON, + new Note(VALID_NOTE_BOB)))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/person/notecommand/AddStrengthCommandTest.java b/src/test/java/seedu/address/logic/commands/person/notecommand/AddStrengthCommandTest.java new file mode 100644 index 00000000000..f088828b30b --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/notecommand/AddStrengthCommandTest.java @@ -0,0 +1,123 @@ +package seedu.address.logic.commands.person.notecommand; + +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.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.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.logic.commands.person.notecommands.AddStrengthCommand; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; +import seedu.address.testutil.PersonBuilder; + +public class AddStrengthCommandTest { + + private static final String NOTE_STUB_1 = "Some note 1"; + private static final String NOTE_STUB_2 = "Some note 2"; + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void execute_addStrengthUnfilteredList_success() { + Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + Person editedPerson = new PersonBuilder(firstPerson).withStrengths(NOTE_STUB_1).build(); + + AddStrengthCommand addStrengthCommand = new AddStrengthCommand( + INDEX_FIRST_PERSON, + new Note(NOTE_STUB_1)); + + String expectedMessage = String.format( + AddStrengthCommand.MESSAGE_SUCCESS, editedPerson); + + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setPerson(firstPerson, editedPerson); + + assertCommandSuccess(addStrengthCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidPersonIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + AddStrengthCommand addStrengthCommand = new AddStrengthCommand(outOfBoundIndex, new Note(VALID_NOTE_BOB)); + + assertCommandFailure(addStrengthCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of address book + */ + @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()); + + AddStrengthCommand addStrengthCommand = new AddStrengthCommand(outOfBoundIndex, new Note(VALID_NOTE_BOB)); + + assertCommandFailure(addStrengthCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void execute_duplicateStrength_failure() { + Person person = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); + assert person.getStrengths().size() > 0; + Note duplicateStrength = person.getStrengths().get(0); + + AddStrengthCommand addStrengthCommand = new AddStrengthCommand(INDEX_SECOND_PERSON, duplicateStrength); + + assertCommandFailure(addStrengthCommand, model, Messages.MESSAGE_DUPLICATE_STRENGTH); + } + + @Test + public void equals() { + final AddStrengthCommand standardCommand = new AddStrengthCommand(INDEX_FIRST_PERSON, + new Note(VALID_NOTE_AMY)); + + // same values -> returns true + AddStrengthCommand commandWithSameValues = new AddStrengthCommand(INDEX_FIRST_PERSON, + 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 ClearPersonCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new AddStrengthCommand(INDEX_SECOND_PERSON, + new Note(VALID_NOTE_AMY)))); + + // different Note -> returns false + assertFalse(standardCommand.equals(new AddStrengthCommand(INDEX_FIRST_PERSON, + new Note(VALID_NOTE_BOB)))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/person/notecommand/AddWeaknessCommandTest.java b/src/test/java/seedu/address/logic/commands/person/notecommand/AddWeaknessCommandTest.java new file mode 100644 index 00000000000..d1516710d0a --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/notecommand/AddWeaknessCommandTest.java @@ -0,0 +1,123 @@ +package seedu.address.logic.commands.person.notecommand; + +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.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.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.logic.commands.person.notecommands.AddWeaknessCommand; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; +import seedu.address.testutil.PersonBuilder; + +public class AddWeaknessCommandTest { + + private static final String NOTE_STUB_1 = "Some note 1"; + private static final String NOTE_STUB_2 = "Some note 2"; + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void execute_addWeaknessUnfilteredList_success() { + Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); + Person editedPerson = new PersonBuilder(firstPerson).withWeaknesses(NOTE_STUB_1).build(); + + AddWeaknessCommand addWeaknessCommand = new AddWeaknessCommand( + INDEX_FIRST_PERSON, + new Note(NOTE_STUB_1)); + + String expectedMessage = String.format( + AddWeaknessCommand.MESSAGE_SUCCESS, editedPerson); + + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setPerson(firstPerson, editedPerson); + + assertCommandSuccess(addWeaknessCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidPersonIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + AddWeaknessCommand addWeaknessCommand = new AddWeaknessCommand(outOfBoundIndex, new Note(VALID_NOTE_BOB)); + + assertCommandFailure(addWeaknessCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of address book + */ + @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()); + + AddWeaknessCommand addWeaknessCommand = new AddWeaknessCommand(outOfBoundIndex, new Note(VALID_NOTE_BOB)); + + assertCommandFailure(addWeaknessCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void execute_duplicateWeakness_failure() { + Person person = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); + assert person.getWeaknesses().size() > 0; + Note duplicateWeakness = person.getWeaknesses().get(0); + + AddWeaknessCommand addWeaknessCommand = new AddWeaknessCommand(INDEX_SECOND_PERSON, duplicateWeakness); + + assertCommandFailure(addWeaknessCommand, model, Messages.MESSAGE_DUPLICATE_WEAKNESS); + } + + @Test + public void equals() { + final AddWeaknessCommand standardCommand = new AddWeaknessCommand(INDEX_FIRST_PERSON, + new Note(VALID_NOTE_AMY)); + + // same values -> returns true + AddWeaknessCommand commandWithSameValues = new AddWeaknessCommand(INDEX_FIRST_PERSON, + 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 ClearPersonCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new AddWeaknessCommand(INDEX_SECOND_PERSON, + new Note(VALID_NOTE_AMY)))); + + // different Note -> returns false + assertFalse(standardCommand.equals(new AddWeaknessCommand(INDEX_FIRST_PERSON, + new Note(VALID_NOTE_BOB)))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteMiscCommandTest.java b/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteMiscCommandTest.java new file mode 100644 index 00000000000..9f62b8d2dae --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteMiscCommandTest.java @@ -0,0 +1,101 @@ +package seedu.address.logic.commands.person.notecommand; + +import static org.junit.jupiter.api.Assertions.assertFalse; +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.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +import static seedu.address.testutil.TypicalIndexes.NOTE_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.NOTE_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.DeleteMiscCommand; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Contains integration tests (interaction with the Model) for + * {@code DeleteMiscCommand}. + */ +public class DeleteMiscCommandTest { + + private final Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_nullIndex_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new DeleteMiscCommand(null, null)); + } + + @Test + public void execute_validNoteIndexUnfilteredList_success() throws Exception { + Person personToDeleteMiscFrom = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); + List newMisc = new ArrayList<>(personToDeleteMiscFrom.getMiscellaneous()); + newMisc.remove(NOTE_FIRST_INDEX.getZeroBased()); + Person personWithModifiedMisc = new Person(personToDeleteMiscFrom.getName(), personToDeleteMiscFrom.getPhone(), + personToDeleteMiscFrom.getEmail(), personToDeleteMiscFrom.getAddress(), + personToDeleteMiscFrom.getTags(), + personToDeleteMiscFrom.getStrengths(), personToDeleteMiscFrom.getWeaknesses(), newMisc); + + DeleteMiscCommand deleteMiscCommand = new DeleteMiscCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + + String expectedMessage = String.format(DeleteMiscCommand.MESSAGE_SUCCESS, personWithModifiedMisc); + + ModelManager expectedModel = new ModelManager(model.getAddressBook(), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); + deleteMiscCommand.execute(expectedModel); + assertCommandSuccess(deleteMiscCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidNoteIndexUnfilteredList_throwsCommandException() { + Index outOfBoundNoteIndex = Index.fromOneBased(model.getFilteredPersonList().get( + INDEX_SECOND_PERSON.getZeroBased()).getMiscellaneous().size() + 1); + DeleteMiscCommand deleteMiscCommand = new DeleteMiscCommand(INDEX_SECOND_PERSON, outOfBoundNoteIndex); + + assertCommandFailure(deleteMiscCommand, model, Messages.MESSAGE_INVALID_NOTE_DISPLAYED_INDEX); + } + + @Test + public void execute_invalidPersonIndexUnfilteredList_throwsCommandException() { + Index outOfBoundNoteIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + DeleteMiscCommand deleteMiscCommand = new DeleteMiscCommand(outOfBoundNoteIndex, NOTE_FIRST_INDEX); + + assertCommandFailure(deleteMiscCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteMiscCommand deleteMiscFirstCommand = new DeleteMiscCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + DeleteMiscCommand deleteMiscSecondCommand = new DeleteMiscCommand(INDEX_SECOND_PERSON, NOTE_SECOND_INDEX); + + // same object -> returns true + assertTrue(deleteMiscFirstCommand.equals(deleteMiscFirstCommand)); + + // same values -> returns true + DeleteMiscCommand deleteMiscFirstCommandCopy = new DeleteMiscCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + assertTrue(deleteMiscFirstCommand.equals(deleteMiscFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteMiscFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteMiscFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(deleteMiscFirstCommand.equals(deleteMiscSecondCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteStrengthCommandTest.java b/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteStrengthCommandTest.java new file mode 100644 index 00000000000..09573606060 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteStrengthCommandTest.java @@ -0,0 +1,106 @@ +package seedu.address.logic.commands.person.notecommand; + +import static org.junit.jupiter.api.Assertions.assertFalse; +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.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +import static seedu.address.testutil.TypicalIndexes.NOTE_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.NOTE_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.DeleteStrengthCommand; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Contains integration tests (interaction with the Model) for + * {@code DeleteStrengthCommand}. + */ +public class DeleteStrengthCommandTest { + + private final Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_nullIndex_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new DeleteStrengthCommand(null, null)); + } + + @Test + public void execute_validNoteIndexUnfilteredList_success() throws Exception { + Person personToDeleteStrengthFrom = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); + List newStrengths = new ArrayList<>(personToDeleteStrengthFrom.getStrengths()); + newStrengths.remove(NOTE_FIRST_INDEX.getZeroBased()); + Person personWithModifiedStrength = new Person(personToDeleteStrengthFrom.getName(), + personToDeleteStrengthFrom.getPhone(), + personToDeleteStrengthFrom.getEmail(), personToDeleteStrengthFrom.getAddress(), + personToDeleteStrengthFrom.getTags(), newStrengths, + personToDeleteStrengthFrom.getWeaknesses(), personToDeleteStrengthFrom.getMiscellaneous()); + + DeleteStrengthCommand deleteStrengthCommand = new DeleteStrengthCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + + String expectedMessage = String.format(DeleteStrengthCommand.MESSAGE_SUCCESS, personWithModifiedStrength); + + ModelManager expectedModel = new ModelManager(model.getAddressBook(), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); + deleteStrengthCommand.execute(expectedModel); + assertCommandSuccess(deleteStrengthCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidNoteIndexUnfilteredList_throwsCommandException() { + Index outOfBoundNoteIndex = Index.fromOneBased(model.getFilteredPersonList().get( + INDEX_SECOND_PERSON.getZeroBased()).getStrengths().size() + 1); + DeleteStrengthCommand deleteStrengthCommand = new DeleteStrengthCommand(INDEX_SECOND_PERSON, + outOfBoundNoteIndex); + + assertCommandFailure(deleteStrengthCommand, model, Messages.MESSAGE_INVALID_NOTE_DISPLAYED_INDEX); + } + + @Test + public void execute_invalidPersonIndexUnfilteredList_throwsCommandException() { + Index outOfBoundNoteIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + DeleteStrengthCommand deleteStrengthCommand = new DeleteStrengthCommand(outOfBoundNoteIndex, NOTE_FIRST_INDEX); + + assertCommandFailure(deleteStrengthCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteStrengthCommand deleteStrengthFirstCommand = + new DeleteStrengthCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + DeleteStrengthCommand deleteStrengthSecondCommand = + new DeleteStrengthCommand(INDEX_SECOND_PERSON, NOTE_SECOND_INDEX); + + // same object -> returns true + assertTrue(deleteStrengthFirstCommand.equals(deleteStrengthFirstCommand)); + + // same values -> returns true + DeleteStrengthCommand deleteStrengthFirstCommandCopy = + new DeleteStrengthCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + assertTrue(deleteStrengthFirstCommand.equals(deleteStrengthFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteStrengthFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteStrengthFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(deleteStrengthFirstCommand.equals(deleteStrengthSecondCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteWeaknessCommandTest.java b/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteWeaknessCommandTest.java new file mode 100644 index 00000000000..d254d5bf31d --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/person/notecommand/DeleteWeaknessCommandTest.java @@ -0,0 +1,106 @@ +package seedu.address.logic.commands.person.notecommand; + +import static org.junit.jupiter.api.Assertions.assertFalse; +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.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +import static seedu.address.testutil.TypicalIndexes.NOTE_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.NOTE_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.person.notecommands.DeleteWeaknessCommand; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; + +/** + * Contains integration tests (interaction with the Model) for + * {@code DeleteWeaknessCommand}. + */ +public class DeleteWeaknessCommandTest { + + private final Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_nullIndex_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new DeleteWeaknessCommand(null, null)); + } + + @Test + public void execute_validNoteIndexUnfilteredList_success() throws Exception { + Person personToDeleteWeaknessFrom = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); + List newWeaknesses = new ArrayList<>(personToDeleteWeaknessFrom.getWeaknesses()); + newWeaknesses.remove(NOTE_FIRST_INDEX.getZeroBased()); + Person personWithModifiedWeakness = + new Person(personToDeleteWeaknessFrom.getName(), personToDeleteWeaknessFrom.getPhone(), + personToDeleteWeaknessFrom.getEmail(), personToDeleteWeaknessFrom.getAddress(), + personToDeleteWeaknessFrom.getTags(), personToDeleteWeaknessFrom.getStrengths(), newWeaknesses, + personToDeleteWeaknessFrom.getMiscellaneous()); + + DeleteWeaknessCommand deleteWeaknessCommand = new DeleteWeaknessCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + + String expectedMessage = String.format(DeleteWeaknessCommand.MESSAGE_SUCCESS, personWithModifiedWeakness); + + ModelManager expectedModel = new ModelManager(model.getAddressBook(), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); + deleteWeaknessCommand.execute(expectedModel); + assertCommandSuccess(deleteWeaknessCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidNoteIndexUnfilteredList_throwsCommandException() { + Index outOfBoundNoteIndex = Index.fromOneBased(model.getFilteredPersonList().get( + INDEX_SECOND_PERSON.getZeroBased()).getWeaknesses().size() + 1); + DeleteWeaknessCommand deleteWeaknessCommand = + new DeleteWeaknessCommand(INDEX_SECOND_PERSON, outOfBoundNoteIndex); + + assertCommandFailure(deleteWeaknessCommand, model, Messages.MESSAGE_INVALID_NOTE_DISPLAYED_INDEX); + } + + @Test + public void execute_invalidPersonIndexUnfilteredList_throwsCommandException() { + Index outOfBoundNoteIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); + DeleteWeaknessCommand deleteWeaknessCommand = new DeleteWeaknessCommand(outOfBoundNoteIndex, NOTE_FIRST_INDEX); + + assertCommandFailure(deleteWeaknessCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteWeaknessCommand deleteWeaknessFirstCommand = + new DeleteWeaknessCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + DeleteWeaknessCommand deleteWeaknessSecondCommand = + new DeleteWeaknessCommand(INDEX_SECOND_PERSON, NOTE_SECOND_INDEX); + + // same object -> returns true + assertTrue(deleteWeaknessFirstCommand.equals(deleteWeaknessFirstCommand)); + + // same values -> returns true + DeleteWeaknessCommand deleteWeaknessFirstCommandCopy = + new DeleteWeaknessCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX); + assertTrue(deleteWeaknessFirstCommand.equals(deleteWeaknessFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteWeaknessFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteWeaknessFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(deleteWeaknessFirstCommand.equals(deleteWeaknessSecondCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/strategy/AddPlayerCommandTest.java b/src/test/java/seedu/address/logic/commands/strategy/AddPlayerCommandTest.java new file mode 100644 index 00000000000..f7e002ef701 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/strategy/AddPlayerCommandTest.java @@ -0,0 +1,71 @@ +package seedu.address.logic.commands.strategy; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.strategy.Player; + +class AddPlayerCommandTest { + private static final String PLAYER_NAME_STUB_1 = "player1"; + private static final String PLAYER_NAME_STUB_2 = "player2"; + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_nullPlayer_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddPlayerCommand(null)); + } + + @Test + public void execute_validPlayer_notNull() throws CommandException { + AddPlayerCommand addPlayerCommand = new AddPlayerCommand(new Player(PLAYER_NAME_STUB_1)); + assertNotNull(addPlayerCommand.execute(model)); + } + + @Test + public void execute_duplicatedPlayer_failure() { + Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + model.addPlayer(new Player(PLAYER_NAME_STUB_1)); + AddPlayerCommand addPlayerCommand = new AddPlayerCommand(new Player(PLAYER_NAME_STUB_1)); + + assertCommandFailure(addPlayerCommand, model, AddPlayerCommand.MESSAGE_DUPLICATE_PLAYER); + } + + @Test + public void equals() { + final Player player1 = new Player(PLAYER_NAME_STUB_1); + final Player player2 = new Player(PLAYER_NAME_STUB_2); + final AddPlayerCommand standardCommand = new AddPlayerCommand(player1); + + // same values -> returns true + AddPlayerCommand commandWithSameValues = new AddPlayerCommand(player1); + 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 ClearPersonCommand())); + + // different player -> returns false + assertFalse(standardCommand.equals(new AddPlayerCommand(player2))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/strategy/DeletePlayerCommandTest.java b/src/test/java/seedu/address/logic/commands/strategy/DeletePlayerCommandTest.java new file mode 100644 index 00000000000..aaabc4df306 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/strategy/DeletePlayerCommandTest.java @@ -0,0 +1,82 @@ +package seedu.address.logic.commands.strategy; + +import static org.junit.jupiter.api.Assertions.assertFalse; +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.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.strategy.Player; + +class DeletePlayerCommandTest { + private static final String PLAYER_NAME_STUB_1 = "player1"; + private static final String PLAYER_NAME_STUB_2 = "player2"; + + private final Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_nullPlayer_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new DeletePlayerCommand(null)); + } + + @Test + public void execute_validPlayerName_success() { + final Player player1 = new Player(PLAYER_NAME_STUB_1); + final String expectedMessage = String.format(DeletePlayerCommand.MESSAGE_SUCCESS, PLAYER_NAME_STUB_1); + + Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + model.addPlayer(player1); + + DeletePlayerCommand deletePlayerCommand = new DeletePlayerCommand(PLAYER_NAME_STUB_1); + + Model expectedModel = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + assertCommandSuccess(deletePlayerCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidPlayerName_failure() { + final String expectedMessage = String.format(DeletePlayerCommand.MESSAGE_PLAYER_NOT_FOUND, PLAYER_NAME_STUB_2); + + Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + model.addPlayer(new Player(PLAYER_NAME_STUB_1)); + + DeletePlayerCommand deletePlayerCommand = new DeletePlayerCommand(PLAYER_NAME_STUB_2); + + assertCommandFailure(deletePlayerCommand, model, expectedMessage); + } + + @Test + public void equals() { + final DeletePlayerCommand standardCommand = new DeletePlayerCommand(PLAYER_NAME_STUB_1); + + // same values -> returns true + DeletePlayerCommand commandWithSameValues = new DeletePlayerCommand(PLAYER_NAME_STUB_1); + 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 ClearPersonCommand())); + + // different player -> returns false + assertFalse(standardCommand.equals(new DeletePlayerCommand(PLAYER_NAME_STUB_2))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/strategy/ExportCommandTest.java b/src/test/java/seedu/address/logic/commands/strategy/ExportCommandTest.java new file mode 100644 index 00000000000..2a9e4fc87f2 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/strategy/ExportCommandTest.java @@ -0,0 +1,24 @@ +package seedu.address.logic.commands.strategy; + +import static seedu.address.commons.core.Tabs.DEFAULT; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.strategy.ExportCommand.MESSAGE_EXPORT_ACKNOWLEDGEMENT; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; + +public class ExportCommandTest { + private Model model = new ModelManager(); + private Model expectedModel = new ModelManager(); + + @Test + public void execute_exit_success() { + CommandResult expectedCommandResult = new CommandResult(MESSAGE_EXPORT_ACKNOWLEDGEMENT, false, false, + DEFAULT, false, null, true); + assertCommandSuccess(new ExportCommand(), model, expectedCommandResult, expectedModel); + } +} + diff --git a/src/test/java/seedu/address/logic/commands/strategy/LoadCourtCommandTest.java b/src/test/java/seedu/address/logic/commands/strategy/LoadCourtCommandTest.java new file mode 100644 index 00000000000..a7301ec579b --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/strategy/LoadCourtCommandTest.java @@ -0,0 +1,51 @@ +package seedu.address.logic.commands.strategy; + +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.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.image.Image; + +public class LoadCourtCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new LoadCourtCommand(null)); + } + + @Test + public void equals() { + Image imageStub1 = new Image("test"); + Image imageStub2 = new Image("test2"); + LoadCourtCommand standardCommand = new LoadCourtCommand(imageStub1); + LoadCourtCommand standardCommand2 = new LoadCourtCommand(imageStub2); + + // same values -> returns true + LoadCourtCommand commandWithSameValues = new LoadCourtCommand(imageStub1); + 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 ClearPersonCommand())); + + // different image -> returns false + assertFalse(standardCommand.equals(standardCommand2)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/strategy/MovePlayerCommandTest.java b/src/test/java/seedu/address/logic/commands/strategy/MovePlayerCommandTest.java new file mode 100644 index 00000000000..c438508dff0 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/strategy/MovePlayerCommandTest.java @@ -0,0 +1,97 @@ +package seedu.address.logic.commands.strategy; + +import static org.junit.jupiter.api.Assertions.assertFalse; +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.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.strategy.Player; + +class MovePlayerCommandTest { + + private static final String PLAYER_NAME_STUB_1 = "player1"; + private static final String PLAYER_NAME_STUB_2 = "player2"; + private static final int POS_X_STUB_1 = 123; + private static final int POS_Y_STUB_1 = 456; + private static final int POS_X_STUB_2 = 321; + private static final int POS_Y_STUB_2 = 654; + + private final Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_nullPlayerName_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> + new MovePlayerCommand(null, POS_X_STUB_1, POS_Y_STUB_1)); + } + + @Test + public void execute_validPlayerNamePosition_success() { + final Player player1 = new Player(PLAYER_NAME_STUB_1, POS_X_STUB_1, POS_Y_STUB_1); + final Player player1WithoutPos = new Player(PLAYER_NAME_STUB_1); + final String expectedMessage = String.format(MovePlayerCommand.MESSAGE_SUCCESS, + PLAYER_NAME_STUB_1, POS_X_STUB_1, POS_Y_STUB_1); + + Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + model.addPlayer(player1WithoutPos); + + MovePlayerCommand movePlayerCommand = new MovePlayerCommand(PLAYER_NAME_STUB_1, POS_X_STUB_1, POS_Y_STUB_1); + + Model expectedModel = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + expectedModel.addPlayer(player1); + + assertCommandSuccess(movePlayerCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidPlayerNamePosition_failure() { + final String expectedMessage = String.format(Messages.MESSAGE_INVALID_PLAYER, PLAYER_NAME_STUB_2); + + Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + model.addPlayer(new Player(PLAYER_NAME_STUB_1)); + MovePlayerCommand movePlayerCommand = new MovePlayerCommand(PLAYER_NAME_STUB_2, POS_X_STUB_1, POS_Y_STUB_1); + + assertCommandFailure(movePlayerCommand, model, expectedMessage); + } + + @Test + public void equals() { + final MovePlayerCommand standardCommand = new MovePlayerCommand(PLAYER_NAME_STUB_1, POS_X_STUB_1, POS_Y_STUB_1); + + // same values -> returns true + MovePlayerCommand commandWithSameValues = new MovePlayerCommand(PLAYER_NAME_STUB_1, POS_X_STUB_1, POS_Y_STUB_1); + 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 ClearPersonCommand())); + + // different player -> returns false + assertFalse(standardCommand.equals(new MovePlayerCommand(PLAYER_NAME_STUB_2, POS_X_STUB_1, POS_Y_STUB_1))); + + // different Position -> returns false + assertFalse(standardCommand.equals(new MovePlayerCommand(PLAYER_NAME_STUB_2, POS_X_STUB_2, POS_Y_STUB_1))); + + // different Position -> returns false + assertFalse(standardCommand.equals(new MovePlayerCommand(PLAYER_NAME_STUB_2, POS_X_STUB_1, POS_Y_STUB_2))); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/AddTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/task/AddTaskCommandTest.java new file mode 100644 index 00000000000..0ada20f311d --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/AddTaskCommandTest.java @@ -0,0 +1,143 @@ +package seedu.address.logic.commands.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +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.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.Task; +import seedu.address.testutil.TaskBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for AddTaskCommand. + */ +public class AddTaskCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddTaskCommand(null)); + } + + @Test + public void execute_taskAcceptedByModel_success() throws Exception { + // empty persons list -> success + Task validTask = new TaskBuilder().build(); + + AddTaskCommand addTaskCommand = new AddTaskCommand(validTask); + + String expectedMessage = String.format(AddTaskCommand.MESSAGE_SUCCESS, validTask); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), new StrategyBoard(), new UserPrefs()); + expectedModel.addTask(validTask); + + assertCommandSuccess(addTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_personsNotInList_throwsCommandException() { + // task with one person not found in the address book -> throws an error + Task invalidTask = new TaskBuilder().withPersons("Johnson").build(); + AddTaskCommand addTaskCommand = new AddTaskCommand(invalidTask); + + String expectedMessage = String.format(AddTaskCommand.MESSAGE_CONTACT_NOT_FOUND, "Johnson"); + assertThrows(CommandException.class, + expectedMessage, () -> addTaskCommand.execute(model)); + + // task with only one person out of the rest found in the address book -> throws an error + invalidTask = new TaskBuilder().withPersons("Johnson", "Alice Pauline").build(); + AddTaskCommand addTaskCommand2 = new AddTaskCommand(invalidTask); + + assertThrows(CommandException.class, + expectedMessage, () -> addTaskCommand2.execute(model)); + + // task with multiple persons not found in the address book -> throws an error + invalidTask = new TaskBuilder().withPersons("Johnson", "Kenny").build(); + AddTaskCommand addTaskCommand3 = new AddTaskCommand(invalidTask); + + assertThrows(CommandException.class, + expectedMessage, () -> addTaskCommand3.execute(model)); + } + + @Test + public void execute_personsInConflictingTask_throwsCommandException() { + // task with same date, same time range, and same persons -> throws an error + Task invalidTask = new TaskBuilder() + .withDate("09-10-2022").withStartTime("09:00").withEndTime("10:00") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + AddTaskCommand addTaskCommand = new AddTaskCommand(invalidTask); + + String expectedMessage = + String.format(AddTaskCommand.MESSAGE_SCHEDULE_CONFLICT, ALICE.getName().fullName); + assertThrows(CommandException.class, + expectedMessage, () -> addTaskCommand.execute(model)); + + // task with same date, overlapping time range, and same persons -> throws an error + invalidTask = new TaskBuilder() + .withDate("09-10-2022").withStartTime("09:30").withEndTime("10:30") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + AddTaskCommand addTaskCommand2 = new AddTaskCommand(invalidTask); + + assertThrows(CommandException.class, + expectedMessage, () -> addTaskCommand2.execute(model)); + + // task with same date, overlapping time range, and only ALICE -> throws an error + invalidTask = new TaskBuilder() + .withDate("09-10-2022").withStartTime("09:30").withEndTime("10:30") + .withPersons(ALICE.getName().fullName).build(); + AddTaskCommand addTaskCommand3 = new AddTaskCommand(invalidTask); + + assertThrows(CommandException.class, + expectedMessage, () -> addTaskCommand3.execute(model)); + } + + @Test + public void execute_duplicateTask_throwsCommandException() { + Task duplicateTask = new TaskBuilder().withName(FIRST_TASK.getName().fullName).build(); + AddTaskCommand addTaskCommand = new AddTaskCommand(duplicateTask); + + assertThrows(CommandException.class, + AddTaskCommand.MESSAGE_DUPLICATE_TASK, () -> addTaskCommand.execute(model)); + } + + @Test + public void equals() { + Task shareholderMeet = new TaskBuilder().withName("Shareholder Meet").build(); + Task welcomeTea = new TaskBuilder().withName("Welcome Tea").build(); + AddTaskCommand addShareholderMeetCommand = new AddTaskCommand(shareholderMeet); + AddTaskCommand addWelcomeTeaCommand = new AddTaskCommand(welcomeTea); + + // same object -> returns true + assertTrue(addShareholderMeetCommand.equals(addShareholderMeetCommand)); + + // same values -> returns true + AddTaskCommand addShareholderMeetCommandCopy = new AddTaskCommand(shareholderMeet); + assertTrue(addShareholderMeetCommand.equals(addShareholderMeetCommandCopy)); + + // different types -> returns false + assertFalse(addShareholderMeetCommand.equals(1)); + + // null -> returns false + assertFalse(addShareholderMeetCommand.equals(null)); + + // different person -> returns false + assertFalse(addShareholderMeetCommand.equals(addWelcomeTeaCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/AddTaskTagCommandTest.java b/src/test/java/seedu/address/logic/commands/task/AddTaskTagCommandTest.java new file mode 100644 index 00000000000..687ba73c56e --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/AddTaskTagCommandTest.java @@ -0,0 +1,92 @@ +package seedu.address.logic.commands.task; + +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.showTaskAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalIndexes.TASK_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.TASK_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Task; +import seedu.address.testutil.TaskBuilder; + +class AddTaskTagCommandTest { + // Test tags + // Because PersonBuilder#withTags takes String ... instead of Set + private static final String TAG1 = "TAG1"; + + // Test model + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + void execute_addTagCommandUnfilteredList_success() { + Task firstTask = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + + // Adding 1 more tag to the firstTask + Set firstTaskTags = new HashSet<>(firstTask.getTags()); // Set copy of Shareholders Meeting (1st task) + firstTaskTags.add(new Tag(TAG1)); + + // Convert Set to array for TaskBuilder#withTags + String[] firstTaskTagsStringArray = firstTaskTags + .stream() + .map(x -> x.tagName) + .toArray(String[]::new); + + // Manually building the edited task + Task editedTask = new TaskBuilder(firstTask).withTags(firstTaskTagsStringArray).build(); + + AddTaskTagCommand addTagToTaskCommand = new AddTaskTagCommand(TASK_FIRST_INDEX, TAG1); + + String expectedMessage = String.format(AddTaskTagCommand.MESSAGE_ADD_TAG_SUCCESS, TAG1); + + // Manually building the expected model + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setTask(firstTask, editedTask); + + assertCommandSuccess(addTagToTaskCommand, model, expectedMessage, expectedModel); + + } + + @Test + public void execute_invalidTagIndexFilteredList_failure() { + showTaskAtIndex(model, TASK_FIRST_INDEX); + Index outOfBoundIndex = TASK_SECOND_INDEX; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getTaskBook().getTaskList().size()); + + AddTaskTagCommand addTagToTaskCommand = new AddTaskTagCommand(outOfBoundIndex, TAG1); + + assertCommandFailure(addTagToTaskCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final AddTaskTagCommand command = new AddTaskTagCommand(INDEX_FIRST_PERSON, TAG1); + + // If they are the same objects, they are equal + assertTrue(command.equals(command)); + + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/ClearTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/task/ClearTaskCommandTest.java new file mode 100644 index 00000000000..6f796e5fbba --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/ClearTaskCommandTest.java @@ -0,0 +1,107 @@ +package seedu.address.logic.commands.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +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.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.EIGHTH_TASK; +import static seedu.address.testutil.TypicalTasks.FIFTH_TASK; +import static seedu.address.testutil.TypicalTasks.SEVENTH_TASK; +import static seedu.address.testutil.TypicalTasks.SIXTH_TASK; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.Date; + +/** + * Contains tests for + * {@code ClearTaskCommand}. + */ +public class ClearTaskCommandTest { + + private final Date dateStub = new Date("03-03-2000"); + private final Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void constructor_nullDate_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new ClearTaskCommand(null)); + } + + @Test + public void execute_clearTaskBook_success() { + Model expectedModel = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + expectedModel.setTaskBook(new TaskBook()); + + assertCommandSuccess(new ClearTaskCommand(), model, ClearTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_validDate_success() { + Model expectedModel = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + TaskBook taskBook = new TaskBook(); + taskBook.addTask(FIFTH_TASK); + expectedModel.setTaskBook(taskBook); + taskBook.addTask(SIXTH_TASK); + taskBook.addTask(SEVENTH_TASK); + taskBook.addTask(EIGHTH_TASK); + model.setTaskBook(taskBook); + + assertCommandSuccess(new ClearTaskCommand(dateStub), model, + String.format(ClearTaskCommand.MESSAGE_SUCCESS_ALT, dateStub), expectedModel); + } + + @Test + public void execute_invalidDate_throwsCommandException() { + ClearTaskCommand clearTaskCommand = new ClearTaskCommand(dateStub); + + assertCommandFailure(clearTaskCommand, model, String.format(ClearTaskCommand.MESSAGE_INVALID_DATE, dateStub)); + } + + @Test + public void execute_emptyTaskList_throwsCommandException() { + Model emptyTaskListModel = new ModelManager(getTypicalAddressBook(), new TaskBook(), + new StrategyBoard(), new UserPrefs()); + ClearTaskCommand clearTaskCommand = new ClearTaskCommand(); + + assertCommandFailure(clearTaskCommand, emptyTaskListModel, ClearTaskCommand.MESSAGE_EMPTY_LIST); + } + + @Test + public void equals() { + Date dateStub2 = new Date("03-03-2001"); + ClearTaskCommand clearTaskFirstCommand = new ClearTaskCommand(dateStub); + ClearTaskCommand clearTaskSecondCommand = new ClearTaskCommand(dateStub2); + ClearTaskCommand clearTaskEmptyCommand = new ClearTaskCommand(); + + // same object -> returns true + assertTrue(clearTaskFirstCommand.equals(clearTaskFirstCommand)); + assertTrue(clearTaskEmptyCommand.equals(clearTaskEmptyCommand)); + + // same values -> returns true + ClearTaskCommand clearTaskFirstCommandCopy = new ClearTaskCommand(dateStub); + ClearTaskCommand clearTaskEmptyCommandCopy = new ClearTaskCommand(); + assertTrue(clearTaskFirstCommand.equals(clearTaskFirstCommandCopy)); + assertTrue(clearTaskEmptyCommand.equals(clearTaskEmptyCommandCopy)); + + // different types -> returns false + assertFalse(clearTaskFirstCommand.equals(1)); + + // null -> returns false + assertFalse(clearTaskFirstCommand.equals(null)); + + // different task -> returns false + assertFalse(clearTaskFirstCommand.equals(clearTaskSecondCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/DeleteTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/task/DeleteTaskCommandTest.java new file mode 100644 index 00000000000..8323bdaa960 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/DeleteTaskCommandTest.java @@ -0,0 +1,74 @@ +package seedu.address.logic.commands.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +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.testutil.TypicalIndexes.TASK_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.TASK_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.Task; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code DeleteTaskCommand}. + */ +public class DeleteTaskCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Task taskToDelete = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + DeleteTaskCommand deleteTaskCommand = new DeleteTaskCommand(TASK_FIRST_INDEX); + + String expectedMessage = String.format(DeleteTaskCommand.MESSAGE_DELETE_TASK_SUCCESS, taskToDelete); + + ModelManager expectedModel = new ModelManager(model.getAddressBook(), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); + expectedModel.deleteTask(taskToDelete); + + assertCommandSuccess(deleteTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1); + DeleteTaskCommand deleteTaskCommand = new DeleteTaskCommand(outOfBoundIndex); + + assertCommandFailure(deleteTaskCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteTaskCommand deleteFirstTaskCommand = new DeleteTaskCommand(TASK_FIRST_INDEX); + DeleteTaskCommand deleteSecondTaskCommand = new DeleteTaskCommand(TASK_SECOND_INDEX); + + // same object -> returns true + assertTrue(deleteFirstTaskCommand.equals(deleteFirstTaskCommand)); + + // same values -> returns true + DeleteTaskCommand deleteFirstTaskCommandCopy = new DeleteTaskCommand(TASK_FIRST_INDEX); + assertTrue(deleteFirstTaskCommand.equals(deleteFirstTaskCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstTaskCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstTaskCommand.equals(null)); + + // different task -> returns false + assertFalse(deleteFirstTaskCommand.equals(deleteSecondTaskCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/DeleteTaskTagCommandTest.java b/src/test/java/seedu/address/logic/commands/task/DeleteTaskTagCommandTest.java new file mode 100644 index 00000000000..98b7e2b1bfc --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/DeleteTaskTagCommandTest.java @@ -0,0 +1,105 @@ +package seedu.address.logic.commands.task; + +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.showTaskAtIndex; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalIndexes.TASK_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.TASK_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Task; +import seedu.address.testutil.TaskBuilder; + +class DeleteTaskTagCommandTest { + // Test tags + // Because PersonBuilder#withTags takes String ... instead of Set + private static final String TAG1 = "colleagues"; + + // Test model + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + void execute_deleteTagCommandUnfilteredList_success() { + Task firstTask = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + + // Adding 1 more tag to the firstTask + Set firstTaskTags = new HashSet<>(firstTask.getTags()); // Set copy of Meetings (first task) + firstTaskTags.remove(new Tag(TAG1)); + + // Convert Set to array for TaskBuilder#withTags + String[] firstTaskTagsStringArray = firstTaskTags + .stream() + .map(x -> x.tagName) + .toArray(String[]::new); + + // Manually building the edited task + Task editedTask = new TaskBuilder(firstTask).withTags(firstTaskTagsStringArray).build(); + + DeleteTaskTagCommand deleteTagFromTaskCommand = new DeleteTaskTagCommand(TASK_FIRST_INDEX, TAG1); + + String expectedMessage = String.format(deleteTagFromTaskCommand.MESSAGE_DEL_TAG_SUCCESS, TAG1); + + // Manually building the expected model + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setTask(firstTask, editedTask); + + assertCommandSuccess(deleteTagFromTaskCommand, model, expectedMessage, expectedModel); + + } + + @Test + public void execute_invalidTagIndexFilteredList_failure() { + showTaskAtIndex(model, TASK_FIRST_INDEX); + Index outOfBoundIndex = TASK_SECOND_INDEX; + // ensures that outOfBoundIndex is still in bounds of address book list + assertTrue(outOfBoundIndex.getZeroBased() < model.getTaskBook().getTaskList().size()); + + DeleteTaskTagCommand deleteTagFromTaskCommand = new DeleteTaskTagCommand(outOfBoundIndex, TAG1); + + assertCommandFailure(deleteTagFromTaskCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + @Test + public void execute_invalidTagName_failure() { + String invalidTag = ""; + + DeleteTaskTagCommand deleteTagFromTaskCommand = new DeleteTaskTagCommand(TASK_FIRST_INDEX, invalidTag); + assertCommandFailure(deleteTagFromTaskCommand, model, Messages.MESSAGE_INVALID_TAG); + + } + + @Test + public void equals() { + final AddTaskTagCommand command = new AddTaskTagCommand(INDEX_FIRST_PERSON, TAG1); + final AddTaskTagCommand command2 = new AddTaskTagCommand(INDEX_FIRST_PERSON, TAG1); + + // If they are the same objects, they are equal + assertTrue(command.equals(command)); + + // If they have the same index and tagName, they are equal (even though they are different objects) + assertTrue(command.equals(command2)); + + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/EditTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/task/EditTaskCommandTest.java new file mode 100644 index 00000000000..f20a5b523fa --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/EditTaskCommandTest.java @@ -0,0 +1,161 @@ +package seedu.address.logic.commands.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.DESC_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_MEET; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.TASK_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.TASK_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.CARL; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.logic.commands.task.exceptions.CommandException; +import seedu.address.model.AddressBook; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.task.Task; +import seedu.address.testutil.EditTaskDescriptorBuilder; +import seedu.address.testutil.TaskBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for EditTaskCommand. + */ +public class EditTaskCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Task editedTask = new TaskBuilder().build(); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder(editedTask).build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(TASK_FIRST_INDEX, descriptor); + + String expectedMessage = String.format(EditTaskCommand.MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); + expectedModel.setTask(model.getFilteredTaskList().get(0), editedTask); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() { + Index indexLastTask = Index.fromOneBased(model.getFilteredTaskList().size()); + Task lastTask = model.getFilteredTaskList().get(indexLastTask.getZeroBased()); + + TaskBuilder taskInList = new TaskBuilder(lastTask); + Task editedTask = taskInList.withName(VALID_NAME_TASK1).withDate(VALID_DATE_TASK1) + .withTags(VALID_TAG_MEET).build(); + + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withName(VALID_NAME_TASK1) + .withDate(VALID_DATE_TASK1).withTags(VALID_TAG_MEET).build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(indexLastTask, descriptor); + + String expectedMessage = String.format(EditTaskCommand.MESSAGE_EDIT_TASK_SUCCESS, editedTask); + + Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), + model.getTaskBook(), new StrategyBoard(), new UserPrefs()); + expectedModel.setTask(lastTask, editedTask); + + assertCommandSuccess(editTaskCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidStartEndTime_failure() { + EditTaskDescriptor invalidTask = new EditTaskDescriptorBuilder().withStartTime("15:00") + .withEndTime("13:00").build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(TASK_FIRST_INDEX, invalidTask); + + String expectedMessage = String.format(EditTaskCommand.MESSAGE_SCHEDULE_CONFLICT_START_END_TIME); + assertThrows(CommandException.class, + expectedMessage, () -> editTaskCommand.execute(model)); + + assertCommandFailure(editTaskCommand, model, expectedMessage); + } + + @Test + public void execute_duplicateTaskUnfilteredList_failure() { + Task firstTask = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder(firstTask).build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(TASK_SECOND_INDEX, descriptor); + + assertCommandFailure(editTaskCommand, model, EditTaskCommand.MESSAGE_DUPLICATE_TASK); + } + + @Test + public void execute_invalidTaskIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1); + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withName(VALID_NAME_TASK1).build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editTaskCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + @Test + public void execute_personNotInList_throwsCommandException() { + // task with one person not found in the address book -> throws an error + EditTaskDescriptor invalidTask = new EditTaskDescriptorBuilder().withPersons("Alice").build(); + EditTaskCommand editTaskCommand = new EditTaskCommand(TASK_FIRST_INDEX, invalidTask); + + String expectedMessage = String.format(EditTaskCommand.MESSAGE_CONTACT_NOT_FOUND_IN_LIST, "Alice"); + assertThrows(CommandException.class, + expectedMessage, () -> editTaskCommand.execute(model)); + + // task with only one person out of the rest found in the address book -> throws an error + invalidTask = new EditTaskDescriptorBuilder().withPersons("Alice", CARL.getName().fullName).build(); + EditTaskCommand editTaskCommand2 = new EditTaskCommand(TASK_FIRST_INDEX, invalidTask); + + assertThrows(CommandException.class, + expectedMessage, () -> editTaskCommand2.execute(model)); + + // task with multiple persons not found in the address book -> throws an error + invalidTask = new EditTaskDescriptorBuilder().withPersons("Alice", "Kenny").build(); + EditTaskCommand editTaskCommand3 = new EditTaskCommand(TASK_FIRST_INDEX, invalidTask); + + assertThrows(CommandException.class, + expectedMessage, () -> editTaskCommand3.execute(model)); + } + + @Test + public void equals() { + final EditTaskCommand standardCommand = new EditTaskCommand(TASK_FIRST_INDEX, DESC_TASK1); + + // same values -> returns true + EditTaskDescriptor copyDescriptor = new EditTaskDescriptor(DESC_TASK1); + EditTaskCommand commandWithSameValues = new EditTaskCommand(TASK_FIRST_INDEX, copyDescriptor); + 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 ClearPersonCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new EditTaskCommand(TASK_SECOND_INDEX, DESC_TASK1))); + + // different descriptor -> returns false + assertFalse(standardCommand.equals(new EditTaskCommand(TASK_FIRST_INDEX, DESC_TASK2))); + } + +} diff --git a/src/test/java/seedu/address/logic/commands/task/EditTaskDescriptorTest.java b/src/test/java/seedu/address/logic/commands/task/EditTaskDescriptorTest.java new file mode 100644 index 00000000000..96035d6b4f3 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/EditTaskDescriptorTest.java @@ -0,0 +1,63 @@ +package seedu.address.logic.commands.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.DESC_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ENDTIME_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_PERSON2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_STARTTIME_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.testutil.EditTaskDescriptorBuilder; + +public class EditTaskDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditTaskDescriptor descriptorWithSameValues = new EditTaskDescriptor(DESC_TASK1); + assertTrue(DESC_TASK1.equals(descriptorWithSameValues)); + + // same object -> returns true + assertTrue(DESC_TASK1.equals(DESC_TASK1)); + + // null -> returns false + assertFalse(DESC_TASK1.equals(null)); + + // different types -> returns false + assertFalse(DESC_TASK1.equals(5)); + + // different values -> returns false + assertFalse(DESC_TASK1.equals(DESC_TASK2)); + + // different name -> returns false + EditTaskDescriptor editedTask1 = new EditTaskDescriptorBuilder(DESC_TASK1).withName(VALID_NAME_TASK2).build(); + assertFalse(DESC_TASK1.equals(editedTask1)); + + // different date -> returns false + editedTask1 = new EditTaskDescriptorBuilder(DESC_TASK1).withDate(VALID_DATE_TASK2).build(); + assertFalse(DESC_TASK1.equals(editedTask1)); + + // different start time -> returns false + editedTask1 = new EditTaskDescriptorBuilder(DESC_TASK1).withStartTime(VALID_STARTTIME_TASK2).build(); + assertFalse(DESC_TASK1.equals(editedTask1)); + + // different end time -> returns false + editedTask1 = new EditTaskDescriptorBuilder(DESC_TASK1).withEndTime(VALID_ENDTIME_TASK2).build(); + assertFalse(DESC_TASK1.equals(editedTask1)); + + // different tags -> returns false + editedTask1 = new EditTaskDescriptorBuilder(DESC_TASK1).withTags(VALID_TAG_FRIEND).build(); + assertFalse(DESC_TASK1.equals(editedTask1)); + + // different persons -> returns false + editedTask1 = new EditTaskDescriptorBuilder(DESC_TASK1).withPersons(VALID_PERSON2).build(); + assertFalse(DESC_TASK1.equals(editedTask1)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/FindTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/task/FindTaskCommandTest.java new file mode 100644 index 00000000000..d1b30c9b3b5 --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/FindTaskCommandTest.java @@ -0,0 +1,103 @@ +package seedu.address.logic.commands.task; + +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.commons.core.Messages.MESSAGE_TASKS_LISTED_OVERVIEW; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.name.TaskNameContainsKeywordsPredicate; +import seedu.address.model.tag.TaskTagContainsKeywordsPredicate; +/** + * Contains integration tests (interaction with the Model) for {@code FindTaskCommand}. + */ +public class FindTaskCommandTest { + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void equals() { + + TaskNameContainsKeywordsPredicate firstPredicate = + new TaskNameContainsKeywordsPredicate(Collections.singletonList("first")); + TaskNameContainsKeywordsPredicate secondPredicate = + new TaskNameContainsKeywordsPredicate(Collections.singletonList("second")); + TaskTagContainsKeywordsPredicate thirdPredicate = + new TaskTagContainsKeywordsPredicate(Collections.singletonList("third")); + TaskTagContainsKeywordsPredicate fourthPredicate = + new TaskTagContainsKeywordsPredicate(Collections.singletonList("fourth")); + + FindTaskCommand findFirstCommand = new FindTaskCommand(firstPredicate, thirdPredicate); + FindTaskCommand findSecondCommand = new FindTaskCommand(secondPredicate, fourthPredicate); + + // same object -> returns true + assertTrue(findFirstCommand.equals(findFirstCommand)); + + // same values -> returns true + FindTaskCommand findFirstCommandCopy = new FindTaskCommand(firstPredicate, thirdPredicate); + assertTrue(findFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(findFirstCommand.equals(1)); + + // null -> returns false + assertFalse(findFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(findFirstCommand.equals(findSecondCommand)); + } + + @Test + public void execute_zeroKeywords_noPersonAndTagFound() { + List nameList = Arrays.asList(); + List tagList = Arrays.asList(); + String expectedMessage = String.format(MESSAGE_TASKS_LISTED_OVERVIEW, 0); + TaskNameContainsKeywordsPredicate namePredicate = prepareNamePredicate(nameList); + TaskTagContainsKeywordsPredicate tagPredicate = prepareTagPredicate(tagList); + FindTaskCommand command = new FindTaskCommand(namePredicate, tagPredicate); + expectedModel.updateFilteredTaskList(namePredicate.or(tagPredicate)); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredTaskList()); + } + + @Test + public void execute_multipleKeywords_multiplePersonsAndTagsFound() { + List nameList = Arrays.asList("Meeting", "Dinner"); + List tagList = Arrays.asList("friends", "neighbours"); + String expectedMessage = String.format(MESSAGE_TASKS_LISTED_OVERVIEW, 1); + TaskNameContainsKeywordsPredicate namePredicate = prepareNamePredicate(nameList); + TaskTagContainsKeywordsPredicate tagPredicate = prepareTagPredicate(tagList); + FindTaskCommand command = new FindTaskCommand(namePredicate, tagPredicate); + expectedModel.updateFilteredTaskList(namePredicate.or(tagPredicate)); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(FIRST_TASK), model.getFilteredTaskList()); + } + + /** + * Creates a {@code TaskNameContainsKeywordsPredicate} using {@code nameList}. + */ + private TaskNameContainsKeywordsPredicate prepareNamePredicate(List nameList) { + return new TaskNameContainsKeywordsPredicate(nameList); + } + /** + * Creates a {@code TaskTagContainsKeywordsPredicate} using {@code tagSet}. + */ + private TaskTagContainsKeywordsPredicate prepareTagPredicate(List tagList) { + return new TaskTagContainsKeywordsPredicate(tagList); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/GetPersonCommandTest.java b/src/test/java/seedu/address/logic/commands/task/GetPersonCommandTest.java new file mode 100644 index 00000000000..215f4e4b48b --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/GetPersonCommandTest.java @@ -0,0 +1,128 @@ +package seedu.address.logic.commands.task; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.commons.core.Tabs.CONTACT_TAB; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showTaskAtIndex; +import static seedu.address.testutil.TypicalIndexes.TASK_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.TASK_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.CommandResult; +import seedu.address.model.Model; +import seedu.address.model.ModelManager; +import seedu.address.model.StrategyBoard; +import seedu.address.model.UserPrefs; +import seedu.address.model.name.Name; +import seedu.address.model.task.Task; + +/** + * Contains integration tests (interaction with the Model) and unit tests for + * {@code GetPersonCommand}. + */ +public class GetPersonCommandTest { + + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Task indicatedTask = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + GetPersonCommand getPersonCommand = new GetPersonCommand(TASK_FIRST_INDEX); + + List keywords = new ArrayList<>(); + for (Name name: indicatedTask.getPersons()) { + keywords.add(name.fullName); + } + GetPersonCommand.CustomPersonNameContainsKeywordsPredicate predicate = + new GetPersonCommand.CustomPersonNameContainsKeywordsPredicate(keywords); + expectedModel.updateFilteredPersonList(predicate); + + String expectedMessage = String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, + expectedModel.getFilteredPersonList().size()); + CommandResult expectedCommandResult = + new CommandResult(expectedMessage, false, false, CONTACT_TAB, false, null, false); + + assertCommandSuccess(getPersonCommand, model, expectedCommandResult, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredTaskList().size() + 1); + + GetPersonCommand getPersonCommand = new GetPersonCommand(outOfBoundIndex); + + assertCommandFailure(getPersonCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showTaskAtIndex(model, TASK_FIRST_INDEX); + + Task indicatedTask = model.getFilteredTaskList().get(TASK_FIRST_INDEX.getZeroBased()); + GetPersonCommand getPersonCommand = new GetPersonCommand(TASK_FIRST_INDEX); + + List keywords = new ArrayList<>(); + for (Name name: indicatedTask.getPersons()) { + keywords.add(name.fullName); + } + GetPersonCommand.CustomPersonNameContainsKeywordsPredicate predicate = + new GetPersonCommand.CustomPersonNameContainsKeywordsPredicate(keywords); + expectedModel.updateFilteredPersonList(predicate); + showTaskAtIndex(expectedModel, TASK_FIRST_INDEX); + + String expectedMessage = String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, + expectedModel.getFilteredPersonList().size()); + CommandResult expectedCommandResult = + new CommandResult(expectedMessage, false, false, CONTACT_TAB, false, null, false); + + assertCommandSuccess(getPersonCommand, model, expectedCommandResult, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showTaskAtIndex(model, TASK_FIRST_INDEX); + + Index outOfBoundIndex = TASK_SECOND_INDEX; + // ensures that outOfBoundIndex is still in bounds of task book + assertTrue(outOfBoundIndex.getZeroBased() < model.getTaskBook().getTaskList().size()); + + GetPersonCommand getPersonCommand = new GetPersonCommand(outOfBoundIndex); + + assertCommandFailure(getPersonCommand, model, Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + + @Test + public void equals() { + GetPersonCommand getPersonFirstCommand = new GetPersonCommand(TASK_FIRST_INDEX); + GetPersonCommand getPersonSecondCommand = new GetPersonCommand(TASK_SECOND_INDEX); + + // same object -> returns true + assertTrue(getPersonFirstCommand.equals(getPersonFirstCommand)); + + // same values -> returns true + GetPersonCommand getPersonFirstCommandCopy = new GetPersonCommand(TASK_FIRST_INDEX); + assertTrue(getPersonFirstCommand.equals(getPersonFirstCommandCopy)); + + // different types -> returns false + assertFalse(getPersonFirstCommand.equals(1)); + + // null -> returns false + assertFalse(getPersonFirstCommand.equals(null)); + + // different person -> returns false + assertFalse(getPersonFirstCommand.equals(getPersonSecondCommand)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/ListTaskCommandTest.java b/src/test/java/seedu/address/logic/commands/task/ListTaskCommandTest.java new file mode 100644 index 00000000000..5c6329c35bf --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/ListTaskCommandTest.java @@ -0,0 +1,43 @@ +package seedu.address.logic.commands.task; + +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.logic.commands.CommandTestUtil.showTaskAtIndex; +import static seedu.address.testutil.TypicalIndexes.TASK_SECOND_INDEX; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +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.StrategyBoard; +import seedu.address.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for ListTaskCommand. + */ +public class ListTaskCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + expectedModel = new ModelManager(model.getAddressBook(), model.getTaskBook(), + new StrategyBoard(), new UserPrefs()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(new ListTaskCommand(), model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + showTaskAtIndex(model, TASK_SECOND_INDEX); + assertCommandSuccess(new ListTaskCommand(), model, ListTaskCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/task/SortTaskByDateCommandTest.java b/src/test/java/seedu/address/logic/commands/task/SortTaskByDateCommandTest.java new file mode 100644 index 00000000000..a8284a0322b --- /dev/null +++ b/src/test/java/seedu/address/logic/commands/task/SortTaskByDateCommandTest.java @@ -0,0 +1,89 @@ +package seedu.address.logic.commands.task; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.address.testutil.TypicalTasks.EIGHTH_TASK; +import static seedu.address.testutil.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.NINTH_TASK; +import static seedu.address.testutil.TypicalTasks.SECOND_TASK; +import static seedu.address.testutil.TypicalTasks.THIRD_TASK; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +import java.util.Arrays; + +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.StrategyBoard; +import seedu.address.model.TaskBook; +import seedu.address.model.UserPrefs; + +class SortTaskByDateCommandTest { + + // Test model + private Model model = new ModelManager(getTypicalAddressBook(), getTypicalTaskBook(), + new StrategyBoard(), new UserPrefs()); + + @Test + void execute_sortTaskByDate_success() { + + SortTaskByDateCommand sortTaskByDateCommand = new SortTaskByDateCommand(); + + String expectedMessage = String.format(SortTaskByDateCommand.MESSAGE_SORT_TASKS_SUCCESS); + + // Creating expected TaskBook + TaskBook expectedTaskBook = new TaskBook(); + expectedTaskBook.setTasks(Arrays.asList(SECOND_TASK, THIRD_TASK, FIRST_TASK)); + + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setTaskBook(expectedTaskBook); + + assertCommandSuccess(sortTaskByDateCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_sortTaskByDateWithSameDay_success() { + + SortTaskByDateCommand sortTaskByDateCommand = new SortTaskByDateCommand(); + + String expectedMessage = String.format(SortTaskByDateCommand.MESSAGE_SORT_TASKS_SUCCESS); + + // Test taskbook + TaskBook testTaskBook = new TaskBook(); + testTaskBook.setTasks(Arrays.asList(EIGHTH_TASK, NINTH_TASK)); + + TaskBook expectedTaskBook = new TaskBook(); + expectedTaskBook.setTasks(Arrays.asList(NINTH_TASK, EIGHTH_TASK)); + + // Test model + Model model = new ModelManager(getTypicalAddressBook(), testTaskBook, + new StrategyBoard(), new UserPrefs()); + + // Expected model + Model expectedModel = new ModelManager( + new AddressBook(model.getAddressBook()), + new TaskBook(model.getTaskBook()), + new StrategyBoard(), + new UserPrefs()); + expectedModel.setTaskBook(expectedTaskBook); + + assertCommandSuccess(sortTaskByDateCommand, model, expectedMessage, expectedModel); + + } + + @Test + public void equals() { + final SortTaskByDateCommand command = new SortTaskByDateCommand(); + + // If they are the same objects, they are equal + assertTrue(command.equals(command)); + + } +} diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java deleted file mode 100644 index d9659205b57..00000000000 --- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package seedu.address.logic.parser; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -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.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; - -public class AddressBookParserTest { - - private final AddressBookParser parser = new AddressBookParser(); - - @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); - } - - @Test - public void parseCommand_clear() throws Exception { - assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD) instanceof ClearCommand); - assertTrue(parser.parseCommand(ClearCommand.COMMAND_WORD + " 3") instanceof ClearCommand); - } - - @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); - } - - @Test - public void parseCommand_edit() throws Exception { - Person person = new PersonBuilder().build(); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); - EditCommand command = (EditCommand) parser.parseCommand(EditCommand.COMMAND_WORD + " " - + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); - assertEquals(new EditCommand(INDEX_FIRST_PERSON, descriptor), command); - } - - @Test - public void parseCommand_exit() throws Exception { - assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand); - assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand); - } - - @Test - public void parseCommand_find() throws Exception { - List keywords = Arrays.asList("foo", "bar", "baz"); - FindCommand command = (FindCommand) parser.parseCommand( - FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); - assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command); - } - - @Test - public void parseCommand_help() throws Exception { - assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); - assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); - } - - @Test - public void parseCommand_list() throws Exception { - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand); - assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand); - } - - @Test - public void parseCommand_unrecognisedInput_throwsParseException() { - assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () - -> parser.parseCommand("")); - } - - @Test - public void parseCommand_unknownCommand_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand")); - } -} diff --git a/src/test/java/seedu/address/logic/parser/Coach2K22ParserTest.java b/src/test/java/seedu/address/logic/parser/Coach2K22ParserTest.java new file mode 100644 index 00000000000..649b76f2949 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/Coach2K22ParserTest.java @@ -0,0 +1,202 @@ +package seedu.address.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; +import static seedu.address.testutil.TypicalIndexes.NOTE_FIRST_INDEX; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.EditPersonDescriptor; +import seedu.address.logic.commands.ExitCommand; +import seedu.address.logic.commands.HelpCommand; +import seedu.address.logic.commands.person.AddPersonCommand; +import seedu.address.logic.commands.person.ClearPersonCommand; +import seedu.address.logic.commands.person.DeletePersonCommand; +import seedu.address.logic.commands.person.EditPersonCommand; +import seedu.address.logic.commands.person.FindPersonCommand; +import seedu.address.logic.commands.person.ListPersonCommand; +import seedu.address.logic.commands.person.SortStrengthCommand; +import seedu.address.logic.commands.person.SortWeaknessCommand; +import seedu.address.logic.commands.person.notecommands.AddMiscCommand; +import seedu.address.logic.commands.person.notecommands.AddStrengthCommand; +import seedu.address.logic.commands.person.notecommands.AddWeaknessCommand; +import seedu.address.logic.commands.person.notecommands.DeleteMiscCommand; +import seedu.address.logic.commands.person.notecommands.DeleteStrengthCommand; +import seedu.address.logic.commands.person.notecommands.DeleteWeaknessCommand; +import seedu.address.logic.commands.strategy.LoadCourtCommand; +import seedu.address.logic.commands.task.ClearTaskCommand; +import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.image.Image; +import seedu.address.model.name.PersonNameContainsKeywordsPredicate; +import seedu.address.model.note.Note; +import seedu.address.model.person.Person; +import seedu.address.model.tag.PersonTagContainsKeywordsPredicate; +import seedu.address.testutil.EditPersonDescriptorBuilder; +import seedu.address.testutil.PersonBuilder; +import seedu.address.testutil.PersonUtil; +import seedu.address.testutil.TestImageCreator; + +public class Coach2K22ParserTest { + + private final Coach2K22Parser parser = new Coach2K22Parser(); + private final Note noteStub = new Note("Note Stub 1"); + + @Test + public void parseCommand_add() throws Exception { + Person person = new PersonBuilder().build(); + AddPersonCommand command = (AddPersonCommand) parser.parseCommand(PersonUtil.getAddCommand(person)); + assertEquals(new AddPersonCommand(person), command); + } + + @Test + public void parseCommand_clear() throws Exception { + assertTrue(parser.parseCommand(ClearPersonCommand.COMMAND_WORD) instanceof ClearPersonCommand); + assertTrue(parser.parseCommand(ClearPersonCommand.COMMAND_WORD + " 3") instanceof ClearPersonCommand); + } + + @Test + public void parseCommand_clearTask() throws Exception { + assertTrue(parser.parseCommand(ClearTaskCommand.COMMAND_WORD + + " " + PREFIX_DATE + "10-10-2022") instanceof ClearTaskCommand); + } + + @Test + public void parseCommand_delete() throws Exception { + DeletePersonCommand command = (DeletePersonCommand) parser.parseCommand( + DeletePersonCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased()); + assertEquals(new DeletePersonCommand(INDEX_FIRST_PERSON), command); + } + + @Test + public void parseCommand_addStrength() throws Exception { + AddStrengthCommand command = (AddStrengthCommand) parser.parseCommand( + AddStrengthCommand.COMMAND_WORD + " " + INDEX_SECOND_PERSON.getOneBased() + + " " + noteStub); + assertEquals(new AddStrengthCommand(INDEX_SECOND_PERSON, noteStub), command); + } + + @Test + public void parseCommand_addWeakness() throws Exception { + AddWeaknessCommand command = (AddWeaknessCommand) parser.parseCommand( + AddWeaknessCommand.COMMAND_WORD + " " + INDEX_SECOND_PERSON.getOneBased() + + " " + noteStub); + assertEquals(new AddWeaknessCommand(INDEX_SECOND_PERSON, noteStub), command); + } + + @Test + public void parseCommand_addMisc() throws Exception { + AddMiscCommand command = (AddMiscCommand) parser.parseCommand( + AddMiscCommand.COMMAND_WORD + " " + INDEX_SECOND_PERSON.getOneBased() + + " " + noteStub); + assertEquals(new AddMiscCommand(INDEX_SECOND_PERSON, noteStub), command); + } + + @Test + public void parseCommand_deleteStrength() throws Exception { + DeleteStrengthCommand command = (DeleteStrengthCommand) parser.parseCommand( + DeleteStrengthCommand.COMMAND_WORD + " " + INDEX_SECOND_PERSON.getOneBased() + + " " + NOTE_FIRST_INDEX.getOneBased()); + assertEquals(new DeleteStrengthCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX), command); + } + + @Test + public void parseCommand_deleteWeakness() throws Exception { + DeleteWeaknessCommand command = (DeleteWeaknessCommand) parser.parseCommand( + DeleteWeaknessCommand.COMMAND_WORD + " " + INDEX_SECOND_PERSON.getOneBased() + + " " + NOTE_FIRST_INDEX.getOneBased()); + assertEquals(new DeleteWeaknessCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX), command); + } + + @Test + public void parseCommand_deleteMisc() throws Exception { + DeleteMiscCommand command = (DeleteMiscCommand) parser.parseCommand( + DeleteMiscCommand.COMMAND_WORD + " " + INDEX_SECOND_PERSON.getOneBased() + + " " + NOTE_FIRST_INDEX.getOneBased()); + assertEquals(new DeleteMiscCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX), command); + } + + @Test + public void parseCommand_loadCourt() throws Exception { + TestImageCreator.createTestImage(); + + Image imageStub = TestImageCreator.getTestImage(); + String imageName = imageStub.imageName; + + LoadCourtCommand command = (LoadCourtCommand) parser.parseCommand( + LoadCourtCommand.COMMAND_WORD + " " + imageName + ); + assertEquals(new LoadCourtCommand(imageStub), command); + + TestImageCreator.deleteTestImage(); + + } + + @Test + public void parseCommand_edit() throws Exception { + Person person = new PersonBuilder().build(); + EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(person).build(); + EditPersonCommand command = (EditPersonCommand) parser.parseCommand(EditPersonCommand.COMMAND_WORD + " " + + INDEX_FIRST_PERSON.getOneBased() + " " + PersonUtil.getEditPersonDescriptorDetails(descriptor)); + assertEquals(new EditPersonCommand(INDEX_FIRST_PERSON, descriptor), command); + } + + @Test + public void parseCommand_exit() throws Exception { + assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD) instanceof ExitCommand); + assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand); + } + + @Test + public void parseCommand_find() throws Exception { + List nameList = Arrays.asList("foo", "bar", "baz"); + List tagList = Arrays.asList("friends", "colleagues"); + FindPersonCommand command = (FindPersonCommand) parser.parseCommand( + FindPersonCommand.COMMAND_WORD + " " + "n/foo n/bar n/baz t/friends t/colleagues"); + assertEquals(new FindPersonCommand(new PersonNameContainsKeywordsPredicate(nameList), + new PersonTagContainsKeywordsPredicate(tagList)), command); + } + + @Test + public void parseCommand_help() throws Exception { + assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand); + assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD + " 3") instanceof HelpCommand); + } + + @Test + public void parseCommand_list() throws Exception { + assertTrue(parser.parseCommand(ListPersonCommand.COMMAND_WORD) instanceof ListPersonCommand); + assertTrue(parser.parseCommand(ListPersonCommand.COMMAND_WORD + " 3") instanceof ListPersonCommand); + } + + @Test + public void parseCommand_sortStrength() throws Exception { + assertTrue(parser.parseCommand(SortStrengthCommand.COMMAND_WORD) instanceof SortStrengthCommand); + assertTrue(parser.parseCommand(SortStrengthCommand.COMMAND_WORD + " 3") instanceof SortStrengthCommand); + } + + @Test + public void parseCommand_sortWeakness() throws Exception { + assertTrue(parser.parseCommand(SortWeaknessCommand.COMMAND_WORD) instanceof SortWeaknessCommand); + assertTrue(parser.parseCommand(SortWeaknessCommand.COMMAND_WORD + " 3") instanceof SortWeaknessCommand); + } + + @Test + public void parseCommand_unrecognisedInput_throwsParseException() { + assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), () + -> parser.parseCommand("")); + } + + @Test + public void parseCommand_unknownCommand_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand")); + } +} diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java deleted file mode 100644 index 70f4f0e79c4..00000000000 --- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -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 { - - private FindCommandParser parser = new FindCommandParser(); - - @Test - public void parse_emptyArg_throwsParseException() { - assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE)); - } - - @Test - public void parse_validArgs_returnsFindCommand() { - // no leading and trailing whitespaces - FindCommand expectedFindCommand = - new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"))); - assertParseSuccess(parser, "Alice Bob", expectedFindCommand); - - // multiple whitespaces between keywords - assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand); - } - -} diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java index 4256788b1a7..aaa8f3baa7d 100644 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java @@ -3,8 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; +import static seedu.address.logic.parser.ParserUtil.MESSAGE_INVALID_NOTE_INDEX; import static seedu.address.testutil.Assert.assertThrows; import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; +import static seedu.address.testutil.TypicalIndexes.NOTE_FIRST_INDEX; import java.util.Arrays; import java.util.Collections; @@ -14,11 +16,13 @@ import org.junit.jupiter.api.Test; import seedu.address.logic.parser.exceptions.ParseException; +import seedu.address.model.image.Image; +import seedu.address.model.name.Name; 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.TestImageCreator; public class ParserUtilTest { private static final String INVALID_NAME = "R@chel"; @@ -27,7 +31,8 @@ public class ParserUtilTest { private static final String INVALID_EMAIL = "example.com"; private static final String INVALID_TAG = "#friend"; - private static final String VALID_NAME = "Rachel Walker"; + private static final String VALID_NAME_1 = "Alex Yeoh"; + private static final String VALID_NAME_2 = "Rachel Walker"; private static final String VALID_PHONE = "123456"; private static final String VALID_ADDRESS = "123 Main Street #0505"; private static final String VALID_EMAIL = "rachel@example.com"; @@ -56,6 +61,26 @@ public void parseIndex_validInput_success() throws Exception { assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 ")); } + @Test + public void parseNoteIndex_invalidInput_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseNoteIndex("10 a")); + } + + @Test + public void parseNoteIndex_outOfRangeInput_throwsParseException() { + assertThrows(ParseException.class, MESSAGE_INVALID_NOTE_INDEX, () + -> ParserUtil.parseNoteIndex(Long.toString(Integer.MAX_VALUE + 1))); + } + + @Test + public void parseNoteIndex_validInput_success() throws Exception { + // No whitespaces + assertEquals(NOTE_FIRST_INDEX, ParserUtil.parseNoteIndex("1")); + + // Leading and trailing whitespaces + assertEquals(NOTE_FIRST_INDEX, ParserUtil.parseIndex(" 1 ")); + } + @Test public void parseName_null_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null)); @@ -68,14 +93,14 @@ public void parseName_invalidValue_throwsParseException() { @Test public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(VALID_NAME)); + Name expectedName = new Name(VALID_NAME_1); + assertEquals(expectedName, ParserUtil.parseName(VALID_NAME_1)); } @Test public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception { - String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE; - Name expectedName = new Name(VALID_NAME); + String nameWithWhitespace = WHITESPACE + VALID_NAME_1 + WHITESPACE; + Name expectedName = new Name(VALID_NAME_1); assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace)); } @@ -193,4 +218,28 @@ public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { assertEquals(expectedTagSet, actualTagSet); } + + @Test + public void parseImage_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseImage(null)); + } + + @Test + public void parseImage_invalidArgs_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseImage(" ")); + assertThrows(ParseException.class, () -> ParserUtil.parseImage("/")); + assertThrows(ParseException.class, () -> ParserUtil.parseImage("<.jpg")); + } + + @Test + public void parseImage_validArgs_returnsImage() throws ParseException { + TestImageCreator testImageCreatorFile = new TestImageCreator(); + Image expectedImage = testImageCreatorFile.getTestImage(); + String testImageName = expectedImage.imageName; + testImageCreatorFile.createTestImage(); + + assertEquals(expectedImage, ParserUtil.parseImage(testImageName)); + + testImageCreatorFile.deleteTestImage(); + } } diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/AddPersonCommandParserTest.java similarity index 87% rename from src/test/java/seedu/address/logic/parser/AddCommandParserTest.java rename to src/test/java/seedu/address/logic/parser/person/AddPersonCommandParserTest.java index 5cf487d7ebb..c6beb9cddaf 100644 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/person/AddPersonCommandParserTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.address.logic.parser.person; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; @@ -31,17 +31,17 @@ import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.AddCommand; +import seedu.address.logic.commands.person.AddPersonCommand; +import seedu.address.model.name.Name; 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; -public class AddCommandParserTest { - private AddCommandParser parser = new AddCommandParser(); +public class AddPersonCommandParserTest { + private AddPersonCommandParser parser = new AddPersonCommandParser(); @Test public void parse_allFieldsPresent_success() { @@ -49,29 +49,29 @@ public void parse_allFieldsPresent_success() { // 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)); + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddPersonCommand(expectedPerson)); // multiple names - last name accepted assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddPersonCommand(expectedPerson)); // multiple phones - last phone accepted assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddPersonCommand(expectedPerson)); // multiple emails - last email accepted assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddPersonCommand(expectedPerson)); // multiple addresses - last address accepted assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_AMY - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); + + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddPersonCommand(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)); + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddPersonCommand(expectedPersonMultipleTags)); } @Test @@ -79,12 +79,12 @@ 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)); + new AddPersonCommand(expectedPerson)); } @Test public void parse_compulsoryFieldMissing_failure() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonCommand.MESSAGE_USAGE); // missing name prefix assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, @@ -136,6 +136,6 @@ public void parse_invalidValue_failure() { // 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, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonCommand.MESSAGE_USAGE)); } } diff --git a/src/test/java/seedu/address/logic/parser/person/AddPersonTagCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/AddPersonTagCommandParserTest.java new file mode 100644 index 00000000000..ce883fce7ff --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/AddPersonTagCommandParserTest.java @@ -0,0 +1,30 @@ +package seedu.address.logic.parser.person; + +import static seedu.address.commons.core.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 org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.AddPersonTagCommand; + +class AddPersonTagCommandParserTest { + + private static final String TAG1 = "TAG1"; + private AddPersonTagCommandParser parser = new AddPersonTagCommandParser(); + + + @Test + void parse_validArgs_returnsAddTagCommand() { + AddPersonTagCommand expectedAddTagCommand = new AddPersonTagCommand(INDEX_FIRST_PERSON, TAG1); + assertParseSuccess(parser, " 1 TAG1", expectedAddTagCommand); + } + + @Test + void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, + " 1 t/friend", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddPersonTagCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/DeletePersonCommandParserTest.java similarity index 66% rename from src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java rename to src/test/java/seedu/address/logic/parser/person/DeletePersonCommandParserTest.java index 27eaec84450..d2ad76a2f2e 100644 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/person/DeletePersonCommandParserTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.address.logic.parser.person; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; @@ -7,7 +7,7 @@ import org.junit.jupiter.api.Test; -import seedu.address.logic.commands.DeleteCommand; +import seedu.address.logic.commands.person.DeletePersonCommand; /** * As we are only doing white-box testing, our test cases do not cover path variations @@ -16,17 +16,18 @@ * The path variation for those two cases occur inside the ParserUtil, and * therefore should be covered by the ParserUtilTest. */ -public class DeleteCommandParserTest { +public class DeletePersonCommandParserTest { - private DeleteCommandParser parser = new DeleteCommandParser(); + private DeletePersonCommandParser parser = new DeletePersonCommandParser(); @Test public void parse_validArgs_returnsDeleteCommand() { - assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); + assertParseSuccess(parser, "1", new DeletePersonCommand(INDEX_FIRST_PERSON)); } @Test public void parse_invalidArgs_throwsParseException() { - assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); + assertParseFailure(parser, "a", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeletePersonCommand.MESSAGE_USAGE)); } } diff --git a/src/test/java/seedu/address/logic/parser/person/DeletePersonTagCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/DeletePersonTagCommandParserTest.java new file mode 100644 index 00000000000..2223d24cab9 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/DeletePersonTagCommandParserTest.java @@ -0,0 +1,30 @@ +package seedu.address.logic.parser.person; + +import static seedu.address.commons.core.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 org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.DeletePersonTagCommand; + +class DeletePersonTagCommandParserTest { + + private static final String TAG1 = "friends"; + private DeletePersonTagCommandParser parser = new DeletePersonTagCommandParser(); + + + @Test + void parse_validArgs_returnsDeleteTagCommand() { + DeletePersonTagCommand expectedDeleteTagCommand = new DeletePersonTagCommand(INDEX_FIRST_PERSON, TAG1); + assertParseSuccess(parser, " 1 friends", expectedDeleteTagCommand); + } + + @Test + void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, + " 1 t/friend", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeletePersonTagCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/EditPersonCommandParserTest.java similarity index 87% rename from src/test/java/seedu/address/logic/parser/EditCommandParserTest.java rename to src/test/java/seedu/address/logic/parser/person/EditPersonCommandParserTest.java index 2ff31522486..860ca6870ea 100644 --- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java +++ b/src/test/java/seedu/address/logic/parser/person/EditPersonCommandParserTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.address.logic.parser.person; import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; @@ -34,23 +34,23 @@ import org.junit.jupiter.api.Test; import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.EditPersonDescriptor; +import seedu.address.logic.commands.person.EditPersonCommand; +import seedu.address.model.name.Name; 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; -public class EditCommandParserTest { +public class EditPersonCommandParserTest { private static final String TAG_EMPTY = " " + PREFIX_TAG; private static final String MESSAGE_INVALID_FORMAT = - String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditPersonCommand.MESSAGE_USAGE); - private EditCommandParser parser = new EditCommandParser(); + private EditPersonCommandParser parser = new EditPersonCommandParser(); @Test public void parse_missingParts_failure() { @@ -58,7 +58,7 @@ public void parse_missingParts_failure() { assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT); // no field specified - assertParseFailure(parser, "1", EditCommand.MESSAGE_NOT_EDITED); + assertParseFailure(parser, "1", EditPersonCommand.MESSAGE_NOT_EDITED); // no index and no field specified assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); @@ -114,7 +114,7 @@ public void parse_allFieldsSpecified_success() { 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); + EditPersonCommand expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); } @@ -126,7 +126,7 @@ public void parse_someFieldsSpecified_success() { EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) .withEmail(VALID_EMAIL_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + EditPersonCommand expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); } @@ -137,31 +137,31 @@ public void parse_oneFieldSpecified_success() { 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); + EditPersonCommand expectedCommand = new EditPersonCommand(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); + expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); // email userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); + expectedCommand = new EditPersonCommand(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); + expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); // tags userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); + expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); } @@ -175,7 +175,7 @@ public void parse_multipleRepeatedFields_acceptsLast() { EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) .build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + EditPersonCommand expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); } @@ -186,7 +186,7 @@ public void parse_invalidValueFollowedByValidValue_success() { Index targetIndex = INDEX_FIRST_PERSON; String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB; EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + EditPersonCommand expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); // other valid values specified @@ -194,7 +194,7 @@ public void parse_invalidValueFollowedByValidValue_success() { + PHONE_DESC_BOB; descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) .withAddress(VALID_ADDRESS_BOB).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); + expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); } @@ -204,7 +204,7 @@ public void parse_resetTags_success() { String userInput = targetIndex.getOneBased() + TAG_EMPTY; EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); + EditPersonCommand expectedCommand = new EditPersonCommand(targetIndex, descriptor); assertParseSuccess(parser, userInput, expectedCommand); } diff --git a/src/test/java/seedu/address/logic/parser/person/FindPersonCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/FindPersonCommandParserTest.java new file mode 100644 index 00000000000..3012bb5abfb --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/FindPersonCommandParserTest.java @@ -0,0 +1,40 @@ +package seedu.address.logic.parser.person; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.FindPersonCommand; +import seedu.address.model.name.PersonNameContainsKeywordsPredicate; +import seedu.address.model.tag.PersonTagContainsKeywordsPredicate; + +public class FindPersonCommandParserTest { + + private FindPersonCommandParser parser = new FindPersonCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindPersonCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsFindCommand() { + List nameList = Arrays.asList("Alex", "Bob"); + List tagList = Arrays.asList("friends", "neighbours"); + // no leading and trailing whitespaces + FindPersonCommand expectedFindCommand = + new FindPersonCommand(new PersonNameContainsKeywordsPredicate(nameList), + new PersonTagContainsKeywordsPredicate(tagList)); + assertParseSuccess(parser, " n/Alex n/Bob t/friends t/neighbours", expectedFindCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " n/Alex n/Bob t/friends t/neighbours ", expectedFindCommand); + } + +} diff --git a/src/test/java/seedu/address/logic/parser/person/notecommand/AddMiscCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/notecommand/AddMiscCommandParserTest.java new file mode 100644 index 00000000000..ed03d2b2f52 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/notecommand/AddMiscCommandParserTest.java @@ -0,0 +1,44 @@ +package seedu.address.logic.parser.person.notecommand; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NOTE_AMY; +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 org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.notecommands.AddMiscCommand; +import seedu.address.logic.parser.person.notecommands.AddMiscCommandParser; +import seedu.address.model.note.Note; + +class AddMiscCommandParserTest { + + private AddMiscCommandParser parser = new AddMiscCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMiscCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsAddMiscCommand() { + // no leading and trailing whitespaces + AddMiscCommand expectedAddMiscCommand = + new AddMiscCommand(INDEX_FIRST_PERSON, new Note(VALID_NOTE_AMY)); + assertParseSuccess(parser, "1 Amy", expectedAddMiscCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n 1 \n \t Amy \t", expectedAddMiscCommand); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "2 ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMiscCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, " Amy", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddMiscCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/person/notecommand/AddStrengthCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/notecommand/AddStrengthCommandParserTest.java new file mode 100644 index 00000000000..869cdfe3807 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/notecommand/AddStrengthCommandParserTest.java @@ -0,0 +1,44 @@ +package seedu.address.logic.parser.person.notecommand; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NOTE_AMY; +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 org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.notecommands.AddStrengthCommand; +import seedu.address.logic.parser.person.notecommands.AddStrengthCommandParser; +import seedu.address.model.note.Note; + +class AddStrengthCommandParserTest { + + private AddStrengthCommandParser parser = new AddStrengthCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStrengthCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsAddStrengthCommand() { + // no leading and trailing whitespaces + AddStrengthCommand expectedAddStrengthCommand = + new AddStrengthCommand(INDEX_FIRST_PERSON, new Note(VALID_NOTE_AMY)); + assertParseSuccess(parser, "1 Amy", expectedAddStrengthCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n 1 \n \t Amy \t", expectedAddStrengthCommand); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "2 ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStrengthCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, " Amy", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddStrengthCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/person/notecommand/AddWeaknessCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/notecommand/AddWeaknessCommandParserTest.java new file mode 100644 index 00000000000..21729835313 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/notecommand/AddWeaknessCommandParserTest.java @@ -0,0 +1,44 @@ +package seedu.address.logic.parser.person.notecommand; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NOTE_AMY; +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 org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.notecommands.AddWeaknessCommand; +import seedu.address.logic.parser.person.notecommands.AddWeaknessCommandParser; +import seedu.address.model.note.Note; + +class AddWeaknessCommandParserTest { + + private AddWeaknessCommandParser parser = new AddWeaknessCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddWeaknessCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsAddWeaknessCommand() { + // no leading and trailing whitespaces + AddWeaknessCommand expectedAddWeaknessCommand = + new AddWeaknessCommand(INDEX_FIRST_PERSON, new Note(VALID_NOTE_AMY)); + assertParseSuccess(parser, "1 Amy", expectedAddWeaknessCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n 1 \n \t Amy \t", expectedAddWeaknessCommand); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "2 ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddWeaknessCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, " Amy", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddWeaknessCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteMiscCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteMiscCommandParserTest.java new file mode 100644 index 00000000000..28ca533c5df --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteMiscCommandParserTest.java @@ -0,0 +1,37 @@ +package seedu.address.logic.parser.person.notecommand; + +import static seedu.address.commons.core.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_SECOND_PERSON; +import static seedu.address.testutil.TypicalIndexes.NOTE_FIRST_INDEX; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.notecommands.DeleteMiscCommand; +import seedu.address.logic.parser.person.notecommands.DeleteMiscCommandParser; + +/** + * Contains tests for + * {@code DeleteMiscCommandParser}. + */ +public class DeleteMiscCommandParserTest { + private final DeleteMiscCommandParser parser = new DeleteMiscCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteCommand() { + assertParseSuccess(parser, "2 1", new DeleteMiscCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "2", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteMiscCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_emptyArgs_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteMiscCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteStrengthCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteStrengthCommandParserTest.java new file mode 100644 index 00000000000..7228e84a580 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteStrengthCommandParserTest.java @@ -0,0 +1,37 @@ +package seedu.address.logic.parser.person.notecommand; + +import static seedu.address.commons.core.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_SECOND_PERSON; +import static seedu.address.testutil.TypicalIndexes.NOTE_FIRST_INDEX; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.notecommands.DeleteStrengthCommand; +import seedu.address.logic.parser.person.notecommands.DeleteStrengthCommandParser; + +/** + * Contains tests for + * {@code DeleteStrengthCommandParser}. + */ +public class DeleteStrengthCommandParserTest { + private final DeleteStrengthCommandParser parser = new DeleteStrengthCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteCommand() { + assertParseSuccess(parser, "2 1", new DeleteStrengthCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "2", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteStrengthCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_emptyArgs_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteStrengthCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteWeaknessCommandParserTest.java b/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteWeaknessCommandParserTest.java new file mode 100644 index 00000000000..db8d56b5536 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/person/notecommand/DeleteWeaknessCommandParserTest.java @@ -0,0 +1,37 @@ +package seedu.address.logic.parser.person.notecommand; + +import static seedu.address.commons.core.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_SECOND_PERSON; +import static seedu.address.testutil.TypicalIndexes.NOTE_FIRST_INDEX; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.person.notecommands.DeleteWeaknessCommand; +import seedu.address.logic.parser.person.notecommands.DeleteWeaknessCommandParser; + +/** + * Contains tests for + * {@code DeleteWeaknessCommandParser}. + */ +public class DeleteWeaknessCommandParserTest { + private final DeleteWeaknessCommandParser parser = new DeleteWeaknessCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteCommand() { + assertParseSuccess(parser, "2 1", new DeleteWeaknessCommand(INDEX_SECOND_PERSON, NOTE_FIRST_INDEX)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "2", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteWeaknessCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_emptyArgs_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteWeaknessCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/strategy/LoadCourtCommandParserTest.java b/src/test/java/seedu/address/logic/parser/strategy/LoadCourtCommandParserTest.java new file mode 100644 index 00000000000..956d3f33b33 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/strategy/LoadCourtCommandParserTest.java @@ -0,0 +1,44 @@ +package seedu.address.logic.parser.strategy; +import static seedu.address.commons.core.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.strategy.LoadCourtCommand; +import seedu.address.model.image.Image; +import seedu.address.testutil.TestImageCreator; + +public class LoadCourtCommandParserTest { + private LoadCourtCommandParser parser = new LoadCourtCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, LoadCourtCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsLoadCourtCommand() { + TestImageCreator.createTestImage(); + Image testImage = TestImageCreator.getTestImage(); + String testImageName = testImage.imageName; + LoadCourtCommand expectedLoadCourtCommand = new LoadCourtCommand(testImage); + + assertParseSuccess(parser, testImageName, expectedLoadCourtCommand); + + TestImageCreator.deleteTestImage(); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, ".jpg", String.format(//'<' symbol is typically illegal for file names + MESSAGE_INVALID_COMMAND_FORMAT, LoadCourtCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, "/", String.format( + MESSAGE_INVALID_COMMAND_FORMAT, LoadCourtCommand.MESSAGE_USAGE)); + + assertParseFailure(parser, "", String.format( + MESSAGE_INVALID_COMMAND_FORMAT, LoadCourtCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/AddTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/AddTaskCommandParserTest.java new file mode 100644 index 00000000000..e44fc30d79e --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/AddTaskCommandParserTest.java @@ -0,0 +1,144 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.ENDTIME_DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.ENDTIME_DESC_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_DATE_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_ENDTIME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_NAME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_STARTTIME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_TAG_DESC; +import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_TASK2; +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.STARTTIME_DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.STARTTIME_DESC_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_EVENT; +import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_MEET; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ENDTIME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_STARTTIME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_EVENT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_MEET; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalTasks.TASK1; +import static seedu.address.testutil.TypicalTasks.TASK2; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.AddTaskCommand; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; +import seedu.address.testutil.TaskBuilder; + +public class AddTaskCommandParserTest { + private AddTaskCommandParser parser = new AddTaskCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Task expectedTask = new TaskBuilder(TASK2).withTags(VALID_TAG_MEET).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_TASK2 + DATE_DESC_TASK2 + + STARTTIME_DESC_TASK2 + ENDTIME_DESC_TASK2 + TAG_DESC_MEET, new AddTaskCommand(expectedTask)); + + // multiple names - last name accepted + assertParseSuccess(parser, NAME_DESC_TASK1 + NAME_DESC_TASK2 + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + + ENDTIME_DESC_TASK2 + TAG_DESC_MEET, new AddTaskCommand(expectedTask)); + + // multiple date - last date accepted + assertParseSuccess(parser, NAME_DESC_TASK2 + DATE_DESC_TASK1 + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + + ENDTIME_DESC_TASK2 + TAG_DESC_MEET, new AddTaskCommand(expectedTask)); + + // multiple starttime - last starttime accepted + assertParseSuccess(parser, NAME_DESC_TASK2 + DATE_DESC_TASK2 + STARTTIME_DESC_TASK1 + + STARTTIME_DESC_TASK2 + ENDTIME_DESC_TASK2 + TAG_DESC_MEET, new AddTaskCommand(expectedTask)); + + // multiple endtime - last endtime accepted + assertParseSuccess(parser, NAME_DESC_TASK2 + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + + ENDTIME_DESC_TASK1 + ENDTIME_DESC_TASK2 + TAG_DESC_MEET, new AddTaskCommand(expectedTask)); + + // multiple tags - all accepted + Task expectedTaskMultipleTags = new TaskBuilder(TASK2).withTags(VALID_TAG_EVENT, VALID_TAG_MEET) + .build(); + assertParseSuccess(parser, NAME_DESC_TASK2 + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + + ENDTIME_DESC_TASK2 + TAG_DESC_EVENT + TAG_DESC_MEET, new AddTaskCommand(expectedTaskMultipleTags)); + } + + @Test + public void parse_optionalFieldsMissing_success() { + // no tags + Task expectedTask = new TaskBuilder(TASK1).withTags().build(); + assertParseSuccess(parser, + NAME_DESC_TASK1 + DATE_DESC_TASK1 + STARTTIME_DESC_TASK1 + ENDTIME_DESC_TASK1, + new AddTaskCommand(expectedTask)); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE); + + // missing name prefix + assertParseFailure(parser, VALID_NAME_TASK1 + DATE_DESC_TASK1 + STARTTIME_DESC_TASK1 + + ENDTIME_DESC_TASK1, expectedMessage); + + // missing date prefix + assertParseFailure(parser, NAME_DESC_TASK1 + VALID_DATE_TASK1 + STARTTIME_DESC_TASK1 + + ENDTIME_DESC_TASK1, expectedMessage); + + // missing starttime prefix + assertParseFailure(parser, NAME_DESC_TASK1 + DATE_DESC_TASK1 + VALID_STARTTIME_TASK1 + + ENDTIME_DESC_TASK1, expectedMessage); + + // missing endtime prefix + assertParseFailure(parser, NAME_DESC_TASK1 + DATE_DESC_TASK1 + STARTTIME_DESC_TASK1 + + VALID_ENDTIME_TASK1, expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_NAME_TASK1 + VALID_DATE_TASK1 + VALID_STARTTIME_TASK1 + + VALID_ENDTIME_TASK1, expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + // invalid name + assertParseFailure(parser, INVALID_TASK_NAME_DESC + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + + ENDTIME_DESC_TASK2 + TAG_DESC_MEET + TAG_DESC_EVENT, Name.MESSAGE_CONSTRAINTS); + + // invalid Date + assertParseFailure(parser, NAME_DESC_TASK2 + INVALID_TASK_DATE_DESC + STARTTIME_DESC_TASK2 + + ENDTIME_DESC_TASK2 + TAG_DESC_MEET + TAG_DESC_EVENT, Date.MESSAGE_CONSTRAINTS); + + // invalid StartTime + assertParseFailure(parser, NAME_DESC_TASK2 + DATE_DESC_TASK2 + INVALID_TASK_STARTTIME_DESC + + ENDTIME_DESC_TASK2 + TAG_DESC_MEET + TAG_DESC_EVENT, StartTime.MESSAGE_CONSTRAINTS); + + // invalid EndTime + assertParseFailure(parser, NAME_DESC_TASK2 + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + + INVALID_TASK_ENDTIME_DESC + TAG_DESC_MEET + TAG_DESC_EVENT, EndTime.MESSAGE_CONSTRAINTS); + + // invalid tag + assertParseFailure(parser, NAME_DESC_TASK2 + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + + ENDTIME_DESC_TASK2 + INVALID_TASK_TAG_DESC + VALID_TAG_EVENT, Tag.MESSAGE_CONSTRAINTS); + + // two invalid values, only first invalid value reported + assertParseFailure(parser, INVALID_TASK_NAME_DESC + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + + INVALID_TASK_ENDTIME_DESC, Name.MESSAGE_CONSTRAINTS); + + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_TASK2 + DATE_DESC_TASK2 + + STARTTIME_DESC_TASK2 + ENDTIME_DESC_TASK2 + TAG_DESC_MEET + TAG_DESC_EVENT, + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskCommand.MESSAGE_USAGE)); + } + + +} diff --git a/src/test/java/seedu/address/logic/parser/task/AddTaskTagCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/AddTaskTagCommandParserTest.java new file mode 100644 index 00000000000..ed8c64ed6cc --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/AddTaskTagCommandParserTest.java @@ -0,0 +1,30 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.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 org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.AddTaskTagCommand; + +class AddTaskTagCommandParserTest { + + private static final String TAG1 = "TAG1"; + private AddTaskTagCommandParser parser = new AddTaskTagCommandParser(); + + + @Test + void parse_validArgs_returnsAddTagToTaskCommand() { + AddTaskTagCommand expectedAddTagToTaskCommand = new AddTaskTagCommand(INDEX_FIRST_PERSON, TAG1); + assertParseSuccess(parser, " 1 TAG1", expectedAddTagToTaskCommand); + } + + @Test + void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, + " 1 t/important", // Tags can only be alphanumeric + String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddTaskTagCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/ClearTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/ClearTaskCommandParserTest.java new file mode 100644 index 00000000000..55b4678a70d --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/ClearTaskCommandParserTest.java @@ -0,0 +1,48 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.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.task.ClearTaskCommand; +import seedu.address.model.task.Date; + +/** + * Contains tests for + * {@code ClearTaskCommandParser}. + */ +public class ClearTaskCommandParserTest { + + private final ClearTaskCommandParser parser = new ClearTaskCommandParser(); + private final String dateString = "03-03-2000"; + + @Test + public void parse_validArgs_returnsClearTaskCommand() { + assertParseSuccess(parser, " d/" + dateString, new ClearTaskCommand(new Date(dateString))); + } + + @Test + public void parse_validArgsBlank_returnsClearTaskCommand() { + assertParseSuccess(parser, "", new ClearTaskCommand()); + } + + @Test + public void parse_validArgsWithWhitespace_returnsClearTaskCommand() { + assertParseSuccess(parser, " ", new ClearTaskCommand()); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "2", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ClearTaskCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_invalidArgsNoPrefix_throwsParseException() { + assertParseFailure(parser, dateString, String.format(MESSAGE_INVALID_COMMAND_FORMAT, + ClearTaskCommand.MESSAGE_USAGE)); + } + +} diff --git a/src/test/java/seedu/address/logic/parser/task/DeleteTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/DeleteTaskCommandParserTest.java new file mode 100644 index 00000000000..d9eddd70308 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/DeleteTaskCommandParserTest.java @@ -0,0 +1,26 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.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.TASK_FIRST_INDEX; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.DeleteTaskCommand; + +public class DeleteTaskCommandParserTest { + + private DeleteTaskCommandParser parser = new DeleteTaskCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteTaskCommand() { + assertParseSuccess(parser, "1", new DeleteTaskCommand(TASK_FIRST_INDEX)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + DeleteTaskCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/DeleteTaskTagCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/DeleteTaskTagCommandParserTest.java new file mode 100644 index 00000000000..eba52cf9717 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/DeleteTaskTagCommandParserTest.java @@ -0,0 +1,43 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX; +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 org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.DeleteTaskTagCommand; +import seedu.address.logic.parser.exceptions.ParseException; + +class DeleteTaskTagCommandParserTest { + + private static final String TAG1 = "TAG1"; + private DeleteTaskTagCommandParser parser = new DeleteTaskTagCommandParser(); + + + @Test + void parse_validArgs_returnsDeleteTagFromTasksCommand() { + DeleteTaskTagCommand expectedDeleteTagFromTaskCommand = + new DeleteTaskTagCommand(INDEX_FIRST_PERSON, TAG1); + assertParseSuccess(parser, " 1 TAG1", expectedDeleteTagFromTaskCommand); + } + + @Test + void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, + " 1 t/important", // Tags can only be alphanumeric + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteTaskTagCommand.MESSAGE_USAGE)); + } + + @Test + void parse_outOfBoundIndex_throwsCommandException() { + try { + parser.parse(" 999 important"); + } catch (ParseException e) { + assert e.getMessage() == MESSAGE_INVALID_TASK_DISPLAYED_INDEX; + } + } + +} diff --git a/src/test/java/seedu/address/logic/parser/task/EditTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/EditTaskCommandParserTest.java new file mode 100644 index 00000000000..50d164574e9 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/EditTaskCommandParserTest.java @@ -0,0 +1,192 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.DATE_DESC_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.ENDTIME_DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.ENDTIME_DESC_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_DATE_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_ENDTIME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_NAME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_STARTTIME_DESC; +import static seedu.address.logic.commands.CommandTestUtil.INVALID_TASK_TAG_DESC; +import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.STARTTIME_DESC_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.STARTTIME_DESC_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_EVENT; +import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_MEET; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ENDTIME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ENDTIME_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_STARTTIME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_STARTTIME_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_EVENT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_MEET; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.address.testutil.TypicalIndexes.TASK_FIRST_INDEX; +import static seedu.address.testutil.TypicalIndexes.TASK_SECOND_INDEX; + +import org.junit.jupiter.api.Test; + +import seedu.address.commons.core.index.Index; +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.logic.commands.task.EditTaskCommand; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.testutil.EditTaskDescriptorBuilder; + +public class EditTaskCommandParserTest { + + private static final String TAG_EMPTY = " " + PREFIX_TAG; + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditTaskCommand.MESSAGE_USAGE); + + private EditTaskCommandParser parser = new EditTaskCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified + assertParseFailure(parser, VALID_NAME_TASK1, MESSAGE_INVALID_FORMAT); + + // no field specified + assertParseFailure(parser, "1", EditTaskCommand.MESSAGE_NOT_EDITED); + + // no index and no field specified + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidPreamble_failure() { + // negative index + assertParseFailure(parser, "-5" + NAME_DESC_TASK1, MESSAGE_INVALID_FORMAT); + + // zero index + assertParseFailure(parser, "0" + NAME_DESC_TASK1, MESSAGE_INVALID_FORMAT); + + // invalid arguments being parsed as preamble + assertParseFailure(parser, "1 some string", MESSAGE_INVALID_FORMAT); + + // invalid prefix being parsed as preamble + assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, "1" + INVALID_TASK_NAME_DESC, Name.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + INVALID_TASK_DATE_DESC, Date.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + INVALID_TASK_STARTTIME_DESC, StartTime.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + INVALID_TASK_ENDTIME_DESC, EndTime.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + INVALID_TASK_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag + + // invalid date followed by valid start time + assertParseFailure(parser, "1" + INVALID_TASK_DATE_DESC + STARTTIME_DESC_TASK1, Date.MESSAGE_CONSTRAINTS); + + // valid date followed by invalid date + assertParseFailure(parser, "1" + DATE_DESC_TASK2 + INVALID_TASK_DATE_DESC, Date.MESSAGE_CONSTRAINTS); + + // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Task} being edited, + // parsing it together with a valid tag results in error + assertParseFailure(parser, "1" + TAG_DESC_MEET + TAG_DESC_EVENT + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_DESC_MEET + TAG_EMPTY + TAG_DESC_EVENT, Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_MEET + TAG_DESC_EVENT, Tag.MESSAGE_CONSTRAINTS); + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_TASK_NAME_DESC + INVALID_TASK_DATE_DESC + + VALID_STARTTIME_TASK1 + VALID_DATE_TASK1, + Name.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + Index targetIndex = TASK_SECOND_INDEX; + String userInput = targetIndex.getOneBased() + DATE_DESC_TASK2 + TAG_DESC_MEET + + STARTTIME_DESC_TASK1 + ENDTIME_DESC_TASK1 + NAME_DESC_TASK1 + TAG_DESC_EVENT; + + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withName(VALID_NAME_TASK1) + .withDate(VALID_DATE_TASK2).withStartTime(VALID_STARTTIME_TASK1).withEndTime(VALID_ENDTIME_TASK1) + .withTags(VALID_TAG_MEET, VALID_TAG_EVENT).build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + Index targetIndex = TASK_FIRST_INDEX; + String userInput = targetIndex.getOneBased() + DATE_DESC_TASK2 + STARTTIME_DESC_TASK1; + + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withDate(VALID_DATE_TASK2) + .withStartTime(VALID_STARTTIME_TASK1).build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + // name + Index targetIndex = TASK_SECOND_INDEX; + String userInput = targetIndex.getOneBased() + NAME_DESC_TASK1; + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withName(VALID_NAME_TASK1).build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // Date + userInput = targetIndex.getOneBased() + DATE_DESC_TASK1; + descriptor = new EditTaskDescriptorBuilder().withDate(VALID_DATE_TASK1).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // start time + userInput = targetIndex.getOneBased() + STARTTIME_DESC_TASK1; + descriptor = new EditTaskDescriptorBuilder().withStartTime(VALID_STARTTIME_TASK1).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // end time + userInput = targetIndex.getOneBased() + ENDTIME_DESC_TASK1; + descriptor = new EditTaskDescriptorBuilder().withEndTime(VALID_ENDTIME_TASK1).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // tags + userInput = targetIndex.getOneBased() + TAG_DESC_MEET; + descriptor = new EditTaskDescriptorBuilder().withTags(VALID_TAG_MEET).build(); + expectedCommand = new EditTaskCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + Index targetIndex = TASK_FIRST_INDEX; + String userInput = targetIndex.getOneBased() + DATE_DESC_TASK1 + STARTTIME_DESC_TASK1 + ENDTIME_DESC_TASK1 + + TAG_DESC_MEET + DATE_DESC_TASK1 + STARTTIME_DESC_TASK1 + ENDTIME_DESC_TASK1 + TAG_DESC_MEET + + DATE_DESC_TASK2 + STARTTIME_DESC_TASK2 + ENDTIME_DESC_TASK2 + TAG_DESC_EVENT; + + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withDate(VALID_DATE_TASK2) + .withStartTime(VALID_STARTTIME_TASK2).withEndTime(VALID_ENDTIME_TASK2).withTags(VALID_TAG_MEET, + VALID_TAG_EVENT).build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_resetTags_success() { + Index targetIndex = TASK_SECOND_INDEX; + String userInput = targetIndex.getOneBased() + TAG_EMPTY; + + EditTaskDescriptor descriptor = new EditTaskDescriptorBuilder().withTags().build(); + EditTaskCommand expectedCommand = new EditTaskCommand(targetIndex, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/address/logic/parser/task/FindTaskCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/FindTaskCommandParserTest.java new file mode 100644 index 00000000000..abb1989e591 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/FindTaskCommandParserTest.java @@ -0,0 +1,40 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.FindTaskCommand; +import seedu.address.model.name.TaskNameContainsKeywordsPredicate; +import seedu.address.model.tag.TaskTagContainsKeywordsPredicate; + +public class FindTaskCommandParserTest { + + private FindTaskCommandParser parser = new FindTaskCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindTaskCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsFindCommand() { + List nameList = Arrays.asList("Meeting", "Dinner"); + List tagList = Arrays.asList("friends", "neighbours"); + // no leading and trailing whitespaces + FindTaskCommand expectedFindCommand = + new FindTaskCommand(new TaskNameContainsKeywordsPredicate(nameList), + new TaskTagContainsKeywordsPredicate(tagList)); + assertParseSuccess(parser, " n/Meeting n/Dinner t/friends t/neighbours", expectedFindCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " n/Meeting n/Dinner t/friends t/neighbours ", expectedFindCommand); + } + +} diff --git a/src/test/java/seedu/address/logic/parser/task/GetPersonCommandParserTest.java b/src/test/java/seedu/address/logic/parser/task/GetPersonCommandParserTest.java new file mode 100644 index 00000000000..d73a7342a08 --- /dev/null +++ b/src/test/java/seedu/address/logic/parser/task/GetPersonCommandParserTest.java @@ -0,0 +1,33 @@ +package seedu.address.logic.parser.task; + +import static seedu.address.commons.core.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.TASK_FIRST_INDEX; + +import org.junit.jupiter.api.Test; + +import seedu.address.logic.commands.task.GetPersonCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside of the GetPersonCommand code. For example, inputs "1" and "1 abc" take the + * same path through the GetPersonCommand, 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 GetPersonCommandParserTest { + + private GetPersonCommandParser parser = new GetPersonCommandParser(); + + @Test + public void parse_validArgs_returnsGetPersonCommand() { + assertParseSuccess(parser, "1", new GetPersonCommand(TASK_FIRST_INDEX)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + GetPersonCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java index 2cf1418d116..0ecc98736c5 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/seedu/address/model/ModelManagerTest.java @@ -4,9 +4,12 @@ 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_TASKS; 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.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.SECOND_TASK; import java.nio.file.Path; import java.nio.file.Paths; @@ -15,8 +18,10 @@ import org.junit.jupiter.api.Test; import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.NameContainsKeywordsPredicate; +import seedu.address.model.name.PersonNameContainsKeywordsPredicate; +import seedu.address.model.name.TaskNameContainsKeywordsPredicate; import seedu.address.testutil.AddressBookBuilder; +import seedu.address.testutil.TaskBookBuilder; public class ModelManagerTest { @@ -27,6 +32,7 @@ public void constructor() { assertEquals(new UserPrefs(), modelManager.getUserPrefs()); assertEquals(new GuiSettings(), modelManager.getGuiSettings()); assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook())); + assertEquals(new TaskBook(), new TaskBook(modelManager.getTaskBook())); } @Test @@ -72,6 +78,18 @@ public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { assertEquals(path, modelManager.getAddressBookFilePath()); } + @Test + public void setTaskBookFilePath_nullPath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setTaskBookFilePath(null)); + } + + @Test + public void setTaskBookFilePath_validPath_setsTaskBookFilePath() { + Path path = Paths.get("task/book/file/path"); + modelManager.setTaskBookFilePath(path); + assertEquals(path, modelManager.getTaskBookFilePath()); + } + @Test public void hasPerson_nullPerson_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null)); @@ -93,15 +111,39 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0)); } + @Test + public void hasTask_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.hasTask(null)); + } + + @Test + public void hasTask_taskNotInTaskBook_returnsFalse() { + assertFalse(modelManager.hasTask(FIRST_TASK)); + } + + @Test + public void hasTask_taskInTaskBook_returnsTrue() { + modelManager.addTask(FIRST_TASK); + assertTrue(modelManager.hasTask(FIRST_TASK)); + } + + @Test + public void getFilteredTaskList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredTaskList().remove(0)); + } + @Test public void equals() { AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); AddressBook differentAddressBook = new AddressBook(); UserPrefs userPrefs = new UserPrefs(); + TaskBook taskBook = new TaskBookBuilder().withTask(FIRST_TASK).withTask(SECOND_TASK).build(); + TaskBook differentTaskBook = new TaskBook(); + StrategyBoard strategyBoard = new StrategyBoard(); // same values -> returns true - modelManager = new ModelManager(addressBook, userPrefs); - ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs); + modelManager = new ModelManager(addressBook, taskBook, strategyBoard, userPrefs); + ModelManager modelManagerCopy = new ModelManager(addressBook, taskBook, strategyBoard, userPrefs); assertTrue(modelManager.equals(modelManagerCopy)); // same object -> returns true @@ -114,19 +156,30 @@ public void equals() { assertFalse(modelManager.equals(5)); // different addressBook -> returns false - assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs))); + assertFalse(modelManager.equals(new ModelManager(differentAddressBook, taskBook, strategyBoard, 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))); + // different filteredPersonList -> returns false + String[] pKeywords = ALICE.getName().fullName.split("\\s+"); + modelManager.updateFilteredPersonList(new PersonNameContainsKeywordsPredicate(Arrays.asList(pKeywords))); + assertFalse(modelManager.equals(new ModelManager(addressBook, taskBook, strategyBoard, userPrefs))); // resets modelManager to initial state for upcoming tests modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + // different taskBook -> returns false + assertFalse(modelManager.equals(new ModelManager(addressBook, differentTaskBook, strategyBoard, userPrefs))); + + // different filteredTaskList -> returns false + String[] tKeywords = FIRST_TASK.getName().fullName.split("\\s+"); + modelManager.updateFilteredTaskList(new TaskNameContainsKeywordsPredicate(Arrays.asList(tKeywords))); + assertFalse(modelManager.equals(new ModelManager(addressBook, taskBook, strategyBoard, userPrefs))); + + // resets modelManager to initial state for upcoming tests + modelManager.updateFilteredTaskList(PREDICATE_SHOW_ALL_TASKS); + // different userPrefs -> returns false UserPrefs differentUserPrefs = new UserPrefs(); differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath")); - assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs))); + assertFalse(modelManager.equals(new ModelManager(addressBook, taskBook, strategyBoard, differentUserPrefs))); } } diff --git a/src/test/java/seedu/address/model/TaskBookTest.java b/src/test/java/seedu/address/model/TaskBookTest.java new file mode 100644 index 00000000000..c5c0be29665 --- /dev/null +++ b/src/test/java/seedu/address/model/TaskBookTest.java @@ -0,0 +1,99 @@ +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.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +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.task.Task; +import seedu.address.model.task.exceptions.DuplicateTaskException; +import seedu.address.testutil.TaskBuilder; + +public class TaskBookTest { + + private final TaskBook taskBook = new TaskBook(); + + @Test + public void constructor() { + assertEquals(Collections.emptyList(), taskBook.getTaskList()); + } + + @Test + public void resetData_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> taskBook.resetData(null)); + } + + @Test + public void resetData_withValidReadOnlyTaskBook_replacesData() { + TaskBook newData = getTypicalTaskBook(); + taskBook.resetData(newData); + assertEquals(newData, taskBook); + } + + @Test + public void resetData_withDuplicateTasks_throwsDuplicateTaskException() { + // Two tasks with the same identity fields + Task editedFirstTask = new TaskBuilder(FIRST_TASK).withDate("10-03-2022").withTags("team1") + .build(); + List newTasks = Arrays.asList(FIRST_TASK, editedFirstTask); + TaskBookTest.TaskBookStub newData = new TaskBookTest.TaskBookStub(newTasks); + + assertThrows(DuplicateTaskException.class, () -> taskBook.resetData(newData)); + } + + @Test + public void hasTask_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> taskBook.hasTask(null)); + } + + @Test + public void hasTask_taskNotInTaskBook_returnsFalse() { + assertFalse(taskBook.hasTask(FIRST_TASK)); + } + + @Test + public void hasTask_taskInTaskBook_returnsTrue() { + taskBook.addTask(FIRST_TASK); + assertTrue(taskBook.hasTask(FIRST_TASK)); + } + + @Test + public void hasTask_taskWithSameIdentityFieldsInTaskBook_returnsTrue() { + taskBook.addTask(FIRST_TASK); + Task editedFirstTask = new TaskBuilder(FIRST_TASK).withDate("10-03-2022").withTags("team1") + .build(); + assertTrue(taskBook.hasTask(editedFirstTask)); + } + + @Test + public void getTaskList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> taskBook.getTaskList().remove(0)); + } + + /** + * A stub ReadOnlyTaskBook whose tasks list can violate interface constraints. + */ + private static class TaskBookStub implements ReadOnlyTaskBook { + private final ObservableList tasks = FXCollections.observableArrayList(); + + TaskBookStub(Collection tasks) { + this.tasks.setAll(tasks); + } + + @Override + public ObservableList getTaskList() { + return tasks; + } + } +} diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/seedu/address/model/UserPrefsTest.java index b1307a70d52..122a940a1e0 100644 --- a/src/test/java/seedu/address/model/UserPrefsTest.java +++ b/src/test/java/seedu/address/model/UserPrefsTest.java @@ -18,4 +18,9 @@ public void setAddressBookFilePath_nullPath_throwsNullPointerException() { assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null)); } + @Test + public void setTaskBookFilePath_nullPath_throwsNullPointerException() { + UserPrefs userPrefs = new UserPrefs(); + assertThrows(NullPointerException.class, () -> userPrefs.setTaskBookFilePath(null)); + } } diff --git a/src/test/java/seedu/address/model/image/ImageTest.java b/src/test/java/seedu/address/model/image/ImageTest.java new file mode 100644 index 00000000000..a647cba849b --- /dev/null +++ b/src/test/java/seedu/address/model/image/ImageTest.java @@ -0,0 +1,50 @@ +package seedu.address.model.image; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static seedu.address.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +class ImageTest { + + private final String imageNameStub = "test"; + private final String imageNameStub1 = "test2"; + private final String imageNameStub2 = "<"; + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Image(null)); + } + + @Test + public void isValidFile_invalidPath_throws() { + assertFalse(Image.isValidFile(imageNameStub2)); + } + + @Test + public void getImagePath_validPathName() { + Image image = new Image(imageNameStub); + + assertDoesNotThrow(() -> image.getImagePath()); + } + + @Test + public void equals() { + Image image = new Image(imageNameStub); + Image image2 = new Image(imageNameStub); + Image image3 = new Image(imageNameStub1); + + assertEquals(image, image2); + assertNotEquals(image, image3); + } + + @Test + void testToString() { + Image image = new Image(imageNameStub); + assertNotNull(image.toString()); + } +} diff --git a/src/test/java/seedu/address/model/name/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/name/NameContainsKeywordsPredicateTest.java new file mode 100644 index 00000000000..a924c02cb87 --- /dev/null +++ b/src/test/java/seedu/address/model/name/NameContainsKeywordsPredicateTest.java @@ -0,0 +1,144 @@ +package seedu.address.model.name; + +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.PersonBuilder; +import seedu.address.testutil.TaskBuilder; + +/** + * Test class for {@code PersonNameContainsKeywordsPredicate} and {@code TaskNameContainsKeywordPredicate} + */ +public class NameContainsKeywordsPredicateTest { + + @Test + public void equals() { + List firstPredicateKeywordList = Collections.singletonList("first"); + List secondPredicateKeywordList = Arrays.asList("first", "second"); + + // Persons Test + PersonNameContainsKeywordsPredicate firstPersonPredicate = + new PersonNameContainsKeywordsPredicate(firstPredicateKeywordList); + PersonNameContainsKeywordsPredicate secondPersonPredicate = + new PersonNameContainsKeywordsPredicate(secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstPersonPredicate.equals(firstPersonPredicate)); + + // same values -> returns true + PersonNameContainsKeywordsPredicate firstPersonPredicateCopy = + new PersonNameContainsKeywordsPredicate(firstPredicateKeywordList); + assertTrue(firstPersonPredicate.equals(firstPersonPredicateCopy)); + + // different types -> returns false + assertFalse(firstPersonPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPersonPredicate.equals(null)); + + // different person -> returns false + assertFalse(firstPersonPredicate.equals(secondPersonPredicate)); + + // Tasks Test + TaskNameContainsKeywordsPredicate firstTaskPredicate = + new TaskNameContainsKeywordsPredicate(firstPredicateKeywordList); + TaskNameContainsKeywordsPredicate secondTaskPredicate = + new TaskNameContainsKeywordsPredicate(secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstTaskPredicate.equals(firstTaskPredicate)); + + // same values -> returns true + TaskNameContainsKeywordsPredicate firstTaskPredicateCopy = + new TaskNameContainsKeywordsPredicate(firstPredicateKeywordList); + assertTrue(firstTaskPredicate.equals(firstTaskPredicateCopy)); + + // different types -> returns false + assertFalse(firstTaskPredicate.equals(1)); + + // null -> returns false + assertFalse(firstTaskPredicate.equals(null)); + + // different person -> returns false + assertFalse(firstTaskPredicate.equals(secondTaskPredicate)); + } + + @Test + public void test_nameContainsKeywords_returnsTrue() { + // Persons Test + // One keyword + PersonNameContainsKeywordsPredicate personPredicate = + new PersonNameContainsKeywordsPredicate(Collections.singletonList("Alice")); + assertTrue(personPredicate.test(new PersonBuilder().withName("Alice Bob").build())); + + // Multiple keywords + personPredicate = new PersonNameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")); + assertTrue(personPredicate.test(new PersonBuilder().withName("Alice Bob").build())); + + // Only one matching keyword + personPredicate = new PersonNameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol")); + assertTrue(personPredicate.test(new PersonBuilder().withName("Alice Carol").build())); + + // Mixed-case keywords + personPredicate = new PersonNameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB")); + assertTrue(personPredicate.test(new PersonBuilder().withName("Alice Bob").build())); + + // Tasks Test + TaskNameContainsKeywordsPredicate taskPredicate = + new TaskNameContainsKeywordsPredicate(Collections.singletonList("Meeting")); + assertTrue(taskPredicate.test(new TaskBuilder().withName("Meeting Dinner").build())); + + // Multiple keywords + taskPredicate = new TaskNameContainsKeywordsPredicate(Arrays.asList("Meeting", "Dinner")); + assertTrue(taskPredicate.test(new TaskBuilder().withName("Meeting Dinner").build())); + + // Only one matching keyword + taskPredicate = new TaskNameContainsKeywordsPredicate(Arrays.asList("Dinner", "Lunch")); + assertTrue(taskPredicate.test(new TaskBuilder().withName("Meeting Lunch").build())); + + // Mixed-case keywords + taskPredicate = new TaskNameContainsKeywordsPredicate(Arrays.asList("MeEtINg", "diNner")); + assertTrue(taskPredicate.test(new TaskBuilder().withName("Meeting Dinner").build())); + } + + @Test + public void test_nameDoesNotContainKeywords_returnsFalse() { + //Persons Test + // Zero keywords + PersonNameContainsKeywordsPredicate personPredicate = + new PersonNameContainsKeywordsPredicate(Collections.emptyList()); + assertFalse(personPredicate.test(new PersonBuilder().withName("Alice").build())); + + // Non-matching keyword + personPredicate = new PersonNameContainsKeywordsPredicate(Arrays.asList("Carol")); + assertFalse(personPredicate.test(new PersonBuilder().withName("Alice Bob").build())); + + // Keywords match phone, email and address, but does not match name + personPredicate = + new PersonNameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street")); + assertFalse(personPredicate.test(new PersonBuilder().withName("Alice").withPhone("12345") + .withEmail("alice@email.com").withAddress("Main Street").build())); + + //Tasks Test + // Zero keywords + TaskNameContainsKeywordsPredicate taskPredicate = + new TaskNameContainsKeywordsPredicate(Collections.emptyList()); + assertFalse(taskPredicate.test(new TaskBuilder().withName("Meeting").build())); + + // Non-matching keyword + taskPredicate = new TaskNameContainsKeywordsPredicate(Arrays.asList("Lunch")); + assertFalse(taskPredicate.test(new TaskBuilder().withName("Meeting Dinner").build())); + + // Keywords match date, start time and end time, but does not match name + taskPredicate = + new TaskNameContainsKeywordsPredicate(Arrays.asList("10-03-2020", "08:00", "09:00")); + assertFalse(taskPredicate.test(new TaskBuilder().withName("Meeting").withDate("10-03-2020") + .withStartTime("08:00").withEndTime("09:00").build())); + } +} diff --git a/src/test/java/seedu/address/model/person/NameTest.java b/src/test/java/seedu/address/model/name/NameTest.java similarity index 97% rename from src/test/java/seedu/address/model/person/NameTest.java rename to src/test/java/seedu/address/model/name/NameTest.java index c9801392874..8679197251e 100644 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ b/src/test/java/seedu/address/model/name/NameTest.java @@ -1,4 +1,4 @@ -package seedu.address.model.person; +package seedu.address.model.name; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; diff --git a/src/test/java/seedu/address/model/note/NoteTest.java b/src/test/java/seedu/address/model/note/NoteTest.java new file mode 100644 index 00000000000..36a294f0443 --- /dev/null +++ b/src/test/java/seedu/address/model/note/NoteTest.java @@ -0,0 +1,64 @@ +package seedu.address.model.note; + +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() { + // a String of length 51 + String invalidNote = "0123456789" + + "0123456789" + + "0123456789" + + "0123456789" + + "0123456789" + + "0"; + assertThrows(IllegalArgumentException.class, () -> new Note(invalidNote)); + } + + @Test + public void isValidNote() { + //null tag name + assertThrows(NullPointerException.class, () -> Note.isValidNote(null)); + + //valid note + assertTrue(Note.isValidNote("note")); + + //over 50 characters + assertFalse(Note.isValidNote("ValidValidValidValidValidValidValidValidValidValidValid")); + } + + @Test + public void equals() { + Note note = new Note("something"); + Note differentNote = new Note("something else"); + + Note noteCopy = new Note("something"); + Note noteCaseInsensitiveCopy = new Note("SomEthiNg"); + + //same object reference + assertTrue(note.equals(note)); + + //same contents + assertTrue(note.equals(noteCopy)); + + //random capitals (equals is case sensitive + assertTrue(note.equals(noteCaseInsensitiveCopy)); + + //null + assertFalse(note.equals(null)); + + //different contents + assertFalse(note.equals(differentNote)); + } +} diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java deleted file mode 100644 index f136664e017..00000000000 --- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package seedu.address.model.person; - -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.PersonBuilder; - -public class NameContainsKeywordsPredicateTest { - - @Test - public void equals() { - List firstPredicateKeywordList = Collections.singletonList("first"); - List secondPredicateKeywordList = Arrays.asList("first", "second"); - - NameContainsKeywordsPredicate firstPredicate = new NameContainsKeywordsPredicate(firstPredicateKeywordList); - NameContainsKeywordsPredicate secondPredicate = new NameContainsKeywordsPredicate(secondPredicateKeywordList); - - // same object -> returns true - assertTrue(firstPredicate.equals(firstPredicate)); - - // same values -> returns true - NameContainsKeywordsPredicate firstPredicateCopy = new NameContainsKeywordsPredicate(firstPredicateKeywordList); - assertTrue(firstPredicate.equals(firstPredicateCopy)); - - // different types -> returns false - assertFalse(firstPredicate.equals(1)); - - // null -> returns false - assertFalse(firstPredicate.equals(null)); - - // different person -> returns false - assertFalse(firstPredicate.equals(secondPredicate)); - } - - @Test - public void test_nameContainsKeywords_returnsTrue() { - // One keyword - NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Collections.singletonList("Alice")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Multiple keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Bob").build())); - - // Only one matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol")); - assertTrue(predicate.test(new PersonBuilder().withName("Alice Carol").build())); - - // Mixed-case keywords - predicate = new NameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB")); - assertTrue(predicate.test(new PersonBuilder().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())); - - // Non-matching keyword - predicate = new NameContainsKeywordsPredicate(Arrays.asList("Carol")); - assertFalse(predicate.test(new PersonBuilder().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())); - } -} diff --git a/src/test/java/seedu/address/model/strategy/PlayerTest.java b/src/test/java/seedu/address/model/strategy/PlayerTest.java new file mode 100644 index 00000000000..79f34f41a15 --- /dev/null +++ b/src/test/java/seedu/address/model/strategy/PlayerTest.java @@ -0,0 +1,97 @@ +package seedu.address.model.strategy; + +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; + +class PlayerTest { + + private static final String VALID_NAME_1 = "caPs"; + private static final String VALID_NAME_2 = "Jankos"; + private static final String INVALID_NAME = "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1234567890" + + "1"; + + @Test + public void constructor_nullName_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new Player(null)); + } + + @Test + public void constructor_emptyName_throwsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> new Player("")); + } + + @Test + public void constructor_invalidName_throwsIllegalArgumentException() { + assertThrows(IllegalArgumentException.class, () -> new Player(INVALID_NAME)); + } + + @Test + public void isValidPlayer_nullName_returnsFalse() { + assertThrows(NullPointerException.class, () -> Player.isValidPlayer(null)); + } + + @Test + public void isValidPlayer_validName_true() { + assertTrue(Player.isValidPlayer(VALID_NAME_1)); + } + + @Test + public void isValidPlayer_invalidName_false() { + assertFalse(Player.isValidPlayer(INVALID_NAME)); + } + + @Test + public void isValidPlayer_emptyName_false() { + assertFalse(Player.isValidPlayer("")); + } + + @Test + public void getName_validName_returnsName() { + Player player = new Player(VALID_NAME_1); + assertEquals(player.getName(), VALID_NAME_1); + } + + @Test + public void getXCoord_validCoord_returnsXCoord() { + Player player = new Player(VALID_NAME_1, 123, 456); + assertEquals(player.getXCoord(), 123); + } + + @Test + public void getYCoord_validCoord_returnsYCoord() { + Player player = new Player(VALID_NAME_1, 123, 456); + assertEquals(player.getYCoord(), 456); + } + + @Test + public void equals() { + Player player1 = new Player(VALID_NAME_1); + Player player1Copy = new Player(VALID_NAME_1); + + Player player2 = new Player(VALID_NAME_2); + Player player1Case = new Player("CaPs"); + + //same object reference + assertTrue(player1.equals(player1)); + + //same contents + assertTrue(player1.equals(player1Copy)); + + //case sensitive + assertFalse(player1.equals(player1Case)); + + //null + assertFalse(player1.equals(null)); + + //different contents + assertFalse(player1.equals(player2)); + } +} diff --git a/src/test/java/seedu/address/model/tag/TagContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/tag/TagContainsKeywordsPredicateTest.java new file mode 100644 index 00000000000..a1a6c509ac9 --- /dev/null +++ b/src/test/java/seedu/address/model/tag/TagContainsKeywordsPredicateTest.java @@ -0,0 +1,145 @@ +package seedu.address.model.tag; + +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.PersonBuilder; +import seedu.address.testutil.TaskBuilder; + +/** + * Test class for {@code TaskTagContainsKeywordsPredicate} and {@code PersonTagContainsKeywordPredicate} + */ +public class TagContainsKeywordsPredicateTest { + + @Test + public void equals() { + List firstPredicateKeywordList = Collections.singletonList("first"); + List secondPredicateKeywordList = Arrays.asList("first", "second"); + + // Persons Test + PersonTagContainsKeywordsPredicate firstPersonPredicate = + new PersonTagContainsKeywordsPredicate(firstPredicateKeywordList); + PersonTagContainsKeywordsPredicate secondPersonPredicate = + new PersonTagContainsKeywordsPredicate(secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstPersonPredicate.equals(firstPersonPredicate)); + + // same values -> returns true + PersonTagContainsKeywordsPredicate firstPersonPredicateCopy = + new PersonTagContainsKeywordsPredicate(firstPredicateKeywordList); + assertTrue(firstPersonPredicate.equals(firstPersonPredicateCopy)); + + // different types -> returns false + assertFalse(firstPersonPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPersonPredicate.equals(null)); + + // different person -> returns false + assertFalse(firstPersonPredicate.equals(secondPersonPredicate)); + + // Tasks Test + TaskTagContainsKeywordsPredicate firstTaskPredicate = + new TaskTagContainsKeywordsPredicate(firstPredicateKeywordList); + TaskTagContainsKeywordsPredicate secondTaskPredicate = + new TaskTagContainsKeywordsPredicate(secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstTaskPredicate.equals(firstTaskPredicate)); + + // same values -> returns true + TaskTagContainsKeywordsPredicate firstTaskPredicateCopy = + new TaskTagContainsKeywordsPredicate(firstPredicateKeywordList); + assertTrue(firstTaskPredicate.equals(firstTaskPredicateCopy)); + + // different types -> returns false + assertFalse(firstTaskPredicate.equals(1)); + + // null -> returns false + assertFalse(firstTaskPredicate.equals(null)); + + // different person -> returns false + assertFalse(firstTaskPredicate.equals(secondTaskPredicate)); + } + + @Test + public void test_tagContainsKeywords_returnsTrue() { + // Persons Test + // One keyword + PersonTagContainsKeywordsPredicate personPredicate = + new PersonTagContainsKeywordsPredicate(Collections.singletonList("Family")); + assertTrue(personPredicate.test(new PersonBuilder().withTags("Family", "Colleague").build())); + + // Multiple keywords + personPredicate = new PersonTagContainsKeywordsPredicate(Arrays.asList("Family", "Colleague")); + assertTrue(personPredicate.test(new PersonBuilder().withTags("Family", "Colleague").build())); + + // Only one matching keyword + personPredicate = new PersonTagContainsKeywordsPredicate(Arrays.asList("Colleague", "Team")); + assertTrue(personPredicate.test(new PersonBuilder().withTags("Family", "Team").build())); + + // Mixed-case keywords + personPredicate = new PersonTagContainsKeywordsPredicate(Arrays.asList("fAmiLy", "coLLEague")); + assertTrue(personPredicate.test(new PersonBuilder().withTags("Family", "Colleague").build())); + + // Tasks Test + TaskTagContainsKeywordsPredicate taskPredicate = + new TaskTagContainsKeywordsPredicate(Collections.singletonList("Family")); + assertTrue(taskPredicate.test(new TaskBuilder().withTags("Family", "Colleague").build())); + + // Multiple keywords + taskPredicate = new TaskTagContainsKeywordsPredicate(Arrays.asList("Family", "Colleague")); + assertTrue(taskPredicate.test(new TaskBuilder().withTags("Family", "Colleague").build())); + + // Only one matching keyword + taskPredicate = new TaskTagContainsKeywordsPredicate(Arrays.asList("Colleague", "Team")); + assertTrue(taskPredicate.test(new TaskBuilder().withTags("Family", "Team").build())); + + // Mixed-case keywords + taskPredicate = new TaskTagContainsKeywordsPredicate(Arrays.asList("fAmiLy", "coLLEague")); + assertTrue(taskPredicate.test(new TaskBuilder().withTags("Family", "Colleague").build())); + + } + + @Test + public void test_nameDoesNotContainKeywords_returnsFalse() { + //Persons Test + // Zero keywords + PersonTagContainsKeywordsPredicate personPredicate = + new PersonTagContainsKeywordsPredicate(Collections.emptyList()); + assertFalse(personPredicate.test(new PersonBuilder().withTags("Family").build())); + + // Non-matching keyword + personPredicate = new PersonTagContainsKeywordsPredicate(Arrays.asList("Team")); + assertFalse(personPredicate.test(new PersonBuilder().withTags("Family", "Colleague").build())); + + // Keywords match name, phone, and address, but does not match tag + personPredicate = new PersonTagContainsKeywordsPredicate( + Arrays.asList("Alice" , "12345", "Main", "Street")); + assertFalse(personPredicate.test(new PersonBuilder().withName("Alice").withPhone("12345") + .withEmail("alice@email.com").withAddress("Main Street").withTags("Family", "Colleague").build())); + + //Tasks Test + // Zero keywords + TaskTagContainsKeywordsPredicate taskPredicate = + new TaskTagContainsKeywordsPredicate(Collections.emptyList()); + assertFalse(taskPredicate.test(new TaskBuilder().withTags("Family").build())); + + // Non-matching keyword + taskPredicate = new TaskTagContainsKeywordsPredicate(Arrays.asList("Team")); + assertFalse(taskPredicate.test(new TaskBuilder().withTags("Family", "Colleague").build())); + + // Keywords match name, but does not match tag + taskPredicate = new TaskTagContainsKeywordsPredicate( + Arrays.asList("Meeting" , "Dinner", "Lunch", "Medical")); + assertFalse(taskPredicate.test(new TaskBuilder().withName("Meeting").withDate("10-03-2020") + .withStartTime("08:00").withEndTime("09:00").withTags("Family", "Colleague").build())); + } +} diff --git a/src/test/java/seedu/address/model/task/DateTest.java b/src/test/java/seedu/address/model/task/DateTest.java new file mode 100644 index 00000000000..c112ca4a847 --- /dev/null +++ b/src/test/java/seedu/address/model/task/DateTest.java @@ -0,0 +1,40 @@ +package seedu.address.model.task; + +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() { + // null date + assertThrows(NullPointerException.class, () -> Date.isValidDate(null)); + + //invalid date + assertFalse(Date.isValidDate("")); // empty string + assertFalse(Date.isValidDate(" ")); // spaces only + assertFalse(Date.isValidDate("29-02-2021")); // non-leap year + assertFalse(Date.isValidDate("28-02-22")); // 2 digits year format + assertFalse(Date.isValidDate("28-2-2022")); // 1 digit month format + assertFalse(Date.isValidDate("8-02-2022")); // 1 digit day format + assertFalse(Date.isValidDate("32-01-2022")); // invalid day range + assertFalse(Date.isValidDate("04-14-2022")); // invalid month range + + //valid date + assertTrue(Date.isValidDate("29-02-2020")); // leap year + assertTrue(Date.isValidDate("20-03-2021")); //valid day, month, and year + } +} diff --git a/src/test/java/seedu/address/model/task/StartEndTimeTest.java b/src/test/java/seedu/address/model/task/StartEndTimeTest.java new file mode 100644 index 00000000000..98d1c6168a0 --- /dev/null +++ b/src/test/java/seedu/address/model/task/StartEndTimeTest.java @@ -0,0 +1,55 @@ +package seedu.address.model.task; + +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 StartEndTimeTest { + @Test + public void constructor_null_throwsNullPointerException() { + //Start Time + assertThrows(NullPointerException.class, () -> new StartTime(null)); + //End Time + assertThrows(NullPointerException.class, () -> new EndTime(null)); + } + + @Test + public void constructor_invalidTime_throwsIllegalArgumentException() { + String invalidTime = ""; + //Start Time + assertThrows(IllegalArgumentException.class, () -> new StartTime(invalidTime)); + //End Time + assertThrows(IllegalArgumentException.class, () -> new EndTime(invalidTime)); + } + + @Test + public void isValidStartEndTime() { + // null time + assertThrows(NullPointerException.class, () -> StartTime.isValidStartTime(null)); + assertThrows(NullPointerException.class, () -> EndTime.isValidEndTime(null)); + + // invalid time + // start time + assertFalse(StartTime.isValidStartTime("")); // empty string + assertFalse(StartTime.isValidStartTime(" ")); // spaces only + assertFalse(StartTime.isValidStartTime("2:00")); // 1 digit hour format + assertFalse(StartTime.isValidStartTime("02:3")); // 1 digit minute format + assertFalse(StartTime.isValidStartTime("25:00")); // invalid hour range + assertFalse(StartTime.isValidStartTime("00:60")); // invalid minute range + // end time + assertFalse(EndTime.isValidEndTime("")); // empty string + assertFalse(EndTime.isValidEndTime(" ")); // spaces only + assertFalse(EndTime.isValidEndTime("2:00")); // 1 digit hour format + assertFalse(EndTime.isValidEndTime("02:3")); // 1 digit minute format + assertFalse(EndTime.isValidEndTime("25:00")); // invalid hour range + assertFalse(EndTime.isValidEndTime("00:60")); // invalid minute range + + //valid time + //start time + assertTrue(StartTime.isValidStartTime("02:00")); // valid hour and minute format + //end time + assertTrue(EndTime.isValidEndTime("02:00")); // valid hour and minute format + } +} diff --git a/src/test/java/seedu/address/model/task/TaskTest.java b/src/test/java/seedu/address/model/task/TaskTest.java new file mode 100644 index 00000000000..c5c3e4b426b --- /dev/null +++ b/src/test/java/seedu/address/model/task/TaskTest.java @@ -0,0 +1,139 @@ +package seedu.address.model.task; + +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.TypicalPersons.ALICE; +import static seedu.address.testutil.TypicalPersons.BENSON; +import static seedu.address.testutil.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.SECOND_TASK; + +import org.junit.jupiter.api.Test; + +import seedu.address.testutil.TaskBuilder; + +public class TaskTest { + @Test + public void asObservableList_modifyList_throwsUnsupportedOperationException() { + Task task = new TaskBuilder().build(); + assertThrows(UnsupportedOperationException.class, () -> task.getTags().remove(0)); + } + + @Test + public void isSameTask() { + //same object -> returns true + assertTrue(FIRST_TASK.isSameTask(FIRST_TASK)); + + //null -> returns false + assertFalse(FIRST_TASK.isSameTask(null)); + + // same name, all other attributes different -> returns true + Task editedFirstTask = new TaskBuilder().withName("Meetings").withDate("09-01-2011") + .withStartTime("00:00").withEndTime("01:00").withTags("hello").build(); + assertTrue(FIRST_TASK.isSameTask(editedFirstTask)); + + // different name, all other attributes same -> returns false + editedFirstTask = new TaskBuilder().withName("Swimming").withDate("09-10-2022") + .withStartTime("09:00").withEndTime("10:00") + .withTags("friends", "colleagues") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + assertFalse(FIRST_TASK.isSameTask(editedFirstTask)); + + // name with trailing spaces, all other attributes same -> returns false + editedFirstTask = new TaskBuilder().withName("Meetings ").withDate("09-10-2022") + .withStartTime("09:00").withEndTime("10:00") + .withTags("friends", "colleagues") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + assertFalse(FIRST_TASK.isSameTask(editedFirstTask)); + } + + @Test + public void hasDateTimeConflict() { + + //same object -> returns true + assertTrue(FIRST_TASK.hasDateTimeConflict(FIRST_TASK)); + + // non-conflicting time range, all attributes remains the same -> returns false + Task editedFirstTask = new TaskBuilder().withName("Meetings").withDate("09-10-2022") + .withStartTime("00:00").withEndTime("01:00") + .withTags("friends", "colleagues") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + assertFalse(FIRST_TASK.hasDateTimeConflict(editedFirstTask)); + + // different date, all attributes remains the same -> returns false + editedFirstTask = new TaskBuilder().withName("Meetings").withDate("10-10-2022") + .withStartTime("09:00").withEndTime("10:00") + .withTags("friends", "colleagues") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + assertFalse(FIRST_TASK.hasDateTimeConflict(editedFirstTask)); + + // conflicting time range v1, all attributes remains the same -> returns true + editedFirstTask = new TaskBuilder().withName("Meetings").withDate("09-10-2022") + .withStartTime("09:00").withEndTime("10:30") + .withTags("friends", "colleagues") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + assertTrue(FIRST_TASK.hasDateTimeConflict(editedFirstTask)); + + // conflicting time range v2, all attributes remains the same -> returns true + editedFirstTask = new TaskBuilder().withName("Meetings").withDate("09-10-2022") + .withStartTime("09:30").withEndTime("10:30") + .withTags("friends", "colleagues") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + assertTrue(FIRST_TASK.hasDateTimeConflict(editedFirstTask)); + + // edited start time is the same as the original end time, all attributes remains the same -> returns false + editedFirstTask = new TaskBuilder().withName("Meetings").withDate("09-10-2022") + .withStartTime("10:00").withEndTime("10:30") + .withTags("friends", "colleagues") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + assertFalse(FIRST_TASK.hasDateTimeConflict(editedFirstTask)); + + } + + @Test + public void equals() { + // same values -> returns true + Task firstTaskCopy = new TaskBuilder().withName("Meetings").withDate("09-10-2022") + .withStartTime("09:00").withEndTime("10:00") + .withTags("friends", "colleagues") + .withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + + assertTrue(FIRST_TASK.equals(firstTaskCopy)); + + // same object -> returns true + assertTrue(FIRST_TASK.equals(FIRST_TASK)); + + // null -> returns false + assertFalse(FIRST_TASK.equals(null)); + + // different type -> returns false + assertFalse(FIRST_TASK.equals(5)); + + // different task -> returns false + assertFalse(FIRST_TASK.equals(SECOND_TASK)); + + // different name -> returns false + Task editedFirstTask = new TaskBuilder(firstTaskCopy).withName("Swimming").build(); + assertFalse(FIRST_TASK.equals(editedFirstTask)); + + // different date -> returns false + editedFirstTask = new TaskBuilder(firstTaskCopy).withDate("01-10-2022").build(); + assertFalse(FIRST_TASK.equals(editedFirstTask)); + + // different start time -> returns false + editedFirstTask = new TaskBuilder(firstTaskCopy).withStartTime("22:00").build(); + assertFalse(FIRST_TASK.equals(editedFirstTask)); + + // different end time -> returns false + editedFirstTask = new TaskBuilder(firstTaskCopy).withEndTime("23:00").build(); + assertFalse(FIRST_TASK.equals(editedFirstTask)); + + // different tags -> returns false + editedFirstTask = new TaskBuilder(firstTaskCopy).withTags("neighbours").build(); + assertFalse(FIRST_TASK.equals(editedFirstTask)); + + // different persons (represented by names) -> returns false + editedFirstTask = new TaskBuilder(firstTaskCopy).withPersons("Alex").build(); + assertFalse(FIRST_TASK.equals(editedFirstTask)); + } +} diff --git a/src/test/java/seedu/address/model/task/UniqueTaskListTest.java b/src/test/java/seedu/address/model/task/UniqueTaskListTest.java new file mode 100644 index 00000000000..9fce2daa981 --- /dev/null +++ b/src/test/java/seedu/address/model/task/UniqueTaskListTest.java @@ -0,0 +1,167 @@ +package seedu.address.model.task; + +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.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.SECOND_TASK; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.address.model.task.exceptions.DuplicateTaskException; +import seedu.address.model.task.exceptions.TaskNotFoundException; +import seedu.address.testutil.TaskBuilder; + +public class UniqueTaskListTest { + private final UniqueTaskList uniqueTaskList = new UniqueTaskList(); + + @Test + public void contains_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueTaskList.contains(null)); + } + + @Test + public void contains_taskNotInList_returnsFalse() { + assertFalse(uniqueTaskList.contains(FIRST_TASK)); + } + + @Test + public void contains_taskInList_returnsTrue() { + uniqueTaskList.add(FIRST_TASK); + assertTrue(uniqueTaskList.contains(FIRST_TASK)); + } + + @Test + public void contains_taskWithSameIdentityFieldsInList_returnsTrue() { + uniqueTaskList.add(FIRST_TASK); + Task editedFirstTask = new TaskBuilder(FIRST_TASK).withDate("10-03-2022").withTags("team1") + .build(); + assertTrue(uniqueTaskList.contains(editedFirstTask)); + } + + @Test + public void add_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueTaskList.add(null)); + } + + @Test + public void add_duplicateTask_throwsDuplicateTaskException() { + uniqueTaskList.add(FIRST_TASK); + assertThrows(DuplicateTaskException.class, () -> uniqueTaskList.add(FIRST_TASK)); + } + + @Test + public void setTask_nullTargetTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueTaskList.setTask(null, FIRST_TASK)); + } + + @Test + public void setTask_nullEditedTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueTaskList.setTask(FIRST_TASK, null)); + } + + @Test + public void setTask_targetTaskNotInList_throwsTaskNotFoundException() { + assertThrows(TaskNotFoundException.class, () -> uniqueTaskList.setTask(FIRST_TASK, FIRST_TASK)); + } + + @Test + public void setTask_editedTaskIsSameTask_success() { + uniqueTaskList.add(FIRST_TASK); + uniqueTaskList.setTask(FIRST_TASK, FIRST_TASK); + UniqueTaskList expectedUniqueTaskList = new UniqueTaskList(); + expectedUniqueTaskList.add(FIRST_TASK); + assertEquals(expectedUniqueTaskList, uniqueTaskList); + } + + @Test + public void setTask_editedTaskHasSameIdentity_success() { + uniqueTaskList.add(FIRST_TASK); + Task editedFirstTask = new TaskBuilder(FIRST_TASK).withDate("10-03-2022").withTags("team1") + .build(); + uniqueTaskList.setTask(FIRST_TASK, editedFirstTask); + UniqueTaskList expectedUniqueTaskList = new UniqueTaskList(); + expectedUniqueTaskList.add(editedFirstTask); + assertEquals(expectedUniqueTaskList, uniqueTaskList); + } + + @Test + public void setTask_editedTaskHasDifferentIdentity_success() { + uniqueTaskList.add(FIRST_TASK); + uniqueTaskList.setTask(FIRST_TASK, SECOND_TASK); + UniqueTaskList expectedUniqueTaskList = new UniqueTaskList(); + expectedUniqueTaskList.add(SECOND_TASK); + assertEquals(expectedUniqueTaskList, uniqueTaskList); + } + + @Test + public void setTask_editedTaskHasNonUniqueIdentity_throwsDuplicateTaskException() { + uniqueTaskList.add(FIRST_TASK); + uniqueTaskList.add(SECOND_TASK); + assertThrows(DuplicateTaskException.class, () -> uniqueTaskList.setTask(FIRST_TASK, SECOND_TASK)); + } + + @Test + public void remove_nullTask_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueTaskList.remove(null)); + } + + @Test + public void remove_taskDoesNotExist_throwsTaskNotFoundException() { + assertThrows(TaskNotFoundException.class, () -> uniqueTaskList.remove(FIRST_TASK)); + } + + @Test + public void remove_existingTask_removesTask() { + uniqueTaskList.add(FIRST_TASK); + uniqueTaskList.remove(FIRST_TASK); + UniqueTaskList expectedUniqueTaskList = new UniqueTaskList(); + assertEquals(expectedUniqueTaskList, uniqueTaskList); + } + + @Test + public void setTasks_nullUniqueTaskList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueTaskList.setTasks((UniqueTaskList) null)); + } + + @Test + public void setTasks_uniqueTaskList_replacesOwnListWithProvidedUniqueTaskList() { + uniqueTaskList.add(FIRST_TASK); + UniqueTaskList expectedUniqueTaskList = new UniqueTaskList(); + expectedUniqueTaskList.add(SECOND_TASK); + uniqueTaskList.setTasks(expectedUniqueTaskList); + assertEquals(expectedUniqueTaskList, uniqueTaskList); + } + + @Test + public void setTasks_nullList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueTaskList.setTasks((List) null)); + } + + @Test + public void setTasks_list_replacesOwnListWithProvidedList() { + uniqueTaskList.add(FIRST_TASK); + List taskList = Collections.singletonList(SECOND_TASK); + uniqueTaskList.setTasks(taskList); + UniqueTaskList expectedUniqueTaskList = new UniqueTaskList(); + expectedUniqueTaskList.add(SECOND_TASK); + assertEquals(expectedUniqueTaskList, uniqueTaskList); + } + + @Test + public void setTasks_listWithDuplicateTasks_throwsDuplicateTaskException() { + List listWithDuplicateTasks = Arrays.asList(FIRST_TASK, FIRST_TASK); + assertThrows(DuplicateTaskException.class, () -> uniqueTaskList.setTasks(listWithDuplicateTasks)); + } + + @Test + public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () + -> uniqueTaskList.asUnmodifiableObservableList().remove(0)); + } +} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java index 83b11331cdb..0e7eeb39a00 100644 --- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java +++ b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java @@ -12,9 +12,9 @@ import org.junit.jupiter.api.Test; import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.name.Name; 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 { @@ -23,6 +23,7 @@ public class JsonAdaptedPersonTest { 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_NOTE = ""; private static final String VALID_NAME = BENSON.getName().toString(); private static final String VALID_PHONE = BENSON.getPhone().toString(); @@ -31,6 +32,15 @@ public class JsonAdaptedPersonTest { private static final List VALID_TAGS = BENSON.getTags().stream() .map(JsonAdaptedTag::new) .collect(Collectors.toList()); + private static final List VALID_STRENGTHS = BENSON.getStrengths().stream() + .map(JsonAdaptedNote::new) + .collect(Collectors.toList()); + private static final List VALID_WEAKNESSES = BENSON.getWeaknesses().stream() + .map(JsonAdaptedNote::new) + .collect(Collectors.toList()); + private static final List VALID_MISC_NOTES = BENSON.getMiscellaneous().stream() + .map(JsonAdaptedNote::new) + .collect(Collectors.toList()); @Test public void toModelType_validPersonDetails_returnsPerson() throws Exception { @@ -41,14 +51,16 @@ public void toModelType_validPersonDetails_returnsPerson() throws Exception { @Test public void toModelType_invalidName_throwsIllegalValueException() { JsonAdaptedPerson person = - new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); + new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS, + VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); 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); + JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, + VALID_TAGS, VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @@ -56,14 +68,16 @@ public void toModelType_nullName_throwsIllegalValueException() { @Test public void toModelType_invalidPhone_throwsIllegalValueException() { JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); + new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, + VALID_TAGS, VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); 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); + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, + VALID_TAGS, VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @@ -71,14 +85,16 @@ public void toModelType_nullPhone_throwsIllegalValueException() { @Test public void toModelType_invalidEmail_throwsIllegalValueException() { JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS); + new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, + VALID_TAGS, VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); 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); + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, + VALID_TAGS, VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @@ -86,14 +102,16 @@ public void toModelType_nullEmail_throwsIllegalValueException() { @Test public void toModelType_invalidAddress_throwsIllegalValueException() { JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS); + new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, + VALID_TAGS, VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); 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); + JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, + VALID_TAGS, VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); } @@ -103,8 +121,8 @@ 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); + new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags, + VALID_STRENGTHS, VALID_WEAKNESSES, VALID_MISC_NOTES); assertThrows(IllegalValueException.class, person::toModelType); } - } diff --git a/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java b/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java new file mode 100644 index 00000000000..f84533c8e69 --- /dev/null +++ b/src/test/java/seedu/address/storage/JsonAdaptedTaskTest.java @@ -0,0 +1,136 @@ +package seedu.address.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.address.storage.JsonAdaptedTask.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.address.testutil.Assert.assertThrows; +import static seedu.address.testutil.TypicalTasks.FIRST_TASK; + +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.name.Name; +import seedu.address.model.task.Date; +import seedu.address.model.task.StartTime; + + +/** + * Contains unit tests for JsonAdaptedTaskTest + */ +public class JsonAdaptedTaskTest { + + private static final String INVALID_NAME = "m33@TING"; + private static final String INVALID_DATE = "29-02-2022"; + private static final String INVALID_START_TIME = "9:90"; + private static final String INVALID_END_TIME = "10;0"; + private static final String INVALID_TAG = "#friend"; + + private static final String VALID_NAME = FIRST_TASK.getName().toString(); + private static final String VALID_DATE = FIRST_TASK.getDate().toString(); + private static final String VALID_START_TIME = FIRST_TASK.getStartTime().toString(); + private static final String VALID_END_TIME = FIRST_TASK.getEndTime().toString(); + private static final List VALID_TAGS = FIRST_TASK.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList()); + private static final List VALID_PERSONS = FIRST_TASK.getPersons().stream() + .map(JsonAdaptedName::new) + .collect(Collectors.toList()); + + @Test + public void toModelType_validTaskDetails_returnsTask() throws Exception { + JsonAdaptedTask task = new JsonAdaptedTask(FIRST_TASK); + assertEquals(FIRST_TASK, task.toModelType()); + } + + @Test + public void toModelType_invalidName_throwsIllegalValueException() { + JsonAdaptedTask task = + new JsonAdaptedTask(INVALID_NAME, VALID_DATE, VALID_START_TIME, + VALID_END_TIME, VALID_TAGS, VALID_PERSONS); + String expectedMessage = Name.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_nullName_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(null, VALID_DATE, VALID_START_TIME, + VALID_END_TIME, VALID_TAGS, VALID_PERSONS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_invalidDate_throwsIllegalValueException() { + JsonAdaptedTask task = + new JsonAdaptedTask(VALID_NAME, INVALID_DATE, VALID_START_TIME, VALID_END_TIME, + VALID_TAGS, VALID_PERSONS); + String expectedMessage = Date.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_nullDate_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(VALID_NAME, null, VALID_START_TIME, VALID_END_TIME, + VALID_TAGS, VALID_PERSONS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Date.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_invalidStartTime_throwsIllegalValueException() { + JsonAdaptedTask task = + new JsonAdaptedTask(VALID_NAME, VALID_DATE, INVALID_START_TIME, VALID_END_TIME, + VALID_TAGS, VALID_PERSONS); + String expectedMessage = StartTime.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_nullStartTime_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(VALID_NAME, VALID_DATE, null, VALID_END_TIME, + VALID_TAGS, VALID_PERSONS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StartTime.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + /* + // To be fixed + @Test + public void toModelType_invalidEndTime_throwsIllegalValueException() { + JsonAdaptedTask task = + new JsonAdaptedTask(VALID_NAME, VALID_DATE, VALID_START_TIME, INVALID_END_TIME, VALID_TAGS); + String expectedMessage = EndTime.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + + @Test + public void toModelType_nullEndTime_throwsIllegalValueException() { + JsonAdaptedTask task = new JsonAdaptedTask(VALID_NAME, VALID_DATE, VALID_START_TIME, null, VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, EndTime.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, task::toModelType); + } + */ + + @Test + public void toModelType_invalidTags_throwsIllegalValueException() { + List invalidTags = new ArrayList<>(VALID_TAGS); + invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); + JsonAdaptedTask task = + new JsonAdaptedTask(VALID_NAME, VALID_DATE, VALID_START_TIME, VALID_END_TIME, invalidTags, + VALID_PERSONS); + assertThrows(IllegalValueException.class, task::toModelType); + } + + @Test + public void toModelType_invalidPersons_throwsIllegalValueException() { + List invalidPersons = new ArrayList<>(VALID_PERSONS); + invalidPersons.add(new JsonAdaptedName(INVALID_NAME)); + JsonAdaptedTask task = + new JsonAdaptedTask(VALID_NAME, VALID_DATE, VALID_START_TIME, VALID_END_TIME, + VALID_TAGS, invalidPersons); + assertThrows(IllegalValueException.class, task::toModelType); + } +} diff --git a/src/test/java/seedu/address/storage/JsonSerializableTaskBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableTaskBookTest.java new file mode 100644 index 00000000000..e8b8ee8dc28 --- /dev/null +++ b/src/test/java/seedu/address/storage/JsonSerializableTaskBookTest.java @@ -0,0 +1,45 @@ +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.TaskBook; +import seedu.address.testutil.TypicalTasks; + +public class JsonSerializableTaskBookTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableTaskBookTest"); + private static final Path TYPICAL_TASKS_FILE = TEST_DATA_FOLDER.resolve("typicalTasksTaskBook.json"); + private static final Path INVALID_TASK_FILE = TEST_DATA_FOLDER.resolve("invalidTasksTaskBook.json"); + private static final Path DUPLICATE_TASK_FILE = TEST_DATA_FOLDER.resolve("duplicateTasksTaskBook.json"); + + @Test + public void toModelType_typicalTasksFile_success() throws Exception { + JsonSerializableTaskBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_TASKS_FILE, + JsonSerializableTaskBook.class).get(); + TaskBook addressBookFromFile = dataFromFile.toModelType(); + TaskBook typicalTasksTaskBook = TypicalTasks.getTypicalTaskBook(); + assertEquals(addressBookFromFile, typicalTasksTaskBook); + } + + @Test + public void toModelType_invalidTaskFile_throwsIllegalValueException() throws Exception { + JsonSerializableTaskBook dataFromFile = JsonUtil.readJsonFile(INVALID_TASK_FILE, + JsonSerializableTaskBook.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } + + @Test + public void toModelType_duplicateTasks_throwsIllegalValueException() throws Exception { + JsonSerializableTaskBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_TASK_FILE, + JsonSerializableTaskBook.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableTaskBook.MESSAGE_DUPLICATE_TASK, + dataFromFile::toModelType); + } +} diff --git a/src/test/java/seedu/address/storage/JsonTaskBookStorageTest.java b/src/test/java/seedu/address/storage/JsonTaskBookStorageTest.java new file mode 100644 index 00000000000..8cb4246b964 --- /dev/null +++ b/src/test/java/seedu/address/storage/JsonTaskBookStorageTest.java @@ -0,0 +1,110 @@ +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.TypicalTasks.FIFTH_TASK; +import static seedu.address.testutil.TypicalTasks.FIRST_TASK; +import static seedu.address.testutil.TypicalTasks.FOURTH_TASK; +import static seedu.address.testutil.TypicalTasks.getTypicalTaskBook; + +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.DataConversionException; +import seedu.address.model.ReadOnlyTaskBook; +import seedu.address.model.TaskBook; + +public class JsonTaskBookStorageTest { + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonTaskBookStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readTaskBook_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readTaskBook(null)); + } + + private java.util.Optional readTaskBook(String filePath) throws Exception { + return new JsonTaskBookStorage(Paths.get(filePath)).readTaskBook(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(readTaskBook("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataConversionException.class, () -> readTaskBook("notJsonFormatTaskBook.json")); + } + + @Test + public void readTaskBook_invalidTasksTaskBook_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTaskBook("invalidTasksTaskBook.json")); + } + + @Test + public void readTaskBook_invalidAndValidTasksTaskBook_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTaskBook("invalidAndValidTasksTaskBook.json")); + } + + @Test + public void readAndSaveTaskBook_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempTaskBook.json"); + TaskBook original = getTypicalTaskBook(); + JsonTaskBookStorage jsonTaskBookStorage = new JsonTaskBookStorage(filePath); + + // Save in new file and read back + jsonTaskBookStorage.saveTaskBook(original, filePath); + ReadOnlyTaskBook readBack = jsonTaskBookStorage.readTaskBook(filePath).get(); + assertEquals(original, new TaskBook(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addTask(FOURTH_TASK); + original.removeTask(FIRST_TASK); + jsonTaskBookStorage.saveTaskBook(original, filePath); + readBack = jsonTaskBookStorage.readTaskBook(filePath).get(); + assertEquals(original, new TaskBook(readBack)); + + // Save and read without specifying file path + original.addTask(FIFTH_TASK); + jsonTaskBookStorage.saveTaskBook(original); // file path not specified + readBack = jsonTaskBookStorage.readTaskBook().get(); // file path not specified + assertEquals(original, new TaskBook(readBack)); + + } + + @Test + public void saveTaskBook_nullTaskBook_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveTaskBook(null, "SomeFile.json")); + } + + /** + * Saves {@code taskBook} at the specified {@code filePath}. + */ + private void saveTaskBook(ReadOnlyTaskBook taskBook, String filePath) { + try { + new JsonTaskBookStorage(Paths.get(filePath)) + .saveTaskBook(taskBook, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveTaskBook_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveTaskBook(new TaskBook(), null)); + } +} diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/address/storage/StorageManagerTest.java index 99a16548970..b72c72a0e10 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/address/storage/StorageManagerTest.java @@ -26,7 +26,8 @@ public class StorageManagerTest { public void setUp() { JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + TaskBookStorage taskBookStorage = null; //to be implemented + storageManager = new StorageManager(addressBookStorage, taskBookStorage, userPrefsStorage); } private Path getTempFilePath(String fileName) { diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java index 4584bd5044e..011ec94cb3f 100644 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java @@ -1,13 +1,15 @@ package seedu.address.testutil; +import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.EditPersonDescriptor; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; 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; @@ -81,6 +83,36 @@ public EditPersonDescriptorBuilder withTags(String... tags) { return this; } + /** + * Parses the {@code strengths} into a {@code List} and set it to the {@code EditPersonDescriptor} + * that we are building. + */ + public EditPersonDescriptorBuilder withStrengths(String... strength) { + List strengthList = Stream.of(strength).map(Note::new).collect(Collectors.toList()); + descriptor.setStrengths(strengthList); + return this; + } + + /** + * Parses the {@code weaknesses} into a {@code List} and set it to the {@code EditPersonDescriptor} + * that we are building. + */ + public EditPersonDescriptorBuilder withWeaknesses(String... weaknesses) { + List weaknessList = Stream.of(weaknesses).map(Note::new).collect(Collectors.toList()); + descriptor.setWeaknesses(weaknessList); + return this; + } + + /** + * Parses the {@code miscellaneous} into a {@code List} and set it to the {@code EditPersonDescriptor} + * that we are building. + */ + public EditPersonDescriptorBuilder withMiscellaneous(String... miscellaneous) { + List misList = Stream.of(miscellaneous).map(Note::new).collect(Collectors.toList()); + descriptor.setMiscellaneous(misList); + return this; + } + public EditPersonDescriptor build() { return descriptor; } diff --git a/src/test/java/seedu/address/testutil/EditTaskDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditTaskDescriptorBuilder.java new file mode 100644 index 00000000000..d728380ea25 --- /dev/null +++ b/src/test/java/seedu/address/testutil/EditTaskDescriptorBuilder.java @@ -0,0 +1,99 @@ +package seedu.address.testutil; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import seedu.address.logic.EditTaskDescriptor; +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; + +/** + * A utility class to help with building EditTaskDescriptor objects. + */ +public class EditTaskDescriptorBuilder { + + private EditTaskDescriptor descriptor; + + public EditTaskDescriptorBuilder() { + descriptor = new EditTaskDescriptor(); + } + + public EditTaskDescriptorBuilder(EditTaskDescriptor descriptor) { + this.descriptor = new EditTaskDescriptor(descriptor); + } + + /** + * Returns an {@code EditTaskDescriptor} with fields containing {@code task}'s details + */ + public EditTaskDescriptorBuilder(Task task) { + descriptor = new EditTaskDescriptor(); + descriptor.setName(task.getName()); + descriptor.setDate(task.getDate()); + descriptor.setStartTime(task.getStartTime()); + descriptor.setEndTime(task.getEndTime()); + descriptor.setTags(task.getTags()); + descriptor.setPersons(task.getPersons()); + } + + /** + * Sets the {@code Name} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withName(String name) { + descriptor.setName(new Name(name)); + return this; + } + + /** + * Sets the {@code Date} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withDate(String date) { + descriptor.setDate(new Date(date)); + return this; + } + + /** + * Sets the {@code StartTime} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withStartTime(String startTime) { + descriptor.setStartTime(new StartTime(startTime)); + return this; + } + + /** + * Sets the {@code EndTime} of the {@code EditTaskDescriptor} that we are building. + */ + public EditTaskDescriptorBuilder withEndTime(String endTime) { + descriptor.setEndTime(new EndTime(endTime)); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor} + * that we are building. + */ + public EditTaskDescriptorBuilder withTags(String... tags) { + Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); + descriptor.setTags(tagSet); + return this; + } + + /** + * Parses the {@code persons} into a {@code Set} and set it to the {@code EditTaskDescriptor} + * that we are building. + */ + public EditTaskDescriptorBuilder withPersons(String... persons) { + Set personSet = Stream.of(persons).map(Name::new).collect(Collectors.toSet()); + descriptor.setPersons(personSet); + return this; + } + + + public EditTaskDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java index 6be381d39ba..eb6b888beae 100644 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ b/src/test/java/seedu/address/testutil/PersonBuilder.java @@ -1,11 +1,14 @@ package seedu.address.testutil; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; +import seedu.address.model.name.Name; +import seedu.address.model.note.Note; 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; @@ -26,6 +29,9 @@ public class PersonBuilder { private Email email; private Address address; private Set tags; + private List strengths; + private List weaknesses; + private List miscellaneous; /** * Creates a {@code PersonBuilder} with the default details. @@ -36,6 +42,9 @@ public PersonBuilder() { email = new Email(DEFAULT_EMAIL); address = new Address(DEFAULT_ADDRESS); tags = new HashSet<>(); + strengths = new ArrayList<>(); + weaknesses = new ArrayList<>(); + miscellaneous = new ArrayList<>(); } /** @@ -47,6 +56,9 @@ public PersonBuilder(Person personToCopy) { email = personToCopy.getEmail(); address = personToCopy.getAddress(); tags = new HashSet<>(personToCopy.getTags()); + strengths = new ArrayList<>(personToCopy.getStrengths()); + weaknesses = new ArrayList<>(personToCopy.getWeaknesses()); + miscellaneous = new ArrayList<>(personToCopy.getMiscellaneous()); } /** @@ -89,8 +101,34 @@ public PersonBuilder withEmail(String email) { return this; } - public Person build() { - return new Person(name, phone, email, address, tags); + /** + * Parses the {@code strengths} into a {@code List} and set it to the {@code Person} that we are building. + */ + public PersonBuilder withStrengths(String... strengths) { + List noteList = SampleDataUtil.getNoteList(strengths); + this.strengths.addAll(noteList); + return this; + } + + /** + * Parses the {@code weaknesses} into a {@code List} and set it to the {@code Person} that we are building. + */ + public PersonBuilder withWeaknesses(String... weaknesses) { + List noteList = SampleDataUtil.getNoteList(weaknesses); + this.weaknesses.addAll(noteList); + return this; + } + + /** + * Parses the {@code misc} into a {@code List} and set it to the {@code Person} that we are building. + */ + public PersonBuilder withMisc(String... misc) { + List noteList = SampleDataUtil.getNoteList(misc); + this.miscellaneous.addAll(noteList); + return this; } + public Person build() { + return new Person(name, phone, email, address, tags, strengths, weaknesses, miscellaneous); + } } diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java index 90849945183..d3635bf6648 100644 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ b/src/test/java/seedu/address/testutil/PersonUtil.java @@ -8,8 +8,8 @@ import java.util.Set; -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; +import seedu.address.logic.EditPersonDescriptor; +import seedu.address.logic.commands.person.AddPersonCommand; import seedu.address.model.person.Person; import seedu.address.model.tag.Tag; @@ -22,7 +22,7 @@ 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); + return AddPersonCommand.COMMAND_WORD + " " + getPersonDetails(person); } /** diff --git a/src/test/java/seedu/address/testutil/TaskBookBuilder.java b/src/test/java/seedu/address/testutil/TaskBookBuilder.java new file mode 100644 index 00000000000..1d45426ad5d --- /dev/null +++ b/src/test/java/seedu/address/testutil/TaskBookBuilder.java @@ -0,0 +1,33 @@ +package seedu.address.testutil; + +import seedu.address.model.TaskBook; +import seedu.address.model.task.Task; + +/** + * A utility class to help with building Taskbook objects. + * Example usage:
+ * {@code TaskBook ab = new TaskBookBuilder().withTask("Meeting", "Dinner").build();} + */ +public class TaskBookBuilder { + private TaskBook taskBook; + + public TaskBookBuilder() { + taskBook = new TaskBook(); + } + + public TaskBookBuilder(TaskBook taskBook) { + this.taskBook = taskBook; + } + + /** + * Adds a new {@code Task} to the {@code TaskBook} that we are building. + */ + public TaskBookBuilder withTask(Task task) { + taskBook.addTask(task); + return this; + } + + public TaskBook build() { + return taskBook; + } +} diff --git a/src/test/java/seedu/address/testutil/TaskBuilder.java b/src/test/java/seedu/address/testutil/TaskBuilder.java new file mode 100644 index 00000000000..356559309ea --- /dev/null +++ b/src/test/java/seedu/address/testutil/TaskBuilder.java @@ -0,0 +1,106 @@ +package seedu.address.testutil; + +import java.util.HashSet; +import java.util.Set; + +import seedu.address.model.name.Name; +import seedu.address.model.tag.Tag; +import seedu.address.model.task.Date; +import seedu.address.model.task.EndTime; +import seedu.address.model.task.StartTime; +import seedu.address.model.task.Task; +import seedu.address.model.util.SampleDataUtil; + +public class TaskBuilder { + public static final String DEFAULT_NAME = "Unique Meeting"; + public static final String DEFAULT_DATE = "09-10-2022"; + public static final String DEFAULT_START_TIME = "08:00"; + public static final String DEFAULT_END_TIME = "12:00"; + + private Name name; + private Date date; + private StartTime startTime; + private EndTime endTime; + private Set tags; + private Set persons; //persons are represented by their names + + /** + * Creates a {@code TaskBuilder} with the default details. + */ + public TaskBuilder() { + this.name = new Name(DEFAULT_NAME); + this.date = new Date(DEFAULT_DATE); + this.startTime = new StartTime(DEFAULT_START_TIME); + this.endTime = new EndTime(DEFAULT_END_TIME); + this.tags = new HashSet<>(); + this.persons = new HashSet<>(); + } + + /** + * Creates a {@code TaskBuilder} with the data of {@code taskToCopy}. + */ + public TaskBuilder(Task taskToCopy) { + this.name = taskToCopy.getName(); + this.date = taskToCopy.getDate(); + this.startTime = taskToCopy.getStartTime(); + this.endTime = taskToCopy.getEndTime(); + this.tags = taskToCopy.getTags(); + this.persons = taskToCopy.getPersons(); + } + + /** + * Sets the {@code Name} of the {@code Task} that we are building. + */ + public TaskBuilder withName(String name) { + this.name = new Name(name); + return this; + } + + /** + * Sets the {@code Date} of the {@code Task} that we are building. + */ + public TaskBuilder withDate(String date) { + this.date = new Date(date); + return this; + } + + /** + * Sets the {@code StartTime} of the {@code Task} that we are building. + */ + public TaskBuilder withStartTime(String startTime) { + this.startTime = new StartTime(startTime); + return this; + } + + /** + * Sets the {@code EndTime} of the {@code Task} that we are building. + */ + public TaskBuilder withEndTime(String endTime) { + this.endTime = new EndTime(endTime); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set} and set it to the {@code Task} that we are building. + */ + public TaskBuilder withTags(String ... tags) { + this.tags = SampleDataUtil.getTagSet(tags); + return this; + } + + /** + * Parses the {@code persons} into a {@code Set} and set it to the {@code Task} that we are building. + */ + public TaskBuilder withPersons(String ... persons) { + this.persons = SampleDataUtil.getPersonSet(persons); + return this; + } + + /** + * Creates {@code Task} and returns it. + */ + public Task build() { + return new Task(name, date, startTime, endTime, tags, persons); + } +} + diff --git a/src/test/java/seedu/address/testutil/TestImageCreator.java b/src/test/java/seedu/address/testutil/TestImageCreator.java new file mode 100644 index 00000000000..e5a6af18f2c --- /dev/null +++ b/src/test/java/seedu/address/testutil/TestImageCreator.java @@ -0,0 +1,44 @@ +package seedu.address.testutil; + +import java.io.File; +import java.io.IOException; + +import seedu.address.model.image.Image; + +/** + * Contains static methods to create sample png files for testing Image-related classes. + */ +public class TestImageCreator { + + public static final String TEST_IMAGE_NAME = "testFile"; + + private static Image testImage = new Image("testFile"); + + /** + * Creates a sample png file for testing Image classes + */ + public static void createTestImage() { + File testFile = testImage.getImagePath(); + File testDirectory = new File(Image.FILE_PATH); + try { + if (!testDirectory.exists()) { + testDirectory.mkdir(); //in case "courts" directory has not been created + } + testFile.createNewFile(); + } catch (IOException e) { + assert false : "Runtime should not reach here"; + } + } + + /** + * Deletes the sample png that was created. + */ + public static void deleteTestImage() { + File testFile = testImage.getImagePath(); + testFile.delete(); + } + + public static Image getTestImage() { + return testImage; + } +} diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java index 1e613937657..6ce1df06045 100644 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ b/src/test/java/seedu/address/testutil/TypicalIndexes.java @@ -9,4 +9,8 @@ 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 NOTE_FIRST_INDEX = Index.fromOneBased(1); + public static final Index NOTE_SECOND_INDEX = Index.fromOneBased(2); + public static final Index TASK_FIRST_INDEX = Index.fromOneBased(1); + public static final Index TASK_SECOND_INDEX = Index.fromOneBased(2); } diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java index fec76fb7129..ec622adc71c 100644 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ b/src/test/java/seedu/address/testutil/TypicalPersons.java @@ -26,15 +26,20 @@ 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(); + .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(); + .withTags("owesMoney", "friends") + .withStrengths("Good Defense", "Great Stamina") + .withWeaknesses("Bad Offense", "Poor Endurance") + .withMisc("birthday tomorrow", "likes Python").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(); + .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends") + .withMisc("likes bing chilling").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") @@ -55,7 +60,27 @@ public class TypicalPersons { .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 + //Manually added - Person's details for sort testing + private static final String noteStub1 = "something1"; + private static final String noteStub2 = "something2"; + private static final String noteStub3 = "something3"; + + public static final Person PERSON_A = new PersonBuilder().withName("PersonA") + .withStrengths(noteStub1, noteStub2, noteStub3).build(); + public static final Person PERSON_B = new PersonBuilder().withName("PersonB").withStrengths(noteStub1, noteStub2) + .withWeaknesses(noteStub1).build(); + public static final Person PERSON_C = new PersonBuilder().withName("PersonC").withStrengths(noteStub1, noteStub2) + .withWeaknesses(noteStub1, noteStub2).build(); + public static final Person PERSON_D = new PersonBuilder().withName("PersonD").withStrengths(noteStub1) + .withWeaknesses(noteStub1).build(); + public static final Person PERSON_E = new PersonBuilder().withName("PersonE") + .withWeaknesses(noteStub1, noteStub2, noteStub3).build(); + public static final Person PERSON_F = new PersonBuilder().withName("PersonF").withStrengths(noteStub1) + .withWeaknesses(noteStub1, noteStub2).build(); + public static final Person PERSON_G = new PersonBuilder().withName("PersonG").withStrengths(noteStub1, noteStub2) + .withWeaknesses(noteStub1, noteStub2).build(); + public static final Person PERSON_H = new PersonBuilder().withName("PersonH").withStrengths(noteStub1) + .withWeaknesses(noteStub1).build(); private TypicalPersons() {} // prevents instantiation @@ -70,7 +95,83 @@ public static AddressBook getTypicalAddressBook() { return ab; } + /** + * Returns an {@code AddressBook} with sorted list of persons for strength-sort test cases. + */ + public static AddressBook getGenericAddressBookWithSortedStrengths() { + AddressBook ab = new AddressBook(); + for (Person person : getGenericPersonsWithSortedStrengths()) { + ab.addPerson(person); + } + return ab; + } + + /** + * Returns an {@code AddressBook} with sorted list of persons for weakness-sort test cases. + */ + public static AddressBook getGenericAddressBookWithSortedWeaknesses() { + AddressBook ab = new AddressBook(); + for (Person person : getGenericPersonsWithSortedWeaknesses()) { + ab.addPerson(person); + } + return ab; + } + + /** + * Returns an {@code AddressBook} with unsorted list of persons for strength-sort test cases. + */ + public static AddressBook getGenericAddressBookWithUnsortedStrengths() { + AddressBook ab = new AddressBook(); + for (Person person : getGenericPersonsWithUnsortedStrengths()) { + ab.addPerson(person); + } + return ab; + } + + /** + * Returns an {@code AddressBook} with unsorted list of persons for weakness-sort test cases. + */ + public static AddressBook getGenericAddressBookWithUnsortedWeaknesses() { + AddressBook ab = new AddressBook(); + for (Person person : getGenericPersonsWithUnsortedWeaknesses()) { + ab.addPerson(person); + } + return ab; + } + public static List getTypicalPersons() { return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); } + + /** + * Returns unsorted persons for sort-strength test cases. + * @return list of persons + */ + public static List getGenericPersonsWithSortedStrengths() { + return new ArrayList<>(Arrays.asList(PERSON_A, PERSON_B, PERSON_C, PERSON_D)); + } + + /** + * Returns unsorted persons for sort-weakness test cases. + * @return list of persons + */ + public static List getGenericPersonsWithSortedWeaknesses() { + return new ArrayList<>(Arrays.asList(PERSON_E, PERSON_F, PERSON_G, PERSON_H)); + } + + /** + * Returns unsorted persons for sort-strength test cases. + * @return list of persons + */ + public static List getGenericPersonsWithUnsortedStrengths() { + return new ArrayList<>(Arrays.asList(PERSON_D, PERSON_A, PERSON_C, PERSON_B)); + } + + /** + * Returns unsorted persons for sort-weakness test cases. + * @return list of persons + */ + public static List getGenericPersonsWithUnsortedWeaknesses() { + return new ArrayList<>(Arrays.asList(PERSON_H, PERSON_E, PERSON_G, PERSON_F)); + } } diff --git a/src/test/java/seedu/address/testutil/TypicalTasks.java b/src/test/java/seedu/address/testutil/TypicalTasks.java new file mode 100644 index 00000000000..9963c04ba25 --- /dev/null +++ b/src/test/java/seedu/address/testutil/TypicalTasks.java @@ -0,0 +1,81 @@ +package seedu.address.testutil; + +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_DATE_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ENDTIME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_ENDTIME_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_STARTTIME_TASK1; +import static seedu.address.logic.commands.CommandTestUtil.VALID_STARTTIME_TASK2; +import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_EVENT; +import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_MEET; +import static seedu.address.testutil.TypicalPersons.ALICE; +import static seedu.address.testutil.TypicalPersons.BENSON; +import static seedu.address.testutil.TypicalPersons.CARL; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.address.model.TaskBook; +import seedu.address.model.task.Task; + +/** + * A utility class containing a list of {@code Task} objects to be used in tests. + */ +public class TypicalTasks { + public static final Task FIRST_TASK = new TaskBuilder().withName("Meetings").withDate("09-10-2022") + .withStartTime("09:00").withEndTime("10:00") + .withTags("friends", "colleagues").withPersons(ALICE.getName().fullName, BENSON.getName().fullName).build(); + public static final Task SECOND_TASK = new TaskBuilder().withName("Training").withDate("29-02-2020") + .withStartTime("14:00").withEndTime("16:00") + .withTags("colleagues").withPersons(CARL.getName().fullName).build(); + public static final Task THIRD_TASK = new TaskBuilder().withName("Shareholder Conference").withDate("29-02-2020") + .withStartTime("14:00").withEndTime("16:00") + .withTags("colleagues").withPersons(BENSON.getName().fullName).build(); + + // Manually added + public static final Task FOURTH_TASK = new TaskBuilder().withName("Soccer Training").withDate("01-03-2020") + .withStartTime("14:30").withEndTime("16:00") + .withTags("team2").build(); + public static final Task FIFTH_TASK = new TaskBuilder().withName("Basketball Training").withDate("02-03-2020") + .withStartTime("14:30").withEndTime("16:00") + .withTags("team1").build(); + public static final Task SIXTH_TASK = new TaskBuilder().withName("Training1").withDate("03-03-2000") + .withStartTime("14:30").withEndTime("16:00") + .withTags("team1").build(); + public static final Task SEVENTH_TASK = new TaskBuilder().withName("Training2").withDate("03-03-2000") + .withStartTime("14:30").withEndTime("16:00") + .withTags("team1").build(); + public static final Task EIGHTH_TASK = new TaskBuilder().withName("Training3").withDate("03-03-2000") + .withStartTime("14:30").withEndTime("16:00") + .withTags("team1").build(); + public static final Task NINTH_TASK = new TaskBuilder().withName("Training4").withDate("03-03-2000") + .withStartTime("13:30").withEndTime("16:00") + .withTags("team1").build(); + + // Manually added - Task details found in {@code CommandTestUtil} + public static final Task TASK1 = new TaskBuilder().withName(VALID_NAME_TASK1).withDate(VALID_DATE_TASK1) + .withStartTime(VALID_STARTTIME_TASK1).withEndTime(VALID_ENDTIME_TASK1).withTags(VALID_TAG_MEET).build(); + public static final Task TASK2 = new TaskBuilder().withName(VALID_NAME_TASK2).withDate(VALID_DATE_TASK2) + .withStartTime(VALID_STARTTIME_TASK2).withEndTime(VALID_ENDTIME_TASK2) + .withTags(VALID_TAG_EVENT, VALID_TAG_MEET).build(); + + private TypicalTasks() {} // prevents instantiation + + /** + * Returns an {@code TaskBook} with all the typical tasks. + */ + public static TaskBook getTypicalTaskBook() { + TaskBook tb = new TaskBook(); + for (Task task : getTypicalTasks()) { + tb.addTask(task); + } + return tb; + } + + public static List getTypicalTasks() { + return new ArrayList<>(Arrays.asList(FIRST_TASK, SECOND_TASK, THIRD_TASK)); + } +}