diff --git a/README.md b/README.md index 13f5c77403f..3c22bb3c3d1 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ -[![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/AY2021S2-CS2103T-W13-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2021S2-CS2103T-W13-4/tp/actions) + +# Teaching Assistant +Teaching Assistant is a desktop application that provides Secondary School or Junior College teachers with an efficient tool to keep track of their **schedules, contacts and tasks** easily. +While it has a GUI, most of the user interactions rely on typing commands as user inputs. ![Ui](docs/images/Ui.png) -* This is **a sample project for Software Engineering (SE) students**.
- Example usages: - * as a starting point of a course project (as opposed to writing everything from scratch) - * as a case study -* The project simulates an ongoing software project for a desktop application (called _AddressBook_) used for managing contact details. - * It is **written in OOP fashion**. It provides a **reasonably well-written** code base **bigger** (around 6 KLoC) than what students usually write in beginner-level SE modules, without being overwhelmingly big. - * It comes with a **reasonable level of user and developer documentation**. -* It is named `AddressBook Level 3` (`AB3` for short) because it was initially created as a part of a series of `AddressBook` projects (`Level 1`, `Level 2`, `Level 3` ...). -* For the detailed documentation of this project, see the **[Address Book Product Website](https://se-education.org/addressbook-level3)**. -* This project is a **part of the se-education.org** initiative. If you would like to contribute code to this project, see [se-education.org](https://se-education.org#https://se-education.org/#contributing) for more info. +## Getting Started +Head over to the [User Guide](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/docs/UserGuide.md) to start using Teaching Assistant! + +## Developer Guide +Take a look at our [Developer Guide](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/docs/DeveloperGuide.md) if you want to know more about our project. + +## Authors +Teaching Assistant is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org) and will be maintained by [our team](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/docs/AboutUs.md). diff --git a/build.gradle b/build.gradle index be2d2905dde..eb0442fbc3b 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { id 'jacoco' } -mainClassName = 'seedu.address.Main' +mainClassName = 'seedu.ta.Main' sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 @@ -20,6 +20,10 @@ checkstyle { toolVersion = '8.29' } +run { + enableAssertions = true +} + test { useJUnitPlatform() finalizedBy jacocoTestReport @@ -66,7 +70,7 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'teachingAssistant.jar' } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..f9440ef46a2 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -7,53 +7,49 @@ We are a team based in the [School of Computing, National University of Singapor You can reach us at the email `seer[at]comp.nus.edu.sg` -## Project team +## Team Members -### John Doe +### Lee Rong Jieh - + -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](https://github.com/lrj689)] +[[portfolio](team/lrj689.md)] -* Role: Project Advisor +### Loh Jun Yong Kevin -### Jane Doe + - +[[github](https://github.com/kevinlohjunyong)] +[[portfolio](team/kevinlohjunyong.md)] -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +### Nicholas Tanvis -* Role: Team Lead -* Responsibilities: UI + -### Johnny Doe +[[github](http://github.com/nicholastanvis)] +[[portfolio](team/nicholastanvis.md)] - +### Shen Yang -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] + -* Role: Developer -* Responsibilities: Data +[[github](http://github.com/goatygoatygoat)] +[[portfolio](team/goatygoatygoat.md)] -### Jean Doe +### Toh Sihui - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/tsh22)] +[[portfolio](team/tsh22.md)] -* Role: Developer -* Responsibilities: Dev Ops + Threading +## Responsibilities -### James Doe - - - -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] - -* Role: Developer -* Responsibilities: UI +Member | Responsibility 1 | Responsibility 2 +-- | -- | -- +Rong Jieh | Deliverables | Entry IC +Kevin Loh | Documentation | Entry IC +Nicholas | Testing | Contact IC +Shen Yang | Code Quality | Scheduling +Toh Sihui | Tool Expert | Integration diff --git a/docs/DevOps.md b/docs/DevOps.md index 4414eea3344..5849b902846 100644 --- a/docs/DevOps.md +++ b/docs/DevOps.md @@ -44,7 +44,7 @@ As part of CI, this project uses Codecov to generate coverage reports. Here are 1. Sign up with Codecov using your GitHub account [here](https://codecov.io/signup). 1. Once you are inside Codecov web app, add your fork to CodeCov. -1. Get the Markdown code for the Codecov badge provided in `Settings > Badges` and update the `docs/index.md` of your repo with it so that the badge [![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3) in that page reflects the coverage of your project. +1. Get the Markdown code for the Codecov badge provided in `Settings > Badges` and update the `docs/index.md` of your repo with it so that the badge [![codecov](https://codecov.io/gh/AY2021S2-CS2103T-W13-4/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2021S2-CS2103T-W13-4/tp) in that page reflects the coverage of your project. ### Repository-wide checks @@ -73,7 +73,7 @@ Any warnings or errors will be printed out to the console. Here are the steps to create a new release. -1. Update the version number in [`MainApp.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). +1. Update the version number in [`MainApp.java`](https://github.com/AY2021S2-CS2103T-W13-4/tp/tree/master/src/main/java/seedu/ta/MainApp.java). 1. Generate a fat JAR file using Gradle (i.e., `gradlew shadow`). 1. Tag the repo with the version number. e.g. `v0.1` 1. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/). Upload the JAR file you created. diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 4829fe43011..31e885dccfd 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -2,32 +2,64 @@ layout: page title: Developer Guide --- -* Table of Contents -{:toc} + +- [Getting Started](#getting-started) +- [Design and Architecture](#design) + - [UI Component](#ui-component) + - [Logic Component](#logic-component) + - [Model Component](#model-component) + - [Storage Component](#storage-component) + - [Common Classes](#common-classes) +- [Implementation](#implementation) + - [Deleting a contact in the address book](#deleting-a-contact-in-the-address-book) + - [Enquire if time interval is free](#enquire-if-time-interval-is-free) + - [Merge Schedule and Task](#merge-schedule-and-task) + - [Filtering entries via tags](#filtering-entries-via-tags) + - [List entry feature](#list-entry-feature) + - [Clear overdue entry feature](#clear-overdue-entry-feature) +- [Appendix: Requirements](#appendix-requirements) + - [Product Scope](#product-scope) + - [User Stories](#user-stories) + - [Use Cases](#use-cases) + - [Non-Functional Requirements](#non-functional-requirements) + - [Glossary](#glossary) +- [Appendix: Instructions for manual testing](#appendix-instructions-for-manual-testing) + - [Adding a contact](#adding-a-contact) + - [Finding contacts](#finding-contacts) + - [Filtering contacts via tags](#filtering-contacts-via-tags) + - [Editing a contact](#editing-a-contact) + - [Deleting a contact](#deleting-a-contact) + - [Adding an entry](#adding-an-entry) + - [Finding an entry](#finding-an-entry) + - [Filtering entries via tags](#filtering-tags) + - [Editing an entry](#editing-an-entry) + - [Listing entries](#listing-entries) + - [Checking if interval is free](#checking-if-interval-is-free) + - [Deleting entries](#deleting-entries) + - [Clearing overdue entries](#clearing-overdue-entries) -------------------------------------------------------------------------------------------------------------------- -## **Setting up, getting started** +## **Getting Started** Refer to the guide [_Setting up and getting started_](SettingUp.md). -------------------------------------------------------------------------------------------------------------------- -## **Design** - -### Architecture - - +# Design -The ***Architecture Diagram*** given above explains the high-level design of the App. Given below is a quick overview of each component. +## Architecture -
+![Architecture Diagram](images/ArchitectureDiagram.png) -: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. +The ***Architecture Diagram*** given above explains the high-level design of the App. Given below is a quick overview of +each component. -
+**`Main`** has two classes +called [`Main`](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/src/main/java/seedu/address/Main.java) +and [`MainApp`](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/src/main/java/seedu/address/MainApp.java). It +is responsible for, -**`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, * 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. @@ -43,184 +75,241 @@ The rest of the App consists of four components. Each of the four components, * defines its *API* in an `interface` with the same name as the Component. -* exposes its functionality using a concrete `{Component Name}Manager` class (which implements the corresponding API `interface` mentioned in the previous point. - -For example, the `Logic` component (see the class diagram given below) defines its API in the `Logic.java` interface and exposes its functionality using the `LogicManager.java` class which implements the `Logic` interface. +* exposes its functionality using a concrete `{Component Name}Manager` class (which implements the corresponding + API `interface` mentioned in the previous point. -![Class Diagram of the Logic Component](images/LogicClassDiagram.png) +--- **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The ***Sequence Diagram*** below shows how the components interact with each other for the scenario where the user +issues the command `cdelete 1`. - +![Sequence Diagram](images/ArchitectureSequenceDiagram.png) The sections below give more details of each component. -### UI component +--- + +### UI Component ![Structure of the UI Component](images/UiClassDiagram.png) **API** : -[`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +[`Ui.java`](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/src/main/java/seedu/address/ui/Ui.java) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `ContactListPanel` +, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class. -The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are +in the `src/main/resources/view` folder. For example, the layout of +the [`MainWindow`](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java) +is specified +in [`MainWindow.fxml`](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * Executes user commands using the `Logic` component. * Listens for changes to `Model` data so that the UI can be updated with the modified data. +--- + ### Logic component ![Structure of the Logic Component](images/LogicClassDiagram.png) **API** : -[`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +[`Logic.java`](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/src/main/java/seedu/address/logic/Logic.java) -1. `Logic` uses the `AddressBookParser` class to parse the user command. +1. `Logic` uses the `TeachingAssistantParser` class to parse the user command. 1. This results in a `Command` object which is executed by the `LogicManager`. 1. The command execution can affect the `Model` (e.g. adding a person). 1. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`. -1. In addition, the `CommandResult` object can also instruct the `Ui` to perform certain actions, such as displaying help to the user. +1. In addition, the `CommandResult` object can also instruct the `Ui` to perform certain actions, such as displaying + help to the user. -Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` API call. +Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("cdelete 1")` API +call. ![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) -
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram. -
+--- ### Model component ![Structure of the Model Component](images/ModelClassDiagram.png) -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +** +API** : [`Model.java`](https://github.com/AY2021S2-CS2103T-W13-4/tp/blob/master/src/main/java/seedu/address/model/Model.java) The `Model`, * stores a `UserPref` object that represents the user’s preferences. -* stores the address book data. -* exposes 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. -* does not depend on any of the other three 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` object.
-![BetterModelClassDiagram](images/BetterModelClassDiagram.png) - -
+* stores Teaching Assistant data. +* exposes an unmodifiable `ObservableList` and `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. +* does not depend on any of the other components. +--- ### Storage component ![Structure of the Storage Component](images/StorageClassDiagram.png) -**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/AY2021S2-CS2103T-W13-4/tp/blob/master/src/main/java/seedu/address/storage/Storage.java) The `Storage` component, + * can save `UserPref` objects in json format and read it back. -* can save the address book data in json format and read it back. +* can save Teaching Assistant data in json format and read it back. -### Common classes +--- -Classes used by multiple components are in the `seedu.addressbook.commons` package. +### Common Classes --------------------------------------------------------------------------------------------------------------------- +Classes used by multiple components are in the `seedu.ta.commons` package. -## **Implementation** +--- + +# Implementation This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +### Deleting a contact in the address book -#### Proposed Implementation +The command immediately responsible for this is the 'DeleteContactCommand' -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: +An outline of the implementation is as follows: -* `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. +The `TeachingAssistantParser` should accept the command word `cdelete`. It will then get +the `DeleteContactCommandParser` +to parse and return the `DeleteContactCommand`. Subsequently, it returns `CommandResult` which is shown on the command +result field of the GUI as the information of the user that is deleted. -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +The following activity diagram summarizes what happens when a user executes the cdelete command: -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +![Delete Contact Command Activity Diagram](images/DeleteContactCommandActivityDiagram.png) -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. +The following sequence diagram summarizes what happens when a user executes the cdelete command: -![UndoRedoState0](images/UndoRedoState0.png) +![Delete Contact Command Sequence Diagram](images/DeleteContactCommandSequenceDiagram.png) -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. +### Enquire if time interval is free -![UndoRedoState1](images/UndoRedoState1.png) +The free command mechanism provides users a quick way to find out if certain time intervals are available. -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`. +An outline of the proposed implementation is as follows: -![UndoRedoState2](images/UndoRedoState2.png) +The `TeachingAssistantParser` should accept another case of command word `free` which eventually returns `FreeCommand` +back to Logic Manager. -
: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`. +This command is then executed to return `CommandResult` which is either shown on the result display of the GUI as: -
+* "You're free!" +* Or "Sorry, you're not free. Entries occupying that time interval listed below!". The occupying entries are shown in + the entry list at the main window. -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. +The following activity diagram summarizes what happens when a user executes the new command: -![UndoRedoState3](images/UndoRedoState3.png) +![Free Intervals Activity Diagram](images/FreeIntervalActivityDiagram.png) -
: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. +The implementation of this command is similar to the List Command but requires a new predicate class to be created. +A `ListOccupyingEntryPredicate` class is implemented that provides a predicate that accepts user provided start and end +interval. The predicate is provided to `ModelManager#updateFilteredEntryList` and updates the entry list according to +the condition under `ListOccupyingEntryPredicate#test`. +The following sequence diagram outlines how the free operation works: + +![Free Intervals Sequence Diagram](images/FreeIntervalSequenceDiagram.png) + +
:information_source: **Note:** Details about creation of `ListOccupyingEntryPredicate` +has been omitted for simplicity.
-The following sequence diagram shows how the undo operation works: +### Merge Schedule and Task -![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +The merger attempts to combine the functionalities of both the Task and Schedule classes. \ +As the Task and Schedule classes are similar in features, we can merge them into an Entry class for maintainability. -
: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. +![Entry Class](images/EntryClassDiagram.png) -
+Previously, we allowed schedules and tasks to be added separately through using two commands, `sadd` and `tadd`. \ +Combining them into an Entry task, we implement an "eadd" command. -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. +Below, we can see the before and after activity diagrams involving this merger. -
: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. +**Before:** \ +![Schedule and Task Activity](images/ScheduleAndTaskActivityDiagram.png) -
+**After:** \ +![Entry Activity](images/EntryActivityDiagram.png) -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. +### Filtering entries via tags -![UndoRedoState4](images/UndoRedoState4.png) +Following the proposal above, there were no commands that utilise the tags attached to the objects. Hence, this proposal +aims to allow filtering these entries via their tags. -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +The Model class will be required to implement the `updateFilteredEntryList` which can incorporate +`updateFilteredTaskList` or `updateFilteredScheduleList` implemented in the previous two classes. This method will then +accept an argument of type `EntryTagContainsKeywordsPredicate`. -![UndoRedoState5](images/UndoRedoState5.png) +The following diagram omits the parser object created, namely `FilterEntryCommandParser` for simplicity. -The following activity diagram summarizes what happens when a user executes a new command: +![Filtering Entries](images/FilterEntrySequenceDiagram.png) -![CommitActivityDiagram](images/CommitActivityDiagram.png) +**Design consideration** -#### Design consideration: +1. Allow filtering by more than one tag. +1. Decide whether the filtering above considers Union or Intersection of tags. -##### Aspect: How undo & redo executes +### List entry feature -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +The list entry mechanism allows users to see all of their entries, or see them by day or by week. -* **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. +An outline of the proposed implementation is as follows: -_{more aspects and alternatives to be added}_ +The `TeachingAssistantParser` should accept command word `elist` and eventually return a `ListEntryCommand` +back to `LogicManager`. This command can take in one of these three arguments: an empty string, the string “day” or the +string “week”. The arguments will be parsed by the `ListEntryCommandParser` to determine the behaviour of +`ListEntryFormatPredicate`. Then, `updateFilteredEntryList` method in the `Model` interface is called, passing in the +`ListEntryFormatPredicate` as an argument. -### \[Proposed\] Data archiving +The following activity diagram (Fig 2.3.1) summarizes what happens when a user executes the list entry command. -_{Explain here how the data archiving feature will be implemented}_ +![Listing entries activity diagram](images/ListEntryActivityDiagram.png) +Fig 2.3.1 +The following sequence diagram (Fig 2.3.2) shows how the list entry operation works: --------------------------------------------------------------------------------------------------------------------- +![Listing entries](images/ListEntrySequenceDiagram.png) +Fig 2.3.2 + +### Clear overdue entry feature + +Clear overdue entry feature allows users to quickly discard entries that are no longer relevant. i.e. entries with end +dates that have passed. + +An outline of the implementation is as follows: + +The `TeachingAssistantParser` should accept another case of command word `eclear` which eventually returns a +`ClearOverdueEntryCommand` back to `LogicManager`. This command has no arguments and will immediately call +`clearOverdueEntries` method in `Model` interface. Finally, a new `CommandResult` is created to handle the result of +this command. + +The following sequence diagram (Fig 2.3.3) shows how clear overdue entry command works: + +![Clear overdue entries sequence diagram](images/ClearOverdueSequenceDiagram.png) -## **Documentation, logging, testing, configuration, dev-ops** +Fig 2.3.3 + +The following activity diagram (Fig 2.3.4) shows how `Model` executes `clearOverdueEntries`: + +![Clear overdue entries activity diagram](images/ClearOverdueEntryCommandActivityDiagram.png) + +--- + +# Documentation, logging, testing, configuration, dev-ops * [Documentation guide](Documentation.md) * [Testing guide](Testing.md) @@ -228,129 +317,362 @@ _{Explain here how the data archiving feature will be implemented}_ * [Configuration guide](Configuration.md) * [DevOps guide](DevOps.md) --------------------------------------------------------------------------------------------------------------------- +--- -## **Appendix: Requirements** +# Appendix: Requirements -### Product scope +## Product Scope **Target user profile**: -* 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 +* Are JC/Secondary school teachers +* Do not have their schedules and students' contacts digitalised +* Prefer to use typing over mouse/voice commands + +**Value proposition**: efficient tool to keep track of schedules (i.e. entries) as well as find and add student contact +information easily + +--- + +## User Stories -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +### Contacts +Priority | As a... | I want to... | So that I can... +--- | --- | --- | --- +high | teacher | add a contact | have a consolidated list of contacts that I require +high | teacher | delete a contact | remove a contact I no longer need +medium | teacher | edit a contact | modify contact details without going through the tedious process of removing and re-adding the contact +high | teacher | find a contact based on name | quickly find the details of a specific contact I need +high | teacher | list all contacts | keep track of the contacts of all the people I have saved +medium | teacher | filter contacts via tags | categorise and find a group of contacts easily -### User stories +### Entries -Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` +Priority | As a... | I want to... | So that I can... +--- | --- | --- | --- +high | teacher | add an entry | have a consolidated list of entries representing my schedule +high | teacher | delete an entry | remove entries that have been cancelled +medium | teacher | edit an entry | modify entry details without going through the tedious process of removing and re-adding the entry; in case an entry has been rescheduled +high | teacher | list my entries according to day/week | view my entries in a more organised way +high | teacher | find an entry based on name | see the details of an entry I have saved +low | teacher | view all entries in my schedule for a specific time period | check if I am free during that timing +medium | teacher | filter entries via tags | categorise and find entries easily +medium | teacher | clear all entries that have passed | easily see ongoing or future entries without past entries cluttering the list -| 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 | +### Others -*{More to be added}* +Priority | As a... | I want to... | So that I can... +--------- | --------- | -------------- | ----------------- +high | forgetful teacher | be prompted for the User Guide | refer to it and type all commands without memorising their syntax +low | first time user | clear all my contacts and entries from teaching assistant | clear all sample data easily -### Use cases +--- + +## Use Cases -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) +(For all use cases below, the **System** is the `Teaching Assistant` and the **Actor** is the `user`, unless specified +otherwise) -**Use case: Delete a person** +### Use case: UC01 - Add a contact **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User requests to add a contact +2. Teaching Assistant adds the contact into the list - Use case ends. + Use case ends. **Extensions** -* 2a. The list is empty. +* 2a. The given field(s) are invalid. + + * 2a1. Teaching Assistant shows an error message + + Use case ends. + + +* 2b. The contact to be added already exists in Teaching Assistant. + + * 2b1. Teaching Assistant shows an error message. + + Use case ends. + +
+ +### Use case: UC02 - Delete an Entry + +**MSS** + +1. User requests to list entries **(UC03)** +2. Teaching Assistant shows a list of entries +3. User requests to delete a specific entry in the list +4. Teaching Assistant deletes the specified entry + + Use case ends. + +**Extensions** + +* 2a. The list of entries is empty. Use case ends. -* 3a. The given index is invalid. +* 3a. The given index is invalid + * 3a1. Teaching Assistant shows an error message. - * 3a1. AddressBook shows an error message. + Use case ends. - Use case resumes at step 2. +
-*{More to be added}* +### Use case: UC03 - List entries -### Non-Functional Requirements +**MSS** -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. +1. User requests to list entries. +2. Teaching Assistant shows a list of entries. -*{More to be added}* + Use case ends. -### Glossary +**Extensions** -* **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +* 1a. User requests to list entries that occurs today. --------------------------------------------------------------------------------------------------------------------- + * 1a1. Teaching Assistant shows a list of entries that happen today. -## **Appendix: Instructions for manual testing** + Use case ends. -Given below are instructions to test the app manually. +* 1b. User requests to list entries that occurs this week. -
:information_source: **Note:** These instructions only provide a starting point for testers to work on; -testers are expected to do more *exploratory* testing. + * 1b1. Teaching Assistant shows a list of entries that occurs this week -
+ Use case ends. -### Launch and shutdown +* 1c. The given parameter is invalid. -1. Initial launch + * 1c1. Teaching Assistant shows an error message. - 1. Download the jar file and copy into an empty folder + Use case ends. - 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum. +
-1. Saving window preferences +### Use case: UC04 - Check if user is free on a given timeslot - 1. Resize the window to an optimum size. Move the window to a different location. Close the window. +**MSS** - 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained. +1. User requests to if he/she is free on a given timeslot. +2. Teaching Assistant replies that the user is free. -1. _{ more test cases …​ }_ + Use case ends. -### Deleting a person +* 1a. The given timeslot is invalid. -1. Deleting a person while all persons are being shown + * 1a1. Teaching Assistant shows an error message. - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. + Use case ends - 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. +* 1b. The user is not free on the given timeslot. - 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + * 1b1. Teaching Assistant replies that the user is not free. - 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. + * 1b2. Teaching Assistant shows a list of entries that coincide with the given timeslot. -1. _{ more test cases …​ }_ + Use case ends -### Saving data +### Use case: UC05 - Find a contact -1. Dealing with missing/corrupted data files +**MSS** - 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ +1. User requests to find a contact with specified keyword(s) +2. Teaching Assistant replies to the user contacts that matches the keyword(s) + + Use case ends. + +* 1a. The given keyword(s) are invalid + + * 1a1. Teaching Assistant shows an error message. + + Use case ends + +* 2a. No contact matches the specified keyword(s). + + * 2a1. Teaching Assistant shows an empty list. + + Use case ends + +--- + +## Non-Functional Requirements + +1. Should work on any mainstream OS as long as it has Java 11 or above installed. +2. 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. +3. The system should be usable by a novice who has never used virtual management applications. +4. The user interface should be intuitive enough for users who are not IT-savvy. +5. The product is offered as an open source software. + +--- + +## Glossary + +### JC + +Junior College (JC) is the post-secondary education level where students are preparing for university. JC is also the +high-school equivalent in other countries. Hence, JC teachers may be packed with consultation schedules which can +leverage our software. + +### Mainstream OS + +Mainstream operating systems are the current operating systems with a significant market share, namely Windows, Linux, +Unix, and OS-X. + +### MSS + +Main Success Scenario (MSS) defines the optimal outcome of our commands, i.e. in the case where no errors occurred. + +--- -1. _{ more test cases …​ }_ +# Appendix: Instructions for manual testing + +### Adding an entry + +1. Adding an entry while all entries are being shown. + 1. Prerequisites: The entry should not already exist in the entry list and the new entry's start and end datetime + should not overlap with existing entries. List all entries using the `elist` command. + 1. Test case: `eadd n/Meeting sd/2021-12-12 13:00 ed/2021-12-12 14:00`
+ Expected: The entry is added to the entry list. Success message with details of the added entry is shown in the + result window. + 1. Test case: `eadd n/Meeting sd/2021-12-12 19:00 ed/2021-12-12 13:00`
+ Expected: The entry is not added. Error message is shown in the result display indicating that start datetime has + to be before end datetime. + +### Finding an entry + +1. Finding an entry while all entries are being shown. + 1. Prerequisites: There should be entries present in the entry list. List all entries using the `elist` command. + 1. Test case: `efind Meeting`
+ Expected: Assuming that there is an entry named "Meeting", a message indicating how many entries are listed is + shown in the result window. Entry list will only list the entry with the matching name. + 1. Test case: `efind Random`
+ Expected: Assuming that there are no entries named Random, a message shown in the result display will indicate 0 + entries being listed. Entry list will be empty. + +### Filtering tags + +1. Filtering tags while all entries are being shown. + 1. Prerequisites: There should be entries with tags present. List all entries using the `elist` command. + 1. Test case: `efilter consultation`
+ Expected: Assuming there is only one entry that has the tag `consultation`, a message is shown in the result + display indicating 1 entry is being listed. Entry list will only list the entry with the matching tag. + 1. Test case: `efilter CCA Tennis`
+ Expected: Assuming there is only one entry that has both tags `CCA` and `Tennis`, a message is shown in the + result display indicating 1 entry is being listed. Entry list will only list the entry with the matching tags. + +### Editing an entry + +1. Editing an existing entry while all entries are being shown. + 1. Prerequisites: There should be entries present in the entry list. List all entries using the `elist` command. + 1. Test case: `eedit 1 p/97865534`
+ Expected: The entry that is numbered "1." is edited such that the previous phone number is now changed to the new + one specified. Details of the edited entry is shown in the result display. + 1. Test case: `eedit 1`
+ Expected: No entry is edited. Error details shown in the result display indicating at least one field should be + given. + +### Listing entries + +1. Listing entries while there are entries present and are not currently all listed. + 1. Prerequisites: There should be entries present in the entry list but not all of them are seen. + 1. Test case: `elist`
+ Expected: Success message indicating how many entries are listed is shown in the result display. All entries that + are present are shown in the entry list. + 1. Test case: `elist day`
+ Expected: A message indicating how many entries are listed is shown in the result display. All entries that have + the same date as the current date are shown in the entry list. + 1. Test case: `elist week`
+ Expected: A message indicating how many entries are listed is shown in the result display. All entries that have + date 6 days from the current date or same date as current date are shown in the entry list. + +### Checking if interval is free + +1. Checking if an interval is free while there are entries present. + 1. Prerequisites: There should be entries present in the entry list. The start and end datetime field provided + should fall within the datetime of at least one entry to test the "Not free" response. + 1. Test case: `free sd/2021-12-12 13:00 ed/2021-12-12 14:00`
+ Expected: Assuming there is only one entry that falls within this interval, message indicating the interval is + not free is shown in the result display. Entry list will only list the entry that occupies the interval. + 1. Test case: `free sd/2021-11-13 13:00 ed/2021-11-13 14:00`
+ Expected: Assuming there are no entries that falls within this interval, message indicating the interval is free + is shown in the result display. Entry list will be empty. + +### Deleting entries + +1. Deleting an entry while all entries are being shown. + 1. Prerequisites: List all entries using the `elist` command. Multiple entries in the list. + 1. Test case: `edelete 0`
+ Expected: No entry is deleted. Error details shown in the result display. Entry list remains the same. + 1. Test case: `edelete 1`
+ Expected: The entry that is numbered "1." is deleted from the entry list. Details of the deleted entry is shown + in the result display. + +### Clearing overdue entries + +1. Clearing overdue entries while there are overdue entries present. + 1. Prerequisites: There should be overdue entries (shown as a red entry box) in the entry list. + 1. Test case: `eclear`
+ Expected: Success message is shown. All entries that are before current date and time should be deleted. + +### Adding a contact + +1. Adding a contact while all contacts are being shown. + 1. Prerequisites: The contact should not already exist in the list. List all contacts using the `clist` command. + 1. Test case: `cadd n/Danny Tan p/98765432 e/danny@email.com`
+ Expected: The contact is added into the contact list. Success message with details of the added contact is shown in the + result window. + 1. Doing the exact same command one more time will fail with an error message as duplicated contacts are not allowed to be added into the list + +### Finding contacts + +1. Finding contacts while all contacts are being shown + 1. Prerequisites: There should be at least a contact present in the contact list. List all contacts using the `clist` command. + 1. Test case: `cfind Danny`
+ Expected: Assuming that there is at least a contact named "Danny", a message indicating how many contacts are listed is + shown in the result window. Contact list will only list the contacts with the matching name. + 1. Test case: `cfind Random`
+ Expected: Assuming that there are no contacts named "Random", a message shown in the result display will indicate 0 + contacts being listed. Contact list will be empty. + +### Filtering contacts via tags + +1. Filtering tags while all contacts are being shown. + 1. Prerequisites: There should be contacts with tags present. List all contacts using the `clist` command. + 1. Test case: `cfilter friend`
+ Expected: Assuming there is only one contact that has the tag `friend`, a message is shown in the result + display indicating 1 contact is being listed. Contact list will only list the contact with the matching tag. + 1. Test case: `cfilter family important`
+ Expected: Assuming there is only one contact that has both the tags `family` and `important`, a message is shown in the + result display indicating 1 contact is being listed. Contact list will only list the contact with the matching tags. + +### Editing a contact + +1. Editing an existing contact while all contacts are being shown. + 1. Prerequisites: There should be contacts present in the contact list. List all contacts using the `clist` command. + 1. Test case: `cedit 1 n/Amy Toh`
+ Expected: The contact that is numbered "1." is edited such that the previous name is now changed to the new + one specified. Details of the edited contact is shown in the result display. + 1. Test case: `cedit 1 n/Amy Toh p/98761234`
+ Expected: The contact that is numbered "1." is edited such that the previous name and phone number + are now changed to the new ones specified. Details of the edited contact is shown in the result display. + 1. Test case: `cedit 1`
+ Expected: No contact will be edited. Error details shown in the result display indicating at least one field should be + given. + +### Deleting a contact + +1. Deleting a contact while all contacts are being shown. + 1. Prerequisites: List all contacts using the `clist` command. Contact list must not be empty. + 1. Test case: `cdelete 0`
+ Expected: No contact is deleted. Error details regarding the invalid argument will be + shown in the result display. Contact list remains the same. + 1. Test case: `cdelete 1`
+ Expected: The contact that is numbered "1." is deleted from the contact list. Details of the deleted contact is shown + in the result display. diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 77667c6d581..c9a7ff40488 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -23,7 +23,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Import the project as a Gradle project**: Follow the guide [_[se-edu/guides] IDEA: Importing a Gradle project_](https://se-education.org/guides/tutorials/intellijImportGradleProject.html) to import the project into IDEA.
:exclamation: Note: Importing a Gradle project is slightly different from importing a normal Java project. 1. **Verify the setup**: - 1. Run the `seedu.address.Main` and try a few commands. + 1. Run the `seedu.ta.Main` and try a few commands. 1. [Run the tests](Testing.md) to ensure they all pass. -------------------------------------------------------------------------------------------------------------------- @@ -45,10 +45,10 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Learn the design** - When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture). + When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [Teaching Assistant’s architecture](DeveloperGuide.md#architecture). 1. **Do the tutorials** - These tutorials will help you get acquainted with the codebase. + These tutorials will help you get acquainted with the Address Book 3 codebase, which is what our codebase was derived from. * [Tracing code](tutorials/TracingCode.md) * [Removing fields](tutorials/RemovingFields.md) diff --git a/docs/Testing.md b/docs/Testing.md index 8a99e82438a..95c872948b2 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -29,8 +29,8 @@ There are two ways to run tests. This project has three types of tests: 1. *Unit tests* targeting the lowest level methods/classes.
- e.g. `seedu.address.commons.StringUtilTest` + e.g. `seedu.ta.commons.util.StringUtilTest` 1. *Integration tests* that are checking the integration of multiple code units (those code units are assumed to be working).
- e.g. `seedu.address.storage.StorageManagerTest` -1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how the are connected together.
- e.g. `seedu.address.logic.LogicManagerTest` + e.g. `seedu.ta.storage.StorageManagerTest` +1. Hybrids of unit and integration tests. These test are checking multiple code units as well as how they are connected together.
+ e.g. `seedu.ta.logic.LogicManagerTest` diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..e0e131301fe 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,192 +1,594 @@ --- -layout: page +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. +Welcome to the User Guide of _Teaching Assistant_! -* Table of Contents -{:toc} +Are you a JC/Secondary school teacher, having troubles keeping track of all your consultations, meetings and your +students' contacts? No worries! Our application, _Teaching Assistant_ will provide an all-in-one platform for you to +organise your entries (schedules) and contacts! --------------------------------------------------------------------------------------------------------------------- +We target JC/Secondary school teachers as they are the teachers who have a greater need to contact their students +compared to primary school schools, yet do not have a standardised platform for communication unlike teachers +in tertiary schools. -## Quick start +_Teaching Assistant_ mainly uses a Command Line Interface ([CLI](#terminology)). For users who type fast, they can use +this application more efficiently than other applications that heavily use Graphical User Interface ([GUI](#terminology)). -1. Ensure you have Java `11` or above installed in your Computer. +If you are interested, jump to [Quick Start](#quick-start) to learn how to learn how to start using +_Teaching Assistant_. -1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases). +An image of our UI is shown below! -1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook. +![Ui](images/Ui.png) -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) +--- + +- [Quick Start](#quick-start) +- [Structure of User Guide](#structure-of-the-user-guide) + - [Reading this User Guide](#reading-this-user-guide) + - [Terminology](#terminology) + - [General Syntax and Symbols](#general-syntax) +- [Features](#features) + - [Viewing help: `help`](#viewing-help-help) + - [Contact](#adding-a-contact-cadd) + - [Add: `cadd`](#adding-a-contact-cadd) + - [Find: `cfind`](#finding-a-contact-cfind) + - [Filter: `cfilter`](#filter-contact-tags-cfilter) + - [Edit: `cedit`](#editing-a-contact-cedit) + - [List: `clist`](#listing-contacts-clist) + - [Delete: `cdelete`](#deleting-a-contact-cdelete) + - [Entry](#adding-an-entry-eadd) + - [Add: `eadd`](#adding-an-entry-eadd) + - [Find: `efind`](#finding-an-entry-efind) + - [Filter: `efilter`](#filter-entry-tags-efilter) + - [Edit: `eedit`](#editing-an-entry-eedit) + - [List: `elist`](#listing-entries-elist) + - [Free: `free`](#checking-if-time-interval-is-free-free) + - [Delete: `edelete`](#deleting-an-entry-edelete) + - [Clear: `eclear`](#clearing-overdue-entries-eclear) + - [Clear: `clear`](#clearing-all-data-clear) + - [Exit: `exit`](#exiting-the-program-exit) + - [Saving the data](#saving-the-data) + - [Editing the data file](#editing-the-data-file) +- [FAQ](#faq) +- [Command Summary](#command-summary) + +--- + +## Quick Start -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.
+1. Ensure you have Java 11 installed in your computer. You may install it [here](https://www.oracle.com/sg/java/technologies/javase-jdk11-downloads.html). +1. Download the latest `teachingAssistant.jar` [here](https://github.com/AY2021S2-CS2103T-W13-4/tp/releases). +1. Copy the file to the folder you want to use as the *home folder* for your Teaching Assistant. +1. Double-click the file to start the app. The GUI similar to the image above should appear. +1. Type the command in the command box and press Enter to execute it. Some example commands you can try: + * **`clist`**: Lists all contacts in Teaching Assistant. + * **`cadd n/Danny Tan p/98765432 e/danny@email.com`**: Adds a contact named `Danny Tan` to Teaching Assistant. + * **`efind consultation`**: Finds an entry named `consultation` or entries that have `consultation` in their names + in Teaching Assistant. + * **`exit`**: Exits the app. +1. Refer to the [Features](#features) below for details of each command. - * **`list`** : Lists all contacts. +--- - * **`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. +## Structure of the User Guide - * **`delete`**`3` : Deletes the 3rd contact shown in the current list. +Worried about how tedious it will be to read this document? Not to worry! We have structured this User Guide in a way +that makes it easy and quick for you to find what you need. In this next subsection, +[Reading this User Guide](#reading-this-user-guide), you can find some tips we have on how to read this guide. The +next section, [Features](#features), documents the main functionalities of _Teaching Assistant_ and how to use them. - * **`clear`** : Deletes all contacts. +The main functions of _Teaching Assistant_ can be summmarised as the following: +* Contact management +* Entry (schedules) management - * **`exit`** : Exits the app. +If you prefer a brief overview of all the commands, head over to [command summary](#command-summary) which provides you +with a table of all available commands. -1. Refer to the [Features](#features) below for details of each command. +### Reading this User Guide +This section introduces you to some technical terms and syntax that will be used throughout the User Guide. +You may want to read through this section thoroughly first before moving on to the next sections. --------------------------------------------------------------------------------------------------------------------- +##### Terminology +The image of our GUI is shown below, annotated with descriptions of each GUI component we refer to in this User Guide. -## Features +![Annotated GUI](images/annotatedUi.png) -
+The table below defines some technical terminology used throughout the User Guide. -**:information_source: Notes about the command format:**
+Terms | Meaning +----------------- | ------------------- +CLI | Command Line Interface. In the context of _Teaching Assistant_, this refers to the command box where you can type your commands. +GUI | Graphical User Interface. It refers to the part of the application you interact with through graphical features such as buttons. +JSON | JavaScript Object Notation. Your Teaching Assistant data is stored as a JSON file format. You can find out more on their official website [here](https://www.json.org/json-en.html)! -* 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`. +##### General Syntax +The table below explains the general syntax used throughout the User Guide. -* 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`. +Syntax | Meaning +----------------- | ------------------- +`command` | This markup is used to specify text that can be entered into the command box. +:bulb: | This icon indicates that the following text is a tip. +:exclamation: | This icon indicates that the following text is a warning. +:information_source: | This icon indicates that the following text is a note. -* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. +--- -* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable. +## Features -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. +**:information_source: Notes about the command format:**
-* 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`. +* Words in `UPPER_CASE` are the parameters to be supplied by the user. e.g. in `cadd n/NAME`, `NAME` is a parameter which + can be used as `cadd n/Danny Tan`. +* Items in the square brackets are optional. Users can choose to leave the field empty. +* Items with `...` after them can be used multiple times including zero times. e.g. `[t/TAG]...` can be used as ` ` + (i.e. 0 times), t/friend, t/friend t/family etc. +* Parameters can be in any order. e.g. if the command specifies `n/NAME p/NUMBER`, `p/NUMBER n/NAME` is also + acceptable. +* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of + the parameter will be taken. e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. +* 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`. -
+--- + +### Viewing help: `help` -### Viewing help : `help` +Shows a message explaining how to access the help page. -Shows a message explaning how to access the help page. +**Format**: `help` -![help message](images/helpMessage.png) +**Breakdown**: +* Command word - `help` -Format: `help` +Scenario: +You are using Teaching Assistant but forget the formats of the commands you want to execute. This command is the only +one you need to remember! It will bring you to this document. + +--- +### Adding a contact: `cadd` -### Adding a person: `add` +Adds a contact into Teaching Assistant. -Adds a person to the address book. +**Format**: `cadd n/NAME p/NUMBER e/EMAIL [t/TAG]...` -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +**Breakdown**: +* Command word - `cadd` +* Prefixes - `n/`, `p/`, `e/`, `t/` +* Parameters - `NAME`, `NUMBER`, `EMAIL`, `TAG`
:bulb: **Tip:** -A person can have any number of tags (including 0) +An entry's name can be tagged to a contact to easily filter for contacts related an entry.
-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` +Example(s): + +* `cadd n/Danny Tan p/98765432 e/danny@email.com t/student t/english t/consultation1` +* `cadd n/Amy Yeoh p/12345678 e/amy@email.com t/Colleague t/OwesMoney t/meeting1` + +Scenario: +You want to save the contact details of your students so that you can easily obtain their details when you want to +reach out to them. + +### Finding a contact: `cfind` + +Finds all contacts whose name contain all of the specified keywords and displays them as a list. + +**Format**: `cfind KEYWORD [MORE_KEYWORDS]...` + +**Breakdown**: +* Command word - `cfind` +* Parameters - `KEYWORD`, `MORE_KEYWORDS` + + +* Only names are searched. +* The search is case-insensitive e.g. `amy` will match `Amy`. +* Only full words will be matched e.g. `Dan` will not match `Danny`. +* Only contacts matching all keywords will be returned (i.e. AND search). E.g. `Danny Tan` will only return `Danny Tan`. +* The order of the keywords does not matter. e.g. `Danny Tan` will match `Tan Danny`. + +Example(s): + +* `cfind Danny` returns `danny` and `Danny Tan` +* `cfind amy yeoh` returns only `Amy Yeoh` + +Below is an illustration of entering `cfind yeoh` on a sample Teaching Assistant: + +![Find contact example](images/FindContactExample.png) + +Scenario: +Your student had booked a consultation with you and you want to find his/her contact details by name without scrolling +through the entire contact list so that you can remind him/her of the arrangement as the date of the consultation draws +nearer. + +### Filter contact tags: `cfilter` + +Filters all contacts that have the tags of the specified keywords and displays them as a list with index numbers. + +**Format**: `cfilter KEYWORD [MORE_KEYWORDS]...` + +**Breakdown**: +* Command word - `cfilter` +* Parameters - `KEYWORD`, `MORE_KEYWORDS` + +* Only tags are searched. +* The filtering is case-insensitive e.g. `Student` will match `student`. +* Only full words will be matched e.g. `Friend` will not match `Friends`. +* If more than one keyword is provided, only contacts with all the keywords provided will be displayed. + E.g. `filter colleagues friends` will only return a contact with both tags `colleagues` and `friends`. Contacts with + only one of the 2 keywords will not be displayed. +* The order of the keywords does not matter. e.g. `colleagues friends` will match `friends colleagues`. -### Listing all persons : `list` +Example(s): -Shows a list of all persons in the address book. +* `cfilter student english` +* `cfilter colleague` -Format: `list` +Scenario: +Your students have decided on the roles and responsibilities they will be taking up in your class (e.g. chairperson, +english representative). You can add a tag to their contact to identify their roles. Then, you can find their contact +details easily by filtering contacts via tags. -### Editing a person : `edit` +### Editing a contact: `cedit` -Edits an existing person in the address book. +Edits an existing contact with the specified index in Teaching Assistant. -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +**Format**: `cedit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [t/TAG]...` -* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …​ +**Breakdown**: +* Command word - `cedit` +* Prefixes - `n/`, `p/`, `e/`, `t/` +* Parameters - `INDEX`, `NAME`, `NUMBER`, `EMAIL`, `TAG` + + +* `INDEX` refers to the index numer shown in the displayed entry list. +* `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. +* When editing tags, the existing tags of the contact will be removed i.e. adding of tags is not cumulative. +* You can remove all the contact’s tags by typing `t/` without specifying any tags after it. -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. +Example(s): -### Locating persons by name: `find` +* `cedit 1 p/91234567 e/alexyeoh@example.com` Edits the phone number and email address of the contact corresponding to + index 1 and to `p/91234567` and `alexyeoh@example.com` respectively. +* `cedit 1 n/Bernice Yu Xiao Ling t/` Edits the name of the contact corresponding to index 1 to be `Bernice Yu Xiao Ling` + and clears all existing tags. + +Below is an illustration of entering `cedit 1 n/Bernice Yu Xiao Ling t/` into a sample Teaching Assistant: -Finds persons whose names contain any of the given keywords. +![Edit Contact Example](images/EditContactExample.png) +Scenario: +Your student had recently changed his/her phone number and you want to update his/her contact details without the +hassle of deleting his/her old contact and then subsequently adding a new updated contact. -Format: `find KEYWORD [MORE_KEYWORDS]` +### Listing contacts: `clist` -* 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` +Lists all the contacts in Teaching Assistant. -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**: `clist` -### Deleting a person : `delete` +**Breakdown**: +* Command word - `clist` -Deletes the specified person from the address book. +Scenario: You want to look at all the contacts you have saved. -Format: `delete INDEX` +### Deleting a contact: `cdelete` -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index **must be a positive integer** 1, 2, 3, …​ +Deletes an existing contact with the specified index in Teaching Assistant. -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. +**Format**: `cdelete INDEX` -### Clearing all entries : `clear` +**Breakdown**: +* Command word - `cdelete` +* Parameters - `INDEX` -Clears all entries from the address book. -Format: `clear` +* `INDEX` refers to the index number shown in the displayed contact list. +* `INDEX` must be a positive integer 1, 2, 3, ... -### Exiting the program : `exit` +Example(s): -Exits the program. +* `cdelete 1` -Format: `exit` +Scenario: Your students have long graduated and you will not be contacting them anymore. You want to remove their +contacts to make space for the contacts of your new students. -### Saving the data +--- -AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +### Adding an entry: `eadd` -### Editing the data file +Adds a new entry into Teaching Assistant. -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. +**Format**: `eadd n/NAME sd/START_DATE ed/END_DATE [t/TAG]...` -
: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. +**Breakdown**: +* Command word - `eadd` +* Prefixes - `n/`, `sd/`, `ed/`, `t/` +* Parameters - `NAME`, `START_DATE`, `END_DATE`, `TAG` + + +* `START DATE` and `END DATE` are in the format `yyyy-mm-dd HH:MM`. +* `START DATE` should be before `END DATE`. +* Entries cannot overlap. i.e. Entries with overlapping timings will not be added. +* Addition of entries which start and end in the past are allowed but the GUI will show a red entry card. + +Example(s): + +* `eadd n/meeting sd/2021-06-06 21:00 ed/2021-06-06 23:00 t/meeting t/needprep` +* `eadd n/consultation 1 sd/2021-06-07 22:00 ed/2021-06-07 23:00 t/consultation` + +Below is an illustration of entering `eadd n/consultation 1 sd/2021-06-07 22:00 ed/2021-06-07 23:00 t/consultation` +into a sample Teaching Assistant: + +![Add Entry Example](images/AddEntryExample.png) + +Scenario: You want to add entries into Teaching Assistant to keep track of your schedule. + +### Finding an entry: `efind` + +Finds all entries whose name contain all of the specified keywords and displays them as a list. + +**Format**: `efind KEYWORD [MORE_KEYWORDS]...` + +**Breakdown**: +* Command word - `efind` +* Parameters - `KEYWORD`, `MORE_KEYWORDS` + + +* Only names are searched. +* The search is case-insensitive e.g. `meeting` will match `Meeting`. +* Only full words will be matched e.g. `meeting` will not match `meetings`. +* Only entries matching all keywords will be returned (i.e. AND search). E.g. `meeting 1` will only return + `meeting 1`. +* The order of the keywords does not matter. e.g. `teaching assistant` will match `assistant teaching`. + +Example(s): + +* `efind consultation` returns `consultation 1` and `consultation 2` +* `efind consultation 2` returns only `consultation 2` + +Scenario: You remember you added a school event entry into Teaching Assistant in the past but forgot its date. +You can find an entry's details by its name without scrolling through the entire entry list. + +### Filter entry tags: `efilter` + +Filters all entries that have the tags of the specified keywords and displays them as a list. + +**Format**: `efilter KEYWORD [MORE_KEYWORDS]...` + +**Breakdown**: +* Command word - `efilter` +* Parameters - `KEYWORD`, `MORE_KEYWORDS` + + +* Only tags are searched. +* The filtering is case-insensitive e.g. `meeting` will match `Meeting`. +* Only full words will be matched e.g. `meeting` will not match `meetings`. +* If more than one keyword is provided, only entries with all the keywords provided will be displayed. + E.g. `filter meeting needprep` will only return an entry with both tags `meeting` and `needprep`. Entries with only one + of the 2 keywords will not be displayed. +* The order of the keywords does not matter. e.g. `meeting needprep` will match `needprep meeting`. + +Example(s): + +* `efilter meeting` +* `efilter meeting needprep` + +Below is an example of entering `efliter history` into a sample Teaching Assistant: + +![Filter tag example](images/FilterTagExample.png) + +Scenario: You want to filter your entries via the consultation tag you have added to your entries in the past, so that +you can plan ahead and prepare for your upcoming consultations. + +### Editing an entry: `eedit` + +Edits an existing entry with the specified index in Teaching Assistant. + +**Format**: `eedit INDEX [n/NAME] [sd/START_DATE] [ed/END_DATE] [t/TAG]...` + +**Breakdown**: +* Command word - `eedit` +* Prefixes - `n/`, `sd/`, `ed/`, `t/` +* Parameters - `INDEX`, `NAME`, `START_DATE`, `END_DATE`, `TAG` + + +* `INDEX` refers to the index numer shown in the displayed entry list. +* `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 entry will be removed i.e. adding of tags is not cumulative. +* You can remove all the entry’s tags by typing t/ without specifying any tags after it. + +Example(s): + +* `eedit 1 sd/2021-06-07 13:00 ed/2021-06-07 14:00` Edits the start and end dates of the entry corresponding to index 1 to + be `2021-06-07 13:00` and `2021-06-07 14:00` respectively. +* `eedit 1 t/` Edits the entry corresponding to index 1 by clearing all existing tags. + +Scenario: The Head of Department (HOD) just rescheduled a department meeting. You want to edit an entry's start and end +dates without first deleting it and then subsequently adding a new entry with the updated dates. + +### Listing entries: `elist` + +Lists all entries in Teaching Assistant by displaying them as a list sorted by date. Entries can also be listed by day/week. + +**Format**: `elist [FORMAT]` + +**Breakdown**: +* Command word - `elist` +* Parameters - `FORMAT` + + +* No argument: listing all entries +* `FORMAT` is only restricted to the following cases + * `day`: listing entries for today + * `week`: listing entries for today as well as the next 6 days + +Example(s): + +* `elist` +* `elist day` +* `elist week` + +Below is an illustration of entering `elist week` into a sample Teaching Assistant (The current date is 2021-04-12): + +![List entry example](images/ListEntryWeek.png) + +Scenario: You want to see what is in store for you today so that you can mentally prepare yourself for the busy day ahead. + +### Checking if time interval is free: `free` + +Indicates if an interval is free. If free, a message indicating that will be shown. If not, entries occupying that +interval will be shown in the entry list. + +**Format**: `free sd/START_DATE ed/END_DATE` + +**Breakdown**: +* Command word - `free` +* Prefixes - `sd/`, `ed/` +* Parameters - `START_DATE`, `END_DATE` + + +* `START DATE` and `END DATE` are in the format `yyyy-mm-dd HH:MM`. +* `START DATE` should be before `END DATE`. + +Example(s): + +* `free sd/2021-06-06 21:30 ed/2021-06-06 22:30` if the time interval is free, entry list will be empty and _"You're + free!"_ message is shown. If not, a message _"Sorry, you're not free. Entries occupying that time interval listed + below!"_ will be shown, accompanied by occupying entries in the entry list. + +Below is an illustration of entering `free sd/2021-06-06 21:30 ed/2021-06-06 22:30` into a sample Teaching Assistant +where there are no entries between the given timeslot: + +![Check free example](images/FreeExample.png) + + +Scenario: Your student just approached you to book a consultation and asks if you are available at a specific timing. + +### Deleting an entry: `edelete` + +Deletes an existing entry with the specified index in Teaching Assistant. + +**Format**: `edelete INDEX` + +**Breakdown**: +* Command word - `edelete` +* Parameters - `INDEX` + + +* `INDEX` refers to the index number shown in the displayed contact list. +* `INDEX` must be a positive integer 1, 2, 3, ... + +Example(s): + +* `edelete 1` + +Scenario: A school event you are involved in has been cancelled because of Covid-19 restrictions. You want to delete +this entry from your schedule. + +### Clearing overdue entries: `eclear` + +Clears all entries that are overdue. + +**Format**: `eclear` + +**Breakdown**: +* Command word - `eclear` +* An Entry is considered overdue if it has an end date and time that is before current date and time. + +Scenario: You still have a lot of entries from the past that you no longer need, and want to quickly remove those +outdated entries to not clutter the entry list. + +--- + +### Clearing all data: `clear` + +Clears all contacts and entries from Teaching Assistant. + +**Format**: `clear` + +**Breakdown**: +* Command word - `clear` + +Scenario: You opened Teaching Assistant for the first time and want to start using it. You want to clear the sample +data given before you proceed. + +### Exiting the program: `exit` + +
:bulb: **Tip:** +You can still safely exit Teaching Assistant by clicking the close button on the top right of Teaching Assistant, so no worries!
-### Archiving data files `[coming in v2.0]` +Exits the program. -_Details coming soon ..._ +**Format**: `exit` --------------------------------------------------------------------------------------------------------------------- +**Breakdown**: +* Command word - `exit` -## FAQ +Scenario: You survived yet another hectic day! Use this command to exit the app. -**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. +--- --------------------------------------------------------------------------------------------------------------------- +### Saving the data +Teaching Assistant data is saved in the hard disc automatically after any command that changes the data is executed. +There is no need to save data manually. + +### Editing the data file +Teaching Assistant data is saved as a JSON file `[JAR file location]/data/teachingassistant.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, Teaching Assistant will discard all data and start with an empty data file at the next run. +
+ +--- + +### FAQ +**Q**: How do I transfer my data to another computer? +**A**: Install Teaching Assistant in the other computer and overwrite the empty data file it creates with the file that +contains the data of your previous Teaching Assistant home folder. + +--- ## 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` +### Others + +Action | Format +------- | ------------------ +**View all commands** | `help` **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` +**Exit** | `exit` + +### Contacts + +Action | Format +-------- | ------------------ +**Add** | `cadd n/NAME p/NUMBER e/EMAIL [t/TAG]...` +**Find** | `cfind KEYWORD [MORE_KEYWORDS]...` +**Filter tags** | `cfilter KEYWORD [MORE_KEYWORDS]...` +**Edit** | `cedit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [t/TAG]...` +**List** | `clist` +**Delete** | `cdelete INDEX` + +### Entries + +Action | Format +-------- | ------------------ +**Add** | `eadd n/NAME sd/START_DATE ed/END_DATE [t/TAG]...` +**Find** | `efind KEYWORD [MORE_KEYWORDS]...` +**Filter tags** | `efilter KEYWORD [MORE_KEYWORDS]...` +**Edit** | `eedit INDEX [n/NAME] [sd/START_DATE] [ed/END_DATE] [t/TAG]...` +**List** | `elist [FORMAT]` +**Check if free** | `free sd/START_DATE ed/END_DATE` +**Delete** | `edelete INDEX` +**Clear overdue entries** | `eclear` + diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..b3db3a02e1e 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: "AB-3" +title: "Teaching Assistant" theme: minima header_pages: @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2021S2-CS2103T-W13-4/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..6a88d520cc2 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -2,24 +2,24 @@ !include style.puml Actor User as user USER_COLOR -Participant ":UI" as ui UI_COLOR +Participant ":Ui" as ui UI_COLOR Participant ":Logic" as logic LOGIC_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR -user -[USER_COLOR]> ui : "delete 1" +user -[USER_COLOR]> ui : "cdelete 1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("cdelete 1") activate logic LOGIC_COLOR -logic -[LOGIC_COLOR]> model : deletePerson(p) +logic -[LOGIC_COLOR]> model : deleteContact(c) activate model MODEL_COLOR model -[MODEL_COLOR]-> logic deactivate model -logic -[LOGIC_COLOR]> storage : saveAddressBook(addressBook) +logic -[LOGIC_COLOR]> storage : saveTeachingAssistant(teachingAssistant) activate storage STORAGE_COLOR storage -[STORAGE_COLOR]> storage : Save to file diff --git a/docs/diagrams/ClearOverdueEntryCommandActivityDiagram.puml b/docs/diagrams/ClearOverdueEntryCommandActivityDiagram.puml new file mode 100644 index 00000000000..fadb86b2c85 --- /dev/null +++ b/docs/diagrams/ClearOverdueEntryCommandActivityDiagram.puml @@ -0,0 +1,14 @@ +@startuml + +start + +repeat + :Model checks entry; + if () then ([Entry End date has passed]) + :Delete the entry; + else ([else]) + endif +repeat while () is ([else]) +->[No entries left]; +stop +@enduml diff --git a/docs/diagrams/ClearOverdueSequenceDiagram.puml b/docs/diagrams/ClearOverdueSequenceDiagram.puml new file mode 100644 index 00000000000..211e184fe3c --- /dev/null +++ b/docs/diagrams/ClearOverdueSequenceDiagram.puml @@ -0,0 +1,51 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":TeachingAssistantParser" as TeachingAssistantParser LOGIC_COLOR +participant ":ClearOverdueEntryCommand" as ClearOverdueEntryCommand 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("eclear") +activate LogicManager + +LogicManager -> TeachingAssistantParser : parseCommand("eclear") +activate TeachingAssistantParser + +create ClearOverdueEntryCommand +TeachingAssistantParser -> ClearOverdueEntryCommand +activate ClearOverdueEntryCommand + +ClearOverdueEntryCommand --> TeachingAssistantParser +deactivate ClearOverdueEntryCommand + +TeachingAssistantParser --> LogicManager +deactivate TeachingAssistantParser + +LogicManager -> ClearOverdueEntryCommand : execute() +activate ClearOverdueEntryCommand + +ClearOverdueEntryCommand -> Model : clearOverdueEntries() +activate Model + +Model --> ClearOverdueEntryCommand +deactivate Model + +create CommandResult +ClearOverdueEntryCommand -> CommandResult +activate CommandResult + +CommandResult --> ClearOverdueEntryCommand +deactivate CommandResult + +ClearOverdueEntryCommand --> LogicManager : result +deactivate ClearOverdueEntryCommand +[<-- LogicManager + +@enduml diff --git a/docs/diagrams/DeleteContactCommandActivityDiagram.puml b/docs/diagrams/DeleteContactCommandActivityDiagram.puml new file mode 100644 index 00000000000..493ad68689d --- /dev/null +++ b/docs/diagrams/DeleteContactCommandActivityDiagram.puml @@ -0,0 +1,17 @@ +@startuml +'https://plantuml.com/activity-diagram-beta + +start +:User executes "cdelete" and specifies +the id of the contact to be deleted; +if () then ([id is valid]) + : GUI shows information of + contact that was deleted + and the updated list of contacts; +else ([id is invalid]) + : GUI shows a prompt to help user + type in the right command; +endif; +stop + +@enduml diff --git a/docs/diagrams/DeleteContactCommandSequenceDiagram.puml b/docs/diagrams/DeleteContactCommandSequenceDiagram.puml new file mode 100644 index 00000000000..5af6cf783d0 --- /dev/null +++ b/docs/diagrams/DeleteContactCommandSequenceDiagram.puml @@ -0,0 +1,69 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":TeachingAssistantParser" as TeachingAssistantParser LOGIC_COLOR +participant ":DeleteContactCommandParser" as DeleteContactCommandParser LOGIC_COLOR +participant "d:DeleteContactCommand" as DeleteContactCommand 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("cdelete 1") +activate LogicManager + +LogicManager -> TeachingAssistantParser : parseCommand("cdelete 1") +activate TeachingAssistantParser + +create DeleteContactCommandParser +TeachingAssistantParser -> DeleteContactCommandParser +activate DeleteContactCommandParser + +DeleteContactCommandParser --> TeachingAssistantParser +deactivate DeleteContactCommandParser + +TeachingAssistantParser -> DeleteContactCommandParser : parse("1") +activate DeleteContactCommandParser + +create DeleteContactCommand +DeleteContactCommandParser -> DeleteContactCommand +activate DeleteContactCommand + +DeleteContactCommand --> DeleteContactCommandParser : d +deactivate DeleteContactCommand + +DeleteContactCommandParser --> TeachingAssistantParser : d +deactivate DeleteContactCommandParser +'Hidden arrow to position the destroy marker below the end of the activation bar. +DeleteContactCommandParser -[hidden]-> TeachingAssistantParser +destroy DeleteContactCommandParser + +TeachingAssistantParser --> LogicManager : d +deactivate TeachingAssistantParser + +LogicManager -> DeleteContactCommand : execute() +activate DeleteContactCommand + +DeleteContactCommand -> Model : deleteContact(1) +activate Model + +Model --> DeleteContactCommand +deactivate Model + +create CommandResult +DeleteContactCommand -> CommandResult +activate CommandResult + +CommandResult --> DeleteContactCommand +deactivate CommandResult + +DeleteContactCommand --> LogicManager : result +deactivate DeleteContactCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..5af6cf783d0 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -3,9 +3,9 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR -participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR -participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant ":TeachingAssistantParser" as TeachingAssistantParser LOGIC_COLOR +participant ":DeleteContactCommandParser" as DeleteContactCommandParser LOGIC_COLOR +participant "d:DeleteContactCommand" as DeleteContactCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR end box @@ -13,56 +13,56 @@ box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("cdelete 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") -activate AddressBookParser +LogicManager -> TeachingAssistantParser : parseCommand("cdelete 1") +activate TeachingAssistantParser -create DeleteCommandParser -AddressBookParser -> DeleteCommandParser -activate DeleteCommandParser +create DeleteContactCommandParser +TeachingAssistantParser -> DeleteContactCommandParser +activate DeleteContactCommandParser -DeleteCommandParser --> AddressBookParser -deactivate DeleteCommandParser +DeleteContactCommandParser --> TeachingAssistantParser +deactivate DeleteContactCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") -activate DeleteCommandParser +TeachingAssistantParser -> DeleteContactCommandParser : parse("1") +activate DeleteContactCommandParser -create DeleteCommand -DeleteCommandParser -> DeleteCommand -activate DeleteCommand +create DeleteContactCommand +DeleteContactCommandParser -> DeleteContactCommand +activate DeleteContactCommand -DeleteCommand --> DeleteCommandParser : d -deactivate DeleteCommand +DeleteContactCommand --> DeleteContactCommandParser : d +deactivate DeleteContactCommand -DeleteCommandParser --> AddressBookParser : d -deactivate DeleteCommandParser +DeleteContactCommandParser --> TeachingAssistantParser : d +deactivate DeleteContactCommandParser 'Hidden arrow to position the destroy marker below the end of the activation bar. -DeleteCommandParser -[hidden]-> AddressBookParser -destroy DeleteCommandParser +DeleteContactCommandParser -[hidden]-> TeachingAssistantParser +destroy DeleteContactCommandParser -AddressBookParser --> LogicManager : d -deactivate AddressBookParser +TeachingAssistantParser --> LogicManager : d +deactivate TeachingAssistantParser -LogicManager -> DeleteCommand : execute() -activate DeleteCommand +LogicManager -> DeleteContactCommand : execute() +activate DeleteContactCommand -DeleteCommand -> Model : deletePerson(1) +DeleteContactCommand -> Model : deleteContact(1) activate Model -Model --> DeleteCommand +Model --> DeleteContactCommand deactivate Model create CommandResult -DeleteCommand -> CommandResult +DeleteContactCommand -> CommandResult activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> DeleteContactCommand deactivate CommandResult -DeleteCommand --> LogicManager : result -deactivate DeleteCommand +DeleteContactCommand --> LogicManager : result +deactivate DeleteContactCommand [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/EntryActivityDiagram.puml b/docs/diagrams/EntryActivityDiagram.puml new file mode 100644 index 00000000000..fbf3178ec30 --- /dev/null +++ b/docs/diagrams/EntryActivityDiagram.puml @@ -0,0 +1,7 @@ +@startuml +start +:User executes "eadd"; +:Instantiates an Entry object; +:Saves to Storage; +stop +@enduml diff --git a/docs/diagrams/EntryClassDiagram.puml b/docs/diagrams/EntryClassDiagram.puml new file mode 100644 index 00000000000..c5b8cfeb9a1 --- /dev/null +++ b/docs/diagrams/EntryClassDiagram.puml @@ -0,0 +1,48 @@ +@startuml +skinparam arrowThickness 1.1 +skinparam classAttributeIconSize 0 + +hide circle + +class EntryName { + + {static} NAME_CONSTRAINTS : String + + {static} VALIDATION_REGEX : String + + name : String + + + {static} isValidName() : boolean + + EntryName(String) +} + +class EntryDate { + + {static} DATE_CONSTRAINTS : String + + value : LocalDateTime + + + {static} isValidDate() : boolean + + DateTime(String) +} + +class Tag { + + {static} MESSAGE_CONSTRAINTS : String + + {static} VALIDATION_REGEX : String + + tagName : string + + + {static} isValidTagName() : boolean + + Tag(String) +} + +class Entry { + - entryName: EntryName + - startDate : EntryDate + - endDate : EntryDate + - tags : HashSet + + + Entry(...) +} + +Entry -right-> "1" EntryName +Entry -down-> "2" EntryDate +EntryDate -right[hidden]-> Tag +EntryName -down[hidden]-> Tag +Entry -> "*" Tag + +@enduml diff --git a/docs/diagrams/FilterEntrySequenceDiagram.puml b/docs/diagrams/FilterEntrySequenceDiagram.puml new file mode 100644 index 00000000000..292847ade0e --- /dev/null +++ b/docs/diagrams/FilterEntrySequenceDiagram.puml @@ -0,0 +1,44 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR +participant "f:FilterEntryCommand" as FilterEntryCommand LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> LogicManager : execute("efilter tag") +activate LogicManager + +LogicManager -> AddressBookParser : parseCommand("efilter tag") +activate AddressBookParser + +create FilterEntryCommand +AddressBookParser -> FilterEntryCommand +activate FilterEntryCommand + +FilterEntryCommand -> AddressBookParser +deactivate FilterEntryCommand + +AddressBookParser --> LogicManager : f +deactivate AddressBookParser + +LogicManager -> FilterEntryCommand : execute() +activate FilterEntryCommand + +FilterEntryCommand -> Model : updateFilteredEntryList(predicate) +activate Model + +Model --> FilterEntryCommand +deactivate Model + +FilterEntryCommand --> LogicManager +deactivate FilterEntryCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/FreeIntervalActivityDiagram.puml b/docs/diagrams/FreeIntervalActivityDiagram.puml new file mode 100644 index 00000000000..e55186d3b36 --- /dev/null +++ b/docs/diagrams/FreeIntervalActivityDiagram.puml @@ -0,0 +1,13 @@ +@startuml +'https://plantuml.com/activity-diagram-beta + +start +:User executes "free" and specifies interval; +if () then ([free]) + :GUI shows free message to user; +else ([not free]) + :GUI shows entries occupying interval; +endif +stop + +@enduml diff --git a/docs/diagrams/FreeIntervalSequenceDiagram.puml b/docs/diagrams/FreeIntervalSequenceDiagram.puml new file mode 100644 index 00000000000..22d5ee5dc17 --- /dev/null +++ b/docs/diagrams/FreeIntervalSequenceDiagram.puml @@ -0,0 +1,45 @@ +@startuml +'https://plantuml.com/sequence-diagram +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":TeachingAssistantParser" as TeachingAssistantParser LOGIC_COLOR +participant ":FreeCommandParser" as FreeCommandParser LOGIC_COLOR +participant "c:FreeCommand" as FreeCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +autoactivate on +[-> LogicManager : execute("free") +LogicManager -> TeachingAssistantParser : parseCommand("free") + +create FreeCommandParser +TeachingAssistantParser -> FreeCommandParser +return + +TeachingAssistantParser -> FreeCommandParser : parse(start, end) + +create FreeCommand +FreeCommandParser -> FreeCommand +return +FreeCommandParser --> TeachingAssistantParser +TeachingAssistantParser --> LogicManager : c +destroy FreeCommandParser + +LogicManager -> FreeCommand : execute() +FreeCommand -> Model : updateFilteredEntryList(predicate) +return + +create CommandResult +FreeCommand -> CommandResult +return +FreeCommand --> LogicManager : result + +[<-- LogicManager + +@enduml diff --git a/docs/diagrams/ListEntryActivityDiagram.puml b/docs/diagrams/ListEntryActivityDiagram.puml new file mode 100644 index 00000000000..66e885ff5bf --- /dev/null +++ b/docs/diagrams/ListEntryActivityDiagram.puml @@ -0,0 +1,15 @@ +@startuml +start +:User executes "elist" and specifies arguments; + +if () then ([argument is empty]) + :GUI lists all user's entries; +else ([else]) + if () then ([argument is "day"]) + :GUI lists user's entries by day; + else ([else]) + :GUI lists user's entries by week; + endif +endif +stop +@enduml diff --git a/docs/diagrams/ListEntrySequenceDiagram.puml b/docs/diagrams/ListEntrySequenceDiagram.puml new file mode 100644 index 00000000000..4758c2a7129 --- /dev/null +++ b/docs/diagrams/ListEntrySequenceDiagram.puml @@ -0,0 +1,60 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":LogicManager" as LogicManager LOGIC_COLOR +participant ":TeachingAssistantParser" as TeachingAssistantParser LOGIC_COLOR +participant ":ListEntryFormatPredicate" as ListEntryFormatPredicate LOGIC_COLOR +participant ":ListEntryCommand" as ListEntryCommand 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("elist day") +activate LogicManager + +LogicManager -> TeachingAssistantParser : parseCommand("elist day") +activate TeachingAssistantParser + +create ListEntryFormatPredicate +TeachingAssistantParser -> ListEntryFormatPredicate +activate ListEntryFormatPredicate + +ListEntryFormatPredicate --> TeachingAssistantParser +deactivate ListEntryFormatPredicate + +create ListEntryCommand +TeachingAssistantParser -> ListEntryCommand +activate ListEntryCommand + +ListEntryCommand --> TeachingAssistantParser +deactivate ListEntryCommand + +TeachingAssistantParser --> LogicManager : command +deactivate TeachingAssistantParser + +LogicManager-> ListEntryCommand : execute() +activate ListEntryCommand + +ListEntryCommand -> Model : updateFilteredEntryList(predicate) +activate Model + +Model --> ListEntryCommand +deactivate Model + +create CommandResult +ListEntryCommand -> CommandResult +activate CommandResult + +CommandResult --> ListEntryCommand +deactivate CommandResult + +ListEntryCommand --> LogicManager : commandResult +deactivate ListEntryCommand + +[<--LogicManager +deactivate LogicManager +@enduml diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index 016ef33e2e2..618646b70c2 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -8,7 +8,7 @@ package Logic { package Parser { Interface Parser <> -Class AddressBookParser +Class TeachingAssistantParser Class XYZCommandParser Class CliSyntax Class ParserUtil @@ -35,8 +35,8 @@ Class HiddenOutside #FFFFFF HiddenOutside ..> Logic LogicManager .up.|> Logic -LogicManager -->"1" AddressBookParser -AddressBookParser .left.> XYZCommandParser: creates > +LogicManager -->"1" TeachingAssistantParser +TeachingAssistantParser .left.> XYZCommandParser: creates > XYZCommandParser ..> XYZCommand : creates > XYZCommandParser ..|> Parser @@ -53,7 +53,7 @@ LogicManager .left.> Command : executes > LogicManager --> Model Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +note right of XYZCommand: XYZCommand = AddContactCommand, \nEditEntryCommand, etc Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 3eae5326a82..cb1d520943a 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -5,52 +5,63 @@ skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR Package Model <>{ -Interface ReadOnlyAddressBook <> +Interface ReadOnlyTeachingAssistant <> Interface Model <> Interface ObservableList <> -Class AddressBook -Class ReadOnlyAddressBook +Class TeachingAssistant +Class ReadOnlyTeachingAssistant Class Model Class ModelManager Class UserPrefs Class ReadOnlyUserPrefs -Package Person { -Class Person -Class Address -Class Email -Class Name -Class Phone -Class UniquePersonList +Package Contact { +Class Contact +Class ContactEmail +Class ContactName +Class ContactPhone +Class UniqueContactList } Package Tag { Class Tag } + +Package Entry { +Class Entry +Class EntryName +Class EntryDate +Class NonOverlappingEntryList +} + } Class HiddenOutside #FFFFFF HiddenOutside ..> Model -AddressBook .up.|> ReadOnlyAddressBook +TeachingAssistant .up.|> ReadOnlyTeachingAssistant ModelManager .up.|> Model Model .right.> ObservableList -ModelManager o--> "1" AddressBook +ModelManager o--> "1" TeachingAssistant ModelManager o-left-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs -AddressBook *--> "1" UniquePersonList -UniquePersonList o--> "*" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag +TeachingAssistant *--> "1" UniqueContactList +TeachingAssistant *--> "1" NonOverlappingEntryList +UniqueContactList o--> "*" Contact +NonOverlappingEntryList o--> "*" Entry +Contact *--> ContactName +Contact *--> ContactPhone +Contact *--> ContactEmail +Contact *--> "*" Tag +Entry *--> EntryName +Entry *--> EntryDate +Entry *--> "*" Tag -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +ContactName -[hidden]right-> ContactPhone +ContactPhone -[hidden]right-> ContactEmail -ModelManager ----->" ~* filtered list" Person +ModelManager ----->" ~* filtered list" Contact +ModelManager ----->" ~* filtered list" Entry @enduml diff --git a/docs/diagrams/ScheduleAndTaskActivityDiagram.puml b/docs/diagrams/ScheduleAndTaskActivityDiagram.puml new file mode 100644 index 00000000000..3550a429722 --- /dev/null +++ b/docs/diagrams/ScheduleAndTaskActivityDiagram.puml @@ -0,0 +1,11 @@ +@startuml +start +:User executes "sadd" or "tadd"; +if () then (["sadd"]) + :Instantiates a Schedule object; +else ([else]) + :Instantiates a Task object; +endif + :Saves to Storage; +stop +@enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 6adb2e156bf..eb096e1c4bc 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -6,19 +6,21 @@ skinparam classBackgroundColor STORAGE_COLOR Interface Storage <> Interface UserPrefsStorage <> -Interface AddressBookStorage <> +Interface TeachingAssistantStorage <> Class StorageManager Class JsonUserPrefsStorage -Class JsonAddressBookStorage +Class JsonTeachingAssistantStorage StorageManager .left.|> Storage StorageManager o-right-> UserPrefsStorage -StorageManager o--> AddressBookStorage +StorageManager o--> TeachingAssistantStorage JsonUserPrefsStorage .left.|> UserPrefsStorage -JsonAddressBookStorage .left.|> AddressBookStorage -JsonAddressBookStorage .down.> JsonSerializableAddressBookStorage -JsonSerializableAddressBookStorage .right.> JsonSerializablePerson -JsonSerializablePerson .right.> JsonAdaptedTag +JsonTeachingAssistantStorage .left.|> TeachingAssistantStorage +JsonTeachingAssistantStorage .down.> JsonSerializableTeachingAssistantStorage +JsonSerializableTeachingAssistantStorage .right.> JsonAdaptedContact +JsonSerializableTeachingAssistantStorage .down.> JsonAdaptedEntry +JsonAdaptedContact .down.> JsonAdaptedTag +JsonAdaptedEntry .right.> JsonAdaptedTag @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 92746f9fcf7..21e53c6dc49 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -11,10 +11,12 @@ Class UiManager Class MainWindow Class HelpWindow Class ResultDisplay -Class PersonListPanel -Class PersonCard +Class ContactListPanel +Class ContactCard Class StatusBarFooter Class CommandBox +Class EntryCard +Class EntryListPanel } package Model <> { @@ -33,25 +35,31 @@ UiManager -down-> MainWindow MainWindow --> HelpWindow MainWindow *-down-> CommandBox MainWindow *-down-> ResultDisplay -MainWindow *-down-> PersonListPanel +MainWindow *-down-> ContactListPanel +MainWindow *-down-> EntryListPanel MainWindow *-down-> StatusBarFooter -PersonListPanel -down-> PersonCard +ContactListPanel --down-> ContactCard +EntryListPanel -down-> EntryCard MainWindow -left-|> UiPart ResultDisplay --|> UiPart CommandBox --|> UiPart -PersonListPanel --|> UiPart -PersonCard --|> UiPart +ContactListPanel --|> UiPart +ContactCard --|> UiPart +EntryListPanel --|> UiPart +EntryCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow -down-|> UiPart -PersonCard ..> Model +ContactCard ..> Model +EntryCard ..> Model UiManager -right-> Logic MainWindow -left-> Logic -PersonListPanel -[hidden]left- HelpWindow +ContactListPanel -[hidden]left- HelpWindow +EntryListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml index fad8b0adeaa..9f2efb771df 100644 --- a/docs/diagrams/style.puml +++ b/docs/diagrams/style.puml @@ -71,5 +71,4 @@ skinparam DefaultTextAlignment center skinparam packageStyle Rectangle hide footbox -hide members hide circle diff --git a/docs/images/AddEntryExample.png b/docs/images/AddEntryExample.png new file mode 100644 index 00000000000..eaa290ef7f2 Binary files /dev/null and b/docs/images/AddEntryExample.png differ diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..d2194c1c35e 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/ClearOverdueEntryCommandActivityDiagram.png b/docs/images/ClearOverdueEntryCommandActivityDiagram.png new file mode 100644 index 00000000000..1cb3fb493bc Binary files /dev/null and b/docs/images/ClearOverdueEntryCommandActivityDiagram.png differ diff --git a/docs/images/ClearOverdueSequenceDiagram.png b/docs/images/ClearOverdueSequenceDiagram.png new file mode 100644 index 00000000000..e85faa46c9e Binary files /dev/null and b/docs/images/ClearOverdueSequenceDiagram.png differ diff --git a/docs/images/DeleteContactCommandActivityDiagram.png b/docs/images/DeleteContactCommandActivityDiagram.png new file mode 100644 index 00000000000..06a4032503f Binary files /dev/null and b/docs/images/DeleteContactCommandActivityDiagram.png differ diff --git a/docs/images/DeleteContactCommandSequenceDiagram.png b/docs/images/DeleteContactCommandSequenceDiagram.png new file mode 100644 index 00000000000..d8aa49114c2 Binary files /dev/null and b/docs/images/DeleteContactCommandSequenceDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..0a1c722532f 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/EditContactExample.png b/docs/images/EditContactExample.png new file mode 100644 index 00000000000..d29cb822b1a Binary files /dev/null and b/docs/images/EditContactExample.png differ diff --git a/docs/images/EntryActivityDiagram.png b/docs/images/EntryActivityDiagram.png new file mode 100644 index 00000000000..726571ac425 Binary files /dev/null and b/docs/images/EntryActivityDiagram.png differ diff --git a/docs/images/EntryClassDiagram.png b/docs/images/EntryClassDiagram.png new file mode 100644 index 00000000000..49ba01ff85e Binary files /dev/null and b/docs/images/EntryClassDiagram.png differ diff --git a/docs/images/FilterEntrySequenceDiagram.png b/docs/images/FilterEntrySequenceDiagram.png new file mode 100644 index 00000000000..659a7dd98bb Binary files /dev/null and b/docs/images/FilterEntrySequenceDiagram.png differ diff --git a/docs/images/FilterTagExample.png b/docs/images/FilterTagExample.png new file mode 100644 index 00000000000..5d44f19bd61 Binary files /dev/null and b/docs/images/FilterTagExample.png differ diff --git a/docs/images/FindContactExample.png b/docs/images/FindContactExample.png new file mode 100644 index 00000000000..018dfedfbbc Binary files /dev/null and b/docs/images/FindContactExample.png differ diff --git a/docs/images/FreeExample.png b/docs/images/FreeExample.png new file mode 100644 index 00000000000..44efaa9062a Binary files /dev/null and b/docs/images/FreeExample.png differ diff --git a/docs/images/FreeIntervalActivityDiagram.png b/docs/images/FreeIntervalActivityDiagram.png new file mode 100644 index 00000000000..5969c8624a9 Binary files /dev/null and b/docs/images/FreeIntervalActivityDiagram.png differ diff --git a/docs/images/FreeIntervalSequenceDiagram.png b/docs/images/FreeIntervalSequenceDiagram.png new file mode 100644 index 00000000000..58581e0eb58 Binary files /dev/null and b/docs/images/FreeIntervalSequenceDiagram.png differ diff --git a/docs/images/ListEntryActivityDiagram.png b/docs/images/ListEntryActivityDiagram.png new file mode 100644 index 00000000000..82c4522672a Binary files /dev/null and b/docs/images/ListEntryActivityDiagram.png differ diff --git a/docs/images/ListEntrySequenceDiagram.png b/docs/images/ListEntrySequenceDiagram.png new file mode 100644 index 00000000000..bc4e13ced49 Binary files /dev/null and b/docs/images/ListEntrySequenceDiagram.png differ diff --git a/docs/images/ListEntryWeek.png b/docs/images/ListEntryWeek.png new file mode 100644 index 00000000000..0494893790c Binary files /dev/null and b/docs/images/ListEntryWeek.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index b9e853cef12..a345d030fda 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 ffd17662d41..009d4549998 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/RJ1.png b/docs/images/RJ1.png new file mode 100644 index 00000000000..f29faf5c3b0 Binary files /dev/null and b/docs/images/RJ1.png differ diff --git a/docs/images/ScheduleAndTaskActivityDiagram.png b/docs/images/ScheduleAndTaskActivityDiagram.png new file mode 100644 index 00000000000..b44938eedb6 Binary files /dev/null and b/docs/images/ScheduleAndTaskActivityDiagram.png differ diff --git a/docs/images/Screenshot (117).png b/docs/images/Screenshot (117).png new file mode 100644 index 00000000000..29ef702cb83 Binary files /dev/null and b/docs/images/Screenshot (117).png differ diff --git a/docs/images/Screenshot (118).png b/docs/images/Screenshot (118).png new file mode 100644 index 00000000000..759ed988330 Binary files /dev/null and b/docs/images/Screenshot (118).png differ diff --git a/docs/images/Screenshot (119).png b/docs/images/Screenshot (119).png new file mode 100644 index 00000000000..0653f5ce3cd Binary files /dev/null and b/docs/images/Screenshot (119).png differ diff --git a/docs/images/Screenshot (124).png b/docs/images/Screenshot (124).png new file mode 100644 index 00000000000..a5300c5c8b5 Binary files /dev/null and b/docs/images/Screenshot (124).png differ diff --git a/docs/images/Screenshot (125).png b/docs/images/Screenshot (125).png new file mode 100644 index 00000000000..86e99ee5ca5 Binary files /dev/null and b/docs/images/Screenshot (125).png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index d87c1216820..54164b49e82 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..39552e54a90 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 7b4b3dbea45..50acebc28fc 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/annotatedUi.png b/docs/images/annotatedUi.png new file mode 100644 index 00000000000..0aa242fc7cb Binary files /dev/null and b/docs/images/annotatedUi.png differ diff --git a/docs/images/goatygoatygoat.png b/docs/images/goatygoatygoat.png new file mode 100644 index 00000000000..5296836f8c3 Binary files /dev/null and b/docs/images/goatygoatygoat.png differ diff --git a/docs/images/kevinlohjunyong.png b/docs/images/kevinlohjunyong.png new file mode 100644 index 00000000000..099ecf20017 Binary files /dev/null and b/docs/images/kevinlohjunyong.png differ diff --git a/docs/images/lrj689.png b/docs/images/lrj689.png new file mode 100644 index 00000000000..fe7c72fec1c Binary files /dev/null and b/docs/images/lrj689.png differ diff --git a/docs/images/nicholastanvis.png b/docs/images/nicholastanvis.png new file mode 100644 index 00000000000..3b7ee70fa08 Binary files /dev/null and b/docs/images/nicholastanvis.png differ diff --git a/docs/images/tsh22.png b/docs/images/tsh22.png new file mode 100644 index 00000000000..e19ce61a374 Binary files /dev/null and b/docs/images/tsh22.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..5e6269efc56 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,19 +1,23 @@ --- layout: page -title: AddressBook Level-3 +title: Teaching Assistant --- -[![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/AY2021S2-CS2103T-W13-4/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2021S2-CS2103T-W13-4/tp/actions) +[![codecov](https://codecov.io/gh/AY2021S2-CS2103T-W13-4/tp/branch/master/graph/badge.svg)](https://codecov.io/gh/AY2021S2-CS2103T-W13-4/tp) ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). - -* 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. +Welcome to our website! _Teaching Assistant_ is a desktop application that helps **JC/Secondary school teachers** have an efficient tool to keep +track of their **contacts and other entries like tasks or schedules easily.** While it has a GUI, most of the user interactions +relies on typing commands as user inputs. +* If you are interested in using _Teaching Assistant_, head over to the [_Quick Start_ section of the **User + Guide**](UserGuide.html#quick-start). +* If you are interested about developing _Teaching Assistant_, the [**Developer Guide**](DeveloperGuide.html) is a good place to + start. **Acknowledgements** -* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson), [JUnit5](https://github.com/junit-team/junit5) +* Libraries used: [JavaFX](https://openjfx.io/), [Jackson](https://github.com/FasterXML/jackson) + , [JUnit5](https://github.com/junit-team/junit5) diff --git a/docs/team/goatygoatygoat.md b/docs/team/goatygoatygoat.md new file mode 100644 index 00000000000..dfe488260eb --- /dev/null +++ b/docs/team/goatygoatygoat.md @@ -0,0 +1,81 @@ +--- +layout: page +title: Shen Yang's Project Portfolio Page +--- + +## Project: Teaching Assistant + +Teaching Assistant is a desktop application used for managing contacts and entries for JC/Secondary school teachers. +The user interacts with it using a Command Line Interface (CLI), and it has a Graphical User Interface (GUI) created +with JavaFX. It is written in Java, and has about 10 (tbc) kLoC. + +Given below are my contributions to the project. +* **New Feature**: Added the finalised feature to allow users to list all their contacts. + * What it does: Allows users to list of all their contacts. + * Justification: This feature is essential as the find and filter features lists a subset of all contacts. In order + to revert the contact list to show all contacts, this feature is needed. + * Pull request [\#207](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/207) + +* **New Feature**: Added the finalised feature to allow users to find their contacts by name. + * What it does: Allows users to find their contacts by name. + * Justification: This feature is important as without this, users have to scroll through the whole contact list to + find the details of a contact they need. + * Highlights: The original AB3 implementation for this feature uses a OR search to search for contacts by name, + which I felt needed to be changed as it does not feel intuitive, especially in the context of searching for names. + Hence, I changed the implementation to use an AND search instead. + * Pull request [\#207](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/207) + +* **New Feature**: Added the finalised feature to allow users to filter their contacts by tags. + * What it does: Allows users to filter their contacts via tags. + * Justification: This feature is useful when users do not remember the names of their contacts, but remember a + characteristic of them that they have previously tagged to their contact. It is also useful for finding more than + one contact at once. + * Pull request [\#207](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/207) + +* **New Feature**: Added an early version of the feature which allows users to list their entries. + * What it does: Allows users to list all their entries, or list them by day or week. + * Justification: This feature is useful when users have a lot of entries and only want to look at their daily or + weekly entries to avoid confusing themselves. + +* **New Feature**: Added an early version of the feature to allow users to find their entries by name. + * What it does: Allows users to find their entries by name. + * Justification: This feature is important as without this, users have to scroll through the whole entry list to + find the details of an entry they need. + +* **New Feature**: Added an early version of the feature to allow users to filter their entries by tags. + * What it does: Allows users to filter their entries via tags. + * Justification: This feature is useful when users do not remember the names of their entries, but remember + their category that they have previously tagged to their entry. It is also useful for finding more than + one entry at once. + +* **Code contributed**: [RepoSense Link](https://nus-cs2103-ay2021s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&since=&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=goatygoatygoat&tabRepo=AY2021S2-CS2103T-W13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false) + +* **Project management**: + * Managed the code quality of the project + (Pull request [\#249](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/249)). + * Managed the assigning and tracking of project tasks. + +* **Enhancements to existing features**: + * Wrote additional tests for existing features that I had finalised + (Pull request [\#207](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/207)). + +* **Documentation**: + * User Guide: + * Added the initial introduction of the User Guide + (Pull request [\#17](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/17)). + * Fixed most documentation bugs found in the PE dry run + (Pull request [\#199](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/199)). + * Added FAQ, saving and editing data features, modified command summary, breakdown of command formats, scenarios + (Pull request [\#227](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/227)). + * Developer Guide: + * Added the initial Product Scope and Glossary of the Developer Guide + (Pull request [\#20](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/20)). + * Added the implementation of the `elist` feature + (Pull requests [\#85](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/85), [\#253](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/253)). + * Added Documentation, logging, testing, configuration, dev-ops section in the Developer Guide and updated the User Stories + (Pull request [\#253](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/253)). + +* **Community**: + * PRs reviewed (with non-trivial review comments): [\#181](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/181), + [\#114](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/114) + * Added PPP files with template for the team: [\#211](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/211) diff --git a/docs/team/kevinlohjunyong.md b/docs/team/kevinlohjunyong.md new file mode 100644 index 00000000000..009badcac65 --- /dev/null +++ b/docs/team/kevinlohjunyong.md @@ -0,0 +1,41 @@ +--- +layout: page +title: Kevin Loh Jun Yong's Project Portfolio Page +--- + +## Project: Teaching Assistant + +Teaching Assistant is a desktop application used for managing contacts and entries for JC/Secondary school teachers. +The user interacts with it using a Command Line Interface (CLI), and it has a Graphical User Interface (GUI) created +with JavaFX. It is written in Java, and has about 10 (tbc) kLoC. + +Given below are my contributions to the project. +* **New Feature**: + * I had developed the `DeleteContactCommand`, `AddContactCommand`, + `DeleteContactCommandParser` and `AddContactCommandParser`, etc. I was + also responsible for refactoring various files [\#208](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/208/files) + [\#89](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/89/files) + + * Additionally, I was in charge of schedules [\#52](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/52) + [\#23](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/23) + +* **Code contributed**: [RepoSense Link](https://nus-cs2103-ay2021s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&since=&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=KevinLohJunYong&tabRepo=AY2021S2-CS2103T-W13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false) + + +* **Testing**: + + * I did tests for `AddContactCommand`, `DeleteContactCommand` + and their corresponding parsers (i.e `AddContactCommandParser` and + `DeleteContactCommandParser`) + * I thought of how to write the code as elegantly as possible [\#208](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/208/files) + +* **Documentation**: + + * I contributed to the developer guide by in the Delete Contact Command section. + I had explained about the `DeleteContactCommand` and I + wrote plantUML code to generate the Activity Diagram and Sequence Diagram [\#245](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/245/files) + +* **Community** + + * i had attended all the team meetings and contributed to discussions. + diff --git a/docs/team/lrj689.md b/docs/team/lrj689.md new file mode 100644 index 00000000000..050685f66b2 --- /dev/null +++ b/docs/team/lrj689.md @@ -0,0 +1,51 @@ +--- +layout: page +title: Lee Rong Jieh's Project Portfolio Page +--- + +## Project: Teaching Assistant + +Teaching Assistant is a desktop application used for managing contacts and entries for JC/Secondary school teachers. +The user interacts with it using a Command Line Interface (CLI), and it has a Graphical User Interface (GUI) created +with JavaFX. It is written in Java, and has about 10 (tbc) kLoC. + +Given below are my contributions to the project. +* **Code contributed**: [RepoSense Link](https://nus-cs2103-ay2021s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&since=&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=lrj689&tabRepo=AY2021S2-CS2103T-W13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false) +* **Enhancements implemented**: + * Task class and its respective attribute classes (Which was updated to Entry class). + * Task, Schedule, Entry class storage functionality. + * Delete Task command and other dependencies(Which later became delete entry command). + * Clear overdue entries command and other dependencies. + * List Entry command by day/week/all and other dependencies. + * Test code for storage. + * Test code for other feature implementations mentioned above. + +* **Contributions to the UG**: + * Task section (Later updated to Entry section) + * Illustrations for command examples. + +* **Contributions to DG**: + * Implementation for ClearOverdueCommand + * Use Cases + +* **Contributions to team-based tasks**: + * Contributing issues in issue tracker + * Removing code not needed for final product + * Released V1.2.1 + +* **Review/mentoring contributions**: + * Reviewed most pull requests. [Pull Request page](https://github.com/AY2021S2-CS2103T-W13-4/tp/pulls) + * Provided suggestions in prs. + * [PR comment](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/183) + * [PR comment 2](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/208) + +* **Contributions to DG extracts**: + * ![Extract1](../images/RJ1.png) + * ![Extract2](../images/Screenshot%20(117).png) + * ![Extract3](../images/Screenshot%20(118).png) + * ![Extract4](../images/Screenshot%20(119).png) + +* **Contributions to UG extracts**: + * ![Extract5](../images/Screenshot%20(124).png) + * ![Extract6](../images/Screenshot%20(125).png) + diff --git a/docs/team/nicholastanvis.md b/docs/team/nicholastanvis.md new file mode 100644 index 00000000000..b9feb0e4ac6 --- /dev/null +++ b/docs/team/nicholastanvis.md @@ -0,0 +1,90 @@ +--- +layout: page +title: Nicholas Tanvis's Project Portfolio Page +--- + +# Project: Teaching Assistant + +## Overview +Teaching Assistant is a desktop application used for managing contacts and entries for JC/Secondary school teachers. +The user interacts with it using a Command Line Interface (CLI), and it has a Graphical User Interface (GUI) created +with JavaFX. It is written in Java, and has about 10 (tbc) kLoC. + +## Summary of Contributions +Given below are some of my major contributions to the project. +* **Code contributed**: [RepoSense Link](https://nus-cs2103-ay2021s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&since=&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=nicholastanvis&tabRepo=AY2021S2-CS2103T-W13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false) +* **Enhancements implemented**: + * Add Schedule class in [#45](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/45) + * Add and find schedule commands in [#54](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/54) + * Delete schedule command in [#60](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/60) + * Refactor Schedule and Task classes into a single Entry class in [#90](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/90) + * Delete entry command in [#94](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/94) and [#102](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/102) + * Edit entry command in [#112](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/112) + * Find and Filter entry command tests in [#192](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/192) and [#200](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/200) + * Refactor Person class into Contact class in [#181](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/181) + * Refactor all tests in [#243](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/243) + * Implement add and edit entry command parsers + * Fix styling + * Look after the entire test suite +* **Documentation**: + * Developer Guide + * User Stories [#22](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/22) + * One implementation [#80](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/80) + * Architecture, Getting Started in [#231](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/231) + * Table of Contents, Appendix: Manual testing [#262](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/262) + * User Guide + * Quick Start, Table in Contents in [#87](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/87) +* **Review Contributions**: + * Major refactoring problems in [#89](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/89) + * Missing PlantUML diagrams in [#245](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/245) + * Enforce UML diagram standard in [#248](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/248) + +## Contributions to the Developer Guide (Extracts) + +### Architecture Sequence Diagram +![Architecture Sequence Diagram](../images/ArchitectureSequenceDiagram.png) + +--- + +### Delete Contact Sequence Diagram +![Delete Contact Sequence Diagram](../images/DeleteSequenceDiagram.png) + +--- + +### Entry Activity Diagram +![Entry Activity Diagram](../images/EntryActivityDiagram.png) + +--- + +### Entry Class Diagram +![Entry Class Diagram](../images/EntryClassDiagram.png) + +--- + +### Filter Entry Sequence Diagram +![Filter Entry Sequence Diagram](../images/FilterEntrySequenceDiagram.png) + +--- + +### Logic Class Diagram +![Logic Class Diagram](../images/LogicClassDiagram.png) + +--- + +### Model Class Diagram +![Model Class Diagram](../images/ModelClassDiagram.png) + +--- + +### Schedule and Task Activity Diagram +![Schedule and Task Activity Diagram](../images/ScheduleAndTaskActivityDiagram.png) + +--- + +### Storage Class Diagram +![Storage Class Diagram](../images/StorageClassDiagram.png) + +--- + +### Ui Class Diagram +![Ui Class Diagram](../images/UiClassDiagram.png) diff --git a/docs/team/tsh22.md b/docs/team/tsh22.md new file mode 100644 index 00000000000..f85ebc7b917 --- /dev/null +++ b/docs/team/tsh22.md @@ -0,0 +1,76 @@ +--- +layout: page title: Toh Sihui's Project Portfolio Page +--- + +## Project: Teaching Assistant + +Teaching Assistant is a desktop application used for managing contacts and entries for JC/Secondary school teachers. The +user interacts with it using a Command Line Interface (CLI), and it has a Graphical User Interface (GUI) created with +JavaFX. It is written in Java, and has about 10 (tbc) kLoC. + +Given below are my contributions to the project. + +* **New Feature**: Added a free command. ([#98](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/98), #191) + * What it does: Allows the user to check if a certain datetime interval is free or occupied. + * Justification: This feature improves the product for our target users. Teachers can have many items on their entry + list. This feature allows them to conveniently find out if they are available for a consultation with a student, + for instance, rather than having to check each entry individually. +* **Code + contributed**: [RepoSense Link](https://nus-cs2103-ay2021s2.github.io/tp-dashboard/?search=&sort=groupTitle&sortWithin=title&since=&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=false&tabOpen=true&tabType=authorship&tabAuthor=tsh22&tabRepo=AY2021S2-CS2103T-W13-4%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code~other&authorshipIsBinaryFileTypeChecked=false) +* **Team-based tasks**: + 1. Set up branch protection rules. + 1. Necessary general code enhancements ([#237](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/237) + , [#228](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/228) + , [#240](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/240)): + 1. Refactored AddressBook instances to TeachingAssistant. + 1. Removed Person class. + 1. Update Ui.png when necessary ([#10](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/10) + , [#113](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/113)) + 1. Enable assertions in build.gradle file ([#81](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/81)) + 1. Linked team's portfolio page in AboutUs page ([#214](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/214)) + 1. Updated AboutUs page with personal photo and github + link ([#6](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/6)) + 1. Updated website contents to match project ([#13](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/13)) +* **Enhancements to existing features**: + 1. Updated the GUI ([#59](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/59) + , [#92](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/92) + , [#182](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/182) + , [#188](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/188) + , [#189](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/189) + , [#205](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/205)) + 1. Link GUI to show contacts instead of persons (#228) + 1. Added testing for ListOccupyingEntryPredicate ([#216](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/216)) + 1. Added testing for GuiSettings ([#205](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/205)) + 1. Added testing for Edit Contact Command, Parser and + ParserUtil ([#204](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/204)) + 1. Added testing for Free Command ([#191](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/191)) + 1. Added testing for EntryDate ([#191](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/191) + , [#247](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/247)) + 1. Started out the implementation of Edit Entry + Command ([#101](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/101)) + 1. [Removed implementation] Implemented Edit Contact with + name ([#105](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/105)) + 1. [Removed implementation] Added Find Task command ([#50](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/50)) +* **Documentation**: + * User Guide: + * Updated User Guide for PE Dry-Run ([#114](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/114)). + * Added "Structure of User Guide" section ([#242](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/242)) + * Updated "Command Summary" section of User Guide in previous + updates ([#15](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/15) + , [#82](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/82)) + * Developer Guide: + * Added implementation details of the `free` + feature. ([#21](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/21) + , [#84](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/84) + , [#241](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/241)) + * Added manual testing section for entry commands (#259) +* **Community**: + * PRs reviewed (not a complete list): [#257](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/257) + , [#227](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/227) + , [#249](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/249) + , [#245](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/245) + , [#215](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/215) + , [#208](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/208) + , [#199](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/199) + , [#258](https://github.com/AY2021S2-CS2103T-W13-4/tp/pull/258) + diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md index 64c9b1f91de..29496ce6b66 100644 --- a/docs/tutorials/AddRemark.md +++ b/docs/tutorials/AddRemark.md @@ -23,9 +23,9 @@ For now, let’s keep `RemarkCommand` as simple as possible and print some outpu **`RemarkCommand.java`:** ``` java -package seedu.address.logic.commands; +package seedu.ta.logic.commands; -import seedu.address.model.Model; +import seedu.ta.model.Model; /** * Changes the remark of an existing person in the address book. @@ -89,7 +89,7 @@ Let’s change `RemarkCommand` to parse input from the user. We start by modifying the constructor of `RemarkCommand` to accept an `Index` and a `String`. While we are at it, let’s change the error message to echo the values. While this is not a replacement for tests, it is an obvious way to tell if our code is functioning as intended. ``` java -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.ta.commons.util.CollectionUtil.requireAllNonNull; //... public class RemarkCommand extends Command { //... @@ -139,7 +139,7 @@ Your code should look something like [this](https://github.com/se-edu/addressboo Now let’s move on to writing a parser that will extract the index and remark from the input provided by the user. -Create a `RemarkCommandParser` class in the `seedu.address.logic.parser` package. The class must extend the `Parser` interface. +Create a `RemarkCommandParser` class in the `seedu.ta.logic.parser` package. The class must extend the `Parser` interface. ![The relationship between Parser and RemarkCommandParser](../images/add-remark/ParserInterface.png) @@ -226,7 +226,7 @@ Now that we have all the information that we need, let’s lay the groundwork fo ### Add a new `Remark` class -Create a new `Remark` in `seedu.address.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. +Create a new `Remark` in `seedu.ta.model.person`. Since a `Remark` is a field that is similar to `Address`, we can reuse a significant bit of code. A copy-paste and search-replace later, you should have something like [this](https://github.com/se-edu/addressbook-level3/commit/4516e099699baa9e2d51801bd26f016d812dedcc#diff-af2f075d24dfcd333876f0fbce321f25). Note how `Remark` has no constrains and thus does not require input validation. @@ -239,7 +239,7 @@ Let’s change `RemarkCommand` and `RemarkCommandParser` to use the new `Remark` Without getting too deep into `fxml`, let’s go on a 5 minute adventure to get some placeholder text to show up for each person. -Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-0c6b6abcfac8c205e075294f25e851fe). +Simply add the following to [`seedu.ta.ui.PersonCard`](https://github.com/se-edu/addressbook-level3/commit/850b78879582f38accb05dd20c245963c65ea599#diff-0c6b6abcfac8c205e075294f25e851fe). **`PersonCard.java`:** diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md index aa8e0baaad9..ea961c432ef 100644 --- a/docs/tutorials/RemovingFields.md +++ b/docs/tutorials/RemovingFields.md @@ -20,7 +20,7 @@ Fortunately, IntelliJ IDEA provides a robust refactoring tool that can identify ### Assisted refactoring -The `address` field in `Person` is actually an instance of the `seedu.address.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. +The `address` field in `Person` is actually an instance of the `seedu.ta.model.person.Address` class. Since removing the `Address` class will break the application, we start by identifying `Address`'s usages. This allows us to see code that depends on `Address` to function properly and edit them on a case-by-case basis. Right-click the `Address` class and select `Refactor` \> `Safe Delete` through the menu. ![Usages detected](../images/remove/UnsafeDelete.png) diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md index bd34ed498cd..6f3dde45f0e 100644 --- a/docs/tutorials/TracingCode.md +++ b/docs/tutorials/TracingCode.md @@ -32,7 +32,7 @@ Before we proceed, ensure that you have done the following: ## Setting a break point -As you know, the first step of debugging is to put in a breakpoint where you want the debugger to pause the execution. For example, if you are trying to understand how the App starts up, you would put a breakpoint in the first statement of the `main` method. In our case, we would want to begin the tracing at the very point where the App start processing user input (i.e., somewhere in the UI component), and then trace through how the execution proceeds through the UI component. However, the execution path through a GUI is often somewhat obscure due to various *event-driven mechanisms* used by GUI frameworks, which happens to be the case here too. Therefore, let us put the breakpoint where the UI transfers control to the Logic component. According to the sequence diagram, the UI component yields control to the Logic component through a method named `execute`. Searching through the code base for `execute()` yields a promising candidate in `seedu.address.ui.CommandBox.CommandExecutor`. +As you know, the first step of debugging is to put in a breakpoint where you want the debugger to pause the execution. For example, if you are trying to understand how the App starts up, you would put a breakpoint in the first statement of the `main` method. In our case, we would want to begin the tracing at the very point where the App start processing user input (i.e., somewhere in the UI component), and then trace through how the execution proceeds through the UI component. However, the execution path through a GUI is often somewhat obscure due to various *event-driven mechanisms* used by GUI frameworks, which happens to be the case here too. Therefore, let us put the breakpoint where the UI transfers control to the Logic component. According to the sequence diagram, the UI component yields control to the Logic component through a method named `execute`. Searching through the code base for `execute()` yields a promising candidate in `seedu.ta.ui.CommandBox.CommandExecutor`. ![Using the `Search for target by name` feature. `Navigate` \> `Symbol`.](../images/tracing/Execute.png) diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java deleted file mode 100644 index 1deb3a1e469..00000000000 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ /dev/null @@ -1,13 +0,0 @@ -package seedu.address.commons.core; - -/** - * Container for user visible messages. - */ -public class Messages { - - public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; - public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - -} diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java deleted file mode 100644 index 92cd8fa605a..00000000000 --- a/src/main/java/seedu/address/logic/Logic.java +++ /dev/null @@ -1,50 +0,0 @@ -package seedu.address.logic; - -import java.nio.file.Path; - -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.parser.exceptions.ParseException; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * API of the Logic component - */ -public interface Logic { - /** - * Executes the command and returns the result. - * @param commandText The command as entered by the user. - * @return the result of the command execution. - * @throws CommandException If an error occurs during command execution. - * @throws ParseException If an error occurs during parsing. - */ - CommandResult execute(String commandText) throws CommandException, ParseException; - - /** - * Returns the AddressBook. - * - * @see seedu.address.model.Model#getAddressBook() - */ - ReadOnlyAddressBook getAddressBook(); - - /** Returns an unmodifiable view of the filtered list of persons */ - ObservableList getFilteredPersonList(); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Set the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); -} diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java deleted file mode 100644 index 71656d7c5c8..00000000000 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import seedu.address.logic.commands.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 static final String COMMAND_WORD = "add"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - - private final Person toAdd; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(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 AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } -} 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/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java deleted file mode 100644 index 7e36114902f..00000000000 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ /dev/null @@ -1,226 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import 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.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Edits the details of an existing person in the address book. - */ -public class EditCommand extends Command { - - public static final String COMMAND_WORD = "edit"; - - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; - public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; - - private final Index index; - private final EditPersonDescriptor editPersonDescriptor; - - /** - * @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) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); - } - - @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()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); - } - - /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. - */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditCommand)) { - return false; - } - - // state check - EditCommand e = (EditCommand) 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/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/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java deleted file mode 100644 index 84be6ad2596..00000000000 --- a/src/main/java/seedu/address/logic/commands/ListCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import seedu.address.model.Model; - -/** - * Lists all persons in the address book to the user. - */ -public class ListCommand extends Command { - - public static final String COMMAND_WORD = "list"; - - public static final String MESSAGE_SUCCESS = "Listed all persons"; - - - @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(MESSAGE_SUCCESS); - } -} diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java deleted file mode 100644 index 3b8bfa035e8..00000000000 --- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; -import java.util.stream.Stream; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new AddCommand object - */ -public class AddCommandParser implements Parser { - - /** - * Parses the given {@code String} of arguments in the context of the AddCommand - * and returns an AddCommand object for execution. - * @throws ParseException if the user input does not conform the expected format - */ - public AddCommand parse(String args) throws ParseException { - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL) - || !argMultimap.getPreamble().isEmpty()) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } - - Name name = ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get()); - Phone phone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get()); - Email email = ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get()); - Address address = ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get()); - Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); - - Person person = new Person(name, phone, email, address, tagList); - - return new AddCommand(person); - } - - /** - * 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/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/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java deleted file mode 100644 index 522b93081cc..00000000000 --- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java +++ /dev/null @@ -1,29 +0,0 @@ -package seedu.address.logic.parser; - -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.parser.exceptions.ParseException; - -/** - * Parses input arguments and creates a new DeleteCommand object - */ -public class DeleteCommandParser 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 { - try { - Index index = ParserUtil.parseIndex(args); - return new DeleteCommand(index); - } catch (ParseException pe) { - throw new ParseException( - String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe); - } - } - -} diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java deleted file mode 100644 index 845644b7dea..00000000000 --- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java +++ /dev/null @@ -1,82 +0,0 @@ -package seedu.address.logic.parser; - -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_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Collection; -import java.util.Collections; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.EditCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.tag.Tag; - -/** - * Parses input arguments and creates a new EditCommand object - */ -public class EditCommandParser 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 { - requireNonNull(args); - ArgumentMultimap argMultimap = - ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG); - - Index index; - - try { - index = ParserUtil.parseIndex(argMultimap.getPreamble()); - } catch (ParseException pe) { - throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe); - } - - EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor(); - if (argMultimap.getValue(PREFIX_NAME).isPresent()) { - editPersonDescriptor.setName(ParserUtil.parseName(argMultimap.getValue(PREFIX_NAME).get())); - } - if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { - editPersonDescriptor.setPhone(ParserUtil.parsePhone(argMultimap.getValue(PREFIX_PHONE).get())); - } - if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { - editPersonDescriptor.setEmail(ParserUtil.parseEmail(argMultimap.getValue(PREFIX_EMAIL).get())); - } - if (argMultimap.getValue(PREFIX_ADDRESS).isPresent()) { - editPersonDescriptor.setAddress(ParserUtil.parseAddress(argMultimap.getValue(PREFIX_ADDRESS).get())); - } - parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags); - - if (!editPersonDescriptor.isAnyFieldEdited()) { - throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); - } - - return new EditCommand(index, editPersonDescriptor); - } - - /** - * Parses {@code Collection tags} into a {@code Set} if {@code tags} is non-empty. - * If {@code tags} contain only one element which is an empty string, it will be parsed into a - * {@code Set} containing zero tags. - */ - private Optional> parseTagsForEdit(Collection tags) throws ParseException { - assert tags != null; - - if (tags.isEmpty()) { - return Optional.empty(); - } - Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; - return Optional.of(ParserUtil.parseTags(tagSet)); - } - -} 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 deleted file mode 100644 index b117acb9c55..00000000000 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ /dev/null @@ -1,124 +0,0 @@ -package seedu.address.logic.parser; - -import static java.util.Objects.requireNonNull; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.parser.exceptions.ParseException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Contains utility methods used for parsing strings in the various *Parser classes. - */ -public class ParserUtil { - - public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; - - /** - * Parses {@code oneBasedIndex} into an {@code Index} 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 parseIndex(String oneBasedIndex) throws ParseException { - String trimmedIndex = oneBasedIndex.trim(); - if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { - throw new ParseException(MESSAGE_INVALID_INDEX); - } - return Index.fromOneBased(Integer.parseInt(trimmedIndex)); - } - - /** - * Parses a {@code String name} into a {@code Name}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code name} is invalid. - */ - public static Name parseName(String name) throws ParseException { - requireNonNull(name); - String trimmedName = name.trim(); - if (!Name.isValidName(trimmedName)) { - throw new ParseException(Name.MESSAGE_CONSTRAINTS); - } - return new Name(trimmedName); - } - - /** - * Parses a {@code String phone} into a {@code Phone}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code phone} is invalid. - */ - public static Phone parsePhone(String phone) throws ParseException { - requireNonNull(phone); - String trimmedPhone = phone.trim(); - if (!Phone.isValidPhone(trimmedPhone)) { - throw new ParseException(Phone.MESSAGE_CONSTRAINTS); - } - return new Phone(trimmedPhone); - } - - /** - * Parses a {@code String address} into an {@code Address}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code address} is invalid. - */ - public static Address parseAddress(String address) throws ParseException { - requireNonNull(address); - String trimmedAddress = address.trim(); - if (!Address.isValidAddress(trimmedAddress)) { - throw new ParseException(Address.MESSAGE_CONSTRAINTS); - } - return new Address(trimmedAddress); - } - - /** - * Parses a {@code String email} into an {@code Email}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code email} is invalid. - */ - public static Email parseEmail(String email) throws ParseException { - requireNonNull(email); - String trimmedEmail = email.trim(); - if (!Email.isValidEmail(trimmedEmail)) { - throw new ParseException(Email.MESSAGE_CONSTRAINTS); - } - return new Email(trimmedEmail); - } - - /** - * Parses a {@code String tag} into a {@code Tag}. - * Leading and trailing whitespaces will be trimmed. - * - * @throws ParseException if the given {@code tag} is invalid. - */ - public static Tag parseTag(String tag) throws ParseException { - requireNonNull(tag); - String trimmedTag = tag.trim(); - if (!Tag.isValidTagName(trimmedTag)) { - throw new ParseException(Tag.MESSAGE_CONSTRAINTS); - } - return new Tag(trimmedTag); - } - - /** - * Parses {@code Collection tags} into a {@code Set}. - */ - public static Set parseTags(Collection tags) throws ParseException { - requireNonNull(tags); - final Set tagSet = new HashSet<>(); - for (String tagName : tags) { - tagSet.add(parseTag(tagName)); - } - return tagSet; - } -} diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java deleted file mode 100644 index 1a943a0781a..00000000000 --- a/src/main/java/seedu/address/model/AddressBook.java +++ /dev/null @@ -1,120 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.UniquePersonList; - -/** - * Wraps all data at the address-book level - * Duplicates are not allowed (by .isSamePerson comparison) - */ -public class AddressBook implements ReadOnlyAddressBook { - - private final UniquePersonList persons; - - /* - * The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication - * between constructors. See https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html - * - * Note that non-static init blocks are not recommended to use. There are other ways to avoid duplication - * among constructors. - */ - { - persons = new UniquePersonList(); - } - - public AddressBook() {} - - /** - * Creates an AddressBook using the Persons in the {@code toBeCopied} - */ - public AddressBook(ReadOnlyAddressBook toBeCopied) { - this(); - resetData(toBeCopied); - } - - //// list overwrite operations - - /** - * Replaces the contents of the person list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - this.persons.setPersons(persons); - } - - /** - * Resets the existing data of this {@code AddressBook} with {@code newData}. - */ - public void resetData(ReadOnlyAddressBook newData) { - requireNonNull(newData); - - setPersons(newData.getPersonList()); - } - - //// person-level operations - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - public boolean hasPerson(Person person) { - requireNonNull(person); - return persons.contains(person); - } - - /** - * Adds a person to the address book. - * The person must not already exist in the address book. - */ - public void addPerson(Person p) { - persons.add(p); - } - - /** - * Replaces the given person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - public void setPerson(Person target, Person editedPerson) { - requireNonNull(editedPerson); - - persons.setPerson(target, editedPerson); - } - - /** - * Removes {@code key} from this {@code AddressBook}. - * {@code key} must exist in the address book. - */ - public void removePerson(Person key) { - persons.remove(key); - } - - //// util methods - - @Override - public String toString() { - return persons.asUnmodifiableObservableList().size() + " persons"; - // TODO: refine later - } - - @Override - public ObservableList getPersonList() { - return persons.asUnmodifiableObservableList(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddressBook // instanceof handles nulls - && persons.equals(((AddressBook) other).persons)); - } - - @Override - public int hashCode() { - return persons.hashCode(); - } -} diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java deleted file mode 100644 index d54df471c1f..00000000000 --- a/src/main/java/seedu/address/model/Model.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.model; - -import java.nio.file.Path; -import java.util.function.Predicate; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.Person; - -/** - * The API of the Model component. - */ -public interface Model { - /** {@code Predicate} that always evaluate to true */ - Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true; - - /** - * Replaces user prefs data with the data in {@code userPrefs}. - */ - void setUserPrefs(ReadOnlyUserPrefs userPrefs); - - /** - * Returns the user prefs. - */ - ReadOnlyUserPrefs getUserPrefs(); - - /** - * Returns the user prefs' GUI settings. - */ - GuiSettings getGuiSettings(); - - /** - * Sets the user prefs' GUI settings. - */ - void setGuiSettings(GuiSettings guiSettings); - - /** - * Returns the user prefs' address book file path. - */ - Path getAddressBookFilePath(); - - /** - * Sets the user prefs' address book file path. - */ - void setAddressBookFilePath(Path addressBookFilePath); - - /** - * Replaces address book data with the data in {@code addressBook}. - */ - void setAddressBook(ReadOnlyAddressBook addressBook); - - /** Returns the AddressBook */ - ReadOnlyAddressBook getAddressBook(); - - /** - * Returns true if a person with the same identity as {@code person} exists in the address book. - */ - boolean hasPerson(Person person); - - /** - * Deletes the given person. - * The person must exist in the address book. - */ - void deletePerson(Person target); - - /** - * Adds the given person. - * {@code person} must not already exist in the address book. - */ - void addPerson(Person person); - - /** - * Replaces the given person {@code target} with {@code editedPerson}. - * {@code target} must exist in the address book. - * The person identity of {@code editedPerson} must not be the same as another existing person in the address book. - */ - void setPerson(Person target, Person editedPerson); - - /** Returns an unmodifiable view of the filtered person list */ - ObservableList getFilteredPersonList(); - - /** - * Updates the filter of the filtered person list to filter by the given {@code predicate}. - * @throws NullPointerException if {@code predicate} is null. - */ - void updateFilteredPersonList(Predicate predicate); -} diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java deleted file mode 100644 index 0650c954f5c..00000000000 --- a/src/main/java/seedu/address/model/ModelManager.java +++ /dev/null @@ -1,151 +0,0 @@ -package seedu.address.model; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.nio.file.Path; -import java.util.function.Predicate; -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.collections.transformation.FilteredList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Represents the in-memory model of the address book data. - */ -public class ModelManager implements Model { - private static final Logger logger = LogsCenter.getLogger(ModelManager.class); - - private final AddressBook addressBook; - private final UserPrefs userPrefs; - private final FilteredList filteredPersons; - - /** - * Initializes a ModelManager with the given addressBook and userPrefs. - */ - public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) { - super(); - requireAllNonNull(addressBook, userPrefs); - - logger.fine("Initializing with address book: " + addressBook + " and user prefs " + userPrefs); - - this.addressBook = new AddressBook(addressBook); - this.userPrefs = new UserPrefs(userPrefs); - filteredPersons = new FilteredList<>(this.addressBook.getPersonList()); - } - - public ModelManager() { - this(new AddressBook(), new UserPrefs()); - } - - //=========== UserPrefs ================================================================================== - - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - requireNonNull(userPrefs); - this.userPrefs.resetData(userPrefs); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - return userPrefs; - } - - @Override - public GuiSettings getGuiSettings() { - return userPrefs.getGuiSettings(); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - requireNonNull(guiSettings); - userPrefs.setGuiSettings(guiSettings); - } - - @Override - public Path getAddressBookFilePath() { - return userPrefs.getAddressBookFilePath(); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - userPrefs.setAddressBookFilePath(addressBookFilePath); - } - - //=========== AddressBook ================================================================================ - - @Override - public void setAddressBook(ReadOnlyAddressBook addressBook) { - this.addressBook.resetData(addressBook); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return addressBook; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return addressBook.hasPerson(person); - } - - @Override - public void deletePerson(Person target) { - addressBook.removePerson(target); - } - - @Override - public void addPerson(Person person) { - addressBook.addPerson(person); - updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - addressBook.setPerson(target, editedPerson); - } - - //=========== Filtered Person List Accessors ============================================================= - - /** - * Returns an unmodifiable view of the list of {@code Person} backed by the internal list of - * {@code versionedAddressBook} - */ - @Override - public ObservableList getFilteredPersonList() { - return filteredPersons; - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - requireNonNull(predicate); - filteredPersons.setPredicate(predicate); - } - - @Override - public boolean equals(Object obj) { - // short circuit if same object - if (obj == this) { - return true; - } - - // instanceof handles nulls - if (!(obj instanceof ModelManager)) { - return false; - } - - // state check - ModelManager other = (ModelManager) obj; - return addressBook.equals(other.addressBook) - && userPrefs.equals(other.userPrefs) - && filteredPersons.equals(other.filteredPersons); - } - -} diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java deleted file mode 100644 index 6ddc2cd9a29..00000000000 --- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java +++ /dev/null @@ -1,17 +0,0 @@ -package seedu.address.model; - -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; - -/** - * Unmodifiable view of an address book - */ -public interface ReadOnlyAddressBook { - - /** - * Returns an unmodifiable view of the persons list. - * This list will not contain any duplicate persons. - */ - ObservableList getPersonList(); - -} diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java deleted file mode 100644 index 60472ca22a0..00000000000 --- a/src/main/java/seedu/address/model/person/Address.java +++ /dev/null @@ -1,57 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's address in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} - */ -public class Address { - - public static final String MESSAGE_CONSTRAINTS = "Addresses can take any values, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[^\\s].*"; - - public final String value; - - /** - * Constructs an {@code Address}. - * - * @param address A valid address. - */ - public Address(String address) { - requireNonNull(address); - checkArgument(isValidAddress(address), MESSAGE_CONSTRAINTS); - value = address; - } - - /** - * Returns true if a given string is a valid email. - */ - public static boolean isValidAddress(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Address // instanceof handles nulls - && value.equals(((Address) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Email.java b/src/main/java/seedu/address/model/person/Email.java deleted file mode 100644 index a5bbe0b6a5f..00000000000 --- a/src/main/java/seedu/address/model/person/Email.java +++ /dev/null @@ -1,67 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's email in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidEmail(String)} - */ -public class Email { - - private static final String SPECIAL_CHARACTERS = "!#$%&'*+/=?`{|}~^.-"; - public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " - + "and adhere to the following constraints:\n" - + "1. The local-part should only contain alphanumeric characters and these special characters, excluding " - + "the parentheses, (" + SPECIAL_CHARACTERS + ") .\n" - + "2. This is followed by a '@' and then a domain name. " - + "The domain name must:\n" - + " - be at least 2 characters long\n" - + " - start and end with alphanumeric characters\n" - + " - consist of alphanumeric characters, a period or a hyphen for the characters in between, if any."; - // alphanumeric and special characters - private static final String LOCAL_PART_REGEX = "^[\\w" + SPECIAL_CHARACTERS + "]+"; - private static final String DOMAIN_FIRST_CHARACTER_REGEX = "[^\\W_]"; // alphanumeric characters except underscore - private static final String DOMAIN_MIDDLE_REGEX = "[a-zA-Z0-9.-]*"; // alphanumeric, period and hyphen - private static final String DOMAIN_LAST_CHARACTER_REGEX = "[^\\W_]$"; - public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" - + DOMAIN_FIRST_CHARACTER_REGEX + DOMAIN_MIDDLE_REGEX + DOMAIN_LAST_CHARACTER_REGEX; - - public final String value; - - /** - * Constructs an {@code Email}. - * - * @param email A valid email address. - */ - public Email(String email) { - requireNonNull(email); - checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); - value = email; - } - - /** - * Returns if a given string is a valid email. - */ - public static boolean isValidEmail(String test) { - return test.matches(VALIDATION_REGEX); - } - - @Override - public String toString() { - return value; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Email // instanceof handles nulls - && value.equals(((Email) other).value)); // state check - } - - @Override - public int hashCode() { - return value.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/Name.java b/src/main/java/seedu/address/model/person/Name.java deleted file mode 100644 index 79244d71cf7..00000000000 --- a/src/main/java/seedu/address/model/person/Name.java +++ /dev/null @@ -1,59 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; - -/** - * Represents a Person's name in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidName(String)} - */ -public class Name { - - public static final String MESSAGE_CONSTRAINTS = - "Names should only contain alphanumeric characters and spaces, and it should not be blank"; - - /* - * The first character of the address must not be a whitespace, - * otherwise " " (a blank string) becomes a valid input. - */ - public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; - - public final String fullName; - - /** - * Constructs a {@code Name}. - * - * @param name A valid name. - */ - public Name(String name) { - requireNonNull(name); - checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); - fullName = name; - } - - /** - * Returns true if a given string is a valid name. - */ - public static boolean isValidName(String test) { - return test.matches(VALIDATION_REGEX); - } - - - @Override - public String toString() { - return fullName; - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof Name // instanceof handles nulls - && fullName.equals(((Name) other).fullName)); // state check - } - - @Override - public int hashCode() { - return fullName.hashCode(); - } - -} diff --git a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java deleted file mode 100644 index c9b5868427c..00000000000 --- a/src/main/java/seedu/address/model/person/NameContainsKeywordsPredicate.java +++ /dev/null @@ -1,31 +0,0 @@ -package seedu.address.model.person; - -import java.util.List; -import java.util.function.Predicate; - -import seedu.address.commons.util.StringUtil; - -/** - * Tests that a {@code Person}'s {@code Name} matches any of the keywords given. - */ -public class NameContainsKeywordsPredicate implements Predicate { - private final List keywords; - - public NameContainsKeywordsPredicate(List keywords) { - this.keywords = keywords; - } - - @Override - public boolean test(Person person) { - return keywords.stream() - .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getName().fullName, keyword)); - } - - @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 - } - -} diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java deleted file mode 100644 index 8ff1d83fe89..00000000000 --- a/src/main/java/seedu/address/model/person/Person.java +++ /dev/null @@ -1,123 +0,0 @@ -package seedu.address.model.person; - -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; - -import seedu.address.model.tag.Tag; - -/** - * Represents a Person in the address book. - * Guarantees: details are present and not null, field values are validated, immutable. - */ -public class Person { - - // Identity fields - private final Name name; - private final Phone phone; - private final Email email; - - // Data fields - private final Address address; - private final Set tags = new HashSet<>(); - - /** - * Every field must be present and not null. - */ - public Person(Name name, Phone phone, Email email, Address address, Set tags) { - requireAllNonNull(name, phone, email, address, tags); - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - this.tags.addAll(tags); - } - - public Name getName() { - return name; - } - - public Phone getPhone() { - return phone; - } - - public Email getEmail() { - return email; - } - - public Address getAddress() { - return address; - } - - /** - * Returns an immutable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - */ - public Set getTags() { - return Collections.unmodifiableSet(tags); - } - - /** - * Returns true if both persons have the same name. - * This defines a weaker notion of equality between two persons. - */ - public boolean isSamePerson(Person otherPerson) { - if (otherPerson == this) { - return true; - } - - return otherPerson != null - && otherPerson.getName().equals(getName()); - } - - /** - * Returns true if both persons have the same identity and data fields. - * This defines a stronger notion of equality between two persons. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof Person)) { - return false; - } - - Person otherPerson = (Person) other; - return otherPerson.getName().equals(getName()) - && otherPerson.getPhone().equals(getPhone()) - && otherPerson.getEmail().equals(getEmail()) - && otherPerson.getAddress().equals(getAddress()) - && otherPerson.getTags().equals(getTags()); - } - - @Override - public int hashCode() { - // use this method for custom fields hashing instead of implementing your own - return Objects.hash(name, phone, email, address, tags); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append(getName()) - .append("; Phone: ") - .append(getPhone()) - .append("; Email: ") - .append(getEmail()) - .append("; Address: ") - .append(getAddress()); - - Set tags = getTags(); - if (!tags.isEmpty()) { - builder.append("; Tags: "); - tags.forEach(builder::append); - } - return builder.toString(); - } - -} diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java deleted file mode 100644 index 0fee4fe57e6..00000000000 --- a/src/main/java/seedu/address/model/person/UniquePersonList.java +++ /dev/null @@ -1,137 +0,0 @@ -package seedu.address.model.person; - -import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; - -import java.util.Iterator; -import java.util.List; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; - -/** - * A list of persons that enforces uniqueness between its elements and does not allow nulls. - * A person is considered unique by comparing using {@code Person#isSamePerson(Person)}. As such, adding and updating of - * persons uses Person#isSamePerson(Person) for equality so as to ensure that the person being added or updated is - * unique in terms of identity in the UniquePersonList. However, the removal of a person uses Person#equals(Object) so - * as to ensure that the person with exactly the same fields will be removed. - * - * Supports a minimal set of list operations. - * - * @see Person#isSamePerson(Person) - */ -public class UniquePersonList implements Iterable { - - private final ObservableList internalList = FXCollections.observableArrayList(); - private final ObservableList internalUnmodifiableList = - FXCollections.unmodifiableObservableList(internalList); - - /** - * Returns true if the list contains an equivalent person as the given argument. - */ - public boolean contains(Person toCheck) { - requireNonNull(toCheck); - return internalList.stream().anyMatch(toCheck::isSamePerson); - } - - /** - * Adds a person to the list. - * The person must not already exist in the list. - */ - public void add(Person toAdd) { - requireNonNull(toAdd); - if (contains(toAdd)) { - throw new DuplicatePersonException(); - } - internalList.add(toAdd); - } - - /** - * Replaces the person {@code target} in the list with {@code editedPerson}. - * {@code target} must exist in the list. - * The person identity of {@code editedPerson} must not be the same as another existing person in the list. - */ - public void setPerson(Person target, Person editedPerson) { - requireAllNonNull(target, editedPerson); - - int index = internalList.indexOf(target); - if (index == -1) { - throw new PersonNotFoundException(); - } - - if (!target.isSamePerson(editedPerson) && contains(editedPerson)) { - throw new DuplicatePersonException(); - } - - internalList.set(index, editedPerson); - } - - /** - * Removes the equivalent person from the list. - * The person must exist in the list. - */ - public void remove(Person toRemove) { - requireNonNull(toRemove); - if (!internalList.remove(toRemove)) { - throw new PersonNotFoundException(); - } - } - - public void setPersons(UniquePersonList replacement) { - requireNonNull(replacement); - internalList.setAll(replacement.internalList); - } - - /** - * Replaces the contents of this list with {@code persons}. - * {@code persons} must not contain duplicate persons. - */ - public void setPersons(List persons) { - requireAllNonNull(persons); - if (!personsAreUnique(persons)) { - throw new DuplicatePersonException(); - } - - internalList.setAll(persons); - } - - /** - * Returns the backing list as an unmodifiable {@code ObservableList}. - */ - public ObservableList asUnmodifiableObservableList() { - return internalUnmodifiableList; - } - - @Override - public Iterator iterator() { - return internalList.iterator(); - } - - @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof UniquePersonList // instanceof handles nulls - && internalList.equals(((UniquePersonList) other).internalList)); - } - - @Override - public int hashCode() { - return internalList.hashCode(); - } - - /** - * Returns true if {@code persons} contains only unique persons. - */ - private boolean personsAreUnique(List persons) { - for (int i = 0; i < persons.size() - 1; i++) { - for (int j = i + 1; j < persons.size(); j++) { - if (persons.get(i).isSamePerson(persons.get(j))) { - return false; - } - } - } - return true; - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java b/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java deleted file mode 100644 index d7290f59442..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/DuplicatePersonException.java +++ /dev/null @@ -1,11 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation will result in duplicate Persons (Persons are considered duplicates if they have the same - * identity). - */ -public class DuplicatePersonException extends RuntimeException { - public DuplicatePersonException() { - super("Operation would result in duplicate persons"); - } -} diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java deleted file mode 100644 index fa764426ca7..00000000000 --- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java +++ /dev/null @@ -1,6 +0,0 @@ -package seedu.address.model.person.exceptions; - -/** - * Signals that the operation is unable to find the specified person. - */ -public class PersonNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java deleted file mode 100644 index 1806da4facf..00000000000 --- a/src/main/java/seedu/address/model/util/SampleDataUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.model.util; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.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; - -/** - * Contains utility methods for populating {@code AddressBook} with sample data. - */ -public class SampleDataUtil { - public static Person[] getSamplePersons() { - return new Person[] { - new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"), - new Address("Blk 30 Geylang Street 29, #06-40"), - getTagSet("friends")), - new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"), - new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"), - getTagSet("colleagues", "friends")), - new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"), - new Address("Blk 11 Ang Mo Kio Street 74, #11-04"), - getTagSet("neighbours")), - new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"), - new Address("Blk 436 Serangoon Gardens Street 26, #16-43"), - getTagSet("family")), - new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"), - new Address("Blk 47 Tampines Street 20, #17-35"), - getTagSet("classmates")), - new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"), - new Address("Blk 45 Aljunied Street 85, #11-31"), - getTagSet("colleagues")) - }; - } - - public static ReadOnlyAddressBook getSampleAddressBook() { - AddressBook sampleAb = new AddressBook(); - for (Person samplePerson : getSamplePersons()) { - sampleAb.addPerson(samplePerson); - } - return sampleAb; - } - - /** - * Returns a tag set containing the list of strings given. - */ - public static Set getTagSet(String... strings) { - return Arrays.stream(strings) - .map(Tag::new) - .collect(Collectors.toSet()); - } - -} diff --git a/src/main/java/seedu/address/storage/AddressBookStorage.java b/src/main/java/seedu/address/storage/AddressBookStorage.java deleted file mode 100644 index 4599182b3f9..00000000000 --- a/src/main/java/seedu/address/storage/AddressBookStorage.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.storage; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.Optional; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyAddressBook; - -/** - * Represents a storage for {@link seedu.address.model.AddressBook}. - */ -public interface AddressBookStorage { - - /** - * Returns the file path of the data file. - */ - Path getAddressBookFilePath(); - - /** - * Returns AddressBook data as a {@link ReadOnlyAddressBook}. - * Returns {@code Optional.empty()} if storage file is not found. - * @throws 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 readAddressBook() throws DataConversionException, IOException; - - /** - * @see #getAddressBookFilePath() - */ - Optional readAddressBook(Path filePath) throws DataConversionException, IOException; - - /** - * Saves the given {@link ReadOnlyAddressBook} to the storage. - * @param addressBook cannot be null. - * @throws IOException if there was any problem writing to the file. - */ - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - - /** - * @see #saveAddressBook(ReadOnlyAddressBook) - */ - void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java deleted file mode 100644 index a6321cec2ea..00000000000 --- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * Jackson-friendly version of {@link Person}. - */ -class JsonAdaptedPerson { - - public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!"; - - private final String name; - private final String phone; - private final String email; - private final String address; - private final List tagged = 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) { - this.name = name; - this.phone = phone; - this.email = email; - this.address = address; - if (tagged != null) { - this.tagged.addAll(tagged); - } - } - - /** - * Converts a given {@code Person} into this class for Jackson use. - */ - public JsonAdaptedPerson(Person source) { - name = source.getName().fullName; - phone = source.getPhone().value; - email = source.getEmail().value; - address = source.getAddress().value; - tagged.addAll(source.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList())); - } - - /** - * Converts this Jackson-friendly adapted person object into the model's {@code Person} object. - * - * @throws IllegalValueException if there were any data constraints violated in the adapted person. - */ - public Person toModelType() throws IllegalValueException { - final List personTags = new ArrayList<>(); - for (JsonAdaptedTag tag : tagged) { - personTags.add(tag.toModelType()); - } - - if (name == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName())); - } - if (!Name.isValidName(name)) { - throw new IllegalValueException(Name.MESSAGE_CONSTRAINTS); - } - final Name modelName = new Name(name); - - if (phone == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName())); - } - if (!Phone.isValidPhone(phone)) { - throw new IllegalValueException(Phone.MESSAGE_CONSTRAINTS); - } - final Phone modelPhone = new Phone(phone); - - if (email == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName())); - } - if (!Email.isValidEmail(email)) { - throw new IllegalValueException(Email.MESSAGE_CONSTRAINTS); - } - final Email modelEmail = new Email(email); - - if (address == null) { - throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName())); - } - if (!Address.isValidAddress(address)) { - throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS); - } - final Address modelAddress = new Address(address); - - final Set modelTags = new HashSet<>(personTags); - return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java deleted file mode 100644 index dfab9daaa0d..00000000000 --- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java +++ /dev/null @@ -1,80 +0,0 @@ -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.ReadOnlyAddressBook; - -/** - * A class to access AddressBook data stored as a json file on the hard disk. - */ -public class JsonAddressBookStorage implements AddressBookStorage { - - private static final Logger logger = LogsCenter.getLogger(JsonAddressBookStorage.class); - - private Path filePath; - - public JsonAddressBookStorage(Path filePath) { - this.filePath = filePath; - } - - public Path getAddressBookFilePath() { - return filePath; - } - - @Override - public Optional readAddressBook() throws DataConversionException { - return readAddressBook(filePath); - } - - /** - * Similar to {@link #readAddressBook()}. - * - * @param filePath location of the data. Cannot be null. - * @throws DataConversionException if the file is not in the correct format. - */ - public Optional readAddressBook(Path filePath) throws DataConversionException { - requireNonNull(filePath); - - Optional jsonAddressBook = JsonUtil.readJsonFile( - filePath, JsonSerializableAddressBook.class); - if (!jsonAddressBook.isPresent()) { - return Optional.empty(); - } - - try { - return Optional.of(jsonAddressBook.get().toModelType()); - } catch (IllegalValueException ive) { - logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); - throw new DataConversionException(ive); - } - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, filePath); - } - - /** - * Similar to {@link #saveAddressBook(ReadOnlyAddressBook)}. - * - * @param filePath location of the data. Cannot be null. - */ - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - requireNonNull(addressBook); - requireNonNull(filePath); - - FileUtil.createIfMissing(filePath); - JsonUtil.saveJsonFile(new JsonSerializableAddressBook(addressBook), filePath); - } - -} diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java deleted file mode 100644 index 5efd834091d..00000000000 --- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java +++ /dev/null @@ -1,60 +0,0 @@ -package seedu.address.storage; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonRootName; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; - -/** - * An Immutable AddressBook that is serializable to JSON format. - */ -@JsonRootName(value = "addressbook") -class JsonSerializableAddressBook { - - public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s)."; - - private final List persons = new ArrayList<>(); - - /** - * Constructs a {@code JsonSerializableAddressBook} with the given persons. - */ - @JsonCreator - public JsonSerializableAddressBook(@JsonProperty("persons") List persons) { - this.persons.addAll(persons); - } - - /** - * Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use. - * - * @param source future changes to this will not affect the created {@code JsonSerializableAddressBook}. - */ - public JsonSerializableAddressBook(ReadOnlyAddressBook source) { - persons.addAll(source.getPersonList().stream().map(JsonAdaptedPerson::new).collect(Collectors.toList())); - } - - /** - * Converts this address book into the model's {@code AddressBook} object. - * - * @throws IllegalValueException if there were any data constraints violated. - */ - public AddressBook toModelType() throws IllegalValueException { - AddressBook addressBook = new AddressBook(); - for (JsonAdaptedPerson jsonAdaptedPerson : persons) { - Person person = jsonAdaptedPerson.toModelType(); - if (addressBook.hasPerson(person)) { - throw new IllegalValueException(MESSAGE_DUPLICATE_PERSON); - } - addressBook.addPerson(person); - } - return addressBook; - } - -} diff --git a/src/main/java/seedu/address/storage/Storage.java b/src/main/java/seedu/address/storage/Storage.java deleted file mode 100644 index beda8bd9f11..00000000000 --- a/src/main/java/seedu/address/storage/Storage.java +++ /dev/null @@ -1,32 +0,0 @@ -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.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * API of the Storage component - */ -public interface Storage extends AddressBookStorage, UserPrefsStorage { - - @Override - Optional readUserPrefs() throws DataConversionException, IOException; - - @Override - void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; - - @Override - Path getAddressBookFilePath(); - - @Override - Optional readAddressBook() throws DataConversionException, IOException; - - @Override - void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException; - -} diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java deleted file mode 100644 index 79868290974..00000000000 --- a/src/main/java/seedu/address/storage/StorageManager.java +++ /dev/null @@ -1,79 +0,0 @@ -package seedu.address.storage; - -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.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; - -/** - * Manages storage of AddressBook data in local storage. - */ -public class StorageManager implements Storage { - - private static final Logger logger = LogsCenter.getLogger(StorageManager.class); - private AddressBookStorage addressBookStorage; - private UserPrefsStorage userPrefsStorage; - - /** - * Creates a {@code StorageManager} with the given {@code AddressBookStorage} and {@code UserPrefStorage}. - */ - public StorageManager(AddressBookStorage addressBookStorage, UserPrefsStorage userPrefsStorage) { - super(); - this.addressBookStorage = addressBookStorage; - this.userPrefsStorage = userPrefsStorage; - } - - // ================ UserPrefs methods ============================== - - @Override - public Path getUserPrefsFilePath() { - return userPrefsStorage.getUserPrefsFilePath(); - } - - @Override - public Optional readUserPrefs() throws DataConversionException, IOException { - return userPrefsStorage.readUserPrefs(); - } - - @Override - public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { - userPrefsStorage.saveUserPrefs(userPrefs); - } - - - // ================ AddressBook methods ============================== - - @Override - public Path getAddressBookFilePath() { - return addressBookStorage.getAddressBookFilePath(); - } - - @Override - public Optional readAddressBook() throws DataConversionException, IOException { - return readAddressBook(addressBookStorage.getAddressBookFilePath()); - } - - @Override - public Optional readAddressBook(Path filePath) throws DataConversionException, IOException { - logger.fine("Attempting to read data from file: " + filePath); - return addressBookStorage.readAddressBook(filePath); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook) throws IOException { - saveAddressBook(addressBook, addressBookStorage.getAddressBookFilePath()); - } - - @Override - public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath) throws IOException { - logger.fine("Attempting to write to data file: " + filePath); - addressBookStorage.saveAddressBook(addressBook, filePath); - } - -} diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java deleted file mode 100644 index f4c501a897b..00000000000 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -package seedu.address.ui; - -import java.util.logging.Logger; - -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.layout.Region; -import seedu.address.commons.core.LogsCenter; -import seedu.address.model.person.Person; - -/** - * Panel containing the list of persons. - */ -public class PersonListPanel extends UiPart { - private static final String FXML = "PersonListPanel.fxml"; - private final Logger logger = LogsCenter.getLogger(PersonListPanel.class); - - @FXML - private ListView personListView; - - /** - * Creates a {@code PersonListPanel} with the given {@code ObservableList}. - */ - public PersonListPanel(ObservableList personList) { - super(FXML); - personListView.setItems(personList); - personListView.setCellFactory(listView -> new PersonListViewCell()); - } - - /** - * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}. - */ - class PersonListViewCell extends ListCell { - @Override - protected void updateItem(Person person, boolean empty) { - super.updateItem(person, empty); - - if (empty || person == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(new PersonCard(person, getIndex() + 1).getRoot()); - } - } - } - -} diff --git a/src/main/java/seedu/address/AppParameters.java b/src/main/java/seedu/ta/AppParameters.java similarity index 93% rename from src/main/java/seedu/address/AppParameters.java rename to src/main/java/seedu/ta/AppParameters.java index ab552c398f3..8f1e43f63a5 100644 --- a/src/main/java/seedu/address/AppParameters.java +++ b/src/main/java/seedu/ta/AppParameters.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.ta; import java.nio.file.Path; import java.nio.file.Paths; @@ -7,8 +7,8 @@ import java.util.logging.Logger; import javafx.application.Application; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.FileUtil; +import seedu.ta.commons.core.LogsCenter; +import seedu.ta.commons.util.FileUtil; /** * Represents the parsed command-line parameters given to the application. diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/ta/Main.java similarity index 97% rename from src/main/java/seedu/address/Main.java rename to src/main/java/seedu/ta/Main.java index 052a5068631..9fcb42ae1d1 100644 --- a/src/main/java/seedu/address/Main.java +++ b/src/main/java/seedu/ta/Main.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.ta; import javafx.application.Application; diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/ta/MainApp.java similarity index 67% rename from src/main/java/seedu/address/MainApp.java rename to src/main/java/seedu/ta/MainApp.java index e5cfb161b73..7e2759d5742 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/ta/MainApp.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.ta; import java.io.IOException; import java.nio.file.Path; @@ -7,29 +7,29 @@ import javafx.application.Application; import javafx.stage.Stage; -import seedu.address.commons.core.Config; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.core.Version; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.ConfigUtil; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; -import seedu.address.logic.LogicManager; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; -import seedu.address.model.util.SampleDataUtil; -import seedu.address.storage.AddressBookStorage; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.Storage; -import seedu.address.storage.StorageManager; -import seedu.address.storage.UserPrefsStorage; -import seedu.address.ui.Ui; -import seedu.address.ui.UiManager; +import seedu.ta.commons.core.Config; +import seedu.ta.commons.core.LogsCenter; +import seedu.ta.commons.core.Version; +import seedu.ta.commons.exceptions.DataConversionException; +import seedu.ta.commons.util.ConfigUtil; +import seedu.ta.commons.util.StringUtil; +import seedu.ta.logic.Logic; +import seedu.ta.logic.LogicManager; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.ReadOnlyUserPrefs; +import seedu.ta.model.TeachingAssistant; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.util.SampleDataUtil; +import seedu.ta.storage.JsonTeachingAssistantStorage; +import seedu.ta.storage.JsonUserPrefsStorage; +import seedu.ta.storage.Storage; +import seedu.ta.storage.StorageManager; +import seedu.ta.storage.TeachingAssistantStorage; +import seedu.ta.storage.UserPrefsStorage; +import seedu.ta.ui.Ui; +import seedu.ta.ui.UiManager; /** * Runs the application. @@ -48,7 +48,7 @@ public class MainApp extends Application { @Override public void init() throws Exception { - logger.info("=============================[ Initializing AddressBook ]==========================="); + logger.info("=============================[ Initializing TeachingAssistant ]==========================="); super.init(); AppParameters appParameters = AppParameters.parse(getParameters()); @@ -56,8 +56,9 @@ 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); + TeachingAssistantStorage teachingAssistantStorage = + new JsonTeachingAssistantStorage(userPrefs.getTeachingAssistantFilePath()); + storage = new StorageManager(teachingAssistantStorage, userPrefsStorage); initLogging(config); @@ -69,25 +70,26 @@ public void init() throws Exception { } /** - * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found, - * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book. + * Returns a {@code ModelManager} with the data from {@code storage}'s teaching assistant and {@code userPrefs}. + *
The data from the sample teaching assistant will be used instead if {@code storage}'s teaching assistant + * is not found, or an empty teaching assistant will be used instead if errors occur when reading {@code storage}'s + * teaching assistant. */ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) { - Optional addressBookOptional; - ReadOnlyAddressBook initialData; + Optional teachingAssistantOptional; + ReadOnlyTeachingAssistant initialData; try { - addressBookOptional = storage.readAddressBook(); - if (!addressBookOptional.isPresent()) { - logger.info("Data file not found. Will be starting with a sample AddressBook"); + teachingAssistantOptional = storage.readTeachingAssistant(); + if (!teachingAssistantOptional.isPresent()) { + logger.info("Data file not found. Will be starting with a sample TeachingAssistant"); } - initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook); + initialData = teachingAssistantOptional.orElseGet(SampleDataUtil::getSampleTeachingAssistant); } catch (DataConversionException e) { - logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Data file not in the correct format. Will be starting with an empty TeachingAssistant"); + initialData = new TeachingAssistant(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); - initialData = new AddressBook(); + logger.warning("Problem while reading from the file. Will be starting with an empty TeachingAssistant"); + initialData = new TeachingAssistant(); } return new ModelManager(initialData, userPrefs); @@ -151,7 +153,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { + "Using default user prefs"); initializedPrefs = new UserPrefs(); } catch (IOException e) { - logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook"); + logger.warning("Problem while reading from the file. Will be starting with an empty TeachingAssistant"); initializedPrefs = new UserPrefs(); } @@ -167,13 +169,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) { @Override public void start(Stage primaryStage) { - logger.info("Starting AddressBook " + MainApp.VERSION); + logger.info("Starting TeachingAssistant " + MainApp.VERSION); ui.start(primaryStage); } @Override public void stop() { - logger.info("============================ [ Stopping Address Book ] ============================="); + logger.info("============================ [ Stopping Teaching Assistant ] ============================="); try { storage.saveUserPrefs(model.getUserPrefs()); } catch (IOException e) { diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/ta/commons/core/Config.java similarity index 97% rename from src/main/java/seedu/address/commons/core/Config.java rename to src/main/java/seedu/ta/commons/core/Config.java index 91145745521..b809e2e7282 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/ta/commons/core/Config.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.ta.commons.core; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/ta/commons/core/GuiSettings.java similarity index 93% rename from src/main/java/seedu/address/commons/core/GuiSettings.java rename to src/main/java/seedu/ta/commons/core/GuiSettings.java index ba33653be67..3b37e7eef80 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/ta/commons/core/GuiSettings.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.ta.commons.core; import java.awt.Point; import java.io.Serializable; @@ -10,8 +10,8 @@ */ public class GuiSettings implements Serializable { - private static final double DEFAULT_HEIGHT = 600; - private static final double DEFAULT_WIDTH = 740; + private static final double DEFAULT_HEIGHT = 700; + private static final double DEFAULT_WIDTH = 1000; private final double windowWidth; private final double windowHeight; diff --git a/src/main/java/seedu/address/commons/core/LogsCenter.java b/src/main/java/seedu/ta/commons/core/LogsCenter.java similarity index 97% rename from src/main/java/seedu/address/commons/core/LogsCenter.java rename to src/main/java/seedu/ta/commons/core/LogsCenter.java index 431e7185e76..353fa56fe91 100644 --- a/src/main/java/seedu/address/commons/core/LogsCenter.java +++ b/src/main/java/seedu/ta/commons/core/LogsCenter.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.ta.commons.core; import java.io.IOException; import java.util.Arrays; @@ -18,7 +18,7 @@ public class LogsCenter { private static final int MAX_FILE_COUNT = 5; private static final int MAX_FILE_SIZE_IN_BYTES = (int) (Math.pow(2, 20) * 5); // 5MB - private static final String LOG_FILE = "addressbook.log"; + private static final String LOG_FILE = "teachingassistant.log"; private static Level currentLogLevel = Level.INFO; private static final Logger logger = LogsCenter.getLogger(LogsCenter.class); private static FileHandler fileHandler; diff --git a/src/main/java/seedu/ta/commons/core/Messages.java b/src/main/java/seedu/ta/commons/core/Messages.java new file mode 100644 index 00000000000..0867d9f9377 --- /dev/null +++ b/src/main/java/seedu/ta/commons/core/Messages.java @@ -0,0 +1,21 @@ +package seedu.ta.commons.core; + +/** + * Container for user visible messages. + */ +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_ENTRY_INDEX = "The entry index provided is invalid"; + public static final String MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX = "The contact index provided is invalid"; + public static final String MESSAGE_ENTRY_START_DATE_IN_PAST = "Time travel is not allowed! Please provide a valid" + + " date time!"; + public static final String MESSAGE_INVALID_DATE_RANGE = "The start date/time must be strictly before" + + " the end date/time!"; + public static final String MESSAGE_CONTACTS_LISTED_OVERVIEW = "%1$d contacts listed!"; + public static final String MESSAGE_ENTRIES_LISTED_OVERVIEW = "%1$d entries listed!"; + public static final String MESSAGE_OVERLAPPING_ENTRY = "This entry has dates that overlap with other" + + " existing entries!"; + public static final String MESSAGE_DUPLICATE_CONTACT = "The contact provided already exists in the address book."; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; +} diff --git a/src/main/java/seedu/address/commons/core/Version.java b/src/main/java/seedu/ta/commons/core/Version.java similarity index 98% rename from src/main/java/seedu/address/commons/core/Version.java rename to src/main/java/seedu/ta/commons/core/Version.java index 12142ec1e32..f3a4ba022c4 100644 --- a/src/main/java/seedu/address/commons/core/Version.java +++ b/src/main/java/seedu/ta/commons/core/Version.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.ta.commons.core; import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/src/main/java/seedu/address/commons/core/index/Index.java b/src/main/java/seedu/ta/commons/core/index/Index.java similarity index 97% rename from src/main/java/seedu/address/commons/core/index/Index.java rename to src/main/java/seedu/ta/commons/core/index/Index.java index 19536439c09..17c90931010 100644 --- a/src/main/java/seedu/address/commons/core/index/Index.java +++ b/src/main/java/seedu/ta/commons/core/index/Index.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core.index; +package seedu.ta.commons.core.index; /** * Represents a zero-based or one-based index. diff --git a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java b/src/main/java/seedu/ta/commons/exceptions/DataConversionException.java similarity index 84% rename from src/main/java/seedu/address/commons/exceptions/DataConversionException.java rename to src/main/java/seedu/ta/commons/exceptions/DataConversionException.java index 1f689bd8e3f..026c9f943b9 100644 --- a/src/main/java/seedu/address/commons/exceptions/DataConversionException.java +++ b/src/main/java/seedu/ta/commons/exceptions/DataConversionException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.ta.commons.exceptions; /** * Represents an error during conversion of data from one format to another diff --git a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java b/src/main/java/seedu/ta/commons/exceptions/IllegalValueException.java similarity index 93% rename from src/main/java/seedu/address/commons/exceptions/IllegalValueException.java rename to src/main/java/seedu/ta/commons/exceptions/IllegalValueException.java index 19124db485c..dd4b7015359 100644 --- a/src/main/java/seedu/address/commons/exceptions/IllegalValueException.java +++ b/src/main/java/seedu/ta/commons/exceptions/IllegalValueException.java @@ -1,4 +1,4 @@ -package seedu.address.commons.exceptions; +package seedu.ta.commons.exceptions; /** * Signals that some given data does not fulfill some constraints. diff --git a/src/main/java/seedu/address/commons/util/AppUtil.java b/src/main/java/seedu/ta/commons/util/AppUtil.java similarity index 94% rename from src/main/java/seedu/address/commons/util/AppUtil.java rename to src/main/java/seedu/ta/commons/util/AppUtil.java index 87aa89c0326..4ef3ae0924d 100644 --- a/src/main/java/seedu/address/commons/util/AppUtil.java +++ b/src/main/java/seedu/ta/commons/util/AppUtil.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import static java.util.Objects.requireNonNull; import javafx.scene.image.Image; -import seedu.address.MainApp; +import seedu.ta.MainApp; /** * A container for App specific utility functions diff --git a/src/main/java/seedu/address/commons/util/CollectionUtil.java b/src/main/java/seedu/ta/commons/util/CollectionUtil.java similarity index 96% rename from src/main/java/seedu/address/commons/util/CollectionUtil.java rename to src/main/java/seedu/ta/commons/util/CollectionUtil.java index eafe4dfd681..b29ebd6cd07 100644 --- a/src/main/java/seedu/address/commons/util/CollectionUtil.java +++ b/src/main/java/seedu/ta/commons/util/CollectionUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/commons/util/ConfigUtil.java b/src/main/java/seedu/ta/commons/util/ConfigUtil.java similarity index 77% rename from src/main/java/seedu/address/commons/util/ConfigUtil.java rename to src/main/java/seedu/ta/commons/util/ConfigUtil.java index f7f8a2bd44c..0129d7db739 100644 --- a/src/main/java/seedu/address/commons/util/ConfigUtil.java +++ b/src/main/java/seedu/ta/commons/util/ConfigUtil.java @@ -1,11 +1,11 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.ta.commons.core.Config; +import seedu.ta.commons.exceptions.DataConversionException; /** * A class for accessing the Config File. diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/ta/commons/util/FileUtil.java similarity index 98% rename from src/main/java/seedu/address/commons/util/FileUtil.java rename to src/main/java/seedu/ta/commons/util/FileUtil.java index b1e2767cdd9..370c60a46c6 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/ta/commons/util/FileUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import java.io.IOException; import java.nio.file.Files; diff --git a/src/main/java/seedu/address/commons/util/JsonUtil.java b/src/main/java/seedu/ta/commons/util/JsonUtil.java similarity index 97% rename from src/main/java/seedu/address/commons/util/JsonUtil.java rename to src/main/java/seedu/ta/commons/util/JsonUtil.java index 8ef609f055d..9b8628e2521 100644 --- a/src/main/java/seedu/address/commons/util/JsonUtil.java +++ b/src/main/java/seedu/ta/commons/util/JsonUtil.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import static java.util.Objects.requireNonNull; @@ -20,8 +20,8 @@ import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.ta.commons.core.LogsCenter; +import seedu.ta.commons.exceptions.DataConversionException; /** * Converts a Java object instance to JSON and vice versa diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/ta/commons/util/StringUtil.java similarity index 95% rename from src/main/java/seedu/address/commons/util/StringUtil.java rename to src/main/java/seedu/ta/commons/util/StringUtil.java index 61cc8c9a1cb..4eb28502191 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/ta/commons/util/StringUtil.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.ta.commons.util.AppUtil.checkArgument; import java.io.PrintWriter; import java.io.StringWriter; diff --git a/src/main/java/seedu/ta/logic/Logic.java b/src/main/java/seedu/ta/logic/Logic.java new file mode 100644 index 00000000000..e394cba1350 --- /dev/null +++ b/src/main/java/seedu/ta/logic/Logic.java @@ -0,0 +1,55 @@ +package seedu.ta.logic; + +import java.nio.file.Path; + +import javafx.collections.ObservableList; +import seedu.ta.commons.core.GuiSettings; +import seedu.ta.logic.commands.CommandResult; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.entry.Entry; + +/** + * API of the Logic component. + */ +public interface Logic { + + /** + * Executes the command and returns the result. + * @param commandText The command as entered by the user. + * @return the result of the command execution. + * @throws CommandException If an error occurs during command execution. + * @throws ParseException If an error occurs during parsing. + */ + CommandResult execute(String commandText) throws CommandException, ParseException; + + /** + * Returns the TeachingAssistant. + * + * @see seedu.ta.model.Model#getTeachingAssistant() + */ + ReadOnlyTeachingAssistant getTeachingAssistant(); + + /** Returns an unmodifiable view of the filtered list of contacts. */ + ObservableList getFilteredContactList(); + + /** Returns an unmodifiable view of the filtered list of entries. */ + ObservableList getFilteredEntryList(); + + /** + * Returns the user prefs' Teaching Assistant file path. + */ + Path getTeachingAssistantFilePath(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Set the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); +} diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/ta/logic/LogicManager.java similarity index 52% rename from src/main/java/seedu/address/logic/LogicManager.java rename to src/main/java/seedu/ta/logic/LogicManager.java index 9d9c6d15bdc..bedbd16b4fd 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/ta/logic/LogicManager.java @@ -1,32 +1,35 @@ -package seedu.address.logic; +package seedu.ta.logic; import java.io.IOException; import java.nio.file.Path; import java.util.logging.Logger; import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -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.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.person.Person; -import seedu.address.storage.Storage; +import seedu.ta.commons.core.GuiSettings; +import seedu.ta.commons.core.LogsCenter; +import seedu.ta.logic.commands.Command; +import seedu.ta.logic.commands.CommandResult; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.logic.parser.TeachingAssistantParser; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.Model; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.entry.Entry; +import seedu.ta.storage.Storage; /** * The main LogicManager of the app. */ public class LogicManager implements Logic { + public static final String FILE_OPS_ERROR_MESSAGE = "Could not save data to file: "; + private final Logger logger = LogsCenter.getLogger(LogicManager.class); private final Model model; private final Storage storage; - private final AddressBookParser addressBookParser; + private final TeachingAssistantParser teachingAssistantParser; /** * Constructs a {@code LogicManager} with the given {@code Model} and {@code Storage}. @@ -34,7 +37,7 @@ public class LogicManager implements Logic { public LogicManager(Model model, Storage storage) { this.model = model; this.storage = storage; - addressBookParser = new AddressBookParser(); + teachingAssistantParser = new TeachingAssistantParser(); } @Override @@ -42,11 +45,11 @@ public CommandResult execute(String commandText) throws CommandException, ParseE logger.info("----------------[USER COMMAND][" + commandText + "]"); CommandResult commandResult; - Command command = addressBookParser.parseCommand(commandText); + Command command = teachingAssistantParser.parseCommand(commandText); commandResult = command.execute(model); try { - storage.saveAddressBook(model.getAddressBook()); + storage.saveTeachingAssistant(model.getTeachingAssistant()); } catch (IOException ioe) { throw new CommandException(FILE_OPS_ERROR_MESSAGE + ioe, ioe); } @@ -55,18 +58,23 @@ public CommandResult execute(String commandText) throws CommandException, ParseE } @Override - public ReadOnlyAddressBook getAddressBook() { - return model.getAddressBook(); + public ReadOnlyTeachingAssistant getTeachingAssistant() { + return model.getTeachingAssistant(); + } + + @Override + public ObservableList getFilteredContactList() { + return model.getFilteredContactList(); } @Override - public ObservableList getFilteredPersonList() { - return model.getFilteredPersonList(); + public ObservableList getFilteredEntryList() { + return model.getFilteredEntryList(); } @Override - public Path getAddressBookFilePath() { - return model.getAddressBookFilePath(); + public Path getTeachingAssistantFilePath() { + return model.getTeachingAssistantFilePath(); } @Override diff --git a/src/main/java/seedu/ta/logic/commands/AddContactCommand.java b/src/main/java/seedu/ta/logic/commands/AddContactCommand.java new file mode 100644 index 00000000000..3c08d164ba3 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/AddContactCommand.java @@ -0,0 +1,65 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.core.Messages.MESSAGE_DUPLICATE_CONTACT; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.contact.Contact; + +/** + * Adds a contact to Teaching Assistant. + */ +public class AddContactCommand extends Command { + + public static final String COMMAND_WORD = "cadd"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a contact to Teaching Assistant. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "Danny Tan " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "danny@email.com " + + PREFIX_TAG + "student " + + PREFIX_TAG + "english " + + PREFIX_TAG + "consultation1"; + + public static final String MESSAGE_SUCCESS = "New contact added: %1$s"; + + private final Contact toAdd; + + /** + * Creates an AddContactCommand to add the specified {@code Contact}. + */ + public AddContactCommand(Contact contact) { + requireNonNull(contact); + toAdd = contact; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasContact(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_CONTACT); + } + + model.addContact(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 AddContactCommand // instanceof handles nulls + && toAdd.equals(((AddContactCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/ta/logic/commands/AddEntryCommand.java b/src/main/java/seedu/ta/logic/commands/AddEntryCommand.java new file mode 100644 index 00000000000..a2cb4eab9d7 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/AddEntryCommand.java @@ -0,0 +1,64 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.core.Messages.MESSAGE_OVERLAPPING_ENTRY; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; + +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.entry.Entry; + +/** + * Adds an Entry to Teaching Assistant. + */ +public class AddEntryCommand extends Command { + + public static final String COMMAND_WORD = "eadd"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an entry to Teaching Assistant. " + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_START_DATE + "START DATE " + + PREFIX_END_DATE + "END DATE " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_NAME + "meeting " + + PREFIX_START_DATE + "2021-06-06 21:00 " + + PREFIX_END_DATE + "2021-06-06 23:00 " + + PREFIX_TAG + "meeting " + + PREFIX_TAG + "needprep"; + + public static final String MESSAGE_SUCCESS = "New entry added: %1$s"; + + private final Entry toAdd; + + /** + * Creates an AddEntryCommand to add the specified {@code Entry}. + */ + public AddEntryCommand(Entry entry) { + requireNonNull(entry); + toAdd = entry; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.isOverlappingEntry(toAdd)) { + throw new CommandException(MESSAGE_OVERLAPPING_ENTRY); + } + + model.addEntry(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof AddEntryCommand + && toAdd.equals(((AddEntryCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/ta/logic/commands/ClearCommand.java similarity index 51% rename from src/main/java/seedu/address/logic/commands/ClearCommand.java rename to src/main/java/seedu/ta/logic/commands/ClearCommand.java index 9c86b1fa6e4..7dc48b1815d 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/ta/logic/commands/ClearCommand.java @@ -1,23 +1,23 @@ -package seedu.address.logic.commands; +package seedu.ta.logic.commands; import static java.util.Objects.requireNonNull; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; +import seedu.ta.model.Model; +import seedu.ta.model.TeachingAssistant; /** - * Clears the address book. + * Clears the data in Teaching Assistant. */ public class ClearCommand extends Command { public static final String COMMAND_WORD = "clear"; - public static final String MESSAGE_SUCCESS = "Address book has been cleared!"; + public static final String MESSAGE_SUCCESS = "Teaching Assistant has been cleared!"; @Override public CommandResult execute(Model model) { requireNonNull(model); - model.setAddressBook(new AddressBook()); + model.setTeachingAssistant(new TeachingAssistant()); return new CommandResult(MESSAGE_SUCCESS); } } diff --git a/src/main/java/seedu/ta/logic/commands/ClearOverdueEntryCommand.java b/src/main/java/seedu/ta/logic/commands/ClearOverdueEntryCommand.java new file mode 100644 index 00000000000..0a12acfce3b --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/ClearOverdueEntryCommand.java @@ -0,0 +1,22 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.ta.model.Model; + +/** + * Clears all overdue entries in Teaching Assistant. + */ +public class ClearOverdueEntryCommand extends Command { + + public static final String COMMAND_WORD = "eclear"; + + public static final String MESSAGE_SUCCESS = "All overdue entries have been cleared!"; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.clearOverdueEntries(); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/ta/logic/commands/Command.java similarity index 77% rename from src/main/java/seedu/address/logic/commands/Command.java rename to src/main/java/seedu/ta/logic/commands/Command.java index 64f18992160..444f74256b2 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/ta/logic/commands/Command.java @@ -1,7 +1,7 @@ -package seedu.address.logic.commands; +package seedu.ta.logic.commands; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.Model; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; /** * Represents a command with hidden internal logic and the ability to be executed. @@ -12,9 +12,8 @@ public abstract class Command { * Executes the command and returns the result message. * * @param model {@code Model} which the command should operate on. - * @return feedback message of the operation result for display + * @return feedback message of the operation result for display. * @throws CommandException If an error occurs during command execution. */ public abstract CommandResult execute(Model model) throws CommandException; - } diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/ta/logic/commands/CommandResult.java similarity index 95% rename from src/main/java/seedu/address/logic/commands/CommandResult.java rename to src/main/java/seedu/ta/logic/commands/CommandResult.java index 92f900b7916..831a61a878c 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/ta/logic/commands/CommandResult.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.ta.logic.commands; import static java.util.Objects.requireNonNull; @@ -9,6 +9,7 @@ */ public class CommandResult { + /** Feedback should be shown to the user. */ private final String feedbackToUser; /** Help information should be shown to the user. */ @@ -52,7 +53,6 @@ public boolean equals(Object other) { return true; } - // instanceof handles nulls if (!(other instanceof CommandResult)) { return false; } @@ -67,5 +67,4 @@ public boolean equals(Object other) { public int hashCode() { return Objects.hash(feedbackToUser, showHelp, exit); } - } diff --git a/src/main/java/seedu/ta/logic/commands/DeleteContactCommand.java b/src/main/java/seedu/ta/logic/commands/DeleteContactCommand.java new file mode 100644 index 00000000000..02b987d1685 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/DeleteContactCommand.java @@ -0,0 +1,56 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.ta.commons.core.Messages; +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.contact.Contact; + +/** + * Deletes a contact identified using its displayed index from the contact list in Teaching Assistant. + */ +public class DeleteContactCommand extends Command { + + public static final String COMMAND_WORD = "cdelete"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the contact identified by the index number used in the displayed contact list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_CONTACT_SUCCESS = "Deleted contact: %1$s"; + + private final Index targetIndex; + + /** + * Creates a DeleteContactCommand to delete the contact at the specified {@code targetIndex}. + */ + public DeleteContactCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredContactList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX); + } + + Contact contactToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteContact(contactToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_CONTACT_SUCCESS, contactToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteContactCommand // instanceof handles nulls + && targetIndex.equals(((DeleteContactCommand) other).targetIndex)); // state check + } +} diff --git a/src/main/java/seedu/ta/logic/commands/DeleteEntryCommand.java b/src/main/java/seedu/ta/logic/commands/DeleteEntryCommand.java new file mode 100644 index 00000000000..2760e097081 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/DeleteEntryCommand.java @@ -0,0 +1,56 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import seedu.ta.commons.core.Messages; +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.entry.Entry; + +/** + * Deletes an entry identified using its displayed index from the entry list in Teaching Assistant. + */ +public class DeleteEntryCommand extends Command { + + public static final String COMMAND_WORD = "edelete"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Deletes the entry identified by the given index in the list.\n" + + "Parameters: INDEX (must be a positive integer)\n" + + "Example: " + COMMAND_WORD + " 1"; + + public static final String MESSAGE_DELETE_ENTRY_SUCCESS = "Deleted Entry: %1$s"; + + private final Index targetIndex; + + /** + * Creates a DeleteEntryCommand to delete the entry at the specified {@code targetIndex}. + */ + public DeleteEntryCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredEntryList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_ENTRY_INDEX); + } + + Entry entryToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteEntry(entryToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_ENTRY_SUCCESS, entryToDelete)); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof DeleteEntryCommand + && targetIndex.equals(((DeleteEntryCommand) other).targetIndex)); + } +} diff --git a/src/main/java/seedu/ta/logic/commands/EditContactCommand.java b/src/main/java/seedu/ta/logic/commands/EditContactCommand.java new file mode 100644 index 00000000000..9cc2a5c9269 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/EditContactCommand.java @@ -0,0 +1,211 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.core.Messages.MESSAGE_DUPLICATE_CONTACT; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.ta.model.Model.PREDICATE_SHOW_ALL_CONTACTS; + +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.commons.util.CollectionUtil; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.tag.Tag; + +/** + * Edits the details of an existing contact in Teaching Assistant. + */ +public class EditContactCommand extends Command { + + public static final String COMMAND_WORD = "cedit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the contact identified " + + "by index used in the displayed contact list. Index must be a positive integer. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_PHONE + "PHONE] " + + "[" + PREFIX_EMAIL + "EMAIL] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + + " 1 " + + PREFIX_PHONE + "91234567 " + + PREFIX_EMAIL + "alexyeoh@example.com"; + + public static final String MESSAGE_EDIT_CONTACT_SUCCESS = "Edited Contact: %1$s"; + + private final Index targetIndex; + + private final EditContactDescriptor editContactDescriptor; + + /** + * Creates an EditContactCommand to edit the contact corresponding to the specified {@code Index} + * with the details as specified by the {@code editContactDescriptor}. + */ + public EditContactCommand(Index targetIndex, EditContactDescriptor editContactDescriptor) { + requireNonNull(targetIndex); + requireNonNull(editContactDescriptor); + + this.targetIndex = targetIndex; + this.editContactDescriptor = new EditContactDescriptor(editContactDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredContactList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX); + } + + Contact contactToEdit = lastShownList.get(targetIndex.getZeroBased()); + Contact editedContact = createEditedContact(contactToEdit, editContactDescriptor); + + if (!contactToEdit.isSameContact(editedContact) && model.hasContact(editedContact)) { + throw new CommandException(MESSAGE_DUPLICATE_CONTACT); + } + + model.setContact(contactToEdit, editedContact); + model.updateFilteredContactList(PREDICATE_SHOW_ALL_CONTACTS); + return new CommandResult(String.format(MESSAGE_EDIT_CONTACT_SUCCESS, editedContact)); + } + + /** + * Creates and returns a {@code Contact} with the details of {@code contactToEdit} + * edited with {@code editContactDescriptor}. + */ + private static Contact createEditedContact(Contact contactToEdit, EditContactDescriptor editContactDescriptor) { + assert contactToEdit != null; + + ContactName updatedName = editContactDescriptor.getName().orElse(contactToEdit.getName()); + ContactPhone updatedPhone = editContactDescriptor.getPhone().orElse(contactToEdit.getPhone()); + ContactEmail updatedEmail = editContactDescriptor.getEmail().orElse(contactToEdit.getEmail()); + Set updatedTags = editContactDescriptor.getTags().orElse(contactToEdit.getTags()); + + return new Contact(updatedName, updatedPhone, updatedEmail, updatedTags); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditContactCommand)) { + return false; + } + + // state check + EditContactCommand otherEditContactCommand = (EditContactCommand) other; + return targetIndex.equals(otherEditContactCommand.targetIndex) + && editContactDescriptor.equals(otherEditContactCommand.editContactDescriptor); + } + + /** + * Stores the details needed to edit the contact. Each non-empty field value will replace the + * corresponding field value of the contact. + */ + public static class EditContactDescriptor { + private ContactName name; + private ContactPhone phone; + private ContactEmail email; + private Set tags; + + public EditContactDescriptor() {} + + /** + * Copy constructor. + * A defensive copy of {@code tags} is used internally. + */ + public EditContactDescriptor(EditContactDescriptor toCopy) { + setName(toCopy.name); + setPhone(toCopy.phone); + setEmail(toCopy.email); + setTags(toCopy.tags); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(name, phone, email, tags); + } + + public void setName(ContactName name) { + this.name = name; + } + + public Optional getName() { + return Optional.ofNullable(name); + } + + public void setPhone(ContactPhone phone) { + this.phone = phone; + } + + public Optional getPhone() { + return Optional.ofNullable(phone); + } + + public void setEmail(ContactEmail email) { + this.email = email; + } + + public Optional getEmail() { + return Optional.ofNullable(email); + } + + /** + * 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 EditContactDescriptor)) { + return false; + } + + // state check + EditContactDescriptor otherEditContactDescriptor = (EditContactDescriptor) other; + return getName().equals(otherEditContactDescriptor.getName()) + && getPhone().equals(otherEditContactDescriptor.getPhone()) + && getEmail().equals(otherEditContactDescriptor.getEmail()) + && getTags().equals(otherEditContactDescriptor.getTags()); + } + } +} diff --git a/src/main/java/seedu/ta/logic/commands/EditEntryCommand.java b/src/main/java/seedu/ta/logic/commands/EditEntryCommand.java new file mode 100644 index 00000000000..608e43ac5a9 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/EditEntryCommand.java @@ -0,0 +1,112 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_DATE_RANGE; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_ENTRY_INDEX; +import static seedu.ta.commons.core.Messages.MESSAGE_OVERLAPPING_ENTRY; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.ta.model.Model.PREDICATE_SHOW_ALL_ENTRIES; + +import java.util.List; +import java.util.Set; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.entry.TemporaryEntry; +import seedu.ta.model.tag.Tag; + +/** + * Edits the details of an existing entry in Teaching Assistant. + */ +public class EditEntryCommand extends Command { + + public static final String COMMAND_WORD = "eedit"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the entry identified " + + "by the entry index used in the displayed entry list. Index must be a positive integer." + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX " + + "[" + PREFIX_NAME + "NAME] " + + "[" + PREFIX_START_DATE + "START_DATE] " + + "[" + PREFIX_END_DATE + "END_DATE] " + + "[" + PREFIX_TAG + "TAG]...\n" + + "Example: " + COMMAND_WORD + " 1 " + + PREFIX_START_DATE + "2021-06-07 13:00 " + + PREFIX_END_DATE + "2021-06-07 14:00"; + + public static final String MESSAGE_EDIT_ENTRY_SUCCESS = "Edited entry: %1$s"; + + private final Index targetIndex; + + private final TemporaryEntry tempEntry; + + /** + * Creates an EditEntryCommand to edit the contact corresponding to the specified {@code Index} + * with the details as specified by the {@code tempEntry}. + */ + public EditEntryCommand(Index targetIndex, TemporaryEntry tempEntry) { + requireNonNull(targetIndex); + requireNonNull(tempEntry); + + this.targetIndex = targetIndex; + this.tempEntry = tempEntry; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredEntryList(); + + Integer index = targetIndex.getOneBased(); + if (index < 1 || index > lastShownList.size()) { + throw new CommandException(MESSAGE_INVALID_ENTRY_INDEX); + } + + Entry targetEntry = lastShownList.get(index - 1); + + EntryName updatedEntryName = tempEntry.getEntryName().orElse(targetEntry.getEntryName()); + EntryDate updatedEntryStartDate = tempEntry.getStartDate().orElse(targetEntry.getOriginalStartDate()); + EntryDate updatedEntryEndDate = tempEntry.getEndDate().orElse(targetEntry.getOriginalEndDate()); + Set updatedTags = tempEntry.getTags().orElse(targetEntry.getTags()); + Entry updatedEntry = new Entry(updatedEntryName, updatedEntryStartDate, updatedEntryEndDate, updatedTags); + + if (!updatedEntryStartDate.isBefore(updatedEntryEndDate)) { + throw new CommandException(MESSAGE_INVALID_DATE_RANGE); + } + + model.deleteEntry(targetEntry); + if (model.isOverlappingEntry(updatedEntry)) { + model.addEntry(targetEntry); + throw new CommandException(MESSAGE_OVERLAPPING_ENTRY); + } + + model.addEntry(updatedEntry); + model.updateFilteredEntryList(PREDICATE_SHOW_ALL_ENTRIES); + return new CommandResult(String.format(MESSAGE_EDIT_ENTRY_SUCCESS, updatedEntry)); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditEntryCommand)) { + return false; + } + + // state check + EditEntryCommand otherEditEntryCommand = (EditEntryCommand) other; + return targetIndex.equals(otherEditEntryCommand.targetIndex) + && tempEntry.equals(otherEditEntryCommand.tempEntry); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/ta/logic/commands/ExitCommand.java similarity index 84% rename from src/main/java/seedu/address/logic/commands/ExitCommand.java rename to src/main/java/seedu/ta/logic/commands/ExitCommand.java index 3dd85a8ba90..b18a44ebdef 100644 --- a/src/main/java/seedu/address/logic/commands/ExitCommand.java +++ b/src/main/java/seedu/ta/logic/commands/ExitCommand.java @@ -1,6 +1,6 @@ -package seedu.address.logic.commands; +package seedu.ta.logic.commands; -import seedu.address.model.Model; +import seedu.ta.model.Model; /** * Terminates the program. @@ -15,5 +15,4 @@ public class ExitCommand extends Command { public CommandResult execute(Model model) { return new CommandResult(MESSAGE_EXIT_ACKNOWLEDGEMENT, false, true); } - } diff --git a/src/main/java/seedu/ta/logic/commands/FilterContactCommand.java b/src/main/java/seedu/ta/logic/commands/FilterContactCommand.java new file mode 100644 index 00000000000..f847c15c645 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/FilterContactCommand.java @@ -0,0 +1,45 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.ta.commons.core.Messages; +import seedu.ta.model.Model; +import seedu.ta.model.contact.ContactTagsContainKeywordsPredicate; + +/** + * Filters and lists all contacts in Teaching Assistant whose tags contain all of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FilterContactCommand extends Command { + + public static final String COMMAND_WORD = "cfilter"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all contacts that have the tags of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " student english"; + + private final ContactTagsContainKeywordsPredicate predicate; + + /** + * Creates a FilterContactCommand to filter the relevant contacts according to the specified {@code predicate}. + */ + public FilterContactCommand(ContactTagsContainKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredContactList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_CONTACTS_LISTED_OVERVIEW, model.getFilteredContactList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FilterContactCommand // instanceof handles nulls + && predicate.equals(((FilterContactCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/ta/logic/commands/FilterEntryCommand.java b/src/main/java/seedu/ta/logic/commands/FilterEntryCommand.java new file mode 100644 index 00000000000..acdfdead19f --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/FilterEntryCommand.java @@ -0,0 +1,45 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.ta.commons.core.Messages; +import seedu.ta.model.Model; +import seedu.ta.model.entry.EntryTagsContainKeywordsPredicate; + +/** + * Filters and lists all entries in Teaching Assistant whose tags contain all of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FilterEntryCommand extends Command { + + public static final String COMMAND_WORD = "efilter"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Filters all entries that have the tags of " + + "the specified keywords (case-insensitive) and displays them as a list with index numbers.\n" + + "Parameters: KEYWORD [MORE_KEYWORDS]...\n" + + "Example: " + COMMAND_WORD + " meeting"; + + private final EntryTagsContainKeywordsPredicate predicate; + + /** + * Creates a FilterEntryCommand to filter the relevant entries according to the specified {@code predicate}. + */ + public FilterEntryCommand(EntryTagsContainKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredEntryList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_ENTRIES_LISTED_OVERVIEW, model.getFilteredEntryList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FilterEntryCommand // instanceof handles nulls + && predicate.equals(((FilterEntryCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/ta/logic/commands/FindContactCommand.java b/src/main/java/seedu/ta/logic/commands/FindContactCommand.java new file mode 100644 index 00000000000..0a7935eb604 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/FindContactCommand.java @@ -0,0 +1,45 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.ta.commons.core.Messages; +import seedu.ta.model.Model; +import seedu.ta.model.contact.ContactNameContainsKeywordsPredicate; + +/** + * Finds and lists all contacts in Teaching Assistant whose name contains all of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindContactCommand extends Command { + + public static final String COMMAND_WORD = "cfind"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all contacts 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 + " Danny"; + + private final ContactNameContainsKeywordsPredicate predicate; + + /** + * Creates a FindContactCommand to find the relevant contacts according to the specified {@code predicate}. + */ + public FindContactCommand(ContactNameContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredContactList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_CONTACTS_LISTED_OVERVIEW, model.getFilteredContactList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindContactCommand // instanceof handles nulls + && predicate.equals(((FindContactCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/ta/logic/commands/FindEntryCommand.java b/src/main/java/seedu/ta/logic/commands/FindEntryCommand.java new file mode 100644 index 00000000000..057424ebef8 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/FindEntryCommand.java @@ -0,0 +1,45 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.ta.commons.core.Messages; +import seedu.ta.model.Model; +import seedu.ta.model.entry.EntryNameContainsKeywordsPredicate; + +/** + * Finds and lists all entries in Teaching Assistant whose name contains all of the argument keywords. + * Keyword matching is case insensitive. + */ +public class FindEntryCommand extends Command { + + public static final String COMMAND_WORD = "efind"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all entries whose name 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 + " consultation"; + + private final EntryNameContainsKeywordsPredicate predicate; + + /** + * Creates a FindEntryCommand to find the relevant entries according to the specified {@code predicate}. + */ + public FindEntryCommand(EntryNameContainsKeywordsPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredEntryList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_ENTRIES_LISTED_OVERVIEW, model.getFilteredEntryList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FindEntryCommand // instanceof handles nulls + && predicate.equals(((FindEntryCommand) other).predicate)); // state check + } +} diff --git a/src/main/java/seedu/ta/logic/commands/FreeCommand.java b/src/main/java/seedu/ta/logic/commands/FreeCommand.java new file mode 100644 index 00000000000..6da43e3b940 --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/FreeCommand.java @@ -0,0 +1,59 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_START_DATE; + +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.entry.ListOccupyingEntryPredicate; + +/** + * Checks all entries in Teaching Assistant to indicates if time interval provided as argument is free. + */ +public class FreeCommand extends Command { + + public static final String COMMAND_WORD = "free"; + + public static final String MESSAGE_FREE = "You're free!"; + + public static final String MESSAGE_NOT_FREE = "Sorry, you're not free. Schedules occupying that time interval " + + "listed below!"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Indicates if an interval is free. " + + "If free, 'free' will be shown. If not, tasks occupying that interval will be shown in " + + "the entries listed below.\n" + + "Parameters: " + + PREFIX_START_DATE + "START_DATE " + + PREFIX_END_DATE + "END_DATE\n" + + "Example: " + COMMAND_WORD + " " + + PREFIX_START_DATE + " 2021-06-06 21:30 " + + PREFIX_END_DATE + " 2021-06-06 22:30 "; + + private final ListOccupyingEntryPredicate predicate; + + /** + * Creates a FreeCommand to search for the relevant entries according to the specified {@code predicate}. + */ + public FreeCommand(ListOccupyingEntryPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + model.updateFilteredEntryList(predicate); + if (model.getFilteredEntryList().size() == 0) { + return new CommandResult(MESSAGE_FREE); + } else { + return new CommandResult(MESSAGE_NOT_FREE); + } + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof FreeCommand // instanceof handles nulls + && predicate.equals(((FreeCommand) other).predicate)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/HelpCommand.java b/src/main/java/seedu/ta/logic/commands/HelpCommand.java similarity index 72% rename from src/main/java/seedu/address/logic/commands/HelpCommand.java rename to src/main/java/seedu/ta/logic/commands/HelpCommand.java index bf824f91bd0..febfcddd01c 100644 --- a/src/main/java/seedu/address/logic/commands/HelpCommand.java +++ b/src/main/java/seedu/ta/logic/commands/HelpCommand.java @@ -1,9 +1,9 @@ -package seedu.address.logic.commands; +package seedu.ta.logic.commands; -import seedu.address.model.Model; +import seedu.ta.model.Model; /** - * Format full help instructions for every command for display. + * Displays a message containing a link to the User Guide of Teaching Assistant where all command formats are stated. */ public class HelpCommand extends Command { diff --git a/src/main/java/seedu/ta/logic/commands/ListContactCommand.java b/src/main/java/seedu/ta/logic/commands/ListContactCommand.java new file mode 100644 index 00000000000..ae53d939aba --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/ListContactCommand.java @@ -0,0 +1,23 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.model.Model.PREDICATE_SHOW_ALL_CONTACTS; + +import seedu.ta.model.Model; + +/** + * Lists all contacts in Teaching Assistant to the user. + */ +public class ListContactCommand extends Command { + + public static final String COMMAND_WORD = "clist"; + + public static final String MESSAGE_SUCCESS = "Listed all contacts"; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredContactList(PREDICATE_SHOW_ALL_CONTACTS); + return new CommandResult(MESSAGE_SUCCESS); + } +} diff --git a/src/main/java/seedu/ta/logic/commands/ListEntryCommand.java b/src/main/java/seedu/ta/logic/commands/ListEntryCommand.java new file mode 100644 index 00000000000..ac5153fb1ea --- /dev/null +++ b/src/main/java/seedu/ta/logic/commands/ListEntryCommand.java @@ -0,0 +1,45 @@ +package seedu.ta.logic.commands; + +import static java.util.Objects.requireNonNull; + +import seedu.ta.commons.core.Messages; +import seedu.ta.model.Model; +import seedu.ta.model.entry.ListEntryFormatPredicate; + +/** + * Lists all entries in Teaching Assistant or lists entries based on day or week. + */ +public class ListEntryCommand extends Command { + + public static final String COMMAND_WORD = "elist"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all entries " + + "by displaying them as a list sorted by date. Entries can also be listed by day/week.\n" + + "Optional parameters: [FORMAT] \n" + + "The format is only restricted to two cases: day/week." + + "Example: " + COMMAND_WORD + " week"; + + final ListEntryFormatPredicate predicate; + + /** + * Creates a ListEntryCommand in the relevant format according to the specified {@code predicate}. + */ + public ListEntryCommand(ListEntryFormatPredicate predicate) { + this.predicate = predicate; + } + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.updateFilteredEntryList(predicate); + return new CommandResult( + String.format(Messages.MESSAGE_ENTRIES_LISTED_OVERVIEW, model.getFilteredEntryList().size())); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof ListEntryCommand + && predicate.equals(((ListEntryCommand) other).predicate)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java b/src/main/java/seedu/ta/logic/commands/exceptions/CommandException.java similarity index 83% rename from src/main/java/seedu/address/logic/commands/exceptions/CommandException.java rename to src/main/java/seedu/ta/logic/commands/exceptions/CommandException.java index a16bd14f2cd..c033701bbaf 100644 --- a/src/main/java/seedu/address/logic/commands/exceptions/CommandException.java +++ b/src/main/java/seedu/ta/logic/commands/exceptions/CommandException.java @@ -1,4 +1,6 @@ -package seedu.address.logic.commands.exceptions; +package seedu.ta.logic.commands.exceptions; + +import seedu.ta.logic.commands.Command; /** * Represents an error which occurs during execution of a {@link Command}. diff --git a/src/main/java/seedu/ta/logic/parser/AddContactCommandParser.java b/src/main/java/seedu/ta/logic/parser/AddContactCommandParser.java new file mode 100644 index 00000000000..b8935c57aec --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/AddContactCommandParser.java @@ -0,0 +1,56 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.ta.logic.commands.AddContactCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddContactCommand object. + */ +public class AddContactCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the AddContactCommand + * and returns an AddContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public AddContactCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_TAG); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddContactCommand.MESSAGE_USAGE)); + } + + ContactName name = ParserUtil.parseContactName(argMultimap.getValue(PREFIX_NAME).get()); + ContactPhone phone = ParserUtil.parseContactPhone(argMultimap.getValue(PREFIX_PHONE).get()); + ContactEmail email = ParserUtil.parseContactEmail(argMultimap.getValue(PREFIX_EMAIL).get()); + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + + Contact contact = new Contact(name, phone, email, tagList); + return new AddContactCommand(contact); + } + + /** + * 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/ta/logic/parser/AddEntryCommandParser.java b/src/main/java/seedu/ta/logic/parser/AddEntryCommandParser.java new file mode 100644 index 00000000000..44f6888e6ed --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/AddEntryCommandParser.java @@ -0,0 +1,59 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_DATE_RANGE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; +import java.util.stream.Stream; + +import seedu.ta.logic.commands.AddEntryCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.tag.Tag; + +/** + * Parses input arguments and creates a new AddEntryCommand Object. + */ +public class AddEntryCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of AddEntryCommand + * and returns an AddEntryCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public AddEntryCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_START_DATE, PREFIX_END_DATE, PREFIX_TAG); + + if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_START_DATE, PREFIX_END_DATE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddEntryCommand.MESSAGE_USAGE)); + } + + EntryName entryName = ParserUtil.parseEntryName(argMultimap.getValue(PREFIX_NAME).get()); + EntryDate startDate = ParserUtil.parseEntryDate(argMultimap.getValue(PREFIX_START_DATE).get()); + EntryDate endDate = ParserUtil.parseEntryDate(argMultimap.getValue(PREFIX_END_DATE).get()); + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG)); + + if (!startDate.isBefore(endDate)) { + throw new ParseException(MESSAGE_INVALID_DATE_RANGE); + } + + Entry entry = new Entry(entryName, startDate, endDate, tagList); + return new AddEntryCommand(entry); + } + + /** + * Returns true if none of the prefixes contains empty {@code Optional} values in the given + * {@code ArgumentMultimap}. + */ + private 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/ArgumentMultimap.java b/src/main/java/seedu/ta/logic/parser/ArgumentMultimap.java similarity index 90% rename from src/main/java/seedu/address/logic/parser/ArgumentMultimap.java rename to src/main/java/seedu/ta/logic/parser/ArgumentMultimap.java index 954c8e18f8e..19a649e9467 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentMultimap.java +++ b/src/main/java/seedu/ta/logic/parser/ArgumentMultimap.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.ta.logic.parser; import java.util.ArrayList; import java.util.HashMap; @@ -15,15 +15,14 @@ */ public class ArgumentMultimap { - /** Prefixes mapped to their respective arguments**/ + /** Prefixes mapped to their respective arguments. */ private final Map> argMultimap = new HashMap<>(); /** * Associates the specified argument value with {@code prefix} key in this map. * If the map previously contained a mapping for the key, the new value is appended to the list of existing values. - * - * @param prefix Prefix key with which the specified argument value is to be associated - * @param argValue Argument value to be associated with the specified prefix key + * @param prefix Prefix key with which the specified argument value is to be associated. + * @param argValue Argument value to be associated with the specified prefix key. */ public void put(Prefix prefix, String argValue) { List argValues = getAllValues(prefix); diff --git a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java b/src/main/java/seedu/ta/logic/parser/ArgumentTokenizer.java similarity index 89% rename from src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java rename to src/main/java/seedu/ta/logic/parser/ArgumentTokenizer.java index 5c9aebfa488..d4adbbfe96a 100644 --- a/src/main/java/seedu/address/logic/parser/ArgumentTokenizer.java +++ b/src/main/java/seedu/ta/logic/parser/ArgumentTokenizer.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.ta.logic.parser; import java.util.ArrayList; import java.util.Arrays; @@ -18,10 +18,9 @@ public class ArgumentTokenizer { /** * Tokenizes an arguments string and returns an {@code ArgumentMultimap} object that maps prefixes to their * respective argument values. Only the given prefixes will be recognized in the arguments string. - * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixes Prefixes to tokenize the arguments string with - * @return ArgumentMultimap object that maps prefixes to their arguments + * @param argsString Arguments string of the form: {@code preamble value value ...}. + * @param prefixes Prefixes to tokenize the arguments string with. + * @return ArgumentMultimap object that maps prefixes to their arguments. */ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { List positions = findAllPrefixPositions(argsString, prefixes); @@ -30,10 +29,9 @@ public static ArgumentMultimap tokenize(String argsString, Prefix... prefixes) { /** * Finds all zero-based prefix positions in the given arguments string. - * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixes Prefixes to find in the arguments string - * @return List of zero-based prefix positions in the given arguments string + * @param argsString Arguments string of the form: {@code preamble value value ...}. + * @param prefixes Prefixes to find in the arguments string. + * @return List of zero-based prefix positions in the given arguments string. */ private static List findAllPrefixPositions(String argsString, Prefix... prefixes) { return Arrays.stream(prefixes) @@ -79,10 +77,9 @@ private static int findPrefixPosition(String argsString, String prefix, int from * Extracts prefixes and their argument values, and returns an {@code ArgumentMultimap} object that maps the * extracted prefixes to their respective arguments. Prefixes are extracted based on their zero-based positions in * {@code argsString}. - * - * @param argsString Arguments string of the form: {@code preamble value value ...} - * @param prefixPositions Zero-based positions of all prefixes in {@code argsString} - * @return ArgumentMultimap object that maps prefixes to their arguments + * @param argsString Arguments string of the form: {@code preamble value value ...}. + * @param prefixPositions Zero-based positions of all prefixes in {@code argsString}. + * @return ArgumentMultimap object that maps prefixes to their arguments. */ private static ArgumentMultimap extractArguments(String argsString, List prefixPositions) { @@ -144,5 +141,4 @@ Prefix getPrefix() { return prefix; } } - } diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/ta/logic/parser/CliSyntax.java similarity index 65% rename from src/main/java/seedu/address/logic/parser/CliSyntax.java rename to src/main/java/seedu/ta/logic/parser/CliSyntax.java index 75b1a9bf119..046d5b6e4d2 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/ta/logic/parser/CliSyntax.java @@ -1,15 +1,15 @@ -package seedu.address.logic.parser; +package seedu.ta.logic.parser; /** * Contains Command Line Interface (CLI) syntax definitions common to multiple commands */ public class CliSyntax { - /* Prefix definitions */ + /** Prefix definitions */ public static final Prefix PREFIX_NAME = new Prefix("n/"); public static final Prefix PREFIX_PHONE = new Prefix("p/"); public static final Prefix PREFIX_EMAIL = new Prefix("e/"); - public static final Prefix PREFIX_ADDRESS = new Prefix("a/"); public static final Prefix PREFIX_TAG = new Prefix("t/"); - + public static final Prefix PREFIX_START_DATE = new Prefix("sd/"); + public static final Prefix PREFIX_END_DATE = new Prefix("ed/"); } diff --git a/src/main/java/seedu/ta/logic/parser/DeleteContactCommandParser.java b/src/main/java/seedu/ta/logic/parser/DeleteContactCommandParser.java new file mode 100644 index 00000000000..08fb869bfc5 --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/DeleteContactCommandParser.java @@ -0,0 +1,29 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.DeleteContactCommand; +import seedu.ta.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteContactCommand object. + */ +public class DeleteContactCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteContactCommand + * and returns a DeleteContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public DeleteContactCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteContactCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteContactCommand.MESSAGE_USAGE), pe); + } + } +} + diff --git a/src/main/java/seedu/ta/logic/parser/DeleteEntryCommandParser.java b/src/main/java/seedu/ta/logic/parser/DeleteEntryCommandParser.java new file mode 100644 index 00000000000..4d9623ab9d9 --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/DeleteEntryCommandParser.java @@ -0,0 +1,28 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.DeleteEntryCommand; +import seedu.ta.logic.parser.exceptions.ParseException; + +/** + * Parses input arguments and creates a new DeleteEntryCommand object. + */ +public class DeleteEntryCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the DeleteEntryCommand + * and returns a DeleteEntryCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public DeleteEntryCommand parse(String args) throws ParseException { + try { + Index index = ParserUtil.parseIndex(args); + return new DeleteEntryCommand(index); + } catch (ParseException pe) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteEntryCommand.MESSAGE_USAGE), pe); + } + } +} diff --git a/src/main/java/seedu/ta/logic/parser/EditContactCommandParser.java b/src/main/java/seedu/ta/logic/parser/EditContactCommandParser.java new file mode 100644 index 00000000000..0b23413dbdd --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/EditContactCommandParser.java @@ -0,0 +1,82 @@ +package seedu.ta.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_NOT_EDITED; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.EditContactCommand; +import seedu.ta.logic.commands.EditContactCommand.EditContactDescriptor; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditContactCommand object. + */ +public class EditContactCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditContactCommand + * and returns an EditContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public EditContactCommand parse(String args) throws ParseException { + requireNonNull(args); + + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_TAG); + + Index index; + + try { + index = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String + .format(MESSAGE_INVALID_COMMAND_FORMAT, EditContactCommand.MESSAGE_USAGE), pe); + } + + EditContactDescriptor editContactDescriptor = new EditContactDescriptor(); + + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + editContactDescriptor.setName(ParserUtil.parseContactName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_PHONE).isPresent()) { + editContactDescriptor.setPhone(ParserUtil.parseContactPhone(argMultimap.getValue(PREFIX_PHONE).get())); + } + if (argMultimap.getValue(PREFIX_EMAIL).isPresent()) { + editContactDescriptor.setEmail(ParserUtil.parseContactEmail(argMultimap.getValue(PREFIX_EMAIL).get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editContactDescriptor::setTags); + + if (!editContactDescriptor.isAnyFieldEdited()) { + throw new ParseException(MESSAGE_NOT_EDITED); + } + + return new EditContactCommand(index, editContactDescriptor); + } + + /** + * 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)); + } +} diff --git a/src/main/java/seedu/ta/logic/parser/EditEntryCommandParser.java b/src/main/java/seedu/ta/logic/parser/EditEntryCommandParser.java new file mode 100644 index 00000000000..f8f1e5d3aab --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/EditEntryCommandParser.java @@ -0,0 +1,88 @@ +package seedu.ta.logic.parser; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_NOT_EDITED; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.EditEntryCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.entry.TemporaryEntry; +import seedu.ta.model.tag.Tag; + +/** + * Parses input arguments and creates a new EditEntryCommand object. + */ +public class EditEntryCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the EditEntryCommand + * and returns an EditEntryCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public EditEntryCommand parse(String args) throws ParseException { + requireNonNull(args); + + ArgumentMultimap argMultimap = ArgumentTokenizer + .tokenize(args, PREFIX_NAME, PREFIX_START_DATE, PREFIX_END_DATE, PREFIX_TAG); + + Index targetIndex; + + try { + targetIndex = ParserUtil.parseIndex(argMultimap.getPreamble()); + } catch (ParseException pe) { + throw new ParseException(String + .format(MESSAGE_INVALID_COMMAND_FORMAT, EditEntryCommand.MESSAGE_USAGE), pe); + } + + TemporaryEntry tempEntry = new TemporaryEntry(); + + if (argMultimap.getValue(PREFIX_NAME).isPresent()) { + tempEntry.setEntryName(ParserUtil.parseEntryName(argMultimap.getValue(PREFIX_NAME).get())); + } + if (argMultimap.getValue(PREFIX_START_DATE).isPresent()) { + tempEntry.setEntryStartDate(ParserUtil + .parseEntryDate(argMultimap.getValue(PREFIX_START_DATE).get())); + } + if (argMultimap.getValue(PREFIX_END_DATE).isPresent()) { + tempEntry.setEntryEndDate(ParserUtil + .parseEntryDate(argMultimap.getValue(PREFIX_END_DATE).get())); + } + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(tempEntry::setTags); + + if (!tempEntry.isUpdated()) { + throw new ParseException(MESSAGE_NOT_EDITED); + } + + if (args.trim().length() == 0) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEntryCommand.MESSAGE_USAGE)); + } + + return new EditEntryCommand(targetIndex, tempEntry); + } + + /** + * 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)); + } +} diff --git a/src/main/java/seedu/ta/logic/parser/FilterContactCommandParser.java b/src/main/java/seedu/ta/logic/parser/FilterContactCommandParser.java new file mode 100644 index 00000000000..e7db4f0483d --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/FilterContactCommandParser.java @@ -0,0 +1,32 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.ta.logic.commands.FilterContactCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.contact.ContactTagsContainKeywordsPredicate; + +/** + * Parses input arguments and creates a new FilterContactCommand object. + */ +public class FilterContactCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FilterContactCommand + * and returns a FilterContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public FilterContactCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterContactCommand.MESSAGE_USAGE)); + } + + String[] tagKeywords = trimmedArgs.split("\\s+"); + return new FilterContactCommand(new ContactTagsContainKeywordsPredicate(Arrays.asList(tagKeywords))); + } +} diff --git a/src/main/java/seedu/ta/logic/parser/FilterEntryCommandParser.java b/src/main/java/seedu/ta/logic/parser/FilterEntryCommandParser.java new file mode 100644 index 00000000000..bae1ddc6ae0 --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/FilterEntryCommandParser.java @@ -0,0 +1,32 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.ta.logic.commands.FilterEntryCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.entry.EntryTagsContainKeywordsPredicate; + +/** + * Parses input arguments and creates a new FilterEntryCommand object. + */ +public class FilterEntryCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FilterEntryCommand + * and returns a FilterEntryCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public FilterEntryCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterEntryCommand.MESSAGE_USAGE)); + } + + String[] tagKeywords = trimmedArgs.split("\\s+"); + return new FilterEntryCommand(new EntryTagsContainKeywordsPredicate(Arrays.asList(tagKeywords))); + } +} diff --git a/src/main/java/seedu/ta/logic/parser/FindContactCommandParser.java b/src/main/java/seedu/ta/logic/parser/FindContactCommandParser.java new file mode 100644 index 00000000000..87d3bb5fd00 --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/FindContactCommandParser.java @@ -0,0 +1,32 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.ta.logic.commands.FindContactCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.contact.ContactNameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindContactCommand object. + */ +public class FindContactCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindContactCommand + * and returns a FindContactCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public FindContactCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindContactCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + return new FindContactCommand(new ContactNameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } +} diff --git a/src/main/java/seedu/ta/logic/parser/FindEntryCommandParser.java b/src/main/java/seedu/ta/logic/parser/FindEntryCommandParser.java new file mode 100644 index 00000000000..d9a97c5ce26 --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/FindEntryCommandParser.java @@ -0,0 +1,32 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import java.util.Arrays; + +import seedu.ta.logic.commands.FindEntryCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.entry.EntryNameContainsKeywordsPredicate; + +/** + * Parses input arguments and creates a new FindEntryCommand object. + */ +public class FindEntryCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FindEntryCommand + * and returns a FindEntryCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public FindEntryCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + + if (trimmedArgs.isEmpty()) { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindEntryCommand.MESSAGE_USAGE)); + } + + String[] nameKeywords = trimmedArgs.split("\\s+"); + return new FindEntryCommand(new EntryNameContainsKeywordsPredicate(Arrays.asList(nameKeywords))); + } +} diff --git a/src/main/java/seedu/ta/logic/parser/FreeCommandParser.java b/src/main/java/seedu/ta/logic/parser/FreeCommandParser.java new file mode 100644 index 00000000000..b25e5a323ea --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/FreeCommandParser.java @@ -0,0 +1,59 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_ENTRY_START_DATE_IN_PAST; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_DATE_RANGE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_START_DATE; + +import java.time.LocalDateTime; +import java.util.stream.Stream; + +import seedu.ta.logic.commands.FreeCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.ListOccupyingEntryPredicate; + +/** + * Parses input arguments and creates a new FreeCommand object. + */ +public class FreeCommandParser implements Parser { + + /** + * Parses the given {@code String} of arguments in the context of the FreeCommand + * and returns a FreeCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public FreeCommand parse(String args) throws ParseException { + ArgumentMultimap argMultimap = + ArgumentTokenizer.tokenize(args, PREFIX_START_DATE, PREFIX_END_DATE); + + if (!arePrefixesPresent(argMultimap, PREFIX_START_DATE, PREFIX_END_DATE) + || !argMultimap.getPreamble().isEmpty()) { + throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, FreeCommand.MESSAGE_USAGE)); + } + + EntryDate startDateTime = ParserUtil.parseEntryDate(argMultimap.getValue(PREFIX_START_DATE).get()); + EntryDate endDateTime = ParserUtil.parseEntryDate(argMultimap.getValue(PREFIX_END_DATE).get()); + + if (startDateTime.isAfter(endDateTime)) { + throw new ParseException(MESSAGE_INVALID_DATE_RANGE); + } + + LocalDateTime now = LocalDateTime.now(); + + if (startDateTime.getDate().isBefore(now)) { + throw new ParseException(MESSAGE_ENTRY_START_DATE_IN_PAST); + } + + return new FreeCommand(new ListOccupyingEntryPredicate(startDateTime, endDateTime)); + } + + /** + * 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/ta/logic/parser/ListEntryCommandParser.java b/src/main/java/seedu/ta/logic/parser/ListEntryCommandParser.java new file mode 100644 index 00000000000..a10bccca07b --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/ListEntryCommandParser.java @@ -0,0 +1,29 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; + +import seedu.ta.logic.commands.ListEntryCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.entry.ListEntryFormatPredicate; + +/** + * Parses input arguments and creates a new ListEntryCommand object. + */ +public class ListEntryCommandParser implements Parser { + + /** + * Parses the give {@code String} of arguments in the context of the ListEntryCommand + * and returns a ListEntryCommand object for execution. + * @throws ParseException if the user input does not conform the expected format. + */ + public ListEntryCommand parse(String args) throws ParseException { + String trimmedArgs = args.trim(); + + if (trimmedArgs.equals("day") || trimmedArgs.equals("week") || trimmedArgs.isEmpty()) { + return new ListEntryCommand(new ListEntryFormatPredicate(trimmedArgs)); + } else { + throw new ParseException( + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListEntryCommand.MESSAGE_USAGE)); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/ta/logic/parser/Parser.java similarity index 67% rename from src/main/java/seedu/address/logic/parser/Parser.java rename to src/main/java/seedu/ta/logic/parser/Parser.java index d6551ad8e3f..cdb39fc08ac 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/ta/logic/parser/Parser.java @@ -1,7 +1,7 @@ -package seedu.address.logic.parser; +package seedu.ta.logic.parser; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.ta.logic.commands.Command; +import seedu.ta.logic.parser.exceptions.ParseException; /** * Represents a Parser that is able to parse user input into a {@code Command} of type {@code T}. @@ -10,7 +10,7 @@ public interface Parser { /** * Parses {@code userInput} into a command and returns it. - * @throws ParseException if {@code userInput} does not conform the expected format + * @throws ParseException if {@code userInput} does not conform the expected format. */ T parse(String userInput) throws ParseException; } diff --git a/src/main/java/seedu/ta/logic/parser/ParserUtil.java b/src/main/java/seedu/ta/logic/parser/ParserUtil.java new file mode 100644 index 00000000000..56b131ac186 --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/ParserUtil.java @@ -0,0 +1,165 @@ +package seedu.ta.logic.parser; + +import static java.util.Objects.requireNonNull; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.commons.util.StringUtil; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.tag.Tag; + +/** + * Contains utility methods used for parsing strings in the various *Parser classes. + */ +public class ParserUtil { + + public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer."; + + // ======= Shared Utils ======= + + /** + * Parses {@code oneBasedIndex} into an {@code Index} 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 parseIndex(String oneBasedIndex) throws ParseException { + String trimmedIndex = oneBasedIndex.trim(); + + if (!StringUtil.isNonZeroUnsignedInteger(trimmedIndex)) { + throw new ParseException(String.format("Index given: %s\n%s", trimmedIndex, MESSAGE_INVALID_INDEX)); + } + + return Index.fromOneBased(Integer.parseInt(trimmedIndex)); + } + + /** + * Parses a {@code String tag} into a {@code Tag}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException if the given {@code tag} is invalid. + */ + public static Tag parseTag(String tag) throws ParseException { + requireNonNull(tag); + + String trimmedTag = tag.trim(); + + if (!Tag.isValidTagName(trimmedTag)) { + throw new ParseException(String.format("Tag given: %s\n%s", trimmedTag, Tag.MESSAGE_CONSTRAINTS)); + } + + return new Tag(trimmedTag); + } + + /** + * Parses {@code Collection tags} into a {@code Set}. + */ + public static Set parseTags(Collection tags) throws ParseException { + requireNonNull(tags); + + final Set tagSet = new HashSet<>(); + + for (String tagName : tags) { + tagSet.add(parseTag(tagName)); + } + + return tagSet; + } + + // ======= Contact Utils ======= + + /** + * Parses a {@code String name} into a {@code ContactName}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException if the given {@code name} is invalid. + */ + public static ContactName parseContactName(String name) throws ParseException { + requireNonNull(name); + + String trimmedName = name.trim(); + + if (!ContactName.isValidName(trimmedName)) { + throw new ParseException(String.format("Name given: %s\n%s", trimmedName, ContactName.MESSAGE_CONSTRAINTS)); + } + + return new ContactName(trimmedName); + } + + /** + * Parses a {@code String phone} into a {@code ContactPhone}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException if the given {@code phone} is invalid. + */ + public static ContactPhone parseContactPhone(String phone) throws ParseException { + requireNonNull(phone); + + String trimmedPhone = phone.trim(); + + if (!ContactPhone.isValidPhone(trimmedPhone)) { + throw new ParseException(String + .format("Phone number given: %s\n%s", trimmedPhone, ContactPhone.MESSAGE_CONSTRAINTS)); + } + + return new ContactPhone(trimmedPhone); + } + + /** + * Parses a {@code String email} into an {@code ContactEmail}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException if the given {@code email} is invalid. + */ + public static ContactEmail parseContactEmail(String email) throws ParseException { + requireNonNull(email); + + String trimmedEmail = email.trim(); + + if (!ContactEmail.isValidEmail(trimmedEmail)) { + throw new ParseException(String + .format("Email given: %s\n%s", trimmedEmail, ContactEmail.MESSAGE_CONSTRAINTS)); + } + + return new ContactEmail(trimmedEmail); + } + + // ======= Entry Utils ======= + + /** + * Parses a {@code String entryName} into a {@code EntryName}. + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException if the given {@code entryName} is invalid. + */ + public static EntryName parseEntryName(String entryName) throws ParseException { + requireNonNull(entryName); + + String trimmedEntryName = entryName.trim(); + + if (!EntryName.isValidName(trimmedEntryName)) { + throw new ParseException(String.format("Name given: %s\n%s", trimmedEntryName, EntryName.NAME_CONSTRAINTS)); + } + + return new EntryName(trimmedEntryName); + } + + /** + * Parses a {@code String entryDate} into a {@code EntryDate} + * Leading and trailing whitespaces will be trimmed. + * @throws ParseException if the given {@code entryDate} is invalid. + */ + public static EntryDate parseEntryDate(String entryDate) throws ParseException { + requireNonNull(entryDate); + + String trimmedEntryDate = entryDate.trim(); + + if (!EntryDate.isValidDate(trimmedEntryDate)) { + throw new ParseException(String.format("Date given: %s\n%s", trimmedEntryDate, EntryDate.DATE_CONSTRAINTS)); + } + + return new EntryDate(trimmedEntryDate); + } +} diff --git a/src/main/java/seedu/address/logic/parser/Prefix.java b/src/main/java/seedu/ta/logic/parser/Prefix.java similarity index 90% rename from src/main/java/seedu/address/logic/parser/Prefix.java rename to src/main/java/seedu/ta/logic/parser/Prefix.java index c859d5fa5db..81c5c4c93aa 100644 --- a/src/main/java/seedu/address/logic/parser/Prefix.java +++ b/src/main/java/seedu/ta/logic/parser/Prefix.java @@ -1,10 +1,11 @@ -package seedu.address.logic.parser; +package seedu.ta.logic.parser; /** * A prefix that marks the beginning of an argument in an arguments string. - * E.g. 't/' in 'add James t/ friend'. + * E.g. 't/' in 'add James t/ friend' */ public class Prefix { + private final String prefix; public Prefix(String prefix) { diff --git a/src/main/java/seedu/ta/logic/parser/TeachingAssistantParser.java b/src/main/java/seedu/ta/logic/parser/TeachingAssistantParser.java new file mode 100644 index 00000000000..ac088a1d5eb --- /dev/null +++ b/src/main/java/seedu/ta/logic/parser/TeachingAssistantParser.java @@ -0,0 +1,112 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import seedu.ta.logic.commands.AddContactCommand; +import seedu.ta.logic.commands.AddEntryCommand; +import seedu.ta.logic.commands.ClearCommand; +import seedu.ta.logic.commands.ClearOverdueEntryCommand; +import seedu.ta.logic.commands.Command; +import seedu.ta.logic.commands.DeleteContactCommand; +import seedu.ta.logic.commands.DeleteEntryCommand; +import seedu.ta.logic.commands.EditContactCommand; +import seedu.ta.logic.commands.EditEntryCommand; +import seedu.ta.logic.commands.ExitCommand; +import seedu.ta.logic.commands.FilterContactCommand; +import seedu.ta.logic.commands.FilterEntryCommand; +import seedu.ta.logic.commands.FindContactCommand; +import seedu.ta.logic.commands.FindEntryCommand; +import seedu.ta.logic.commands.FreeCommand; +import seedu.ta.logic.commands.HelpCommand; +import seedu.ta.logic.commands.ListContactCommand; +import seedu.ta.logic.commands.ListEntryCommand; +import seedu.ta.logic.parser.exceptions.ParseException; + +/** + * Parses user input. + */ +public class TeachingAssistantParser { + + /** + * 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 AddContactCommand.COMMAND_WORD: + return new AddContactCommandParser().parse(arguments); + + case AddEntryCommand.COMMAND_WORD: + return new AddEntryCommandParser().parse(arguments); + + case ClearCommand.COMMAND_WORD: + return new ClearCommand(); + + case ClearOverdueEntryCommand.COMMAND_WORD: + return new ClearOverdueEntryCommand(); + + case DeleteContactCommand.COMMAND_WORD: + return new DeleteContactCommandParser().parse(arguments); + + case DeleteEntryCommand.COMMAND_WORD: + return new DeleteEntryCommandParser().parse(arguments); + + case EditContactCommand.COMMAND_WORD: + return new EditContactCommandParser().parse(arguments); + + case EditEntryCommand.COMMAND_WORD: + return new EditEntryCommandParser().parse(arguments); + + case ExitCommand.COMMAND_WORD: + return new ExitCommand(); + + case FilterContactCommand.COMMAND_WORD: + return new FilterContactCommandParser().parse(arguments); + + case FilterEntryCommand.COMMAND_WORD: + return new FilterEntryCommandParser().parse(arguments); + + case FindContactCommand.COMMAND_WORD: + return new FindContactCommandParser().parse(arguments); + + case FindEntryCommand.COMMAND_WORD: + return new FindEntryCommandParser().parse(arguments); + + case FreeCommand.COMMAND_WORD: + return new FreeCommandParser().parse(arguments); + + case HelpCommand.COMMAND_WORD: + return new HelpCommand(); + + case ListContactCommand.COMMAND_WORD: + return new ListContactCommand(); + + case ListEntryCommand.COMMAND_WORD: + return new ListEntryCommandParser().parse(arguments); + + default: + throw new ParseException(MESSAGE_UNKNOWN_COMMAND); + } + } +} diff --git a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java b/src/main/java/seedu/ta/logic/parser/exceptions/ParseException.java similarity index 73% rename from src/main/java/seedu/address/logic/parser/exceptions/ParseException.java rename to src/main/java/seedu/ta/logic/parser/exceptions/ParseException.java index 158a1a54c1c..07198fd5532 100644 --- a/src/main/java/seedu/address/logic/parser/exceptions/ParseException.java +++ b/src/main/java/seedu/ta/logic/parser/exceptions/ParseException.java @@ -1,6 +1,6 @@ -package seedu.address.logic.parser.exceptions; +package seedu.ta.logic.parser.exceptions; -import seedu.address.commons.exceptions.IllegalValueException; +import seedu.ta.commons.exceptions.IllegalValueException; /** * Represents a parse error encountered by a parser. diff --git a/src/main/java/seedu/ta/model/Model.java b/src/main/java/seedu/ta/model/Model.java new file mode 100644 index 00000000000..53ed534ff14 --- /dev/null +++ b/src/main/java/seedu/ta/model/Model.java @@ -0,0 +1,141 @@ +package seedu.ta.model; + +import java.nio.file.Path; +import java.util.function.Predicate; + +import javafx.collections.ObservableList; +import seedu.ta.commons.core.GuiSettings; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.entry.Entry; + +/** + * The API of the Model component. + */ +public interface Model { + + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_ENTRIES = unused -> true; + + /** {@code Predicate} that always evaluate to true */ + Predicate PREDICATE_SHOW_ALL_CONTACTS = unused -> true; + + /** + * Replaces user prefs data with the data in {@code userPrefs}. + */ + void setUserPrefs(ReadOnlyUserPrefs userPrefs); + + /** + * Returns the user prefs. + */ + ReadOnlyUserPrefs getUserPrefs(); + + /** + * Returns the user prefs' GUI settings. + */ + GuiSettings getGuiSettings(); + + /** + * Sets the user prefs' GUI settings. + */ + void setGuiSettings(GuiSettings guiSettings); + + /** + * Returns the user prefs' teaching assistant file path. + */ + Path getTeachingAssistantFilePath(); + + /** + * Sets the user prefs' teaching assistant file path. + */ + void setTeachingAssistantFilePath(Path teachingAssistantFilePath); + + /** + * Replaces teaching assistant data with the data in {@code teachingAssistant}. + */ + void setTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant); + + /** Returns the TeachingAssistant */ + ReadOnlyTeachingAssistant getTeachingAssistant(); + + // ====== Contact ====== + + /** + * Returns true if a contact with the same identity as {@code contact} exists in Teaching Assistant. + */ + boolean hasContact(Contact contact); + + /** + * Deletes the given contact. + * {@code target} must exist in Teaching Assistant. + */ + void deleteContact(Contact target); + + /** + * Adds the given contact. + * {@code contact} must not already exist in Teaching Assistant. + */ + void addContact(Contact contact); + + /** + * Replaces the given contact {@code target} with {@code editedContact}. + * {@code target} must exist in Teaching Assistant. + * The contact identity of {@code editedContact} must not be the same as another existing + * contact in Teaching Assistant. + */ + void setContact(Contact target, Contact editedContact); + + /** Returns an unmodifiable view of the filtered contact list */ + ObservableList getFilteredContactList(); + + /** + * Updates the filter of the filtered contact list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredContactList(Predicate predicate); + + // ====== Entry ====== + + /** + * Returns true if the entry exists in the list. + */ + boolean hasEntry(Entry entry); + + /** + * Deletes the given entry. + * {@code entry} must exist in the list. + */ + void deleteEntry(Entry entry); + + /** + * Adds the given entry. + * {@code entry} must not overlap with existing entries in the list. + */ + void addEntry(Entry entry); + + /** + * Replaces the given entry {@code target} with {@code editedEntry}. + * {@code target} must exist in the list. + * {@code editedEntry} must not overlap with existing entries in the list. + */ + void setEntry(Entry target, Entry editedEntry); + + /** + * Returns true if the given entry {@code toAdd} has dates overlapping with other entries in the list. + */ + boolean isOverlappingEntry(Entry toAdd); + + /** + * Removes all entries that are overdue. + */ + void clearOverdueEntries(); + + /** Returns an unmodifiable view of the filtered entry list. */ + ObservableList getFilteredEntryList(); + + /** + * Updates the filter of the filtered entry list to filter by the given {@code predicate}. + * @throws NullPointerException if {@code predicate} is null. + */ + void updateFilteredEntryList(Predicate predicate); + +} diff --git a/src/main/java/seedu/ta/model/ModelManager.java b/src/main/java/seedu/ta/model/ModelManager.java new file mode 100644 index 00000000000..71e241e99dd --- /dev/null +++ b/src/main/java/seedu/ta/model/ModelManager.java @@ -0,0 +1,211 @@ +package seedu.ta.model; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.util.CollectionUtil.requireAllNonNull; + +import java.nio.file.Path; +import java.util.function.Predicate; +import java.util.logging.Logger; + +import javafx.collections.ObservableList; +import javafx.collections.transformation.FilteredList; +import seedu.ta.commons.core.GuiSettings; +import seedu.ta.commons.core.LogsCenter; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.entry.Entry; + +/** + * Represents the in-memory model of the Teaching Assistant data. + */ +public class ModelManager implements Model { + + private static final Logger logger = LogsCenter.getLogger(ModelManager.class); + + private final TeachingAssistant teachingAssistant; + private final UserPrefs userPrefs; + private final FilteredList filteredContacts; + private final FilteredList filteredEntries; + + /** + * Initializes a ModelManager with the given teachingAssistant and userPrefs. + */ + public ModelManager(ReadOnlyTeachingAssistant teachingAssistant, ReadOnlyUserPrefs userPrefs) { + super(); + requireAllNonNull(teachingAssistant, userPrefs); + + logger.fine("Initializing with teaching assistant: " + teachingAssistant + " and user prefs " + userPrefs); + + this.teachingAssistant = new TeachingAssistant(teachingAssistant); + this.userPrefs = new UserPrefs(userPrefs); + filteredContacts = new FilteredList<>(this.teachingAssistant.getContactList()); + filteredEntries = new FilteredList<>(this.teachingAssistant.getEntryList()); + } + + public ModelManager() { + this(new TeachingAssistant(), new UserPrefs()); + } + + //=========== UserPrefs ================================================================================== + + @Override + public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { + requireNonNull(userPrefs); + this.userPrefs.resetData(userPrefs); + } + + @Override + public ReadOnlyUserPrefs getUserPrefs() { + return userPrefs; + } + + @Override + public GuiSettings getGuiSettings() { + return userPrefs.getGuiSettings(); + } + + @Override + public void setGuiSettings(GuiSettings guiSettings) { + requireNonNull(guiSettings); + userPrefs.setGuiSettings(guiSettings); + } + + @Override + public Path getTeachingAssistantFilePath() { + return userPrefs.getTeachingAssistantFilePath(); + } + + @Override + public void setTeachingAssistantFilePath(Path teachingAssistantFilePath) { + requireNonNull(teachingAssistantFilePath); + userPrefs.setTeachingAssistantFilePath(teachingAssistantFilePath); + } + + // ====== Teaching Assistant ====== + + @Override + public void setTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant) { + this.teachingAssistant.resetData(teachingAssistant); + } + + @Override + public ReadOnlyTeachingAssistant getTeachingAssistant() { + return teachingAssistant; + } + + // ====== Contact ====== + + @Override + public boolean hasContact(Contact contact) { + requireNonNull(contact); + return teachingAssistant.hasContact(contact); + } + + @Override + public void deleteContact(Contact target) { + teachingAssistant.removeContact(target); + } + + @Override + public void addContact(Contact contact) { + teachingAssistant.addContact(contact); + updateFilteredContactList(PREDICATE_SHOW_ALL_CONTACTS); + } + + @Override + public void setContact(Contact target, Contact editedContact) { + requireAllNonNull(target, editedContact); + + teachingAssistant.setContact(target, editedContact); + } + + // ====== Entry ====== + + @Override + public boolean hasEntry(Entry entry) { + return teachingAssistant.hasEntry(entry); + } + + @Override + public void addEntry(Entry entry) { + teachingAssistant.addEntry(entry); + updateFilteredEntryList(PREDICATE_SHOW_ALL_ENTRIES); + } + + @Override + public void deleteEntry(Entry entry) { + teachingAssistant.removeEntry(entry); + } + + @Override + public void setEntry(Entry target, Entry editedEntry) { + requireAllNonNull(target, editedEntry); + teachingAssistant.setEntry(target, editedEntry); + } + + @Override + public boolean isOverlappingEntry(Entry toAdd) { + requireNonNull(toAdd); + return teachingAssistant.isOverlappingEntry(toAdd); + } + + @Override + public void clearOverdueEntries() { + teachingAssistant.clearOverdueEntries(); + } + + //=========== Filtered Contact List Accessors ============================================================= + + /** + * Returns an unmodifiable view of the list of {@code Contact} backed by the internal list of + * {@code versionedTeachingAssistant}. + */ + @Override + public ObservableList getFilteredContactList() { + return filteredContacts; + } + + @Override + public void updateFilteredContactList(Predicate predicate) { + requireNonNull(predicate); + filteredContacts.setPredicate(predicate); + } + + //=========== Filtered Entry List Accessors ============================================================== + + /** + * Returns an unmodifiable view of the list of {@code Entry} backed by the internal list of + * {@code versionedTeachingAssistant}. + */ + @Override + public ObservableList getFilteredEntryList() { + return filteredEntries; + } + + @Override + public void updateFilteredEntryList(Predicate predicate) { + requireNonNull(predicate); + filteredEntries.setPredicate(predicate); + } + + //=========== Misc =============== + + @Override + public boolean equals(Object obj) { + // short circuit if same object + if (obj == this) { + return true; + } + + // instanceof handles nulls + if (!(obj instanceof ModelManager)) { + return false; + } + + // state check + ModelManager other = (ModelManager) obj; + return teachingAssistant.equals(other.teachingAssistant) + && userPrefs.equals(other.userPrefs) + && filteredContacts.equals(other.filteredContacts) + && filteredEntries.equals(other.filteredEntries); + } +} diff --git a/src/main/java/seedu/ta/model/ReadOnlyTeachingAssistant.java b/src/main/java/seedu/ta/model/ReadOnlyTeachingAssistant.java new file mode 100644 index 00000000000..8b5362872bb --- /dev/null +++ b/src/main/java/seedu/ta/model/ReadOnlyTeachingAssistant.java @@ -0,0 +1,23 @@ +package seedu.ta.model; + +import javafx.collections.ObservableList; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.entry.Entry; + +/** + * Unmodifiable view of Teaching Assistant. + */ +public interface ReadOnlyTeachingAssistant { + + /** + * Returns an unmodifiable view of the contacts list. + * This list will not contain any duplicate contacts. + */ + ObservableList getContactList(); + + /** + * Returns an unmodifiable view of the entries list. + * This list will not contain any overlapping entries. + */ + ObservableList getEntryList(); +} diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/ta/model/ReadOnlyUserPrefs.java similarity index 57% rename from src/main/java/seedu/address/model/ReadOnlyUserPrefs.java rename to src/main/java/seedu/ta/model/ReadOnlyUserPrefs.java index befd58a4c73..02206650ef2 100644 --- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java +++ b/src/main/java/seedu/ta/model/ReadOnlyUserPrefs.java @@ -1,8 +1,8 @@ -package seedu.address.model; +package seedu.ta.model; import java.nio.file.Path; -import seedu.address.commons.core.GuiSettings; +import seedu.ta.commons.core.GuiSettings; /** * Unmodifiable view of user prefs. @@ -11,6 +11,5 @@ public interface ReadOnlyUserPrefs { GuiSettings getGuiSettings(); - Path getAddressBookFilePath(); - + Path getTeachingAssistantFilePath(); } diff --git a/src/main/java/seedu/ta/model/TeachingAssistant.java b/src/main/java/seedu/ta/model/TeachingAssistant.java new file mode 100644 index 00000000000..a5f3e98ebf8 --- /dev/null +++ b/src/main/java/seedu/ta/model/TeachingAssistant.java @@ -0,0 +1,190 @@ +package seedu.ta.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +import javafx.collections.ObservableList; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.UniqueContactList; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.NonOverlappingEntryList; + + +/** + * Wraps all data at the teaching-assistant level. + * Duplicates are not allowed (by .isSameContact comparison). + */ +public class TeachingAssistant implements ReadOnlyTeachingAssistant { + + private final UniqueContactList contacts; + private final NonOverlappingEntryList entries; + + /* + * 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. + */ + { + entries = new NonOverlappingEntryList(); + contacts = new UniqueContactList(); + } + + public TeachingAssistant() {} + + /** + * Creates an TeachingAssistant using the data in the {@code toBeCopied}. + */ + public TeachingAssistant(ReadOnlyTeachingAssistant toBeCopied) { + this(); + resetData(toBeCopied); + } + + //// ===== List Operations ===== + + /** + * Replaces the contents of the contact list with {@code contacts}. + * {@code contacts} must not contain duplicate contacts. + */ + public void setContacts(List contacts) { + this.contacts.setContacts(contacts); + } + + /** + * Replaces the contents of the entry list with {@code entries}. + * {@code entries} must not contain overlapping entries. + */ + public void setEntries(List entries) { + this.entries.setEntries(entries); + } + + /** + * Resets the existing data of this {@code TeachingAssistant} with {@code newData}. + */ + public void resetData(ReadOnlyTeachingAssistant newData) { + requireNonNull(newData); + setContacts(newData.getContactList()); + setEntries(newData.getEntryList()); + } + + //// ===== Contact Operations ===== + + /** + * Returns true if a contact with the same identity as {@code contact} exists in Teaching Assistant. + */ + public boolean hasContact(Contact contact) { + requireNonNull(contact); + return contacts.contains(contact); + } + + /** + * Adds a contact to Teaching Assistant. + * The contact must not already exist in Teaching Assistant. + */ + public void addContact(Contact contact) { + contacts.add(contact); + } + + /** + * Replaces the given contact {@code target} in the list with {@code editedContact}. + * {@code target} must exist in Teaching Assistant. + * The contact identity of {@code editedContact} must not be the same as another existing + * contact in Teaching Assistant. + */ + public void setContact(Contact target, Contact editedContact) { + requireNonNull(editedContact); + contacts.setContact(target, editedContact); + } + + /** + * Removes {@code contact} from this {@code Teaching Assistant}. + * {@code contact} must exist in Teaching Assistant. + */ + public void removeContact(Contact contact) { + contacts.remove(contact); + } + + //// ===== Entry Operations ===== + + /** + * Returns true if an entry with the same identity as {@code entry} exists in Teaching Assistant. + */ + public boolean hasEntry(Entry entry) { + requireNonNull(entry); + return entries.contains(entry); + } + + /** + * Adds an entry to the list. + * The entry must not overlap with existing entries in the list. + */ + public void addEntry(Entry entry) { + entries.add(entry); + } + + /** + * Removes an entry {@code entry} from the list. + * {@code entry} must exist in the list. + */ + public void removeEntry(Entry entry) { + requireNonNull(entry); + entries.remove(entry); + } + + /** + * Replaces the given entry {@code target} in the list with {@code editedEntry}. + * {@code target} must exist in the list. + * {@code editedEntry} must not overlap with existing entries in the list. + */ + public void setEntry(Entry target, Entry editedEntry) { + requireNonNull(editedEntry); + entries.setEntry(target, editedEntry); + } + + /** + * Returns true if the give entry overlaps with existing entries in the list. + */ + public boolean isOverlappingEntry(Entry entry) { + requireNonNull(entry); + return entries.overlapsWith(entry); + } + + /** + * Clears all overdue entries in Teaching Assistant. + */ + public void clearOverdueEntries() { + entries.clearOverdueEntries(); + } + + // ===== Util ===== + + @Override + public String toString() { + return contacts.asUnmodifiableObservableList().size() + " contacts"; + // TODO: refine later + } + + @Override + public ObservableList getContactList() { + return contacts.asUnmodifiableObservableList(); + } + + @Override + public ObservableList getEntryList() { + return entries.asUnmodifiableObservableList(); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof TeachingAssistant // instanceof handles nulls + && contacts.equals(((TeachingAssistant) other).contacts)); + } + + @Override + public int hashCode() { + return contacts.hashCode(); + } +} diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/ta/model/UserPrefs.java similarity index 68% rename from src/main/java/seedu/address/model/UserPrefs.java rename to src/main/java/seedu/ta/model/UserPrefs.java index 25a5fd6eab9..8ff047191b4 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/ta/model/UserPrefs.java @@ -1,4 +1,4 @@ -package seedu.address.model; +package seedu.ta.model; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.nio.file.Paths; import java.util.Objects; -import seedu.address.commons.core.GuiSettings; +import seedu.ta.commons.core.GuiSettings; /** * Represents User's preferences. @@ -14,7 +14,7 @@ public class UserPrefs implements ReadOnlyUserPrefs { private GuiSettings guiSettings = new GuiSettings(); - private Path addressBookFilePath = Paths.get("data" , "addressbook.json"); + private Path teachingAssistantFilePath = Paths.get("data" , "teachingassistant.json"); /** * Creates a {@code UserPrefs} with default values. @@ -35,7 +35,7 @@ public UserPrefs(ReadOnlyUserPrefs userPrefs) { public void resetData(ReadOnlyUserPrefs newUserPrefs) { requireNonNull(newUserPrefs); setGuiSettings(newUserPrefs.getGuiSettings()); - setAddressBookFilePath(newUserPrefs.getAddressBookFilePath()); + setTeachingAssistantFilePath(newUserPrefs.getTeachingAssistantFilePath()); } public GuiSettings getGuiSettings() { @@ -47,13 +47,13 @@ public void setGuiSettings(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public Path getAddressBookFilePath() { - return addressBookFilePath; + public Path getTeachingAssistantFilePath() { + return teachingAssistantFilePath; } - public void setAddressBookFilePath(Path addressBookFilePath) { - requireNonNull(addressBookFilePath); - this.addressBookFilePath = addressBookFilePath; + public void setTeachingAssistantFilePath(Path teachingAssistantFilePath) { + requireNonNull(teachingAssistantFilePath); + this.teachingAssistantFilePath = teachingAssistantFilePath; } @Override @@ -68,20 +68,19 @@ public boolean equals(Object other) { UserPrefs o = (UserPrefs) other; return guiSettings.equals(o.guiSettings) - && addressBookFilePath.equals(o.addressBookFilePath); + && teachingAssistantFilePath.equals(o.teachingAssistantFilePath); } @Override public int hashCode() { - return Objects.hash(guiSettings, addressBookFilePath); + return Objects.hash(guiSettings, teachingAssistantFilePath); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Gui Settings : " + guiSettings); - sb.append("\nLocal data file location : " + addressBookFilePath); + sb.append("\nLocal data file location : " + teachingAssistantFilePath); return sb.toString(); } - } diff --git a/src/main/java/seedu/ta/model/contact/Contact.java b/src/main/java/seedu/ta/model/contact/Contact.java new file mode 100644 index 00000000000..1a397b736bd --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/Contact.java @@ -0,0 +1,103 @@ +package seedu.ta.model.contact; + +import static seedu.ta.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.ta.model.tag.Tag; + +/** + * Represents a Contact in Teaching Assistant. + */ +public class Contact { + + private final ContactName name; + private final ContactPhone phone; + private final ContactEmail email; + private final Set tags = new HashSet<>(); + + /** + * Creates a Contact where every field is present and not null. + */ + public Contact(ContactName name, ContactPhone phone, ContactEmail email, Set tags) { + requireAllNonNull(name, phone, email, tags); + this.name = name; + this.phone = phone; + this.email = email; + this.tags.addAll(tags); + } + + public ContactName getName() { + return name; + } + + public ContactPhone getPhone() { + return phone; + } + + public ContactEmail getEmail() { + return email; + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns true if both contacts have the same identity. + */ + public boolean isSameContact(Contact otherContact) { + return equals(otherContact); + } + + /** + * Returns true if both contacts have the same identity and data fields. + * This defines a stronger notion of equality between two contacts. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Contact)) { + return false; + } + + Contact otherContact = (Contact) other; + return otherContact.getName().equals(getName()) + && otherContact.getPhone().equals(getPhone()) + && otherContact.getEmail().equals(getEmail()); + } + + @Override + public int hashCode() { + // use this method for custom fields hashing instead of implementing your own + return Objects.hash(name, phone, email, tags); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getName()) + .append("; Phone: ") + .append(getPhone()) + .append("; Email: ") + .append(getEmail()); + + Set tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + return builder.toString(); + } +} + diff --git a/src/main/java/seedu/ta/model/contact/ContactComparator.java b/src/main/java/seedu/ta/model/contact/ContactComparator.java new file mode 100644 index 00000000000..47c99bd0b1d --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/ContactComparator.java @@ -0,0 +1,14 @@ +package seedu.ta.model.contact; + +import java.util.Comparator; + +/** + * Compares two contacts based on their names. + */ +public class ContactComparator implements Comparator { + + @Override + public int compare(Contact contact, Contact otherContact) { + return contact.getName().fullName.compareTo(otherContact.getName().fullName); + } +} diff --git a/src/main/java/seedu/ta/model/contact/ContactEmail.java b/src/main/java/seedu/ta/model/contact/ContactEmail.java new file mode 100644 index 00000000000..420c5da4888 --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/ContactEmail.java @@ -0,0 +1,69 @@ +package seedu.ta.model.contact; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.util.AppUtil.checkArgument; + +/** + * Represents a Contact's email. + */ +public class ContactEmail { + + private static final String PRINTABLE_CHARACTERS = ".!#$%&'*+-/=?^_`{|}~"; + + public static final String MESSAGE_CONSTRAINTS = "Emails should be of the format local-part@domain " + + "and adhere to the following constraints:\n" + + "The local-part must:\n" + + "- be at least 2 characters long\n" + + "- start and end with alphanumeric characters\n" + + "- contain only alphanumeric characters and these special characters: " + PRINTABLE_CHARACTERS + "\n\n" + + "The domain name must:\n" + + "- be at least 2 characters long\n" + + "- start and end with alphanumeric characters\n" + + "- consist of alphanumeric characters, a period or a hyphen for the characters in between, if any."; + + // local-part validation + private static final String LOCAL_PART_REGEX = "^\\w[\\w" + PRINTABLE_CHARACTERS + "]*\\w"; + + // domain validation + private static final String DOMAIN_FIRST_CHARACTER_REGEX = "\\w"; // alphanumeric characters except underscore + private static final String DOMAIN_MIDDLE_REGEX = "[\\w.-]*"; // alphanumeric, period and hyphen + private static final String DOMAIN_LAST_CHARACTER_REGEX = "\\w$"; + public static final String VALIDATION_REGEX = LOCAL_PART_REGEX + "@" + + DOMAIN_FIRST_CHARACTER_REGEX + DOMAIN_MIDDLE_REGEX + DOMAIN_LAST_CHARACTER_REGEX; + + public final String value; + + /** + * Creates a ContactEmail with a valid email address. + */ + public ContactEmail(String email) { + requireNonNull(email); + checkArgument(isValidEmail(email), MESSAGE_CONSTRAINTS); + value = email; + } + + /** + * Returns true if a given string is a valid email. + */ + public static boolean isValidEmail(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return value; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ContactEmail // instanceof handles nulls + && value.equalsIgnoreCase(((ContactEmail) other).value)); // state check + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} + diff --git a/src/main/java/seedu/ta/model/contact/ContactName.java b/src/main/java/seedu/ta/model/contact/ContactName.java new file mode 100644 index 00000000000..37f1fd2827e --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/ContactName.java @@ -0,0 +1,57 @@ +package seedu.ta.model.contact; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.util.AppUtil.checkArgument; + +/** + * Represents a Contact's name. + */ +public class ContactName { + + public static final String MESSAGE_CONSTRAINTS = "The name must:\n" + + "- be at least 2 characters long\n" + + "- consist of only alphabets, spaces and hyphens\n" + + "- start and end with alphabets"; + + /** + * The first character of the name must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "^[A-Za-z][-A-Za-z ]*[A-Za-z]$"; + + public final String fullName; + + /** + * Creates a ContactName with a vaild name. + */ + public ContactName(String name) { + requireNonNull(name); + checkArgument(isValidName(name), MESSAGE_CONSTRAINTS); + fullName = name; + } + + /** + * Returns true if a given string is a valid name. + */ + public static boolean isValidName(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return fullName; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ContactName // instanceof handles nulls + && fullName.equalsIgnoreCase(((ContactName) other).fullName)); // state check + } + + @Override + public int hashCode() { + return fullName.hashCode(); + } +} + diff --git a/src/main/java/seedu/ta/model/contact/ContactNameContainsKeywordsPredicate.java b/src/main/java/seedu/ta/model/contact/ContactNameContainsKeywordsPredicate.java new file mode 100644 index 00000000000..306a2722fec --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/ContactNameContainsKeywordsPredicate.java @@ -0,0 +1,32 @@ +package seedu.ta.model.contact; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.ta.commons.util.StringUtil; + +/** + * Tests that a {@code Contact}'s {@code ContactName} matches all of the keywords given. + */ +public class ContactNameContainsKeywordsPredicate implements Predicate { + + private final List keywords; + + public ContactNameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Contact contact) { + return keywords.stream() + .allMatch(keyword -> StringUtil.containsWordIgnoreCase(contact.getName().fullName, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof ContactNameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((ContactNameContainsKeywordsPredicate) other).keywords)); // state check + } +} + diff --git a/src/main/java/seedu/address/model/person/Phone.java b/src/main/java/seedu/ta/model/contact/ContactPhone.java similarity index 52% rename from src/main/java/seedu/address/model/person/Phone.java rename to src/main/java/seedu/ta/model/contact/ContactPhone.java index 872c76b382f..43248743579 100644 --- a/src/main/java/seedu/address/model/person/Phone.java +++ b/src/main/java/seedu/ta/model/contact/ContactPhone.java @@ -1,26 +1,24 @@ -package seedu.address.model.person; +package seedu.ta.model.contact; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.ta.commons.util.AppUtil.checkArgument; /** - * Represents a Person's phone number in the address book. - * Guarantees: immutable; is valid as declared in {@link #isValidPhone(String)} + * Represents a Contact's phone number. */ -public class Phone { - +public class ContactPhone { public static final String MESSAGE_CONSTRAINTS = - "Phone numbers should only contain numbers, and it should be at least 3 digits long"; - public static final String VALIDATION_REGEX = "\\d{3,}"; + "Phone numbers are 8 digits long, must not start with a 0 and should not have symbols."; + + public static final String VALIDATION_REGEX = "^[1-9]\\d{7}$"; + public final String value; /** - * Constructs a {@code Phone}. - * - * @param phone A valid phone number. + * Creates a ContactPhone with a valid phone number. */ - public Phone(String phone) { + public ContactPhone(String phone) { requireNonNull(phone); checkArgument(isValidPhone(phone), MESSAGE_CONSTRAINTS); value = phone; @@ -41,13 +39,13 @@ public String toString() { @Override public boolean equals(Object other) { return other == this // short circuit if same object - || (other instanceof Phone // instanceof handles nulls - && value.equals(((Phone) other).value)); // state check + || (other instanceof ContactPhone // instanceof handles nulls + && value.equals(((ContactPhone) other).value)); // state check } @Override public int hashCode() { return value.hashCode(); } - } + diff --git a/src/main/java/seedu/ta/model/contact/ContactTagsContainKeywordsPredicate.java b/src/main/java/seedu/ta/model/contact/ContactTagsContainKeywordsPredicate.java new file mode 100644 index 00000000000..2dd8f544bc7 --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/ContactTagsContainKeywordsPredicate.java @@ -0,0 +1,32 @@ +package seedu.ta.model.contact; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.ta.commons.util.StringUtil; + +/** + * Tests that a {@code Contact}'s {@code Tags} matches all of the keywords given. + */ +public class ContactTagsContainKeywordsPredicate implements Predicate { + + private final List keywords; + + public ContactTagsContainKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Contact contact) { + return keywords.stream() + .allMatch(keyword -> contact.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 ContactTagsContainKeywordsPredicate // instanceof handles nulls + && keywords.equals(((ContactTagsContainKeywordsPredicate) other).keywords)); // state check + } +} diff --git a/src/main/java/seedu/ta/model/contact/UniqueContactList.java b/src/main/java/seedu/ta/model/contact/UniqueContactList.java new file mode 100644 index 00000000000..6474ed8d91e --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/UniqueContactList.java @@ -0,0 +1,147 @@ +package seedu.ta.model.contact; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.ta.model.contact.exceptions.ContactNotFoundException; +import seedu.ta.model.contact.exceptions.DuplicateContactException; + + +/** + * A list of contacts that enforces uniqueness between its elements and does not allow nulls. + * A contact is considered unique by comparing using {@code Contact#isSameContact(Contact)}. + * + * As such, adding, deleting and updating of contacts uses Contact#isSameContact(Contact) for equality + * so as to ensure that the contact being added or updated is unique in terms of identity + * in the UniqueContactList. + * + * Supports a minimal set of list operations. + * + * @see Contact#isSameContact(Contact) + */ +public class UniqueContactList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains an equivalent contact as the given argument. + */ + public boolean contains(Contact toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameContact); + } + + /** + * Adds a contact to the list. + * The contact must not already exist in the list. + */ + public void add(Contact toAdd) { + requireNonNull(toAdd); + if (contains(toAdd)) { + throw new DuplicateContactException(); + } + internalList.add(toAdd); + FXCollections.sort(internalList, new ContactComparator()); + } + + /** + * Replaces the contact {@code target} in the list with {@code editedContact}. + * {@code target} must exist in the list. + * The contact identity of {@code editedContact} must not be the same as another + * existing contact in the list. + */ + public void setContact(Contact target, Contact editedContact) { + requireAllNonNull(target, editedContact); + + int index = internalList.indexOf(target); + + if (index == -1) { + throw new ContactNotFoundException(); + } + + if (!target.equals(editedContact) && contains(editedContact)) { + throw new DuplicateContactException(); + } + + internalList.set(index, editedContact); + } + + /** + * Removes the equivalent contact from the list. + * The contact must exist in the list. + */ + public void remove(Contact toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new ContactNotFoundException(); + } + } + + /** + * Replaces the contents of this list with {@replacement}. + * All contacts in {@replacement} are unique. + */ + public void setContacts(UniqueContactList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code contacts}. + * {@code contacts} must not contain duplicate contacts. + */ + public void setContacts(List contacts) { + requireAllNonNull(contacts); + + if (!contactsAreUnique(contacts)) { + throw new DuplicateContactException(); + } + + internalList.setAll(contacts); + } + + /** + * 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 UniqueContactList // instanceof handles nulls + && internalList.equals(((UniqueContactList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code contacts} contains only unique contacts. + */ + private boolean contactsAreUnique(List contacts) { + for (int i = 0; i < contacts.size() - 1; i++) { + for (int j = i + 1; j < contacts.size(); j++) { + if (contacts.get(i).isSameContact(contacts.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/ta/model/contact/exceptions/ContactNotFoundException.java b/src/main/java/seedu/ta/model/contact/exceptions/ContactNotFoundException.java new file mode 100644 index 00000000000..03884b738b1 --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/exceptions/ContactNotFoundException.java @@ -0,0 +1,7 @@ +package seedu.ta.model.contact.exceptions; + +/** + * Represents an error that occurs because a specified contact referenced + * by an operation is unable to be found. + */ +public class ContactNotFoundException extends RuntimeException {} diff --git a/src/main/java/seedu/ta/model/contact/exceptions/DuplicateContactException.java b/src/main/java/seedu/ta/model/contact/exceptions/DuplicateContactException.java new file mode 100644 index 00000000000..4b248f2de39 --- /dev/null +++ b/src/main/java/seedu/ta/model/contact/exceptions/DuplicateContactException.java @@ -0,0 +1,11 @@ +package seedu.ta.model.contact.exceptions; + +/** + * Represents an error that occurs when an operation results in the creation of duplicate + * contacts (contacts are considered duplicates if they have the same identity). + */ +public class DuplicateContactException extends RuntimeException { + public DuplicateContactException() { + super("Operation would result in duplicate contacts"); + } +} diff --git a/src/main/java/seedu/ta/model/entry/Entry.java b/src/main/java/seedu/ta/model/entry/Entry.java new file mode 100644 index 00000000000..e0afa623be5 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/Entry.java @@ -0,0 +1,151 @@ +package seedu.ta.model.entry; + +import static seedu.ta.commons.util.CollectionUtil.requireAllNonNull; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import seedu.ta.model.tag.Tag; + +/** + * Represents an Entry in Teaching Assistant. + */ +public class Entry { + + private final EntryName entryName; + private final EntryDate startDate; + private final EntryDate endDate; + private final Set tags = new HashSet<>(); + + /** + * Creates an Entry where every field is present and not null. + */ + public Entry(EntryName entryName, EntryDate startDate, + EntryDate endDate, Set tags) { + requireAllNonNull(entryName, startDate, endDate, tags); + this.entryName = entryName; + this.startDate = startDate; + this.endDate = endDate; + this.tags.addAll(tags); + } + + public EntryName getEntryName() { + return entryName; + } + + public EntryDate getOriginalStartDate() { + return startDate; + } + + public EntryDate getOriginalEndDate() { + return endDate; + } + + public LocalDateTime getStartDate() { + return startDate.getDate(); + } + + public LocalDateTime getEndDate() { + return endDate.getDate(); + } + + /** + * Returns an immutable tag set, which throws {@code UnsupportedOperationException} + * if modification is attempted. + */ + public Set getTags() { + return Collections.unmodifiableSet(tags); + } + + /** + * Returns true if both entries have the same fields. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof Entry)) { + return false; + } + + Entry otherEntry = (Entry) other; + return otherEntry.getEntryName().equals(getEntryName()) + && otherEntry.getOriginalStartDate().equals(getOriginalStartDate()) + && otherEntry.getOriginalEndDate().equals(getOriginalEndDate()) + && otherEntry.getTags().equals(getTags()); + } + + /** + * Returns true if both entries are the same objects. + */ + public boolean isSameEntry(Entry otherEntry) { + return otherEntry == this; + } + + /** + * Returns true if both entries have overlapping time intervals. + */ + public boolean overlapsWith(Entry otherEntry) { + if (otherEntry == null) { + return true; + } + + LocalDateTime firstStart = this.getStartDate(); + LocalDateTime firstEnd = this.getEndDate(); + LocalDateTime secondStart = otherEntry.getStartDate(); + LocalDateTime secondEnd = otherEntry.getEndDate(); + + return (firstStart.isBefore(secondEnd) && secondStart.isBefore(firstEnd)); + } + + /** + * Returns true if the current date is after end date. + */ + public boolean isOverdue() { + LocalDateTime currentDateTime = LocalDateTime.now(); + return currentDateTime.isAfter(getEndDate()); + } + + /** + * Returns true if start date is different from end date. + */ + public boolean haveDifferentDates() { + return !getStartDate().isEqual(getEndDate()); + } + + /** + * Returns the string representation of the start date and time + * formatted with the default formatter. + */ + public String startTimestamp() { + return getStartDate().format(EntryDate.DEFAULT_FORMATTER); + } + + /** + * Returns the string representation of the end date and time + * formatted with the default formatter. + */ + public String endTimestamp() { + return getEndDate().format(EntryDate.DEFAULT_FORMATTER); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getEntryName()) + .append(haveDifferentDates() ? new StringBuilder("; Start Date: ").append(startTimestamp()) : "") + .append("; End Date: ") + .append(endTimestamp()); + + Set tags = getTags(); + if (!tags.isEmpty()) { + builder.append("; Tags: "); + tags.forEach(builder::append); + } + return builder.toString(); + } +} diff --git a/src/main/java/seedu/ta/model/entry/EntryComparator.java b/src/main/java/seedu/ta/model/entry/EntryComparator.java new file mode 100644 index 00000000000..96cff7e1b22 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/EntryComparator.java @@ -0,0 +1,18 @@ +package seedu.ta.model.entry; + +import java.util.Comparator; + +/** + * Compares two entries based on their date times. + */ +public class EntryComparator implements Comparator { + + @Override + public int compare(Entry entry, Entry otherEntry) { + if (entry.getStartDate().isBefore(otherEntry.getStartDate())) { + return -1; + } else { + return 1; + } + } +} diff --git a/src/main/java/seedu/ta/model/entry/EntryDate.java b/src/main/java/seedu/ta/model/entry/EntryDate.java new file mode 100644 index 00000000000..38cd68a9bd6 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/EntryDate.java @@ -0,0 +1,79 @@ +package seedu.ta.model.entry; + +import static java.time.format.ResolverStyle.STRICT; +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.util.AppUtil.checkArgument; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +/** + * Represents a Entry's date and time. + */ +public class EntryDate { + + public static final String DATE_CONSTRAINTS = + "Dates should be valid and in the format yyyy-mm-dd HH:MM. Time must be in the 24-hour clock notation."; + + public static final DateTimeFormatter DEFAULT_FORMATTER = + DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm").withResolverStyle(STRICT); + + public final LocalDateTime value; + + /** + * Creates an EntryDate with valid date times. + */ + public EntryDate(String date) { + requireNonNull(date); + checkArgument(isValidDate(date), DATE_CONSTRAINTS); + value = LocalDateTime.parse(date, DEFAULT_FORMATTER); + } + + /** + * Returns true if a given string is a valid date. + */ + public static boolean isValidDate(String dateStr) { + try { + DEFAULT_FORMATTER.parse(dateStr); + } catch (DateTimeParseException e) { + return false; + } + return true; + } + + /** + * Returns true if this entryDate is after the specified entryDate. + */ + public boolean isAfter(EntryDate other) { + return value.isAfter(other.getDate()); + } + + /** + * Returns true if this entryDate is before the specified entryDate. + */ + public boolean isBefore(EntryDate other) { + return value.isBefore(other.getDate()); + } + + public LocalDateTime getDate() { + return value; + } + + @Override + public String toString() { + return getDate().format(DEFAULT_FORMATTER); + } + + @Override + public boolean equals(Object other) { + return other == this + || (other instanceof EntryDate + && value.equals(((EntryDate) other).value)); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/src/main/java/seedu/ta/model/entry/EntryName.java b/src/main/java/seedu/ta/model/entry/EntryName.java new file mode 100644 index 00000000000..ec6791fe8f2 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/EntryName.java @@ -0,0 +1,54 @@ +package seedu.ta.model.entry; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.util.AppUtil.checkArgument; + +/** + * Represents a Entry's name. + */ +public class EntryName { + + public static final String NAME_CONSTRAINTS = + "Names should only contain alphanumeric characters and spaces, and it should not be blank."; + + /** + * The first character of the name must not be a whitespace, + * otherwise " " (a blank string) becomes a valid input. + */ + public static final String VALIDATION_REGEX = "[\\p{Alnum}][\\p{Alnum} ]*"; + + public final String name; + + /** + * Creates an EntryName with a vaild name. + */ + public EntryName(String name) { + requireNonNull(name); + checkArgument(isValidName(name), NAME_CONSTRAINTS); + this.name = name; + } + + /** + * Returns true if a given string is a valid name. + */ + public static boolean isValidName(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public String toString() { + return name; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EntryName // instanceof handles nulls + && name.equals(((EntryName) other).name)); // state check + } + + @Override + public int hashCode() { + return name.hashCode(); + } +} diff --git a/src/main/java/seedu/ta/model/entry/EntryNameContainsKeywordsPredicate.java b/src/main/java/seedu/ta/model/entry/EntryNameContainsKeywordsPredicate.java new file mode 100644 index 00000000000..b36715704fa --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/EntryNameContainsKeywordsPredicate.java @@ -0,0 +1,31 @@ +package seedu.ta.model.entry; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.ta.commons.util.StringUtil; + +/** + * Tests that an {@code Entry}'s {@code EntryName} matches all of the keywords given. + */ +public class EntryNameContainsKeywordsPredicate implements Predicate { + + private final List keywords; + + public EntryNameContainsKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Entry entry) { + return keywords.stream() + .allMatch(keyword -> StringUtil.containsWordIgnoreCase(entry.getEntryName().name, keyword)); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof EntryNameContainsKeywordsPredicate // instanceof handles nulls + && keywords.equals(((EntryNameContainsKeywordsPredicate) other).keywords)); // state check + } +} diff --git a/src/main/java/seedu/ta/model/entry/EntryTagsContainKeywordsPredicate.java b/src/main/java/seedu/ta/model/entry/EntryTagsContainKeywordsPredicate.java new file mode 100644 index 00000000000..663a9dea608 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/EntryTagsContainKeywordsPredicate.java @@ -0,0 +1,31 @@ +package seedu.ta.model.entry; + +import java.util.List; +import java.util.function.Predicate; + +import seedu.ta.commons.util.StringUtil; + +/** + * Tests that an {@code Entry}'s {@code Tags} matches all of the keywords given. + */ +public class EntryTagsContainKeywordsPredicate implements Predicate { + private final List keywords; + + public EntryTagsContainKeywordsPredicate(List keywords) { + this.keywords = keywords; + } + + @Override + public boolean test(Entry entry) { + return keywords.stream() + .allMatch(keyword -> entry.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 EntryTagsContainKeywordsPredicate // instanceof handles nulls + && keywords.equals(((EntryTagsContainKeywordsPredicate) other).keywords)); // state check + } +} diff --git a/src/main/java/seedu/ta/model/entry/ListEntryFormatPredicate.java b/src/main/java/seedu/ta/model/entry/ListEntryFormatPredicate.java new file mode 100644 index 00000000000..3017e2ecbd7 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/ListEntryFormatPredicate.java @@ -0,0 +1,50 @@ +package seedu.ta.model.entry; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.function.Predicate; + +/** + * Tests for the correct formatting needed based on the keyword given in order to display + * the {@code Entry} list. + */ +public class ListEntryFormatPredicate implements Predicate { + + private final String keyword; + + public ListEntryFormatPredicate(String keyword) { + this.keyword = keyword; + } + + @Override + public boolean test(Entry entry) { + + LocalDate today = LocalDate.from(LocalDateTime.now()); + LocalDate yesterday = today.plusDays(-1); + LocalDate lastDay = today.plusDays(7); + + LocalDate startDate = LocalDate.from(entry.getStartDate()); + LocalDate endDate = LocalDate.from(entry.getEndDate()); + + if (keyword.equals("day")) { + return today.equals(startDate) || today.equals(endDate) + || startDate.isBefore(today) && endDate.isAfter(today); + } else if (keyword.equals("week")) { + return startDate.isAfter(yesterday) && startDate.isBefore(lastDay) + || endDate.isAfter(yesterday) && endDate.isBefore(lastDay) + || startDate.isBefore(yesterday) && endDate.isAfter(lastDay); + } else { + return true; + } + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + return (other instanceof ListEntryFormatPredicate) + && ((ListEntryFormatPredicate) other).keyword.equals(keyword); + } +} diff --git a/src/main/java/seedu/ta/model/entry/ListOccupyingEntryPredicate.java b/src/main/java/seedu/ta/model/entry/ListOccupyingEntryPredicate.java new file mode 100644 index 00000000000..04ede87aac7 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/ListOccupyingEntryPredicate.java @@ -0,0 +1,29 @@ +package seedu.ta.model.entry; + +import java.time.LocalDateTime; +import java.util.function.Predicate; + +/** + * Tests that an {@code Entry}'s start and end dates matches the target interval given. + */ +public class ListOccupyingEntryPredicate implements Predicate { + + private final LocalDateTime targetStart; + private final LocalDateTime targetEnd; + + /** + * Creates a ListOccupyingEntryPredicate with target interval. + */ + public ListOccupyingEntryPredicate(EntryDate startDateTime, EntryDate endDateTime) { + this.targetStart = startDateTime.getDate(); + this.targetEnd = endDateTime.getDate(); + } + + @Override + public boolean test(Entry entry) { + LocalDateTime entryStart = entry.getStartDate(); + LocalDateTime entryEnd = entry.getEndDate(); + return (targetStart.isBefore(entryEnd) && entryStart.isBefore(targetEnd)); + } +} + diff --git a/src/main/java/seedu/ta/model/entry/NonOverlappingEntryList.java b/src/main/java/seedu/ta/model/entry/NonOverlappingEntryList.java new file mode 100644 index 00000000000..5dfb58928e4 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/NonOverlappingEntryList.java @@ -0,0 +1,157 @@ +package seedu.ta.model.entry; + +import static java.util.Objects.requireNonNull; +import static seedu.ta.commons.util.CollectionUtil.requireAllNonNull; + +import java.util.Iterator; +import java.util.List; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import seedu.ta.model.entry.exceptions.EntryNotFoundException; +import seedu.ta.model.entry.exceptions.OverlappingEntryException; + +/** + * A list of entries that enforces that its elements do not overlap and are not nulls. + * An entry is considered overlapping by testing using {@code Entry#overlapsWith(Entry)}. + * + * Supports a minimal set of list operations. + * + * @see Entry#overlapsWith(Entry) + */ +public class NonOverlappingEntryList implements Iterable { + + private final ObservableList internalList = FXCollections.observableArrayList(); + private final ObservableList internalUnmodifiableList = + FXCollections.unmodifiableObservableList(internalList); + + /** + * Returns true if the list contains the same entry as the given argument. + */ + public boolean contains(Entry toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::isSameEntry); + } + + /** + * Returns true if the list contains an overlapping entry as the given argument. + */ + public boolean overlapsWith(Entry toCheck) { + requireNonNull(toCheck); + return internalList.stream().anyMatch(toCheck::overlapsWith); + } + + /** + * Adds an entry to the list. + * The entry must not overlap with existing entries in the list. + */ + public void add(Entry toAdd) { + requireNonNull(toAdd); + + if (overlapsWith(toAdd)) { + throw new OverlappingEntryException(); + } + + internalList.add(toAdd); + FXCollections.sort(internalList, new EntryComparator()); + } + + /** + * Replaces the entry {@code target} in the list with {@code editedEntry}. + * {@code target} must exist in the list. + * {@code editedEntry} must not overlap with existing entries in the list. + */ + public void setEntry(Entry target, Entry editedEntry) { + requireAllNonNull(target, editedEntry); + + int index = internalList.indexOf(target); + + if (index == -1) { + throw new EntryNotFoundException(); + } + + if (target.overlapsWith(editedEntry) || overlapsWith(editedEntry)) { + throw new OverlappingEntryException(); + } + + internalList.set(index, editedEntry); + } + + /** + * Removes the equivalent entry from the list. + * The entry must exist in the list. + */ + public void remove(Entry toRemove) { + requireNonNull(toRemove); + if (!internalList.remove(toRemove)) { + throw new EntryNotFoundException(); + } + } + + /** + * Replaces the contents of this list with {@replacement}. + * All entries in {@replacement} are non-overlapping. + */ + public void setEntries(NonOverlappingEntryList replacement) { + requireNonNull(replacement); + internalList.setAll(replacement.internalList); + } + + /** + * Replaces the contents of this list with {@code entries}. + * {@code entries} must not contain overlapping entries. + */ + public void setEntries(List entries) { + requireAllNonNull(entries); + if (!entriesAreNotOverlapping(entries)) { + throw new OverlappingEntryException(); + } + + internalList.setAll(entries); + } + + /** + * Removes all overdue entries in this list. + */ + public void clearOverdueEntries() { + internalList.removeIf(Entry::isOverdue); + } + + /** + * 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 NonOverlappingEntryList // instanceof handles nulls + && internalList.equals(((NonOverlappingEntryList) other).internalList)); + } + + @Override + public int hashCode() { + return internalList.hashCode(); + } + + /** + * Returns true if {@code entries} contains only non-overlapping entries. + */ + private boolean entriesAreNotOverlapping(List entries) { + for (int i = 0; i < entries.size() - 1; i++) { + for (int j = i + 1; j < entries.size(); j++) { + if (entries.get(i).overlapsWith(entries.get(j))) { + return false; + } + } + } + return true; + } +} diff --git a/src/main/java/seedu/ta/model/entry/TemporaryEntry.java b/src/main/java/seedu/ta/model/entry/TemporaryEntry.java new file mode 100644 index 00000000000..b0725556f94 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/TemporaryEntry.java @@ -0,0 +1,92 @@ +package seedu.ta.model.entry; + +import java.util.Optional; +import java.util.Set; + +import seedu.ta.model.tag.Tag; + +/** + * Represents a TemporaryEntry. + */ +public class TemporaryEntry { + + private boolean isAnyFieldEdited; + private Optional entryName; + private Optional startDate; + private Optional endDate; + private Optional> tags; + + /** + * Creates a TemporaryEntry object which is a partial Entry object + * with optional fields. + */ + public TemporaryEntry() { + isAnyFieldEdited = false; + entryName = Optional.empty(); + startDate = Optional.empty(); + endDate = Optional.empty(); + tags = Optional.empty(); + } + + public boolean isUpdated() { + return isAnyFieldEdited; + } + + public Optional getEntryName() { + return entryName; + } + + public Optional getStartDate() { + return startDate; + } + + public Optional getEndDate() { + return endDate; + } + + public Optional> getTags() { + return tags; + } + + public TemporaryEntry setEntryName(EntryName name) { + isAnyFieldEdited = true; + entryName = Optional.of(name); + return this; + } + + public TemporaryEntry setEntryStartDate(EntryDate date) { + isAnyFieldEdited = true; + startDate = Optional.of(date); + return this; + } + + public TemporaryEntry setEntryEndDate(EntryDate date) { + isAnyFieldEdited = true; + endDate = Optional.of(date); + return this; + } + + public TemporaryEntry setTags(Set tags) { + isAnyFieldEdited = true; + this.tags = Optional.of(tags); + return this; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof TemporaryEntry)) { + return false; + } + + TemporaryEntry otherEntry = (TemporaryEntry) other; + return otherEntry.entryName.equals(entryName) + && otherEntry.startDate.equals(startDate) + && otherEntry.endDate.equals(endDate) + && otherEntry.tags.equals(tags) + && otherEntry.isAnyFieldEdited == isAnyFieldEdited; + } +} diff --git a/src/main/java/seedu/ta/model/entry/exceptions/EntryNotFoundException.java b/src/main/java/seedu/ta/model/entry/exceptions/EntryNotFoundException.java new file mode 100644 index 00000000000..bcb50852fd6 --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/exceptions/EntryNotFoundException.java @@ -0,0 +1,8 @@ +package seedu.ta.model.entry.exceptions; + +/** + * Represents an error that occurs because a specified entry referenced + * by an operation is unable to be found. + */ +public class EntryNotFoundException extends RuntimeException { +} diff --git a/src/main/java/seedu/ta/model/entry/exceptions/OverlappingEntryException.java b/src/main/java/seedu/ta/model/entry/exceptions/OverlappingEntryException.java new file mode 100644 index 00000000000..1b0a59c63bc --- /dev/null +++ b/src/main/java/seedu/ta/model/entry/exceptions/OverlappingEntryException.java @@ -0,0 +1,11 @@ +package seedu.ta.model.entry.exceptions; + +/** + * Represents an error that occurs when an operation results in the creation of overlapping + * entries (entries are considered overlapping if their dates clash). + */ +public class OverlappingEntryException extends RuntimeException { + public OverlappingEntryException() { + super("Operation would result in overlapping entries"); + } +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/ta/model/tag/Tag.java similarity index 72% rename from src/main/java/seedu/address/model/tag/Tag.java rename to src/main/java/seedu/ta/model/tag/Tag.java index b0ea7e7dad7..a8ef5eb843e 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/ta/model/tag/Tag.java @@ -1,23 +1,21 @@ -package seedu.address.model.tag; +package seedu.ta.model.tag; import static java.util.Objects.requireNonNull; -import static seedu.address.commons.util.AppUtil.checkArgument; +import static seedu.ta.commons.util.AppUtil.checkArgument; /** - * Represents a Tag in the address book. - * Guarantees: immutable; name is valid as declared in {@link #isValidTagName(String)} + * Represents a Tag in Teaching Assistant. */ public class Tag { - public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; + public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric and a single word."; + public static final String VALIDATION_REGEX = "\\p{Alnum}+"; public final String tagName; /** - * Constructs a {@code Tag}. - * - * @param tagName A valid tag name. + * Creates a Tag with a valid name. */ public Tag(String tagName) { requireNonNull(tagName); @@ -26,7 +24,7 @@ public Tag(String tagName) { } /** - * Returns true if a given string is a valid tag name. + * Returns true if a given string is a valid name. */ public static boolean isValidTagName(String test) { return test.matches(VALIDATION_REGEX); @@ -50,5 +48,4 @@ public int hashCode() { public String toString() { return '[' + tagName + ']'; } - } diff --git a/src/main/java/seedu/ta/model/util/SampleDataUtil.java b/src/main/java/seedu/ta/model/util/SampleDataUtil.java new file mode 100644 index 00000000000..d65977c9def --- /dev/null +++ b/src/main/java/seedu/ta/model/util/SampleDataUtil.java @@ -0,0 +1,75 @@ +package seedu.ta.model.util; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.TeachingAssistant; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.tag.Tag; + +/** + * Contains utility methods for populating {@code TeachingAssistant} with sample data. + */ +public class SampleDataUtil { + public static Contact[] getSampleContacts() { + return new Contact[] { + new Contact(new ContactName("Alex Yeoh"), new ContactPhone("87438807"), + new ContactEmail("alexyeoh@example.com"), getTagSet("friends")), + new Contact(new ContactName("Bernice Yu"), new ContactPhone("99272758"), + new ContactEmail("berniceyu@example.com"), getTagSet("colleagues", "friends")), + new Contact(new ContactName("Charlotte Oliveiro"), new ContactPhone("93210283"), + new ContactEmail("charlotte@example.com"), getTagSet("neighbours")), + new Contact(new ContactName("David Li"), new ContactPhone("91031282"), + new ContactEmail("lidavid@example.com"), getTagSet("family")), + new Contact(new ContactName("Irfan Ibrahim"), new ContactPhone("92492021"), + new ContactEmail("irfan@example.com"), getTagSet("classmates")), + new Contact(new ContactName("Roy Balakrishnan"), new ContactPhone("92624417"), + new ContactEmail("royb@example.com"), getTagSet("colleagues")) + }; + } + + public static Entry[] getSampleEntries() { + return new Entry[] { + new Entry(new EntryName("Consultation with 2A"), new EntryDate("2021-05-04 13:00"), + new EntryDate("2021-05-04 15:00"), getTagSet("consultation")), + new Entry(new EntryName("Department meeting"), new EntryDate("2021-05-05 16:00"), + new EntryDate("2021-05-05 17:00"), getTagSet("meeting", "needprep")), + new Entry(new EntryName("CCA"), new EntryDate("2021-05-11 16:00"), + new EntryDate("2021-05-11 18:00"), getTagSet("cca")), + new Entry(new EntryName("Leadership Camp"), new EntryDate("2021-05-15 07:00"), + new EntryDate("2021-05-16 18:00"), getTagSet("event")), + new Entry(new EntryName("Sec 2 teachers meeting"), new EntryDate("2021-05-17 13:00"), + new EntryDate("2021-05-17 14:00"), getTagSet("meeting")), + new Entry(new EntryName("Consultation with 2B"), new EntryDate("2021-05-18 13:00"), + new EntryDate("2021-05-18 15:00"), getTagSet("consultation")) + }; + } + + public static ReadOnlyTeachingAssistant getSampleTeachingAssistant() { + TeachingAssistant sampleAb = new TeachingAssistant(); + for (Contact sampleContact : getSampleContacts()) { + sampleAb.addContact(sampleContact); + } + for (Entry sampleEntry : getSampleEntries()) { + sampleAb.addEntry(sampleEntry); + } + return sampleAb; + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set getTagSet(String... strings) { + return Arrays.stream(strings) + .map(Tag::new) + .collect(Collectors.toSet()); + } +} diff --git a/src/main/java/seedu/ta/storage/JsonAdaptedContact.java b/src/main/java/seedu/ta/storage/JsonAdaptedContact.java new file mode 100644 index 00000000000..12bd565c2cc --- /dev/null +++ b/src/main/java/seedu/ta/storage/JsonAdaptedContact.java @@ -0,0 +1,100 @@ +package seedu.ta.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.ta.commons.exceptions.IllegalValueException; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.tag.Tag; + +/** + * Jackson-friendly version of {@link Contact}. + */ +class JsonAdaptedContact { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Contact's %s field is missing!"; + + private final String name; + private final String phone; + private final String email; + private final List tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedContact} with the given contact details. + */ + @JsonCreator + public JsonAdaptedContact(@JsonProperty("name") String name, @JsonProperty("phone") String phone, + @JsonProperty("email") String email, + @JsonProperty("tagged") List tagged) { + this.name = name; + this.phone = phone; + this.email = email; + if (tagged != null) { + this.tagged.addAll(tagged); + } + } + + /** + * Converts a given {@code Contact} into this class for Jackson use. + */ + public JsonAdaptedContact(Contact source) { + name = source.getName().fullName; + phone = source.getPhone().value; + email = source.getEmail().value; + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + /** + * Converts this Jackson-friendly adapted contact object into the model's {@code Contact} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted contact. + */ + public Contact toModelType() throws IllegalValueException { + final List contactTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + contactTags.add(tag.toModelType()); + } + + if (name == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ContactName.class.getSimpleName())); + } + if (!ContactName.isValidName(name)) { + throw new IllegalValueException(ContactName.MESSAGE_CONSTRAINTS); + } + final ContactName modelName = new ContactName(name); + + if (phone == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ContactPhone.class.getSimpleName())); + } + if (!ContactPhone.isValidPhone(phone)) { + throw new IllegalValueException(ContactPhone.MESSAGE_CONSTRAINTS); + } + final ContactPhone modelPhone = new ContactPhone(phone); + + if (email == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + ContactEmail.class.getSimpleName())); + } + if (!ContactEmail.isValidEmail(email)) { + throw new IllegalValueException(ContactEmail.MESSAGE_CONSTRAINTS); + } + final ContactEmail modelEmail = new ContactEmail(email); + + final Set modelTags = new HashSet<>(contactTags); + return new Contact(modelName, modelPhone, modelEmail, modelTags); + } + +} diff --git a/src/main/java/seedu/ta/storage/JsonAdaptedEntry.java b/src/main/java/seedu/ta/storage/JsonAdaptedEntry.java new file mode 100644 index 00000000000..861f1b4565a --- /dev/null +++ b/src/main/java/seedu/ta/storage/JsonAdaptedEntry.java @@ -0,0 +1,101 @@ +package seedu.ta.storage; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_DATE_RANGE; + +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.JsonProperty; + +import seedu.ta.commons.exceptions.IllegalValueException; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.tag.Tag; + +public class JsonAdaptedEntry { + + public static final String MISSING_FIELD_MESSAGE_FORMAT = "Entry's %s field is missing!"; + + private final String entryName; + private final String startDate; + private final String endDate; + private final List tagged = new ArrayList<>(); + + /** + * Constructs a {@code JsonAdaptedEntry} with the given entry details. + */ + public JsonAdaptedEntry(@JsonProperty("entryName") String entryName, + @JsonProperty("startDate") String startDate, + @JsonProperty("endDate") String endDate, + @JsonProperty("tagged") List tagged) { + this.entryName = entryName; + this.startDate = startDate; + this.endDate = endDate; + if (tagged != null) { + this.tagged.addAll(tagged); + } + } + + /** + * Converts a gievn {@code Entry} into this class for Jackson use. + */ + public JsonAdaptedEntry(Entry source) { + entryName = source.getEntryName().name; + startDate = source.getOriginalStartDate().toString(); + endDate = source.getOriginalEndDate().toString(); + tagged.addAll(source.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); + } + + /** + * Convert this Jackson-friendly adapted entry object into the model's {@code Entry} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted entry. + */ + public Entry toModelType() throws IllegalValueException { + final List entryTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tagged) { + entryTags.add(tag.toModelType()); + } + + if (entryName == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + EntryName.class.getSimpleName())); + } + if (!EntryName.isValidName(entryName)) { + throw new IllegalValueException(EntryName.NAME_CONSTRAINTS); + } + final EntryName modelEntryName = new EntryName(entryName); + + if (startDate == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + EntryDate.class.getSimpleName())); + } + if (!EntryDate.isValidDate(startDate)) { + throw new IllegalValueException(EntryDate.DATE_CONSTRAINTS); + } + final EntryDate modelStartDate = new EntryDate(startDate); + + if (endDate == null) { + throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, + EntryDate.class.getSimpleName())); + } + if (!EntryDate.isValidDate(endDate)) { + throw new IllegalValueException(EntryDate.DATE_CONSTRAINTS); + } + + final EntryDate modelEndDate = new EntryDate(endDate); + + if (modelStartDate.isAfter(modelEndDate)) { + throw new IllegalValueException(MESSAGE_INVALID_DATE_RANGE); + } + + final Set modelTags = new HashSet<>(entryTags); + return new Entry(modelEntryName, modelStartDate, modelEndDate, modelTags); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedTag.java b/src/main/java/seedu/ta/storage/JsonAdaptedTag.java similarity index 89% rename from src/main/java/seedu/address/storage/JsonAdaptedTag.java rename to src/main/java/seedu/ta/storage/JsonAdaptedTag.java index 0df22bdb754..0dd58a0e654 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedTag.java +++ b/src/main/java/seedu/ta/storage/JsonAdaptedTag.java @@ -1,10 +1,10 @@ -package seedu.address.storage; +package seedu.ta.storage; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.tag.Tag; +import seedu.ta.commons.exceptions.IllegalValueException; +import seedu.ta.model.tag.Tag; /** * Jackson-friendly version of {@link Tag}. diff --git a/src/main/java/seedu/ta/storage/JsonSerializableTeachingAssistant.java b/src/main/java/seedu/ta/storage/JsonSerializableTeachingAssistant.java new file mode 100644 index 00000000000..ef3013fbc36 --- /dev/null +++ b/src/main/java/seedu/ta/storage/JsonSerializableTeachingAssistant.java @@ -0,0 +1,79 @@ +package seedu.ta.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.ta.commons.exceptions.IllegalValueException; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.TeachingAssistant; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.entry.Entry; + +/** + * An Immutable TeachingAssistant that is serializable to JSON format. + */ +@JsonRootName(value = "teachingassistant") +class JsonSerializableTeachingAssistant { + + public static final String MESSAGE_DUPLICATE_CONTACT = "Contact list contains duplicate contacts(s)."; + public static final String MESSAGE_DUPLICATE_ENTRY = "Entry List contains duplicate entry(s)"; + public static final String MESSAGE_OVERLAPPING_ENTRY = "Entry List contains entries with overlapping dates"; + + private final List contacts = new ArrayList<>(); + private final List entries = new ArrayList<>(); + + /** + * Constructs a {@code JsonSerializableTeachingAssistant} with the given contacts. + */ + @JsonCreator + public JsonSerializableTeachingAssistant(@JsonProperty("contacts") List contacts, + @JsonProperty("entries") List entries) { + this.contacts.addAll(contacts); + this.entries.addAll(entries); + } + + /** + * Converts a given {@code ReadOnlyTeachingAssistant} into this class for Jackson use. + * + * @param source future changes to this will not affect the created {@code JsonSerializableTeachingAssistant}. + */ + public JsonSerializableTeachingAssistant(ReadOnlyTeachingAssistant source) { + contacts.addAll(source.getContactList().stream().map(JsonAdaptedContact::new).collect(Collectors.toList())); + entries.addAll(source.getEntryList().stream().map(JsonAdaptedEntry::new).collect(Collectors.toList())); + } + + /** + * Converts this teaching assistant into the model's {@code TeachingAssistant} object. + * + * @throws IllegalValueException if there were any data constraints violated. + */ + public TeachingAssistant toModelType() throws IllegalValueException { + TeachingAssistant teachingAssistant = new TeachingAssistant(); + for (JsonAdaptedContact jsonAdaptedContact : contacts) { + Contact contact = jsonAdaptedContact.toModelType(); + if (teachingAssistant.hasContact(contact)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_CONTACT); + } + teachingAssistant.addContact(contact); + } + + for (JsonAdaptedEntry jsonAdaptedEntry : entries) { + Entry entry = jsonAdaptedEntry.toModelType(); + if (teachingAssistant.hasEntry(entry)) { + throw new IllegalValueException(MESSAGE_DUPLICATE_ENTRY); + } + + if (teachingAssistant.isOverlappingEntry(entry)) { + throw new IllegalValueException(MESSAGE_OVERLAPPING_ENTRY); + } + teachingAssistant.addEntry(entry); + } + return teachingAssistant; + } + +} diff --git a/src/main/java/seedu/ta/storage/JsonTeachingAssistantStorage.java b/src/main/java/seedu/ta/storage/JsonTeachingAssistantStorage.java new file mode 100644 index 00000000000..155bdc6b073 --- /dev/null +++ b/src/main/java/seedu/ta/storage/JsonTeachingAssistantStorage.java @@ -0,0 +1,80 @@ +package seedu.ta.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.ta.commons.core.LogsCenter; +import seedu.ta.commons.exceptions.DataConversionException; +import seedu.ta.commons.exceptions.IllegalValueException; +import seedu.ta.commons.util.FileUtil; +import seedu.ta.commons.util.JsonUtil; +import seedu.ta.model.ReadOnlyTeachingAssistant; + +/** + * A class to access TeachingAssistant data stored as a json file on the hard disk. + */ +public class JsonTeachingAssistantStorage implements TeachingAssistantStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonTeachingAssistantStorage.class); + + private Path filePath; + + public JsonTeachingAssistantStorage(Path filePath) { + this.filePath = filePath; + } + + public Path getTeachingAssistantFilePath() { + return filePath; + } + + @Override + public Optional readTeachingAssistant() throws DataConversionException { + return readTeachingAssistant(filePath); + } + + /** + * Similar to {@link #readTeachingAssistant()}. + * + * @param filePath location of the data. Cannot be null. + * @throws DataConversionException if the file is not in the correct format. + */ + public Optional readTeachingAssistant(Path filePath) throws DataConversionException { + requireNonNull(filePath); + + Optional jsonTeachingAssistant = JsonUtil.readJsonFile( + filePath, JsonSerializableTeachingAssistant.class); + if (!jsonTeachingAssistant.isPresent()) { + return Optional.empty(); + } + + try { + return Optional.of(jsonTeachingAssistant.get().toModelType()); + } catch (IllegalValueException ive) { + logger.info("Illegal values found in " + filePath + ": " + ive.getMessage()); + throw new DataConversionException(ive); + } + } + + @Override + public void saveTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant) throws IOException { + saveTeachingAssistant(teachingAssistant, filePath); + } + + /** + * Similar to {@link #saveTeachingAssistant(ReadOnlyTeachingAssistant)}. + * + * @param filePath location of the data. Cannot be null. + */ + public void saveTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant, Path filePath) throws IOException { + requireNonNull(teachingAssistant); + requireNonNull(filePath); + + FileUtil.createIfMissing(filePath); + JsonUtil.saveJsonFile(new JsonSerializableTeachingAssistant(teachingAssistant), filePath); + } + +} diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/ta/storage/JsonUserPrefsStorage.java similarity index 83% rename from src/main/java/seedu/address/storage/JsonUserPrefsStorage.java rename to src/main/java/seedu/ta/storage/JsonUserPrefsStorage.java index bc2bbad84aa..1cb01649b11 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/ta/storage/JsonUserPrefsStorage.java @@ -1,13 +1,13 @@ -package seedu.address.storage; +package seedu.ta.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.ta.commons.exceptions.DataConversionException; +import seedu.ta.commons.util.JsonUtil; +import seedu.ta.model.ReadOnlyUserPrefs; +import seedu.ta.model.UserPrefs; /** * A class to access UserPrefs stored in the hard disk as a json file diff --git a/src/main/java/seedu/ta/storage/Storage.java b/src/main/java/seedu/ta/storage/Storage.java new file mode 100644 index 00000000000..6a7c72b613e --- /dev/null +++ b/src/main/java/seedu/ta/storage/Storage.java @@ -0,0 +1,32 @@ +package seedu.ta.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.ta.commons.exceptions.DataConversionException; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.ReadOnlyUserPrefs; +import seedu.ta.model.UserPrefs; + +/** + * API of the Storage component + */ +public interface Storage extends TeachingAssistantStorage, UserPrefsStorage { + + @Override + Optional readUserPrefs() throws DataConversionException, IOException; + + @Override + void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException; + + @Override + Path getTeachingAssistantFilePath(); + + @Override + Optional readTeachingAssistant() throws DataConversionException, IOException; + + @Override + void saveTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant) throws IOException; + +} diff --git a/src/main/java/seedu/ta/storage/StorageManager.java b/src/main/java/seedu/ta/storage/StorageManager.java new file mode 100644 index 00000000000..bbcca91d13e --- /dev/null +++ b/src/main/java/seedu/ta/storage/StorageManager.java @@ -0,0 +1,80 @@ +package seedu.ta.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.ta.commons.core.LogsCenter; +import seedu.ta.commons.exceptions.DataConversionException; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.ReadOnlyUserPrefs; +import seedu.ta.model.UserPrefs; + +/** + * Manages storage of TeachingAssistant data in local storage. + */ +public class StorageManager implements Storage { + + private static final Logger logger = LogsCenter.getLogger(StorageManager.class); + private TeachingAssistantStorage teachingAssistantStorage; + private UserPrefsStorage userPrefsStorage; + + /** + * Creates a {@code StorageManager} with the given {@code TeachingAssistantStorage} and {@code UserPrefStorage}. + */ + public StorageManager(TeachingAssistantStorage teachingAssistantStorage, UserPrefsStorage userPrefsStorage) { + super(); + this.teachingAssistantStorage = teachingAssistantStorage; + this.userPrefsStorage = userPrefsStorage; + } + + // ================ UserPrefs methods ============================== + + @Override + public Path getUserPrefsFilePath() { + return userPrefsStorage.getUserPrefsFilePath(); + } + + @Override + public Optional readUserPrefs() throws DataConversionException, IOException { + return userPrefsStorage.readUserPrefs(); + } + + @Override + public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException { + userPrefsStorage.saveUserPrefs(userPrefs); + } + + + // ================ TeachingAssistant methods ============================== + + @Override + public Path getTeachingAssistantFilePath() { + return teachingAssistantStorage.getTeachingAssistantFilePath(); + } + + @Override + public Optional readTeachingAssistant() throws DataConversionException, IOException { + return readTeachingAssistant(teachingAssistantStorage.getTeachingAssistantFilePath()); + } + + @Override + public Optional readTeachingAssistant(Path filePath) + throws DataConversionException, IOException { + logger.fine("Attempting to read data from file: " + filePath); + return teachingAssistantStorage.readTeachingAssistant(filePath); + } + + @Override + public void saveTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant) throws IOException { + saveTeachingAssistant(teachingAssistant, teachingAssistantStorage.getTeachingAssistantFilePath()); + } + + @Override + public void saveTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant, Path filePath) throws IOException { + logger.fine("Attempting to write to data file: " + filePath); + teachingAssistantStorage.saveTeachingAssistant(teachingAssistant, filePath); + } + +} diff --git a/src/main/java/seedu/ta/storage/TeachingAssistantStorage.java b/src/main/java/seedu/ta/storage/TeachingAssistantStorage.java new file mode 100644 index 00000000000..492e53c3554 --- /dev/null +++ b/src/main/java/seedu/ta/storage/TeachingAssistantStorage.java @@ -0,0 +1,46 @@ +package seedu.ta.storage; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; + +import seedu.ta.commons.exceptions.DataConversionException; +import seedu.ta.model.ReadOnlyTeachingAssistant; + +/** + * Represents a storage for {@link seedu.ta.model.TeachingAssistant}. + */ +public interface TeachingAssistantStorage { + + /** + * Returns the file path of the data file. + */ + Path getTeachingAssistantFilePath(); + + /** + * Returns TeachingAssistant data as a {@link ReadOnlyTeachingAssistant}. + * 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 readTeachingAssistant() throws DataConversionException, IOException; + + /** + * @see #getTeachingAssistantFilePath() + */ + Optional readTeachingAssistant(Path filePath) + throws DataConversionException, IOException; + + /** + * Saves the given {@link ReadOnlyTeachingAssistant} to the storage. + * @param teachingAssistant cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant) throws IOException; + + /** + * @see #saveTeachingAssistant(ReadOnlyTeachingAssistant) + */ + void saveTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant, Path filePath) throws IOException; + +} diff --git a/src/main/java/seedu/address/storage/UserPrefsStorage.java b/src/main/java/seedu/ta/storage/UserPrefsStorage.java similarity index 71% rename from src/main/java/seedu/address/storage/UserPrefsStorage.java rename to src/main/java/seedu/ta/storage/UserPrefsStorage.java index 29eef178dbc..62e6c0278de 100644 --- a/src/main/java/seedu/address/storage/UserPrefsStorage.java +++ b/src/main/java/seedu/ta/storage/UserPrefsStorage.java @@ -1,15 +1,15 @@ -package seedu.address.storage; +package seedu.ta.storage; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.UserPrefs; +import seedu.ta.commons.exceptions.DataConversionException; +import seedu.ta.model.ReadOnlyUserPrefs; +import seedu.ta.model.UserPrefs; /** - * Represents a storage for {@link seedu.address.model.UserPrefs}. + * Represents a storage for {@link seedu.ta.model.UserPrefs}. */ public interface UserPrefsStorage { @@ -27,7 +27,7 @@ public interface UserPrefsStorage { Optional readUserPrefs() throws DataConversionException, IOException; /** - * Saves the given {@link seedu.address.model.ReadOnlyUserPrefs} to the storage. + * Saves the given {@link seedu.ta.model.ReadOnlyUserPrefs} to the storage. * @param userPrefs cannot be null. * @throws IOException if there was any problem writing to the file. */ diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/ta/ui/CommandBox.java similarity index 89% rename from src/main/java/seedu/address/ui/CommandBox.java rename to src/main/java/seedu/ta/ui/CommandBox.java index 9e75478664b..5d4c7e59e04 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/ta/ui/CommandBox.java @@ -1,12 +1,12 @@ -package seedu.address.ui; +package seedu.ta.ui; import javafx.collections.ObservableList; import javafx.fxml.FXML; 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.parser.exceptions.ParseException; +import seedu.ta.logic.commands.CommandResult; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.logic.parser.exceptions.ParseException; /** * The UI component that is responsible for receiving user command inputs. @@ -77,7 +77,7 @@ public interface CommandExecutor { /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.ta.logic.Logic#execute(String) */ CommandResult execute(String commandText) throws CommandException, ParseException; } diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/ta/ui/ContactCard.java similarity index 61% rename from src/main/java/seedu/address/ui/PersonCard.java rename to src/main/java/seedu/ta/ui/ContactCard.java index 7fc927bc5d9..2a03fa7a877 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/ta/ui/ContactCard.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import java.util.Comparator; @@ -7,14 +7,14 @@ import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; -import seedu.address.model.person.Person; +import seedu.ta.model.contact.Contact; /** - * An UI component that displays information of a {@code Person}. + * An UI component that displays information of a {@code Contact}. */ -public class PersonCard extends UiPart { +public class ContactCard extends UiPart { - private static final String FXML = "PersonListCard.fxml"; + private static final String FXML = "ContactListCard.fxml"; /** * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX. @@ -24,7 +24,7 @@ public class PersonCard extends UiPart { * @see The issue on AddressBook level 4 */ - public final Person person; + public final Contact contact; @FXML private HBox cardPane; @@ -35,24 +35,21 @@ public class PersonCard extends UiPart { @FXML private Label phone; @FXML - private Label address; - @FXML private Label email; @FXML private FlowPane tags; /** - * Creates a {@code PersonCode} with the given {@code Person} and index to display. + * Creates a {@code ContactCode} with the given {@code Contact} and index to display. */ - public PersonCard(Person person, int displayedIndex) { + public ContactCard(Contact contact, int displayedIndex) { super(FXML); - this.person = person; + this.contact = contact; id.setText(displayedIndex + ". "); - name.setText(person.getName().fullName); - phone.setText(person.getPhone().value); - address.setText(person.getAddress().value); - email.setText(person.getEmail().value); - person.getTags().stream() + name.setText(contact.getName().fullName); + phone.setText(contact.getPhone().value); + email.setText(contact.getEmail().value); + contact.getTags().stream() .sorted(Comparator.comparing(tag -> tag.tagName)) .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); } @@ -65,13 +62,13 @@ public boolean equals(Object other) { } // instanceof handles nulls - if (!(other instanceof PersonCard)) { + if (!(other instanceof ContactCard)) { return false; } // state check - PersonCard card = (PersonCard) other; + ContactCard card = (ContactCard) other; return id.getText().equals(card.id.getText()) - && person.equals(card.person); + && contact.equals(card.contact); } } diff --git a/src/main/java/seedu/ta/ui/ContactListPanel.java b/src/main/java/seedu/ta/ui/ContactListPanel.java new file mode 100644 index 00000000000..222780dc8af --- /dev/null +++ b/src/main/java/seedu/ta/ui/ContactListPanel.java @@ -0,0 +1,49 @@ +package seedu.ta.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.ta.commons.core.LogsCenter; +import seedu.ta.model.contact.Contact; + +/** + * Panel containing the list of contacts. + */ +public class ContactListPanel extends UiPart { + private static final String FXML = "ContactListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(ContactListPanel.class); + + @FXML + private ListView contactListView; + + /** + * Creates a {@code ContactListPanel} with the given {@code ObservableList}. + */ + public ContactListPanel(ObservableList contactList) { + super(FXML); + contactListView.setItems(contactList); + contactListView.setCellFactory(listView -> new ContactListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Contact} using a {@code ContactCard}. + */ + class ContactListViewCell extends ListCell { + @Override + protected void updateItem(Contact contact, boolean empty) { + super.updateItem(contact, empty); + + if (empty || contact == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new ContactCard(contact, getIndex() + 1).getRoot()); + } + } + } + +} diff --git a/src/main/java/seedu/ta/ui/EntryCard.java b/src/main/java/seedu/ta/ui/EntryCard.java new file mode 100644 index 00000000000..677d7ddda93 --- /dev/null +++ b/src/main/java/seedu/ta/ui/EntryCard.java @@ -0,0 +1,57 @@ +package seedu.ta.ui; + +import java.time.format.DateTimeFormatter; +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.ta.model.entry.Entry; + +/** + * An UI component that displays information of a {@code Entry}. + */ +public class EntryCard extends UiPart { + private static final String FXML = "EntryListCard.fxml"; + private static final DateTimeFormatter DEFAULT_FORMATTER = + DateTimeFormatter.ofPattern("E, dd MMM yyyy h:mm a"); + + public final Entry entry; + + @FXML + private HBox cardPane; + @FXML + private Label id; + @FXML + private Label entryName; + @FXML + private Label startDate; + @FXML + private Label endDate; + @FXML + private FlowPane tags; + + /** + * Creates a {@code EntryCard} to display. + */ + public EntryCard(Entry entry, int displayedIndex) { + super(FXML); + this.entry = entry; + id.setText(displayedIndex + ". "); + entryName.setText(entry.getEntryName().name); + startDate.setText("From: " + + entry.getStartDate().format(DEFAULT_FORMATTER)); + endDate.setText("To: " + + entry.getEndDate().format(DEFAULT_FORMATTER)); + + if (entry.isOverdue()) { + cardPane.setStyle("-fx-background-color: derive(#B22222, 20%)"); + } + + entry.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + } +} diff --git a/src/main/java/seedu/ta/ui/EntryListPanel.java b/src/main/java/seedu/ta/ui/EntryListPanel.java new file mode 100644 index 00000000000..6de65cf68a5 --- /dev/null +++ b/src/main/java/seedu/ta/ui/EntryListPanel.java @@ -0,0 +1,48 @@ +package seedu.ta.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.ta.commons.core.LogsCenter; +import seedu.ta.model.entry.Entry; + +/** + * Panel containing the list of entries. + */ +public class EntryListPanel extends UiPart { + private static final String FXML = "EntryListPanel.fxml"; + private final Logger logger = LogsCenter.getLogger(EntryListPanel.class); + + @FXML + private ListView entryListView; + + /** + * Creates a {@code EntryListPanel} with the given {@code ObservableList}. + */ + public EntryListPanel(ObservableList entryList) { + super(FXML); + entryListView.setItems(entryList); + entryListView.setCellFactory(listView -> new EntryListPanel.EntryListViewCell()); + } + + /** + * Custom {@code ListCell} that displays the graphics of a {@code Entry} using a {@code EntryCard}. + */ + class EntryListViewCell extends ListCell { + @Override + protected void updateItem(Entry entry, boolean empty) { + super.updateItem(entry, empty); + + if (empty || entry == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(new EntryCard(entry, getIndex() + 1).getRoot()); + } + } + } +} diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/ta/ui/HelpWindow.java similarity index 92% rename from src/main/java/seedu/address/ui/HelpWindow.java rename to src/main/java/seedu/ta/ui/HelpWindow.java index 9a665915949..25d1b0c8ed9 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/ta/ui/HelpWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import java.util.logging.Logger; @@ -8,14 +8,14 @@ import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.stage.Stage; -import seedu.address.commons.core.LogsCenter; +import seedu.ta.commons.core.LogsCenter; /** * Controller for a help page */ public class HelpWindow extends UiPart { - public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html"; + public static final String USERGUIDE_URL = "https://ay2021s2-cs2103t-w13-4.github.io/tp/UserGuide.html"; public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL; private static final Logger logger = LogsCenter.getLogger(HelpWindow.class); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/ta/ui/MainWindow.java similarity index 85% rename from src/main/java/seedu/address/ui/MainWindow.java rename to src/main/java/seedu/ta/ui/MainWindow.java index 9106c3aa6e5..ecee37ed6b5 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/ta/ui/MainWindow.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import java.util.logging.Logger; @@ -10,12 +10,12 @@ import javafx.scene.input.KeyEvent; 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.parser.exceptions.ParseException; +import seedu.ta.commons.core.GuiSettings; +import seedu.ta.commons.core.LogsCenter; +import seedu.ta.logic.Logic; +import seedu.ta.logic.commands.CommandResult; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.logic.parser.exceptions.ParseException; /** * The Main Window. Provides the basic application layout containing @@ -31,7 +31,9 @@ public class MainWindow extends UiPart { private Logic logic; // Independent Ui parts residing in this Ui container - private PersonListPanel personListPanel; + private ContactListPanel contactListPanel; + private EntryListPanel entryListPanel; + private ResultDisplay resultDisplay; private HelpWindow helpWindow; @@ -42,7 +44,10 @@ public class MainWindow extends UiPart { private MenuItem helpMenuItem; @FXML - private StackPane personListPanelPlaceholder; + private StackPane contactListPanelPlaceholder; + + @FXML + private StackPane entryListPanelPlaceholder; @FXML private StackPane resultDisplayPlaceholder; @@ -110,13 +115,16 @@ private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { * Fills up all the placeholders of this window. */ void fillInnerParts() { - personListPanel = new PersonListPanel(logic.getFilteredPersonList()); - personListPanelPlaceholder.getChildren().add(personListPanel.getRoot()); + contactListPanel = new ContactListPanel(logic.getFilteredContactList()); + contactListPanelPlaceholder.getChildren().add(contactListPanel.getRoot()); + + entryListPanel = new EntryListPanel(logic.getFilteredEntryList()); + entryListPanelPlaceholder.getChildren().add(entryListPanel.getRoot()); resultDisplay = new ResultDisplay(); resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot()); - StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getAddressBookFilePath()); + StatusBarFooter statusBarFooter = new StatusBarFooter(logic.getTeachingAssistantFilePath()); statusbarPlaceholder.getChildren().add(statusBarFooter.getRoot()); CommandBox commandBox = new CommandBox(this::executeCommand); @@ -163,14 +171,14 @@ private void handleExit() { primaryStage.hide(); } - public PersonListPanel getPersonListPanel() { - return personListPanel; + public ContactListPanel getContactListPanel() { + return contactListPanel; } /** * Executes the command and returns the result. * - * @see seedu.address.logic.Logic#execute(String) + * @see seedu.ta.logic.Logic#execute(String) */ private CommandResult executeCommand(String commandText) throws CommandException, ParseException { try { diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/ta/ui/ResultDisplay.java similarity index 95% rename from src/main/java/seedu/address/ui/ResultDisplay.java rename to src/main/java/seedu/ta/ui/ResultDisplay.java index 7d98e84eedf..72c6e82abc8 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/ta/ui/ResultDisplay.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import static java.util.Objects.requireNonNull; diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/ta/ui/StatusBarFooter.java similarity index 96% rename from src/main/java/seedu/address/ui/StatusBarFooter.java rename to src/main/java/seedu/ta/ui/StatusBarFooter.java index b577f829423..d6916c205d6 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/ta/ui/StatusBarFooter.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import java.nio.file.Path; import java.nio.file.Paths; diff --git a/src/main/java/seedu/address/ui/Ui.java b/src/main/java/seedu/ta/ui/Ui.java similarity index 86% rename from src/main/java/seedu/address/ui/Ui.java rename to src/main/java/seedu/ta/ui/Ui.java index 17aa0b494fe..6f7f5d49d6f 100644 --- a/src/main/java/seedu/address/ui/Ui.java +++ b/src/main/java/seedu/ta/ui/Ui.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import javafx.stage.Stage; diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/ta/ui/UiManager.java similarity index 94% rename from src/main/java/seedu/address/ui/UiManager.java rename to src/main/java/seedu/ta/ui/UiManager.java index 882027e4537..87ddd15dd37 100644 --- a/src/main/java/seedu/address/ui/UiManager.java +++ b/src/main/java/seedu/ta/ui/UiManager.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import java.util.logging.Logger; @@ -7,10 +7,10 @@ import javafx.scene.control.Alert.AlertType; import javafx.scene.image.Image; import javafx.stage.Stage; -import seedu.address.MainApp; -import seedu.address.commons.core.LogsCenter; -import seedu.address.commons.util.StringUtil; -import seedu.address.logic.Logic; +import seedu.ta.MainApp; +import seedu.ta.commons.core.LogsCenter; +import seedu.ta.commons.util.StringUtil; +import seedu.ta.logic.Logic; /** * The manager of the UI component. diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/ta/ui/UiPart.java similarity index 97% rename from src/main/java/seedu/address/ui/UiPart.java rename to src/main/java/seedu/ta/ui/UiPart.java index fc820e01a9c..4339b991fc5 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/ta/ui/UiPart.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import static java.util.Objects.requireNonNull; @@ -6,7 +6,7 @@ import java.net.URL; import javafx.fxml.FXMLLoader; -import seedu.address.MainApp; +import seedu.ta.MainApp; /** * Represents a distinct part of the UI. e.g. Windows, dialogs, panels, status bars, etc. diff --git a/src/main/resources/view/ContactListCard.fxml b/src/main/resources/view/ContactListCard.fxml new file mode 100644 index 00000000000..aee24972015 --- /dev/null +++ b/src/main/resources/view/ContactListCard.fxml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/ContactListPanel.fxml b/src/main/resources/view/ContactListPanel.fxml new file mode 100644 index 00000000000..f0cfd05697f --- /dev/null +++ b/src/main/resources/view/ContactListPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 36e6b001cd8..dc2928aab88 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -100,20 +100,20 @@ } .list-cell:filled:even { + -fx-border-color: #383838; + -fx-border-width: 5; -fx-background-color: #3c3e3f; } .list-cell:filled:odd { + -fx-border-color: #383838; + -fx-border-width: 5; -fx-background-color: #515658; } -.list-cell:filled:selected { - -fx-background-color: #424d5f; -} - .list-cell:filled:selected #cardPane { -fx-border-color: #3e7b91; - -fx-border-width: 1; + -fx-border-width: 2; } .list-cell .label { @@ -328,7 +328,7 @@ -fx-text-fill: white; } -#filterField, #personListPanel, #personWebpage { +#filterField, #contactListPanel, #contactWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); } diff --git a/src/main/resources/view/EntryListCard.fxml b/src/main/resources/view/EntryListCard.fxml new file mode 100644 index 00000000000..1567eda158d --- /dev/null +++ b/src/main/resources/view/EntryListCard.fxml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/EntryListPanel.fxml similarity index 77% rename from src/main/resources/view/PersonListPanel.fxml rename to src/main/resources/view/EntryListPanel.fxml index 8836d323cc5..fb9bcb33e9c 100644 --- a/src/main/resources/view/PersonListPanel.fxml +++ b/src/main/resources/view/EntryListPanel.fxml @@ -4,5 +4,5 @@ - + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index a431648f6c0..e52d56c6942 100644 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -6,55 +6,73 @@ - + + + - - - - - - - - - + title="Teaching Assistant" minWidth="700" minHeight="600" onCloseRequest="#handleExit"> + + + + + + + + + - - - - - - - - - + + + + + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - + + + + + + + + + + - - - - + + + + diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml deleted file mode 100644 index f08ea32ad55..00000000000 --- a/src/main/resources/view/PersonListCard.fxml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json deleted file mode 100644 index ccd21f7d1a9..00000000000 --- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Person with invalid name field: Ha!ns Mu@ster", - "phone": "9482424", - "email": "hans@example.com", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json deleted file mode 100644 index ad3f135ae42..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "persons": [ { - "name": "Hans Muster", - "phone": "9482424", - "email": "invalid@email!3e", - "address": "4th street" - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json deleted file mode 100644 index f10eddee12e..00000000000 --- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()", - "persons" : [ { - "name" : "Alice Pauline", - "phone" : "94351253", - "email" : "alice@example.com", - "address" : "123, Jurong West Ave 6, #08-111", - "tagged" : [ "friends" ] - }, { - "name" : "Benson Meier", - "phone" : "98765432", - "email" : "johnd@example.com", - "address" : "311, Clementi Ave 2, #02-25", - "tagged" : [ "owesMoney", "friends" ] - }, { - "name" : "Carl Kurz", - "phone" : "95352563", - "email" : "heinz@example.com", - "address" : "wall street", - "tagged" : [ ] - }, { - "name" : "Daniel Meier", - "phone" : "87652533", - "email" : "cornelia@example.com", - "address" : "10th street", - "tagged" : [ "friends" ] - }, { - "name" : "Elle Meyer", - "phone" : "9482224", - "email" : "werner@example.com", - "address" : "michegan ave", - "tagged" : [ ] - }, { - "name" : "Fiona Kunz", - "phone" : "9482427", - "email" : "lydia@example.com", - "address" : "little tokyo", - "tagged" : [ ] - }, { - "name" : "George Best", - "phone" : "9482442", - "email" : "anna@example.com", - "address" : "4th street", - "tagged" : [ ] - } ] -} diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableTeachingAssistantTest/duplicateContactTeachingAssistant.json similarity index 56% rename from src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json rename to src/test/data/JsonSerializableTeachingAssistantTest/duplicateContactTeachingAssistant.json index 48831cc7674..bf431233796 100644 --- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json +++ b/src/test/data/JsonSerializableTeachingAssistantTest/duplicateContactTeachingAssistant.json @@ -1,14 +1,14 @@ { - "persons": [ { + "contacts": [ { "name": "Alice Pauline", "phone": "94351253", "email": "alice@example.com", - "address": "123, Jurong West Ave 6, #08-111", "tagged": [ "friends" ] }, { "name": "Alice Pauline", "phone": "94351253", - "email": "pauline@example.com", - "address": "4th street" - } ] + "email": "alice@example.com", + "tagged": [ "friends" ] + } ], + "entries" : [ ] } diff --git a/src/test/data/JsonSerializableTeachingAssistantTest/invalidContactTeachingAssistant.json b/src/test/data/JsonSerializableTeachingAssistantTest/invalidContactTeachingAssistant.json new file mode 100644 index 00000000000..a5db0a428b3 --- /dev/null +++ b/src/test/data/JsonSerializableTeachingAssistantTest/invalidContactTeachingAssistant.json @@ -0,0 +1,8 @@ +{ + "contacts": [ { + "name": "Hans Muster", + "phone": "9482424", + "email": "invalid@email!3e" + } ], + "entries" : [ ] +} diff --git a/src/test/data/JsonSerializableTeachingAssistantTest/invalidEntryTeachingAssistant.json b/src/test/data/JsonSerializableTeachingAssistantTest/invalidEntryTeachingAssistant.json new file mode 100644 index 00000000000..7fb8d0a4a11 --- /dev/null +++ b/src/test/data/JsonSerializableTeachingAssistantTest/invalidEntryTeachingAssistant.json @@ -0,0 +1,9 @@ +{ + "contacts": [ ], + "entries" : [ { + "entryName" : "Go do@@ something important", + "startDate" : "2022-02-04 09:00", + "endDate" : "2022-02-04 09:30", + "tagged" : [ "Important", "Stuff" ] + } ] +} diff --git a/src/test/data/JsonSerializableTeachingAssistantTest/overlappingEntryTeachingAssistant.json b/src/test/data/JsonSerializableTeachingAssistantTest/overlappingEntryTeachingAssistant.json new file mode 100644 index 00000000000..f7196f4d315 --- /dev/null +++ b/src/test/data/JsonSerializableTeachingAssistantTest/overlappingEntryTeachingAssistant.json @@ -0,0 +1,69 @@ +{ + "contacts" : [ { + "name" : "Ava Pauline", + "phone" : "94351253", + "email" : "alice@example.com", + "tagged" : [ "friends" ] + }, { + "name" : "Ben Meier", + "phone" : "98765432", + "email" : "johnd@example.com", + "tagged" : [ "owesMoney", "friends" ] + }, { + "name" : "Claire Kurz", + "phone" : "95352563", + "email" : "heinz@example.com", + "tagged" : [ "colleagues" ] + }, { + "name" : "David Meier", + "phone" : "87652533", + "email" : "cornelia@example.com", + "tagged" : [ "friends" ] + }, { + "name" : "Elle Meyer", + "phone" : "94822244", + "email" : "werner@example.com", + "tagged" : [ ] + }, { + "name" : "Finn Kunz", + "phone" : "94824277", + "email" : "lydia@example.com", + "tagged" : [ ] + }, { + "name" : "Gabriel Best", + "phone" : "94824422", + "email" : "anna@example.com", + "tagged" : [ ] + } ], + "entries" : [ { + "entryName" : "Consultation", + "startDate" : "2022-02-01 13:00", + "endDate" : "2022-02-01 14:30", + "tagged" : [ "History" ] + }, { + "entryName" : "Class Meeting", + "startDate" : "2022-02-01 15:00", + "endDate" : "2022-02-01 16:30", + "tagged" : [ "21S07" ] + }, { + "entryName" : "Short Quiz", + "startDate" : "2022-02-02 14:30", + "endDate" : "2022-02-02 15:15", + "tagged" : [ "Geography", "21A01" ] + }, { + "entryName" : "Extra Class", + "startDate" : "2022-02-02 17:00", + "endDate" : "2022-02-02 18:30", + "tagged" : [ "Math" ] + }, { + "entryName" : "Go do something important", + "startDate" : "2022-02-04 09:00", + "endDate" : "2022-02-04 09:30", + "tagged" : [ "Important", "Stuff" ] + }, { + "entryName" : "This will overlap with the entry above", + "startDate" : "2022-02-04 09:00", + "endDate" : "2022-02-04 09:30", + "tagged" : [ "Important", "Stuff" ] + } ] +} diff --git a/src/test/data/JsonSerializableTeachingAssistantTest/typicalTeachingAssistant.json b/src/test/data/JsonSerializableTeachingAssistantTest/typicalTeachingAssistant.json new file mode 100644 index 00000000000..d94099117c4 --- /dev/null +++ b/src/test/data/JsonSerializableTeachingAssistantTest/typicalTeachingAssistant.json @@ -0,0 +1,65 @@ +{ + "_comment": "Teaching Assistant save file which contains the same values as in TypicalTeachingAssistant#get()", + "contacts" : [ { + "name" : "Ava Pauline", + "phone" : "94351253", + "email" : "alice@example.com", + "tagged" : ["friends"] + }, { + "name" : "Ben Meier", + "phone" : "98765432", + "email" : "johnd@example.com", + "tagged" : ["owesMoney", "friends"] + }, { + "name" : "Claire Kurz", + "phone" : "95352563", + "email" : "heinz@example.com", + "tagged" : ["colleagues"] + }, { + "name" : "David Meier", + "phone" : "87652533", + "email" : "cornelia@example.com", + "tagged" : ["friends"] + }, { + "name" : "Ellie Meyer", + "phone" : "94822244", + "email" : "werner@example.com", + "tagged" : [] + }, { + "name" : "Finn Kunz", + "phone" : "94824277", + "email" : "lydia@example.com", + "tagged" : [] + }, { + "name" : "Gabriel Best", + "phone" : "94824422", + "email" : "anna@example.com", + "tagged" : [] + } ], + "entries" : [ { + "entryName" : "Consultation", + "startDate" : "2022-02-01 13:00", + "endDate" : "2022-02-01 14:30", + "tagged" : ["History"] + }, { + "entryName" : "Class Meeting", + "startDate" : "2022-02-01 15:00", + "endDate" : "2022-02-01 16:30", + "tagged" : ["21S07"] + },{ + "entryName" : "Extra Class", + "startDate" : "2022-02-02 17:00", + "endDate" : "2022-02-02 18:30", + "tagged" : ["Math"] + }, { + "entryName" : "Short Quiz", + "startDate" : "2022-02-02 14:30", + "endDate" : "2022-02-02 15:15", + "tagged" : ["Geography", "21A01"] + }, { + "entryName" : "Go do something important", + "startDate" : "2022-02-04 09:00", + "endDate" : "2022-02-04 09:30", + "tagged" : ["Important", "Stuff"] + } ] +} diff --git a/src/test/data/JsonTeachingAssistantStorageTest/invalidAndValidEntryTeachingAssistant.json b/src/test/data/JsonTeachingAssistantStorageTest/invalidAndValidEntryTeachingAssistant.json new file mode 100644 index 00000000000..62afa3a573d --- /dev/null +++ b/src/test/data/JsonTeachingAssistantStorageTest/invalidAndValidEntryTeachingAssistant.json @@ -0,0 +1,14 @@ +{ + "contacts": [ ], + "entries" : [ { + "entryName" : "Valid Name", + "startDate" : "2021-05-22 15:00", + "endDate" : "2021-05-22 15:30", + "tagged" : [ "Fiona", "Benson", "Biology" ] + }, { + "entryName" : "Inv@@lid Name ))))))", + "startDate" : "2021-05-22 16:00", + "endDate" : "2021-05-22 17:59", + "tagged" : [ "Math" ] + } ] +} diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonTeachingAssistantStorageTest/invalidAndValidTeachingAssistant.json similarity index 60% rename from src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json rename to src/test/data/JsonTeachingAssistantStorageTest/invalidAndValidTeachingAssistant.json index 6a4d2b7181c..27ee9efdb4d 100644 --- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json +++ b/src/test/data/JsonTeachingAssistantStorageTest/invalidAndValidTeachingAssistant.json @@ -1,13 +1,14 @@ { - "persons": [ { - "name": "Valid Person", + "contacts": [ { + "name": "Valid Contact", "phone": "9482424", "email": "hans@example.com", "address": "4th street" }, { - "name": "Person With Invalid Phone Field", + "name": "Contact With Invalid Phone Field", "phone": "948asdf2424", "email": "hans@example.com", "address": "4th street" - } ] + } ], + "entries" : [ ] } diff --git a/src/test/data/JsonTeachingAssistantStorageTest/invalidContactTeachingAssistant.json b/src/test/data/JsonTeachingAssistantStorageTest/invalidContactTeachingAssistant.json new file mode 100644 index 00000000000..143390781b5 --- /dev/null +++ b/src/test/data/JsonTeachingAssistantStorageTest/invalidContactTeachingAssistant.json @@ -0,0 +1,8 @@ +{ + "contacts": [ { + "name": "Contact with invalid name field: Ha!ns Mu@ster", + "phone": "9482424", + "email": "hans@example.com", + } ], + "entries" : [ ] +} diff --git a/src/test/data/JsonTeachingAssistantStorageTest/invalidEntryTeachingAssistant.json b/src/test/data/JsonTeachingAssistantStorageTest/invalidEntryTeachingAssistant.json new file mode 100644 index 00000000000..b6ea095af9e --- /dev/null +++ b/src/test/data/JsonTeachingAssistantStorageTest/invalidEntryTeachingAssistant.json @@ -0,0 +1,9 @@ +{ + "contacts": [ ], + "entries" : [ { + "entryName" : "Invalid entry name field !@#$#$%", + "startDate" : "2021-05-22 15:00", + "endDate" : "2021-05-22 14:59", + "tagged" : [ "Fiona", "Benson", "Biology" ] + } ] +} diff --git a/src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json b/src/test/data/JsonTeachingAssistantStorageTest/notJsonFormatTeachingAssistant.json similarity index 100% rename from src/test/data/JsonAddressBookStorageTest/notJsonFormatAddressBook.json rename to src/test/data/JsonTeachingAssistantStorageTest/notJsonFormatTeachingAssistant.json diff --git a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json index 1037548a9cd..29bab5aca40 100644 --- a/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/ExtraValuesUserPref.json @@ -9,5 +9,5 @@ "z" : 99 } }, - "addressBookFilePath" : "addressbook.json" + "teachingAssistantFilePath" : "teachingassistant.json" } diff --git a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json index b819bed900a..32257795f08 100644 --- a/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json +++ b/src/test/data/JsonUserPrefsStorageTest/TypicalUserPref.json @@ -7,5 +7,5 @@ "y" : 100 } }, - "addressBookFilePath" : "addressbook.json" + "teachingAssistantFilePath" : "teachingassistant.json" } diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java deleted file mode 100644 index cb8714bb055..00000000000 --- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; - -/** - * Contains integration tests (interaction with the Model) for {@code AddCommand}. - */ -public class AddCommandIntegrationTest { - - private Model model; - - @BeforeEach - public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - } - - @Test - public void execute_newPerson_success() { - Person validPerson = new PersonBuilder().build(); - - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.addPerson(validPerson); - - assertCommandSuccess(new AddCommand(validPerson), model, - String.format(AddCommand.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); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java deleted file mode 100644 index 5865713d5dd..00000000000 --- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package seedu.address.logic.commands; - -import static java.util.Objects.requireNonNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.function.Predicate; - -import org.junit.jupiter.api.Test; - -import javafx.collections.ObservableList; -import seedu.address.commons.core.GuiSettings; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.ReadOnlyUserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandTest { - - @Test - public void constructor_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new AddCommand(null)); - } - - @Test - public void execute_personAcceptedByModel_addSuccessful() throws Exception { - ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded(); - Person validPerson = new PersonBuilder().build(); - - CommandResult commandResult = new AddCommand(validPerson).execute(modelStub); - - assertEquals(String.format(AddCommand.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); - ModelStub modelStub = new ModelStubWithPerson(validPerson); - - assertThrows(CommandException.class, AddCommand.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); - - // same object -> returns true - assertTrue(addAliceCommand.equals(addAliceCommand)); - - // same values -> returns true - AddCommand addAliceCommandCopy = new AddCommand(alice); - assertTrue(addAliceCommand.equals(addAliceCommandCopy)); - - // different types -> returns false - assertFalse(addAliceCommand.equals(1)); - - // null -> returns false - assertFalse(addAliceCommand.equals(null)); - - // different person -> returns false - assertFalse(addAliceCommand.equals(addBobCommand)); - } - - /** - * A default model stub that have all of the methods failing. - */ - private class ModelStub implements Model { - @Override - public void setUserPrefs(ReadOnlyUserPrefs userPrefs) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyUserPrefs getUserPrefs() { - throw new AssertionError("This method should not be called."); - } - - @Override - public GuiSettings getGuiSettings() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setGuiSettings(GuiSettings guiSettings) { - throw new AssertionError("This method should not be called."); - } - - @Override - public Path getAddressBookFilePath() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBookFilePath(Path addressBookFilePath) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void addPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setAddressBook(ReadOnlyAddressBook newData) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - throw new AssertionError("This method should not be called."); - } - - @Override - public boolean hasPerson(Person person) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void deletePerson(Person target) { - throw new AssertionError("This method should not be called."); - } - - @Override - public void setPerson(Person target, Person editedPerson) { - throw new AssertionError("This method should not be called."); - } - - @Override - public ObservableList getFilteredPersonList() { - throw new AssertionError("This method should not be called."); - } - - @Override - public void updateFilteredPersonList(Predicate predicate) { - throw new AssertionError("This method should not be called."); - } - } - - /** - * A Model stub that contains a single person. - */ - private class ModelStubWithPerson extends ModelStub { - private final Person person; - - ModelStubWithPerson(Person person) { - requireNonNull(person); - this.person = person; - } - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return this.person.isSamePerson(person); - } - } - - /** - * A Model stub that always accept the person being added. - */ - private class ModelStubAcceptingPersonAdded extends ModelStub { - final ArrayList personsAdded = new ArrayList<>(); - - @Override - public boolean hasPerson(Person person) { - requireNonNull(person); - return personsAdded.stream().anyMatch(person::isSamePerson); - } - - @Override - public void addPerson(Person person) { - requireNonNull(person); - personsAdded.add(person); - } - - @Override - public ReadOnlyAddressBook getAddressBook() { - return new AddressBook(); - } - } - -} 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/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java deleted file mode 100644 index 643a1d08069..00000000000 --- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java +++ /dev/null @@ -1,128 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.testutil.Assert.assertThrows; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.commons.core.index.Index; -import seedu.address.logic.commands.exceptions.CommandException; -import seedu.address.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -/** - * Contains helper methods for testing commands. - */ -public class CommandTestUtil { - - public static final String VALID_NAME_AMY = "Amy Bee"; - public static final String VALID_NAME_BOB = "Bob Choo"; - public static final String VALID_PHONE_AMY = "11111111"; - public static final String VALID_PHONE_BOB = "22222222"; - public static final String VALID_EMAIL_AMY = "amy@example.com"; - public static final String VALID_EMAIL_BOB = "bob@example.com"; - public static final String VALID_ADDRESS_AMY = "Block 312, Amy Street 1"; - public static final String VALID_ADDRESS_BOB = "Block 123, Bobby Street 3"; - public static final String VALID_TAG_HUSBAND = "husband"; - public static final String VALID_TAG_FRIEND = "friend"; - - public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; - public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; - public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; - public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB; - public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY; - public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB; - public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY; - public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB; - public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; - public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; - - public static final String 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 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; - - static { - DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_FRIEND).build(); - DESC_BOB = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - } - - /** - * 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) { - try { - CommandResult result = command.execute(actualModel); - assertEquals(expectedCommandResult, result); - assertEquals(expectedModel, actualModel); - } catch (CommandException ce) { - throw new AssertionError("Execution of command should not fail.", ce); - } - } - - /** - * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} - * that takes a string {@code expectedMessage}. - */ - public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, - Model expectedModel) { - CommandResult expectedCommandResult = new CommandResult(expectedMessage); - assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); - } - - /** - * Executes the given {@code command}, confirms that
- * - a {@code CommandException} is thrown
- * - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged - */ - public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { - // we are unable to defensively copy the model for comparison later, so we can - // only do so by copying its components. - AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook()); - List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList()); - - assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); - 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. - */ - public static void showPersonAtIndex(Model model, Index targetIndex) { - assertTrue(targetIndex.getZeroBased() < model.getFilteredPersonList().size()); - - Person person = model.getFilteredPersonList().get(targetIndex.getZeroBased()); - final String[] splitName = person.getName().fullName.split("\\s+"); - model.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); - - assertEquals(1, model.getFilteredPersonList().size()); - } - -} diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java deleted file mode 100644 index 45a8c910ba1..00000000000 --- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.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 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.UserPrefs; -import seedu.address.model.person.Person; - -/** - * Contains integration tests (interaction with the Model) and unit tests for - * {@code DeleteCommand}. - */ -public class DeleteCommandTest { - - private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - - @Test - public void execute_validIndexUnfilteredList_success() { - Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON); - - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - - ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidIndexUnfilteredList_throwsCommandException() { - Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1); - DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex); - - assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - 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); - - String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, personToDelete); - - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - expectedModel.deletePerson(personToDelete); - showNoPerson(expectedModel); - - assertCommandSuccess(deleteCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_invalidIndexFilteredList_throwsCommandException() { - 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()); - - DeleteCommand deleteCommand = new DeleteCommand(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); - - // same object -> returns true - assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); - - // same values -> returns true - DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON); - assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); - - // different types -> returns false - assertFalse(deleteFirstCommand.equals(1)); - - // null -> returns false - assertFalse(deleteFirstCommand.equals(null)); - - // different person -> returns false - assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); - } - - /** - * Updates {@code model}'s filtered list to show no one. - */ - private void showNoPerson(Model model) { - model.updateFilteredPersonList(p -> false); - - assertTrue(model.getFilteredPersonList().isEmpty()); - } -} diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java deleted file mode 100644 index 214c6c2507b..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java +++ /dev/null @@ -1,173 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import 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.model.AddressBook; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.testutil.EditPersonDescriptorBuilder; -import seedu.address.testutil.PersonBuilder; - -/** - * Contains integration tests (interaction with the Model) and unit tests for EditCommand. - */ -public class EditCommandTest { - - private Model model = new ModelManager(getTypicalAddressBook(), 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); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_someFieldsSpecifiedUnfilteredList_success() { - Index indexLastPerson = Index.fromOneBased(model.getFilteredPersonList().size()); - Person lastPerson = model.getFilteredPersonList().get(indexLastPerson.getZeroBased()); - - PersonBuilder personInList = new PersonBuilder(lastPerson); - Person editedPerson = personInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withTags(VALID_TAG_HUSBAND).build(); - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB) - .withPhone(VALID_PHONE_BOB).withTags(VALID_TAG_HUSBAND).build(); - EditCommand editCommand = new EditCommand(indexLastPerson, descriptor); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(lastPerson, editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_noFieldSpecifiedUnfilteredList_success() { - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, new EditPersonDescriptor()); - Person editedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_filteredList_success() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - Person personInFilteredList = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - Person editedPerson = new PersonBuilder(personInFilteredList).withName(VALID_NAME_BOB).build(); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - - String expectedMessage = String.format(EditCommand.MESSAGE_EDIT_PERSON_SUCCESS, editedPerson); - - Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs()); - expectedModel.setPerson(model.getFilteredPersonList().get(0), editedPerson); - - assertCommandSuccess(editCommand, model, expectedMessage, expectedModel); - } - - @Test - public void execute_duplicatePersonUnfilteredList_failure() { - Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()); - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(firstPerson).build(); - EditCommand editCommand = new EditCommand(INDEX_SECOND_PERSON, descriptor); - - assertCommandFailure(editCommand, model, EditCommand.MESSAGE_DUPLICATE_PERSON); - } - - @Test - public void execute_duplicatePersonFilteredList_failure() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - - // edit person in filtered list into a duplicate in address book - Person personInList = model.getAddressBook().getPersonList().get(INDEX_SECOND_PERSON.getZeroBased()); - EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, - new EditPersonDescriptorBuilder(personInList).build()); - - assertCommandFailure(editCommand, model, EditCommand.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); - - assertCommandFailure(editCommand, 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()); - - EditCommand editCommand = new EditCommand(outOfBoundIndex, - new EditPersonDescriptorBuilder().withName(VALID_NAME_BOB).build()); - - assertCommandFailure(editCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - @Test - public void equals() { - final EditCommand standardCommand = new EditCommand(INDEX_FIRST_PERSON, DESC_AMY); - - // same values -> returns true - EditPersonDescriptor copyDescriptor = new EditPersonDescriptor(DESC_AMY); - EditCommand commandWithSameValues = new EditCommand(INDEX_FIRST_PERSON, 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 ClearCommand())); - - // different index -> returns false - assertFalse(standardCommand.equals(new EditCommand(INDEX_SECOND_PERSON, DESC_AMY))); - - // different descriptor -> returns false - assertFalse(standardCommand.equals(new EditCommand(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/EditPersonDescriptorTest.java deleted file mode 100644 index e0288792e72..00000000000 --- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package seedu.address.logic.commands; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.testutil.EditPersonDescriptorBuilder; - -public class EditPersonDescriptorTest { - - @Test - public void equals() { - // same values -> returns true - EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY); - assertTrue(DESC_AMY.equals(descriptorWithSameValues)); - - // same object -> returns true - assertTrue(DESC_AMY.equals(DESC_AMY)); - - // null -> returns false - assertFalse(DESC_AMY.equals(null)); - - // different types -> returns false - assertFalse(DESC_AMY.equals(5)); - - // different values -> returns false - assertFalse(DESC_AMY.equals(DESC_BOB)); - - // different name -> returns false - EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different phone -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different email -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different address -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - - // different tags -> returns false - editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(DESC_AMY.equals(editedAmy)); - } -} 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/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java deleted file mode 100644 index 435ff1f7275..00000000000 --- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package seedu.address.logic.commands; - -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.UserPrefs; - -/** - * Contains integration tests (interaction with the Model) and unit tests for ListCommand. - */ -public class ListCommandTest { - - private Model model; - private Model expectedModel; - - @BeforeEach - public void setUp() { - model = new ModelManager(getTypicalAddressBook(), new UserPrefs()); - expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); - } - - @Test - public void execute_listIsNotFiltered_showsSameList() { - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); - } - - @Test - public void execute_listIsFiltered_showsEverything() { - showPersonAtIndex(model, INDEX_FIRST_PERSON); - assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel); - } -} diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java deleted file mode 100644 index 5cf487d7ebb..00000000000 --- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; -import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalPersons.AMY; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.testutil.PersonBuilder; - -public class AddCommandParserTest { - private AddCommandParser parser = new AddCommandParser(); - - @Test - public void parse_allFieldsPresent_success() { - Person expectedPerson = new PersonBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); - - // whitespace only preamble - assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_FRIEND, new AddCommand(expectedPerson)); - - // multiple 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)); - - // 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)); - - // 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)); - - // 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)); - - // 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)); - } - - @Test - public void parse_optionalFieldsMissing_success() { - // zero tags - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); - assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY, - new AddCommand(expectedPerson)); - } - - @Test - public void parse_compulsoryFieldMissing_failure() { - String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE); - - // missing name prefix - assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing phone prefix - assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing email prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB, - expectedMessage); - - // missing address prefix - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB, - expectedMessage); - - // all prefixes missing - assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB, - expectedMessage); - } - - @Test - public void parse_invalidValue_failure() { - // invalid name - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS); - - // invalid phone - assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS); - - // invalid email - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS); - - // invalid address - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC - + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Address.MESSAGE_CONSTRAINTS); - - // invalid tag - assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB - + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS); - - // two invalid values, only first invalid value reported - assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + INVALID_ADDRESS_DESC, - Name.MESSAGE_CONSTRAINTS); - - // non-empty preamble - assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB - + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, - String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE)); - } -} 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/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java deleted file mode 100644 index 27eaec84450..00000000000 --- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java +++ /dev/null @@ -1,32 +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 static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.commands.DeleteCommand; - -/** - * As we are only doing white-box testing, our test cases do not cover path variations - * outside of the DeleteCommand code. For example, inputs "1" and "1 abc" take the - * same path through the DeleteCommand, 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 DeleteCommandParserTest { - - private DeleteCommandParser parser = new DeleteCommandParser(); - - @Test - public void parse_validArgs_returnsDeleteCommand() { - assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON)); - } - - @Test - public void parse_invalidArgs_throwsParseException() { - assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE)); - } -} diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java deleted file mode 100644 index 2ff31522486..00000000000 --- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java +++ /dev/null @@ -1,211 +0,0 @@ -package seedu.address.logic.parser; - -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; -import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure; -import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON; -import static seedu.address.testutil.TypicalIndexes.INDEX_THIRD_PERSON; - -import 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.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 { - - private static final String TAG_EMPTY = " " + PREFIX_TAG; - - private static final String MESSAGE_INVALID_FORMAT = - String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE); - - private EditCommandParser parser = new EditCommandParser(); - - @Test - public void parse_missingParts_failure() { - // no index specified - assertParseFailure(parser, VALID_NAME_AMY, MESSAGE_INVALID_FORMAT); - - // no field specified - assertParseFailure(parser, "1", EditCommand.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_AMY, MESSAGE_INVALID_FORMAT); - - // zero index - assertParseFailure(parser, "0" + NAME_DESC_AMY, MESSAGE_INVALID_FORMAT); - - // invalid arguments being parsed as preamble - assertParseFailure(parser, "1 some random 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_NAME_DESC, Name.MESSAGE_CONSTRAINTS); // invalid name - assertParseFailure(parser, "1" + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); // invalid phone - assertParseFailure(parser, "1" + INVALID_EMAIL_DESC, Email.MESSAGE_CONSTRAINTS); // invalid email - assertParseFailure(parser, "1" + INVALID_ADDRESS_DESC, Address.MESSAGE_CONSTRAINTS); // invalid address - assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag - - // invalid phone followed by valid email - assertParseFailure(parser, "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, Phone.MESSAGE_CONSTRAINTS); - - // valid phone followed by invalid phone. The test case for invalid phone followed by valid phone - // is tested at {@code parse_invalidValueFollowedByValidValue_success()} - assertParseFailure(parser, "1" + PHONE_DESC_BOB + INVALID_PHONE_DESC, Phone.MESSAGE_CONSTRAINTS); - - // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Person} being edited, - // parsing it together with a valid tag results in error - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - assertParseFailure(parser, "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, Tag.MESSAGE_CONSTRAINTS); - - // multiple invalid values, but only the first invalid value is captured - assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_ADDRESS_AMY + VALID_PHONE_AMY, - Name.MESSAGE_CONSTRAINTS); - } - - @Test - public void parse_allFieldsSpecified_success() { - Index targetIndex = INDEX_SECOND_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + TAG_DESC_HUSBAND - + EMAIL_DESC_AMY + ADDRESS_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY) - .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY) - .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_someFieldsSpecified_success() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + EMAIL_DESC_AMY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_oneFieldSpecified_success() { - // name - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY).build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // phone - userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // email - userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withEmail(VALID_EMAIL_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // address - userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY; - descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - - // tags - userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; - descriptor = new EditPersonDescriptorBuilder().withTags(VALID_TAG_FRIEND).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_multipleRepeatedFields_acceptsLast() { - Index targetIndex = INDEX_FIRST_PERSON; - String userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY - + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND - + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; - - 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); - - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_invalidValueFollowedByValidValue_success() { - // no other valid values specified - 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); - assertParseSuccess(parser, userInput, expectedCommand); - - // other valid values specified - userInput = targetIndex.getOneBased() + EMAIL_DESC_BOB + INVALID_PHONE_DESC + ADDRESS_DESC_BOB - + PHONE_DESC_BOB; - descriptor = new EditPersonDescriptorBuilder().withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).build(); - expectedCommand = new EditCommand(targetIndex, descriptor); - assertParseSuccess(parser, userInput, expectedCommand); - } - - @Test - public void parse_resetTags_success() { - Index targetIndex = INDEX_THIRD_PERSON; - String userInput = targetIndex.getOneBased() + TAG_EMPTY; - - EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build(); - EditCommand expectedCommand = new EditCommand(targetIndex, descriptor); - - assertParseSuccess(parser, userInput, expectedCommand); - } -} diff --git a/src/test/java/seedu/address/logic/parser/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 deleted file mode 100644 index 4256788b1a7..00000000000 --- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java +++ /dev/null @@ -1,196 +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.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import org.junit.jupiter.api.Test; - -import seedu.address.logic.parser.exceptions.ParseException; -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; - -public class ParserUtilTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = "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"; - private static final String VALID_TAG_1 = "friend"; - private static final String VALID_TAG_2 = "neighbour"; - - private static final String WHITESPACE = " \t\r\n"; - - @Test - public void parseIndex_invalidInput_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); - } - - @Test - public void parseIndex_outOfRangeInput_throwsParseException() { - assertThrows(ParseException.class, MESSAGE_INVALID_INDEX, () - -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); - } - - @Test - public void parseIndex_validInput_success() throws Exception { - // No whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex("1")); - - // Leading and trailing whitespaces - assertEquals(INDEX_FIRST_PERSON, ParserUtil.parseIndex(" 1 ")); - } - - @Test - public void parseName_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseName((String) null)); - } - - @Test - public void parseName_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseName(INVALID_NAME)); - } - - @Test - public void parseName_validValueWithoutWhitespace_returnsName() throws Exception { - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(VALID_NAME)); - } - - @Test - public void parseName_validValueWithWhitespace_returnsTrimmedName() throws Exception { - String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE; - Name expectedName = new Name(VALID_NAME); - assertEquals(expectedName, ParserUtil.parseName(nameWithWhitespace)); - } - - @Test - public void parsePhone_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parsePhone((String) null)); - } - - @Test - public void parsePhone_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parsePhone(INVALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithoutWhitespace_returnsPhone() throws Exception { - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(VALID_PHONE)); - } - - @Test - public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception { - String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE; - Phone expectedPhone = new Phone(VALID_PHONE); - assertEquals(expectedPhone, ParserUtil.parsePhone(phoneWithWhitespace)); - } - - @Test - public void parseAddress_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null)); - } - - @Test - public void parseAddress_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception { - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS)); - } - - @Test - public void parseAddress_validValueWithWhitespace_returnsTrimmedAddress() throws Exception { - String addressWithWhitespace = WHITESPACE + VALID_ADDRESS + WHITESPACE; - Address expectedAddress = new Address(VALID_ADDRESS); - assertEquals(expectedAddress, ParserUtil.parseAddress(addressWithWhitespace)); - } - - @Test - public void parseEmail_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseEmail((String) null)); - } - - @Test - public void parseEmail_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseEmail(INVALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(VALID_EMAIL)); - } - - @Test - public void parseEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception { - String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE; - Email expectedEmail = new Email(VALID_EMAIL); - assertEquals(expectedEmail, ParserUtil.parseEmail(emailWithWhitespace)); - } - - @Test - public void parseTag_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); - } - - @Test - public void parseTag_invalidValue_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); - } - - @Test - public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); - } - - @Test - public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { - String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; - Tag expectedTag = new Tag(VALID_TAG_1); - assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); - } - - @Test - public void parseTags_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); - } - - @Test - public void parseTags_collectionWithInvalidTags_throwsParseException() { - assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); - } - - @Test - public void parseTags_emptyCollection_returnsEmptySet() throws Exception { - assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); - } - - @Test - public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { - Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); - Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); - - assertEquals(expectedTagSet, actualTagSet); - } -} diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java deleted file mode 100644 index 87782528ecd..00000000000 --- a/src/test/java/seedu/address/model/AddressBookTest.java +++ /dev/null @@ -1,102 +0,0 @@ -package seedu.address.model; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import seedu.address.model.person.Person; -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.testutil.PersonBuilder; - -public class AddressBookTest { - - private final AddressBook addressBook = new AddressBook(); - - @Test - public void constructor() { - assertEquals(Collections.emptyList(), addressBook.getPersonList()); - } - - @Test - public void resetData_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.resetData(null)); - } - - @Test - public void resetData_withValidReadOnlyAddressBook_replacesData() { - AddressBook newData = getTypicalAddressBook(); - addressBook.resetData(newData); - assertEquals(newData, addressBook); - } - - @Test - public void resetData_withDuplicatePersons_throwsDuplicatePersonException() { - // Two persons with the same identity fields - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - List newPersons = Arrays.asList(ALICE, editedAlice); - AddressBookStub newData = new AddressBookStub(newPersons); - - assertThrows(DuplicatePersonException.class, () -> addressBook.resetData(newData)); - } - - @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> addressBook.hasPerson(null)); - } - - @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - assertTrue(addressBook.hasPerson(ALICE)); - } - - @Test - public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() { - addressBook.addPerson(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(addressBook.hasPerson(editedAlice)); - } - - @Test - public void getPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0)); - } - - /** - * A stub ReadOnlyAddressBook whose persons list can violate interface constraints. - */ - private static class AddressBookStub implements ReadOnlyAddressBook { - private final ObservableList persons = FXCollections.observableArrayList(); - - AddressBookStub(Collection persons) { - this.persons.setAll(persons); - } - - @Override - public ObservableList getPersonList() { - return persons; - } - } - -} diff --git a/src/test/java/seedu/address/model/person/AddressTest.java b/src/test/java/seedu/address/model/person/AddressTest.java deleted file mode 100644 index dcd3be87b3a..00000000000 --- a/src/test/java/seedu/address/model/person/AddressTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class AddressTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Address(null)); - } - - @Test - public void constructor_invalidAddress_throwsIllegalArgumentException() { - String invalidAddress = ""; - assertThrows(IllegalArgumentException.class, () -> new Address(invalidAddress)); - } - - @Test - public void isValidAddress() { - // null address - assertThrows(NullPointerException.class, () -> Address.isValidAddress(null)); - - // invalid addresses - assertFalse(Address.isValidAddress("")); // empty string - assertFalse(Address.isValidAddress(" ")); // spaces only - - // valid addresses - assertTrue(Address.isValidAddress("Blk 456, Den Road, #01-355")); - assertTrue(Address.isValidAddress("-")); // one character - assertTrue(Address.isValidAddress("Leng Inc; 1234 Market St; San Francisco CA 2349879; USA")); // long address - } -} diff --git a/src/test/java/seedu/address/model/person/EmailTest.java b/src/test/java/seedu/address/model/person/EmailTest.java deleted file mode 100644 index 7fa726ceb18..00000000000 --- a/src/test/java/seedu/address/model/person/EmailTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class EmailTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Email(null)); - } - - @Test - public void constructor_invalidEmail_throwsIllegalArgumentException() { - String invalidEmail = ""; - assertThrows(IllegalArgumentException.class, () -> new Email(invalidEmail)); - } - - @Test - public void isValidEmail() { - // null email - assertThrows(NullPointerException.class, () -> Email.isValidEmail(null)); - - // blank email - assertFalse(Email.isValidEmail("")); // empty string - assertFalse(Email.isValidEmail(" ")); // spaces only - - // missing parts - assertFalse(Email.isValidEmail("@example.com")); // missing local part - assertFalse(Email.isValidEmail("peterjackexample.com")); // missing '@' symbol - assertFalse(Email.isValidEmail("peterjack@")); // missing domain name - - // invalid parts - assertFalse(Email.isValidEmail("peterjack@-")); // invalid domain name - assertFalse(Email.isValidEmail("peterjack@exam_ple.com")); // underscore in domain name - assertFalse(Email.isValidEmail("peter jack@example.com")); // spaces in local part - assertFalse(Email.isValidEmail("peterjack@exam ple.com")); // spaces in domain name - assertFalse(Email.isValidEmail(" peterjack@example.com")); // leading space - assertFalse(Email.isValidEmail("peterjack@example.com ")); // trailing space - assertFalse(Email.isValidEmail("peterjack@@example.com")); // double '@' symbol - assertFalse(Email.isValidEmail("peter@jack@example.com")); // '@' symbol in local part - assertFalse(Email.isValidEmail("peterjack@example@com")); // '@' symbol in domain name - assertFalse(Email.isValidEmail("peterjack@.example.com")); // domain name starts with a period - assertFalse(Email.isValidEmail("peterjack@example.com.")); // domain name ends with a period - assertFalse(Email.isValidEmail("peterjack@-example.com")); // domain name starts with a hyphen - assertFalse(Email.isValidEmail("peterjack@example.com-")); // domain name ends with a hyphen - - // valid email - assertTrue(Email.isValidEmail("PeterJack_1190@example.com")); - assertTrue(Email.isValidEmail("a@bc")); // minimal - assertTrue(Email.isValidEmail("test@localhost")); // alphabets only - assertTrue(Email.isValidEmail("!#$%&'*+/=?`{|}~^.-@example.org")); // special characters local part - assertTrue(Email.isValidEmail("123@145")); // numeric local part and domain name - assertTrue(Email.isValidEmail("a1+be!@example1.com")); // mixture of alphanumeric and special characters - assertTrue(Email.isValidEmail("peter_jack@very-very-very-long-example.com")); // long domain name - assertTrue(Email.isValidEmail("if.you.dream.it_you.can.do.it@example.com")); // long local part - } -} 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/person/NameTest.java b/src/test/java/seedu/address/model/person/NameTest.java deleted file mode 100644 index c9801392874..00000000000 --- a/src/test/java/seedu/address/model/person/NameTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class NameTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Name(null)); - } - - @Test - public void constructor_invalidName_throwsIllegalArgumentException() { - String invalidName = ""; - assertThrows(IllegalArgumentException.class, () -> new Name(invalidName)); - } - - @Test - public void isValidName() { - // null name - assertThrows(NullPointerException.class, () -> Name.isValidName(null)); - - // invalid name - assertFalse(Name.isValidName("")); // empty string - assertFalse(Name.isValidName(" ")); // spaces only - assertFalse(Name.isValidName("^")); // only non-alphanumeric characters - assertFalse(Name.isValidName("peter*")); // contains non-alphanumeric characters - - // valid name - assertTrue(Name.isValidName("peter jack")); // alphabets only - assertTrue(Name.isValidName("12345")); // numbers only - assertTrue(Name.isValidName("peter the 2nd")); // alphanumeric characters - assertTrue(Name.isValidName("Capital Tan")); // with capital letters - assertTrue(Name.isValidName("David Roger Jackson Ray Jr 2nd")); // long names - } -} diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java deleted file mode 100644 index b29c097cfd4..00000000000 --- a/src/test/java/seedu/address/model/person/PersonTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import org.junit.jupiter.api.Test; - -import seedu.address.testutil.PersonBuilder; - -public class PersonTest { - - @Test - public void asObservableList_modifyList_throwsUnsupportedOperationException() { - Person person = new PersonBuilder().build(); - assertThrows(UnsupportedOperationException.class, () -> person.getTags().remove(0)); - } - - @Test - public void isSamePerson() { - // same object -> returns true - assertTrue(ALICE.isSamePerson(ALICE)); - - // null -> returns false - assertFalse(ALICE.isSamePerson(null)); - - // same name, all other attributes different -> returns true - Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB) - .withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND).build(); - assertTrue(ALICE.isSamePerson(editedAlice)); - - // different name, all other attributes same -> returns false - editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.isSamePerson(editedAlice)); - - // name differs in case, all other attributes same -> returns false - Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build(); - assertFalse(BOB.isSamePerson(editedBob)); - - // name has trailing spaces, all other attributes same -> returns false - String nameWithTrailingSpaces = VALID_NAME_BOB + " "; - editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build(); - assertFalse(BOB.isSamePerson(editedBob)); - } - - @Test - public void equals() { - // same values -> returns true - Person aliceCopy = new PersonBuilder(ALICE).build(); - assertTrue(ALICE.equals(aliceCopy)); - - // same object -> returns true - assertTrue(ALICE.equals(ALICE)); - - // null -> returns false - assertFalse(ALICE.equals(null)); - - // different type -> returns false - assertFalse(ALICE.equals(5)); - - // different person -> returns false - assertFalse(ALICE.equals(BOB)); - - // different name -> returns false - Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different phone -> returns false - editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different email -> returns false - editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different address -> returns false - editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build(); - assertFalse(ALICE.equals(editedAlice)); - - // different tags -> returns false - editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build(); - assertFalse(ALICE.equals(editedAlice)); - } -} diff --git a/src/test/java/seedu/address/model/person/PhoneTest.java b/src/test/java/seedu/address/model/person/PhoneTest.java deleted file mode 100644 index 8dd52766a5f..00000000000 --- a/src/test/java/seedu/address/model/person/PhoneTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; - -import org.junit.jupiter.api.Test; - -public class PhoneTest { - - @Test - public void constructor_null_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> new Phone(null)); - } - - @Test - public void constructor_invalidPhone_throwsIllegalArgumentException() { - String invalidPhone = ""; - assertThrows(IllegalArgumentException.class, () -> new Phone(invalidPhone)); - } - - @Test - public void isValidPhone() { - // null phone number - assertThrows(NullPointerException.class, () -> Phone.isValidPhone(null)); - - // invalid phone numbers - assertFalse(Phone.isValidPhone("")); // empty string - assertFalse(Phone.isValidPhone(" ")); // spaces only - assertFalse(Phone.isValidPhone("91")); // less than 3 numbers - assertFalse(Phone.isValidPhone("phone")); // non-numeric - assertFalse(Phone.isValidPhone("9011p041")); // alphabets within digits - assertFalse(Phone.isValidPhone("9312 1534")); // spaces within digits - - // valid phone numbers - assertTrue(Phone.isValidPhone("911")); // exactly 3 numbers - assertTrue(Phone.isValidPhone("93121534")); - assertTrue(Phone.isValidPhone("124293842033123")); // long phone numbers - } -} diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java deleted file mode 100644 index 1cc5fe9e0fe..00000000000 --- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java +++ /dev/null @@ -1,170 +0,0 @@ -package seedu.address.model.person; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BOB; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import seedu.address.model.person.exceptions.DuplicatePersonException; -import seedu.address.model.person.exceptions.PersonNotFoundException; -import seedu.address.testutil.PersonBuilder; - -public class UniquePersonListTest { - - private final UniquePersonList uniquePersonList = new UniquePersonList(); - - @Test - public void contains_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.contains(null)); - } - - @Test - public void contains_personNotInList_returnsFalse() { - assertFalse(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personInList_returnsTrue() { - uniquePersonList.add(ALICE); - assertTrue(uniquePersonList.contains(ALICE)); - } - - @Test - public void contains_personWithSameIdentityFieldsInList_returnsTrue() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - assertTrue(uniquePersonList.contains(editedAlice)); - } - - @Test - public void add_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.add(null)); - } - - @Test - public void add_duplicatePerson_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.add(ALICE)); - } - - @Test - public void setPerson_nullTargetPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(null, ALICE)); - } - - @Test - public void setPerson_nullEditedPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPerson(ALICE, null)); - } - - @Test - public void setPerson_targetPersonNotInList_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.setPerson(ALICE, ALICE)); - } - - @Test - public void setPerson_editedPersonIsSamePerson_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(ALICE); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasSameIdentity_success() { - uniquePersonList.add(ALICE); - Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND) - .build(); - uniquePersonList.setPerson(ALICE, editedAlice); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(editedAlice); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasDifferentIdentity_success() { - uniquePersonList.add(ALICE); - uniquePersonList.setPerson(ALICE, BOB); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPerson_editedPersonHasNonUniqueIdentity_throwsDuplicatePersonException() { - uniquePersonList.add(ALICE); - uniquePersonList.add(BOB); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPerson(ALICE, BOB)); - } - - @Test - public void remove_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.remove(null)); - } - - @Test - public void remove_personDoesNotExist_throwsPersonNotFoundException() { - assertThrows(PersonNotFoundException.class, () -> uniquePersonList.remove(ALICE)); - } - - @Test - public void remove_existingPerson_removesPerson() { - uniquePersonList.add(ALICE); - uniquePersonList.remove(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullUniquePersonList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((UniquePersonList) null)); - } - - @Test - public void setPersons_uniquePersonList_replacesOwnListWithProvidedUniquePersonList() { - uniquePersonList.add(ALICE); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - uniquePersonList.setPersons(expectedUniquePersonList); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_nullList_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> uniquePersonList.setPersons((List) null)); - } - - @Test - public void setPersons_list_replacesOwnListWithProvidedList() { - uniquePersonList.add(ALICE); - List personList = Collections.singletonList(BOB); - uniquePersonList.setPersons(personList); - UniquePersonList expectedUniquePersonList = new UniquePersonList(); - expectedUniquePersonList.add(BOB); - assertEquals(expectedUniquePersonList, uniquePersonList); - } - - @Test - public void setPersons_listWithDuplicatePersons_throwsDuplicatePersonException() { - List listWithDuplicatePersons = Arrays.asList(ALICE, ALICE); - assertThrows(DuplicatePersonException.class, () -> uniquePersonList.setPersons(listWithDuplicatePersons)); - } - - @Test - public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () - -> uniquePersonList.asUnmodifiableObservableList().remove(0)); - } -} diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java deleted file mode 100644 index 83b11331cdb..00000000000 --- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.storage.JsonAdaptedPerson.MISSING_FIELD_MESSAGE_FORMAT; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.BENSON; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Phone; - -public class JsonAdaptedPersonTest { - private static final String INVALID_NAME = "R@chel"; - private static final String INVALID_PHONE = "+651234"; - private static final String INVALID_ADDRESS = " "; - private static final String INVALID_EMAIL = "example.com"; - private static final String INVALID_TAG = "#friend"; - - private static final String VALID_NAME = BENSON.getName().toString(); - private static final String VALID_PHONE = BENSON.getPhone().toString(); - private static final String VALID_EMAIL = BENSON.getEmail().toString(); - private static final String VALID_ADDRESS = BENSON.getAddress().toString(); - private static final List VALID_TAGS = BENSON.getTags().stream() - .map(JsonAdaptedTag::new) - .collect(Collectors.toList()); - - @Test - public void toModelType_validPersonDetails_returnsPerson() throws Exception { - JsonAdaptedPerson person = new JsonAdaptedPerson(BENSON); - assertEquals(BENSON, person.toModelType()); - } - - @Test - public void toModelType_invalidName_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Name.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullName_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Phone.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullPhone_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = Email.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullEmail_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS); - String expectedMessage = Address.MESSAGE_CONSTRAINTS; - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_nullAddress_throwsIllegalValueException() { - JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS); - String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()); - assertThrows(IllegalValueException.class, expectedMessage, person::toModelType); - } - - @Test - public void toModelType_invalidTags_throwsIllegalValueException() { - List invalidTags = new ArrayList<>(VALID_TAGS); - invalidTags.add(new JsonAdaptedTag(INVALID_TAG)); - JsonAdaptedPerson person = - new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags); - assertThrows(IllegalValueException.class, person::toModelType); - } - -} diff --git a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java b/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java deleted file mode 100644 index ac3c3af9566..00000000000 --- a/src/test/java/seedu/address/storage/JsonAddressBookStorageTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.HOON; -import static seedu.address.testutil.TypicalPersons.IDA; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; - -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; - -public class JsonAddressBookStorageTest { - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonAddressBookStorageTest"); - - @TempDir - public Path testFolder; - - @Test - public void readAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> readAddressBook(null)); - } - - private java.util.Optional readAddressBook(String filePath) throws Exception { - return new JsonAddressBookStorage(Paths.get(filePath)).readAddressBook(addToTestDataPathIfNotNull(filePath)); - } - - private Path addToTestDataPathIfNotNull(String prefsFileInTestDataFolder) { - return prefsFileInTestDataFolder != null - ? TEST_DATA_FOLDER.resolve(prefsFileInTestDataFolder) - : null; - } - - @Test - public void read_missingFile_emptyResult() throws Exception { - assertFalse(readAddressBook("NonExistentFile.json").isPresent()); - } - - @Test - public void read_notJsonFormat_exceptionThrown() { - assertThrows(DataConversionException.class, () -> readAddressBook("notJsonFormatAddressBook.json")); - } - - @Test - public void readAddressBook_invalidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidPersonAddressBook.json")); - } - - @Test - public void readAddressBook_invalidAndValidPersonAddressBook_throwDataConversionException() { - assertThrows(DataConversionException.class, () -> readAddressBook("invalidAndValidPersonAddressBook.json")); - } - - @Test - public void readAndSaveAddressBook_allInOrder_success() throws Exception { - Path filePath = testFolder.resolve("TempAddressBook.json"); - AddressBook original = getTypicalAddressBook(); - JsonAddressBookStorage jsonAddressBookStorage = new JsonAddressBookStorage(filePath); - - // Save in new file and read back - jsonAddressBookStorage.saveAddressBook(original, filePath); - ReadOnlyAddressBook readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Modify data, overwrite exiting file, and read back - original.addPerson(HOON); - original.removePerson(ALICE); - jsonAddressBookStorage.saveAddressBook(original, filePath); - readBack = jsonAddressBookStorage.readAddressBook(filePath).get(); - assertEquals(original, new AddressBook(readBack)); - - // Save and read without specifying file path - original.addPerson(IDA); - jsonAddressBookStorage.saveAddressBook(original); // file path not specified - readBack = jsonAddressBookStorage.readAddressBook().get(); // file path not specified - assertEquals(original, new AddressBook(readBack)); - - } - - @Test - public void saveAddressBook_nullAddressBook_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(null, "SomeFile.json")); - } - - /** - * Saves {@code addressBook} at the specified {@code filePath}. - */ - private void saveAddressBook(ReadOnlyAddressBook addressBook, String filePath) { - try { - new JsonAddressBookStorage(Paths.get(filePath)) - .saveAddressBook(addressBook, addToTestDataPathIfNotNull(filePath)); - } catch (IOException ioe) { - throw new AssertionError("There should not be an error writing to the file.", ioe); - } - } - - @Test - public void saveAddressBook_nullFilePath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> saveAddressBook(new AddressBook(), null)); - } -} diff --git a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java b/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java deleted file mode 100644 index 188c9058d20..00000000000 --- a/src/test/java/seedu/address/storage/JsonSerializableAddressBookTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package seedu.address.storage; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.testutil.Assert.assertThrows; - -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.jupiter.api.Test; - -import seedu.address.commons.exceptions.IllegalValueException; -import seedu.address.commons.util.JsonUtil; -import seedu.address.model.AddressBook; -import seedu.address.testutil.TypicalPersons; - -public class JsonSerializableAddressBookTest { - - private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonSerializableAddressBookTest"); - private static final Path TYPICAL_PERSONS_FILE = TEST_DATA_FOLDER.resolve("typicalPersonsAddressBook.json"); - private static final Path INVALID_PERSON_FILE = TEST_DATA_FOLDER.resolve("invalidPersonAddressBook.json"); - private static final Path DUPLICATE_PERSON_FILE = TEST_DATA_FOLDER.resolve("duplicatePersonAddressBook.json"); - - @Test - public void toModelType_typicalPersonsFile_success() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(TYPICAL_PERSONS_FILE, - JsonSerializableAddressBook.class).get(); - AddressBook addressBookFromFile = dataFromFile.toModelType(); - AddressBook typicalPersonsAddressBook = TypicalPersons.getTypicalAddressBook(); - assertEquals(addressBookFromFile, typicalPersonsAddressBook); - } - - @Test - public void toModelType_invalidPersonFile_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(INVALID_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, dataFromFile::toModelType); - } - - @Test - public void toModelType_duplicatePersons_throwsIllegalValueException() throws Exception { - JsonSerializableAddressBook dataFromFile = JsonUtil.readJsonFile(DUPLICATE_PERSON_FILE, - JsonSerializableAddressBook.class).get(); - assertThrows(IllegalValueException.class, JsonSerializableAddressBook.MESSAGE_DUPLICATE_PERSON, - dataFromFile::toModelType); - } - -} diff --git a/src/test/java/seedu/address/testutil/AddressBookBuilder.java b/src/test/java/seedu/address/testutil/AddressBookBuilder.java deleted file mode 100644 index d53799fd110..00000000000 --- a/src/test/java/seedu/address/testutil/AddressBookBuilder.java +++ /dev/null @@ -1,34 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class to help with building Addressbook objects. - * Example usage:
- * {@code AddressBook ab = new AddressBookBuilder().withPerson("John", "Doe").build();} - */ -public class AddressBookBuilder { - - private AddressBook addressBook; - - public AddressBookBuilder() { - addressBook = new AddressBook(); - } - - public AddressBookBuilder(AddressBook addressBook) { - this.addressBook = addressBook; - } - - /** - * Adds a new {@code Person} to the {@code AddressBook} that we are building. - */ - public AddressBookBuilder withPerson(Person person) { - addressBook.addPerson(person); - return this; - } - - public AddressBook build() { - return addressBook; - } -} diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java deleted file mode 100644 index 4584bd5044e..00000000000 --- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java +++ /dev/null @@ -1,87 +0,0 @@ -package seedu.address.testutil; - -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; - -/** - * A utility class to help with building EditPersonDescriptor objects. - */ -public class EditPersonDescriptorBuilder { - - private EditPersonDescriptor descriptor; - - public EditPersonDescriptorBuilder() { - descriptor = new EditPersonDescriptor(); - } - - public EditPersonDescriptorBuilder(EditPersonDescriptor descriptor) { - this.descriptor = new EditPersonDescriptor(descriptor); - } - - /** - * Returns an {@code EditPersonDescriptor} with fields containing {@code person}'s details - */ - public EditPersonDescriptorBuilder(Person person) { - descriptor = new EditPersonDescriptor(); - descriptor.setName(person.getName()); - descriptor.setPhone(person.getPhone()); - descriptor.setEmail(person.getEmail()); - descriptor.setAddress(person.getAddress()); - descriptor.setTags(person.getTags()); - } - - /** - * Sets the {@code Name} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withName(String name) { - descriptor.setName(new Name(name)); - return this; - } - - /** - * Sets the {@code Phone} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withPhone(String phone) { - descriptor.setPhone(new Phone(phone)); - return this; - } - - /** - * Sets the {@code Email} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withEmail(String email) { - descriptor.setEmail(new Email(email)); - return this; - } - - /** - * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building. - */ - public EditPersonDescriptorBuilder withAddress(String address) { - descriptor.setAddress(new Address(address)); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code EditPersonDescriptor} - * that we are building. - */ - public EditPersonDescriptorBuilder withTags(String... tags) { - Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); - descriptor.setTags(tagSet); - return this; - } - - public EditPersonDescriptor build() { - return descriptor; - } -} diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java deleted file mode 100644 index 6be381d39ba..00000000000 --- a/src/test/java/seedu/address/testutil/PersonBuilder.java +++ /dev/null @@ -1,96 +0,0 @@ -package seedu.address.testutil; - -import java.util.HashSet; -import java.util.Set; - -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -import seedu.address.model.util.SampleDataUtil; - -/** - * A utility class to help with building Person objects. - */ -public class PersonBuilder { - - public static final String DEFAULT_NAME = "Amy Bee"; - public static final String DEFAULT_PHONE = "85355255"; - public static final String DEFAULT_EMAIL = "amy@gmail.com"; - public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111"; - - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - /** - * Creates a {@code PersonBuilder} with the default details. - */ - public PersonBuilder() { - name = new Name(DEFAULT_NAME); - phone = new Phone(DEFAULT_PHONE); - email = new Email(DEFAULT_EMAIL); - address = new Address(DEFAULT_ADDRESS); - tags = new HashSet<>(); - } - - /** - * Initializes the PersonBuilder with the data of {@code personToCopy}. - */ - public PersonBuilder(Person personToCopy) { - name = personToCopy.getName(); - phone = personToCopy.getPhone(); - email = personToCopy.getEmail(); - address = personToCopy.getAddress(); - tags = new HashSet<>(personToCopy.getTags()); - } - - /** - * Sets the {@code Name} of the {@code Person} that we are building. - */ - public PersonBuilder withName(String name) { - this.name = new Name(name); - return this; - } - - /** - * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building. - */ - public PersonBuilder withTags(String ... tags) { - this.tags = SampleDataUtil.getTagSet(tags); - return this; - } - - /** - * Sets the {@code Address} of the {@code Person} that we are building. - */ - public PersonBuilder withAddress(String address) { - this.address = new Address(address); - return this; - } - - /** - * Sets the {@code Phone} of the {@code Person} that we are building. - */ - public PersonBuilder withPhone(String phone) { - this.phone = new Phone(phone); - return this; - } - - /** - * Sets the {@code Email} of the {@code Person} that we are building. - */ - public PersonBuilder withEmail(String email) { - this.email = new Email(email); - return this; - } - - public Person build() { - return new Person(name, phone, email, address, tags); - } - -} diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java deleted file mode 100644 index 90849945183..00000000000 --- a/src/test/java/seedu/address/testutil/PersonUtil.java +++ /dev/null @@ -1,62 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - -import java.util.Set; - -import seedu.address.logic.commands.AddCommand; -import seedu.address.logic.commands.EditCommand.EditPersonDescriptor; -import seedu.address.model.person.Person; -import seedu.address.model.tag.Tag; - -/** - * A utility class for Person. - */ -public class PersonUtil { - - /** - * Returns an add command string for adding the {@code person}. - */ - public static String getAddCommand(Person person) { - return AddCommand.COMMAND_WORD + " " + getPersonDetails(person); - } - - /** - * Returns the part of command string for the given {@code person}'s details. - */ - public static String getPersonDetails(Person person) { - StringBuilder sb = new StringBuilder(); - sb.append(PREFIX_NAME + person.getName().fullName + " "); - sb.append(PREFIX_PHONE + person.getPhone().value + " "); - sb.append(PREFIX_EMAIL + person.getEmail().value + " "); - sb.append(PREFIX_ADDRESS + person.getAddress().value + " "); - person.getTags().stream().forEach( - s -> sb.append(PREFIX_TAG + s.tagName + " ") - ); - return sb.toString(); - } - - /** - * Returns the part of command string for the given {@code EditPersonDescriptor}'s details. - */ - public static String getEditPersonDescriptorDetails(EditPersonDescriptor descriptor) { - StringBuilder sb = new StringBuilder(); - descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); - descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); - descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); - descriptor.getAddress().ifPresent(address -> sb.append(PREFIX_ADDRESS).append(address.value).append(" ")); - if (descriptor.getTags().isPresent()) { - Set tags = descriptor.getTags().get(); - if (tags.isEmpty()) { - sb.append(PREFIX_TAG); - } else { - tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" ")); - } - } - return sb.toString(); - } -} diff --git a/src/test/java/seedu/address/testutil/TypicalIndexes.java b/src/test/java/seedu/address/testutil/TypicalIndexes.java deleted file mode 100644 index 1e613937657..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalIndexes.java +++ /dev/null @@ -1,12 +0,0 @@ -package seedu.address.testutil; - -import seedu.address.commons.core.index.Index; - -/** - * A utility class containing a list of {@code Index} objects to be used in tests. - */ -public class TypicalIndexes { - public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1); - public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2); - public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3); -} diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java deleted file mode 100644 index fec76fb7129..00000000000 --- a/src/test/java/seedu/address/testutil/TypicalPersons.java +++ /dev/null @@ -1,76 +0,0 @@ -package seedu.address.testutil; - -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY; -import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; -import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import seedu.address.model.AddressBook; -import seedu.address.model.person.Person; - -/** - * A utility class containing a list of {@code Person} objects to be used in tests. - */ -public class TypicalPersons { - - public static final Person ALICE = new PersonBuilder().withName("Alice Pauline") - .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com") - .withPhone("94351253") - .withTags("friends").build(); - public static final Person BENSON = new PersonBuilder().withName("Benson Meier") - .withAddress("311, Clementi Ave 2, #02-25") - .withEmail("johnd@example.com").withPhone("98765432") - .withTags("owesMoney", "friends").build(); - public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563") - .withEmail("heinz@example.com").withAddress("wall street").build(); - public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533") - .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build(); - public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224") - .withEmail("werner@example.com").withAddress("michegan ave").build(); - public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427") - .withEmail("lydia@example.com").withAddress("little tokyo").build(); - public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442") - .withEmail("anna@example.com").withAddress("4th street").build(); - - // Manually added - public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424") - .withEmail("stefan@example.com").withAddress("little india").build(); - public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131") - .withEmail("hans@example.com").withAddress("chicago ave").build(); - - // Manually added - Person's details found in {@code CommandTestUtil} - public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY) - .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build(); - public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) - .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND) - .build(); - - public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER - - private TypicalPersons() {} // prevents instantiation - - /** - * Returns an {@code AddressBook} with all the typical persons. - */ - public static AddressBook getTypicalAddressBook() { - AddressBook ab = new AddressBook(); - for (Person person : getTypicalPersons()) { - ab.addPerson(person); - } - return ab; - } - - public static List getTypicalPersons() { - return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE)); - } -} diff --git a/src/test/java/seedu/address/AppParametersTest.java b/src/test/java/seedu/ta/AppParametersTest.java similarity index 98% rename from src/test/java/seedu/address/AppParametersTest.java rename to src/test/java/seedu/ta/AppParametersTest.java index 61326b2d31a..96372a7a99e 100644 --- a/src/test/java/seedu/address/AppParametersTest.java +++ b/src/test/java/seedu/ta/AppParametersTest.java @@ -1,4 +1,4 @@ -package seedu.address; +package seedu.ta; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/src/test/java/seedu/address/commons/core/ConfigTest.java b/src/test/java/seedu/ta/commons/core/ConfigTest.java similarity index 95% rename from src/test/java/seedu/address/commons/core/ConfigTest.java rename to src/test/java/seedu/ta/commons/core/ConfigTest.java index 07cd7f73d53..3f02df82773 100644 --- a/src/test/java/seedu/address/commons/core/ConfigTest.java +++ b/src/test/java/seedu/ta/commons/core/ConfigTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.core; +package seedu.ta.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/src/test/java/seedu/ta/commons/core/GuiSettingsTest.java b/src/test/java/seedu/ta/commons/core/GuiSettingsTest.java new file mode 100644 index 00000000000..196db64fbaf --- /dev/null +++ b/src/test/java/seedu/ta/commons/core/GuiSettingsTest.java @@ -0,0 +1,67 @@ +package seedu.ta.commons.core; + +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 java.awt.Point; + +import org.junit.jupiter.api.Test; + +public class GuiSettingsTest { + private GuiSettings settings = new GuiSettings(); + private double windowWidth = 1000; + private double windowHeight = 700; + private Point windowCoordinates = null; + + @Test + public void testGetWindowWidth() { + assertEquals(windowWidth, settings.getWindowWidth()); + } + + @Test + public void testGetWindowHeight() { + assertEquals(windowHeight, settings.getWindowHeight()); + } + + @Test + public void getWindowCoordinates_nullAttribute_returnNull() { + assertEquals(windowCoordinates, settings.getWindowCoordinates()); + } + + @Test + public void getWindowCoordinates_nonNullAttribute_returnPointObject() { + GuiSettings settings = new GuiSettings(1000.0, 700.0, 1, 1); + Point windowCoordinates = new Point(1, 1); + assertEquals(windowCoordinates, settings.getWindowCoordinates()); + } + + @Test + public void equals() { + GuiSettings firstSettings = new GuiSettings(1000.0, 700.0, 1, 1); + GuiSettings secondSettings = new GuiSettings(700.0, 1000.0, 2, 2); + + // same object -> returns true + assertTrue(firstSettings.equals(firstSettings)); + + // same values -> returns true + GuiSettings firstSettingsCopy = new GuiSettings(1000.0, 700.0, 1, 1); + assertTrue(firstSettings.equals(firstSettingsCopy)); + + // different types -> returns false + assertFalse(firstSettings.equals(1)); + + // null -> returns false + assertFalse(firstSettings.equals(null)); + + // different settings -> returns false + assertFalse(firstSettings.equals(secondSettings)); + } + + @Test + public void toString_nullWindowCoordinates_success() { + assertEquals("Width : " + windowWidth + "\n" + + "Height : " + windowHeight + "\n" + + "Position : " + windowCoordinates, settings.toString()); + } +} diff --git a/src/test/java/seedu/address/commons/core/VersionTest.java b/src/test/java/seedu/ta/commons/core/VersionTest.java similarity index 98% rename from src/test/java/seedu/address/commons/core/VersionTest.java rename to src/test/java/seedu/ta/commons/core/VersionTest.java index 495cd231554..af505a7e777 100644 --- a/src/test/java/seedu/address/commons/core/VersionTest.java +++ b/src/test/java/seedu/ta/commons/core/VersionTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.core; +package seedu.ta.commons.core; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.ta.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/core/index/IndexTest.java b/src/test/java/seedu/ta/commons/core/index/IndexTest.java similarity index 74% rename from src/test/java/seedu/address/commons/core/index/IndexTest.java rename to src/test/java/seedu/ta/commons/core/index/IndexTest.java index a3ec6f8e747..b024a85baf9 100644 --- a/src/test/java/seedu/address/commons/core/index/IndexTest.java +++ b/src/test/java/seedu/ta/commons/core/index/IndexTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.core.index; +package seedu.ta.commons.core.index; 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.ta.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -39,22 +39,22 @@ public void createZeroBasedIndex() { @Test public void equals() { - final Index fifthPersonIndex = Index.fromOneBased(5); + final Index fifthContactIndex = Index.fromOneBased(5); // same values -> returns true - assertTrue(fifthPersonIndex.equals(Index.fromOneBased(5))); - assertTrue(fifthPersonIndex.equals(Index.fromZeroBased(4))); + assertTrue(fifthContactIndex.equals(Index.fromOneBased(5))); + assertTrue(fifthContactIndex.equals(Index.fromZeroBased(4))); // same object -> returns true - assertTrue(fifthPersonIndex.equals(fifthPersonIndex)); + assertTrue(fifthContactIndex.equals(fifthContactIndex)); // null -> returns false - assertFalse(fifthPersonIndex.equals(null)); + assertFalse(fifthContactIndex.equals(null)); // different types -> returns false - assertFalse(fifthPersonIndex.equals(5.0f)); + assertFalse(fifthContactIndex.equals(5.0f)); // different index -> returns false - assertFalse(fifthPersonIndex.equals(Index.fromOneBased(1))); + assertFalse(fifthContactIndex.equals(Index.fromOneBased(1))); } } diff --git a/src/test/java/seedu/address/commons/util/AppUtilTest.java b/src/test/java/seedu/ta/commons/util/AppUtilTest.java similarity index 91% rename from src/test/java/seedu/address/commons/util/AppUtilTest.java rename to src/test/java/seedu/ta/commons/util/AppUtilTest.java index 594de1e6365..76c5e95a5d5 100644 --- a/src/test/java/seedu/address/commons/util/AppUtilTest.java +++ b/src/test/java/seedu/ta/commons/util/AppUtilTest.java @@ -1,7 +1,7 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.ta.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java b/src/test/java/seedu/ta/commons/util/CollectionUtilTest.java similarity index 96% rename from src/test/java/seedu/address/commons/util/CollectionUtilTest.java rename to src/test/java/seedu/ta/commons/util/CollectionUtilTest.java index b467a3dc025..235e55194e5 100644 --- a/src/test/java/seedu/address/commons/util/CollectionUtilTest.java +++ b/src/test/java/seedu/ta/commons/util/CollectionUtilTest.java @@ -1,9 +1,9 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.ta.commons.util.CollectionUtil.requireAllNonNull; +import static seedu.ta.testutil.Assert.assertThrows; import java.util.Arrays; import java.util.Collection; diff --git a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java b/src/test/java/seedu/ta/commons/util/ConfigUtilTest.java similarity index 94% rename from src/test/java/seedu/address/commons/util/ConfigUtilTest.java rename to src/test/java/seedu/ta/commons/util/ConfigUtilTest.java index d2ab2839a52..f80a0279b66 100644 --- a/src/test/java/seedu/address/commons/util/ConfigUtilTest.java +++ b/src/test/java/seedu/ta/commons/util/ConfigUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; 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.ta.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -13,8 +13,8 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.Config; -import seedu.address.commons.exceptions.DataConversionException; +import seedu.ta.commons.core.Config; +import seedu.ta.commons.exceptions.DataConversionException; public class ConfigUtilTest { diff --git a/src/test/java/seedu/address/commons/util/FileUtilTest.java b/src/test/java/seedu/ta/commons/util/FileUtilTest.java similarity index 84% rename from src/test/java/seedu/address/commons/util/FileUtilTest.java rename to src/test/java/seedu/ta/commons/util/FileUtilTest.java index 1fe5478c756..e5508f5ae46 100644 --- a/src/test/java/seedu/address/commons/util/FileUtilTest.java +++ b/src/test/java/seedu/ta/commons/util/FileUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; 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.ta.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; diff --git a/src/test/java/seedu/address/commons/util/JsonUtilTest.java b/src/test/java/seedu/ta/commons/util/JsonUtilTest.java similarity index 92% rename from src/test/java/seedu/address/commons/util/JsonUtilTest.java rename to src/test/java/seedu/ta/commons/util/JsonUtilTest.java index d4907539dee..a763f1498c2 100644 --- a/src/test/java/seedu/address/commons/util/JsonUtilTest.java +++ b/src/test/java/seedu/ta/commons/util/JsonUtilTest.java @@ -1,4 +1,4 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -7,8 +7,8 @@ import org.junit.jupiter.api.Test; -import seedu.address.testutil.SerializableTestClass; -import seedu.address.testutil.TestUtil; +import seedu.ta.testutil.SerializableTestClass; +import seedu.ta.testutil.TestUtil; /** * Tests JSON Read and Write diff --git a/src/test/java/seedu/address/commons/util/StringUtilTest.java b/src/test/java/seedu/ta/commons/util/StringUtilTest.java similarity index 98% rename from src/test/java/seedu/address/commons/util/StringUtilTest.java rename to src/test/java/seedu/ta/commons/util/StringUtilTest.java index c56d407bf3f..abf457b1ab7 100644 --- a/src/test/java/seedu/address/commons/util/StringUtilTest.java +++ b/src/test/java/seedu/ta/commons/util/StringUtilTest.java @@ -1,8 +1,8 @@ -package seedu.address.commons.util; +package seedu.ta.commons.util; 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.ta.testutil.Assert.assertThrows; import java.io.FileNotFoundException; diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/ta/logic/LogicManagerTest.java similarity index 59% rename from src/test/java/seedu/address/logic/LogicManagerTest.java rename to src/test/java/seedu/ta/logic/LogicManagerTest.java index ad923ac249a..0f1cad012e5 100644 --- a/src/test/java/seedu/address/logic/LogicManagerTest.java +++ b/src/test/java/seedu/ta/logic/LogicManagerTest.java @@ -1,14 +1,13 @@ -package seedu.address.logic; +package seedu.ta.logic; import static org.junit.jupiter.api.Assertions.assertEquals; -import static seedu.address.commons.core.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX; -import static seedu.address.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; -import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY; -import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.AMY; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX; +import static seedu.ta.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.ta.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.AMY; import java.io.IOException; import java.nio.file.Path; @@ -17,22 +16,23 @@ 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.parser.exceptions.ParseException; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; -import seedu.address.model.person.Person; -import seedu.address.storage.JsonAddressBookStorage; -import seedu.address.storage.JsonUserPrefsStorage; -import seedu.address.storage.StorageManager; -import seedu.address.testutil.PersonBuilder; +import seedu.ta.logic.commands.AddContactCommand; +import seedu.ta.logic.commands.CommandResult; +import seedu.ta.logic.commands.ListContactCommand; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.contact.Contact; +import seedu.ta.storage.JsonTeachingAssistantStorage; +import seedu.ta.storage.JsonUserPrefsStorage; +import seedu.ta.storage.StorageManager; +import seedu.ta.testutil.ContactBuilder; public class LogicManagerTest { + private static final IOException DUMMY_IO_EXCEPTION = new IOException("dummy exception"); @TempDir @@ -43,10 +43,10 @@ public class LogicManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookStorage(temporaryFolder.resolve("addressBook.json")); + JsonTeachingAssistantStorage teachingAssistantStorage = + new JsonTeachingAssistantStorage(temporaryFolder.resolve("teachingassistant.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("userPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + StorageManager storage = new StorageManager(teachingAssistantStorage, userPrefsStorage); logic = new LogicManager(model, storage); } @@ -58,39 +58,44 @@ public void execute_invalidCommandFormat_throwsParseException() { @Test public void execute_commandExecutionError_throwsCommandException() { - String deleteCommand = "delete 9"; - assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + String deleteCommand = "cdelete 9"; + assertCommandException(deleteCommand, MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX); } @Test public void execute_validCommand_success() throws Exception { - String listCommand = ListCommand.COMMAND_WORD; - assertCommandSuccess(listCommand, ListCommand.MESSAGE_SUCCESS, model); + String listCommand = ListContactCommand.COMMAND_WORD; + assertCommandSuccess(listCommand, ListContactCommand.MESSAGE_SUCCESS, model); } @Test public void execute_storageThrowsIoException_throwsCommandException() { - // Setup LogicManager with JsonAddressBookIoExceptionThrowingStub - JsonAddressBookStorage addressBookStorage = - new JsonAddressBookIoExceptionThrowingStub(temporaryFolder.resolve("ioExceptionAddressBook.json")); + // Setup LogicManager with JsonTeachingAssistantIoExceptionThrowingStub + JsonTeachingAssistantStorage teachingAssistantStorage = + new JsonTeachingAssistantIoExceptionThrowingStub(temporaryFolder + .resolve("ioExceptionTeachingAssistant.json")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(temporaryFolder.resolve("ioExceptionUserPrefs.json")); - StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage); + StorageManager storage = new StorageManager(teachingAssistantStorage, userPrefsStorage); logic = new LogicManager(model, storage); // Execute add command - String addCommand = AddCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY - + ADDRESS_DESC_AMY; - Person expectedPerson = new PersonBuilder(AMY).withTags().build(); + String addCommand = AddContactCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY; + Contact expectedContact = new ContactBuilder(AMY).withTags().build(); ModelManager expectedModel = new ModelManager(); - expectedModel.addPerson(expectedPerson); + expectedModel.addContact(expectedContact); String expectedMessage = LogicManager.FILE_OPS_ERROR_MESSAGE + DUMMY_IO_EXCEPTION; assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel); } @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredPersonList().remove(0)); + public void getFilteredContactList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredContactList().remove(0)); + } + + @Test + public void getFilteredEntryList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> logic.getFilteredEntryList().remove(0)); } /** @@ -129,7 +134,7 @@ private void assertCommandException(String inputCommand, String expectedMessage) */ private void assertCommandFailure(String inputCommand, Class expectedException, String expectedMessage) { - Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs()); + Model expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel); } @@ -149,13 +154,14 @@ private void assertCommandFailure(String inputCommand, Class new AddContactCommand(null)); + } + + @Test + public void execute_contactAcceptedByModel_addSuccessful() throws CommandException { + Contact validContact = new ContactBuilder().build(); + CommandResult commandResult = new AddContactCommand(validContact).execute(model); + String feedBack = commandResult.getFeedbackToUser(); + assertEquals(String.format(AddContactCommand.MESSAGE_SUCCESS, validContact), feedBack); + } + + @Test + public void execute_duplicateContact_throwsDuplicateContactException() { + Contact validContact = new ContactBuilder().build(); + AddContactCommand addContactCommand = new AddContactCommand(validContact); + model.addContact(validContact); + String expectedMsg = MESSAGE_DUPLICATE_CONTACT; + assertThrows(CommandException.class, expectedMsg, () -> addContactCommand.execute(model)); + } + + @Test + public void equals() { + Contact alice = new ContactBuilder().withName("Alice").build(); + Contact bob = new ContactBuilder().withName("Bob").build(); + AddContactCommand addAliceCommand = new AddContactCommand(alice); + AddContactCommand addBobCommand = new AddContactCommand(bob); + + // same object -> returns true + assertTrue(addAliceCommand.equals(addAliceCommand)); + + // same values -> returns true + AddContactCommand addAliceCommandCopy = new AddContactCommand(alice); + assertTrue(addAliceCommand.equals(addAliceCommandCopy)); + + // different types -> returns false + assertFalse(addAliceCommand.equals(1)); + + // null -> returns false + assertFalse(addAliceCommand.equals(null)); + + // different contact -> returns false + assertFalse(addAliceCommand.equals(addBobCommand)); + } +} + diff --git a/src/test/java/seedu/ta/logic/commands/AddEntryCommandTest.java b/src/test/java/seedu/ta/logic/commands/AddEntryCommandTest.java new file mode 100644 index 00000000000..290dbd1a303 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/AddEntryCommandTest.java @@ -0,0 +1,109 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.CLASS_MEETING; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION; +import static seedu.ta.testutil.TypicalTeachingAssistant.EXTRA_CLASS; +import static seedu.ta.testutil.TypicalTeachingAssistant.getEmptyTypicalTeachingAssistant; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.exceptions.OverlappingEntryException; +import seedu.ta.testutil.EntryBuilder; + +/** + * Contains integration tests (interaction with the Model) for {@code AddEntryCommand}. + */ +public class AddEntryCommandTest { + + private Model emptyModel = new ModelManager(getEmptyTypicalTeachingAssistant(), new UserPrefs()); + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + + @Test + public void constructor_nullArgument_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new AddEntryCommand(null)); + } + + @Test + public void equals() { + Entry copiedConsultation = new EntryBuilder(CONSULTATION).build(); + + AddEntryCommand addEntryCommand0 = new AddEntryCommand(CONSULTATION); + AddEntryCommand addEntryCommand1 = new AddEntryCommand(CONSULTATION); + AddEntryCommand addEntryCommand2 = new AddEntryCommand(copiedConsultation); + AddEntryCommand addEntryCommand3 = new AddEntryCommand(CLASS_MEETING); + + assertEquals(addEntryCommand0, addEntryCommand1); + assertEquals(addEntryCommand0, addEntryCommand2); + assertNotEquals(addEntryCommand0, addEntryCommand3); + assertNotEquals(addEntryCommand1, addEntryCommand3); + } + + @Test + public void execute_nonOverlappingEntry_commandSuccess() { + // totally disjoint entries + emptyModel.addEntry(CLASS_MEETING); + emptyModel.addEntry(CONSULTATION); + + // one's start date is the same as the other's end date + Entry edgeCaseEntry0 = new EntryBuilder(EXTRA_CLASS) + .withStartDate("2022-02-01 16:30") + .build(); + emptyModel.addEntry(edgeCaseEntry0); + + // one's end date is the same as the other's start date + Entry edgeCaseEntry1 = new EntryBuilder(CONSULTATION) + .withStartDate("2022-01-01 01:00") + .withEndDate("2022-02-01 13:00") + .build(); + emptyModel.addEntry(edgeCaseEntry1); + } + + @Test + public void execute_sameEntryObjects_throwsOverlappingEntryException() { + emptyModel.addEntry(CONSULTATION); + assertThrows(OverlappingEntryException.class, () -> emptyModel.addEntry(CONSULTATION)); + assertThrows(OverlappingEntryException.class, () -> model.addEntry(CONSULTATION)); + } + + @Test + public void execute_sameFieldEntryObjects_throwsOverlappingEntryException() { + emptyModel.addEntry(CONSULTATION); + Entry copiedConsultation = new EntryBuilder(CONSULTATION).build(); + + assertThrows(OverlappingEntryException.class, () -> emptyModel.addEntry(copiedConsultation)); + assertThrows(OverlappingEntryException.class, () -> model.addEntry(copiedConsultation)); + } + + @Test + public void execute_overlappingEntryObjects_throwsOverlappingEntryException() { + Entry overlappingEntry0 = new EntryBuilder(CONSULTATION) + .withStartDate("2022-02-01 14:00") + .withEndDate("2022-02-01 15:00") + .build(); + Entry overlappingEntry1 = new EntryBuilder(CONSULTATION) + .withStartDate("2022-02-04 08:15") + .withEndDate("2022-02-04 09:15") + .build(); + + emptyModel.addEntry(CONSULTATION); + assertThrows(OverlappingEntryException.class, () -> emptyModel.addEntry(overlappingEntry0)); + assertThrows(OverlappingEntryException.class, () -> model.addEntry(overlappingEntry1)); + } + + @Test + public void execute_overdueEntry_commandSuccess() { + Entry overdueEntry = new EntryBuilder(CONSULTATION) + .withStartDate("2000-01-01 01:00") + .withEndDate("2000-01-01 02:00") + .build(); + emptyModel.addEntry(overdueEntry); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/ClearCommandTest.java b/src/test/java/seedu/ta/logic/commands/ClearCommandTest.java new file mode 100644 index 00000000000..dc61906d6ff --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/ClearCommandTest.java @@ -0,0 +1,31 @@ +package seedu.ta.logic.commands; + +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.getEmptyTypicalTeachingAssistant; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code ClearCommand}. + */ +public class ClearCommandTest { + + @Test + public void execute_emptyTeachingAssistant_success() { + Model expectedModel = new ModelManager(getEmptyTypicalTeachingAssistant(), new UserPrefs()); + Model model = new ModelManager(getEmptyTypicalTeachingAssistant(), new UserPrefs()); + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nonEmptyTeachingAssistant_success() { + Model expectedModel = new ModelManager(getEmptyTypicalTeachingAssistant(), new UserPrefs()); + Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + assertCommandSuccess(new ClearCommand(), model, ClearCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/ClearOverdueEntryCommandTest.java b/src/test/java/seedu/ta/logic/commands/ClearOverdueEntryCommandTest.java new file mode 100644 index 00000000000..e22b2f2859c --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/ClearOverdueEntryCommandTest.java @@ -0,0 +1,33 @@ +package seedu.ta.logic.commands; + +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code ClearOverdueEntryCommand}. + */ +public class ClearOverdueEntryCommandTest { + + @Test + public void execute_emptyTeachingAssistant_success() { + Model model = new ModelManager(); + Model expectedModel = new ModelManager(); + assertCommandSuccess(new ClearOverdueEntryCommand(), model, + ClearOverdueEntryCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_nonEmptyTeachingAssistant_success() { + Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + Model expectedModel = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + expectedModel.clearOverdueEntries(); + assertCommandSuccess(new ClearOverdueEntryCommand(), model, + ClearOverdueEntryCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/address/logic/commands/CommandResultTest.java b/src/test/java/seedu/ta/logic/commands/CommandResultTest.java similarity index 95% rename from src/test/java/seedu/address/logic/commands/CommandResultTest.java rename to src/test/java/seedu/ta/logic/commands/CommandResultTest.java index 4f3eb46e9ef..cde8cc61d9b 100644 --- a/src/test/java/seedu/address/logic/commands/CommandResultTest.java +++ b/src/test/java/seedu/ta/logic/commands/CommandResultTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.commands; +package seedu.ta.logic.commands; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -7,7 +7,11 @@ import org.junit.jupiter.api.Test; +/** + * Contains tests for {@code CommandResult}. + */ public class CommandResultTest { + @Test public void equals() { CommandResult commandResult = new CommandResult("feedback"); diff --git a/src/test/java/seedu/ta/logic/commands/CommandTestUtil.java b/src/test/java/seedu/ta/logic/commands/CommandTestUtil.java new file mode 100644 index 00000000000..08b63ced536 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/CommandTestUtil.java @@ -0,0 +1,170 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_END_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_START_DATE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.ta.testutil.Assert.assertThrows; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.TeachingAssistant; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactNameContainsKeywordsPredicate; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.EntryNameContainsKeywordsPredicate; +import seedu.ta.model.entry.ListEntryFormatPredicate; +import seedu.ta.testutil.EditContactDescriptorBuilder; + +/** + * Contains helper methods for testing commands. + */ +public class CommandTestUtil { + + public static final String VALID_NAME_AMY = "Amy Bee"; + public static final String VALID_NAME_BOB = "Bob Choo"; + public static final String VALID_PHONE_AMY = "11111111"; + public static final String VALID_PHONE_BOB = "22222222"; + public static final String VALID_EMAIL_AMY = "amy@example.com"; + public static final String VALID_EMAIL_BOB = "bob@example.com"; + public static final String VALID_TAG_HUSBAND = "husband"; + public static final String VALID_TAG_FRIEND = "friend"; + + public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY; + public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB; + public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY; + public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB; + public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY; + public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB; + public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND; + public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND; + + public static final String INVALID_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_TAG_DESC = " " + PREFIX_TAG + "hubby*"; // '*' not allowed in tags + + public static final String VALID_NAME_CONSULTATION = "Consultation"; + public static final String VALID_NAME_EXTRA = "Extra class"; + public static final String VALID_START_DATE_CONSULTATION = "2022-02-01 13:00"; + public static final String VALID_START_DATE_EXTRA = "2022-02-02 17:00"; + public static final String VALID_END_DATE_CONSULTATION = "2022-02-01 14:30"; + public static final String VALID_END_DATE_EXTRA = "2022-02-02 18:30"; + public static final String VALID_TAG_CONSULTATION = "History"; + public static final String VALID_TAG_EXTRA = "Math"; + + public static final String NAME_DESC_CONSULTATION = " " + PREFIX_NAME + VALID_NAME_CONSULTATION; + public static final String NAME_DESC_EXTRA = " " + PREFIX_NAME + VALID_NAME_EXTRA; + public static final String START_DESC_CONSULTATION = " " + PREFIX_START_DATE + VALID_START_DATE_CONSULTATION; + public static final String START_DESC_EXTRA = " " + PREFIX_START_DATE + VALID_START_DATE_EXTRA; + public static final String END_DESC_CONSULTATION = " " + PREFIX_END_DATE + VALID_END_DATE_CONSULTATION; + public static final String END_DESC_EXTRA = " " + PREFIX_END_DATE + VALID_END_DATE_EXTRA; + public static final String TAG_DESC_CONSULTATION = " " + PREFIX_TAG + VALID_TAG_CONSULTATION; + public static final String TAG_DESC_EXTRA = " " + PREFIX_TAG + VALID_TAG_EXTRA; + + public static final String INVALID_START_DATE = " " + PREFIX_START_DATE + "2020-02-30 10:00"; + public static final String INVALID_END_DATE = " " + PREFIX_END_DATE + "2021-10-10 24:30"; + public static final String INVALID_DATE_RANGE = " " + PREFIX_START_DATE + "2021-01-01 13:00" + + " " + PREFIX_END_DATE + "2021-01-01 12:00"; + public static final String PAST_DATE_INTERVAL = " " + PREFIX_START_DATE + "2020-01-01 12:00" + + " " + PREFIX_END_DATE + "2020-01-01 13:00"; + + public static final String PREAMBLE_WHITESPACE = "\t \r \n"; + public static final String PREAMBLE_NON_EMPTY = "NonEmptyPreamble"; + + public static final EditContactCommand.EditContactDescriptor CONTACT_DESC_AMY; + public static final EditContactCommand.EditContactDescriptor CONTACT_DESC_BOB; + + public static final ListEntryFormatPredicate ALL_PREDICATE = new ListEntryFormatPredicate(""); + public static final ListEntryFormatPredicate DAY_PREDICATE = new ListEntryFormatPredicate("day"); + public static final ListEntryFormatPredicate WEEK_PREDICATE = new ListEntryFormatPredicate("week"); + + static { + CONTACT_DESC_AMY = new EditContactDescriptorBuilder().withContactName(VALID_NAME_AMY) + .withContactPhone(VALID_PHONE_AMY).withContactEmail(VALID_EMAIL_AMY) + .withContactTags(VALID_TAG_FRIEND).build(); + CONTACT_DESC_BOB = new EditContactDescriptorBuilder().withContactName(VALID_NAME_BOB) + .withContactPhone(VALID_PHONE_BOB).withContactEmail(VALID_EMAIL_BOB) + .withContactTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).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) { + try { + CommandResult result = command.execute(actualModel); + assertEquals(expectedCommandResult, result); + assertEquals(expectedModel, actualModel); + } catch (CommandException ce) { + throw new AssertionError("Execution of command should not fail.", ce); + } + } + + /** + * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)} + * that takes a string {@code expectedMessage}. + */ + public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage, + Model expectedModel) { + CommandResult expectedCommandResult = new CommandResult(expectedMessage); + assertCommandSuccess(command, actualModel, expectedCommandResult, expectedModel); + } + + /** + * Executes the given {@code command}, confirms that
+ * - a {@code CommandException} is thrown
+ * - the CommandException message matches {@code expectedMessage}
+ * - the teaching assistant, filtered contact list and selected contact in {@code actualModel} remain unchanged + */ + public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) { + // we are unable to defensively copy the model for comparison later, so we can + // only do so by copying its components. + TeachingAssistant expectedTeachingAssistant = new TeachingAssistant(actualModel.getTeachingAssistant()); + List expectedFilteredList = new ArrayList<>(actualModel.getFilteredContactList()); + + assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel)); + assertEquals(expectedTeachingAssistant, actualModel.getTeachingAssistant()); + assertEquals(expectedFilteredList, actualModel.getFilteredContactList()); + } + + /** + * Updates {@code model}'s filtered list to show only the contact at the given {@code targetIndex} in the + * {@code model}'s teaching assistant. + */ + public static void showContactAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getFilteredContactList().size()); + + Contact contact = model.getFilteredContactList().get(targetIndex.getZeroBased()); + final String[] splitName = contact.getName().fullName.split("\\s+"); + model.updateFilteredContactList(new ContactNameContainsKeywordsPredicate(Arrays.asList(splitName[0]))); + + assertEquals(1, model.getFilteredContactList().size()); + } + + /** + * Updates {@code model}'s filtered list to show only the entry at the given {@code targetIndex} in the + * {@code model}'s list. + */ + public static void showEntryAtIndex(Model model, Index targetIndex) { + assertTrue(targetIndex.getZeroBased() < model.getFilteredEntryList().size()); + + Entry entry = model.getFilteredEntryList().get(targetIndex.getZeroBased()); + final String[] splitEntryName = entry.getEntryName().name.split("\\s+"); + model.updateFilteredEntryList(new EntryNameContainsKeywordsPredicate(Arrays.asList(splitEntryName[0]))); + + assertEquals(1, model.getFilteredEntryList().size()); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/DeleteContactCommandTest.java b/src/test/java/seedu/ta/logic/commands/DeleteContactCommandTest.java new file mode 100644 index 00000000000..c39950ebafc --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/DeleteContactCommandTest.java @@ -0,0 +1,110 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.logic.commands.CommandTestUtil.showContactAtIndex; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; +import static seedu.ta.testutil.TypicalIndices.INDEX_SECOND; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.core.Messages; +import seedu.ta.commons.core.index.Index; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.contact.Contact; + +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code DeleteContactCommand}. + */ +public class DeleteContactCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Contact contactToDelete = model.getFilteredContactList().get(INDEX_FIRST.getZeroBased()); + DeleteContactCommand deleteContactCommand = new DeleteContactCommand(INDEX_FIRST); + + String expectedMessage = String.format(DeleteContactCommand.MESSAGE_DELETE_CONTACT_SUCCESS, contactToDelete); + + ModelManager expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); + expectedModel.deleteContact(contactToDelete); + + assertCommandSuccess(deleteContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredContactList().size() + 1); + DeleteContactCommand deleteContactCommand = new DeleteContactCommand(outOfBoundIndex); + + assertCommandFailure(deleteContactCommand, model, Messages.MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showContactAtIndex(model, INDEX_FIRST); + + Contact contactToDelete = model.getFilteredContactList().get(INDEX_FIRST.getZeroBased()); + DeleteContactCommand deleteContactCommand = new DeleteContactCommand(INDEX_FIRST); + + String expectedMessage = String.format(DeleteContactCommand.MESSAGE_DELETE_CONTACT_SUCCESS, contactToDelete); + + Model expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); + expectedModel.deleteContact(contactToDelete); + showNoContact(expectedModel); + + assertCommandSuccess(deleteContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showContactAtIndex(model, INDEX_FIRST); + + Index outOfBoundIndex = INDEX_SECOND; + + // ensures that outOfBoundIndex is still within the bounds of the contact list of Teaching Assistant + assertTrue(outOfBoundIndex.getZeroBased() < model.getTeachingAssistant().getContactList().size()); + + DeleteContactCommand deleteContactCommand = new DeleteContactCommand(outOfBoundIndex); + + assertCommandFailure(deleteContactCommand, model, Messages.MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX); + } + + @Test + public void equals() { + DeleteContactCommand deleteFirstCommand = new DeleteContactCommand(INDEX_FIRST); + DeleteContactCommand deleteSecondCommand = new DeleteContactCommand(INDEX_SECOND); + + // same object -> returns true + assertTrue(deleteFirstCommand.equals(deleteFirstCommand)); + + // same values -> returns true + DeleteContactCommand deleteFirstCommandCopy = new DeleteContactCommand(INDEX_FIRST); + assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy)); + + // different types -> returns false + assertFalse(deleteFirstCommand.equals(1)); + + // null -> returns false + assertFalse(deleteFirstCommand.equals(null)); + + // different contact -> returns false + assertFalse(deleteFirstCommand.equals(deleteSecondCommand)); + } + + /** + * Updates {@code model}'s filtered list to show no one. + */ + private void showNoContact(Model model) { + model.updateFilteredContactList(p -> false); + + assertTrue(model.getFilteredContactList().isEmpty()); + } +} + diff --git a/src/test/java/seedu/ta/logic/commands/DeleteEntryCommandTest.java b/src/test/java/seedu/ta/logic/commands/DeleteEntryCommandTest.java new file mode 100644 index 00000000000..2bff9516b09 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/DeleteEntryCommandTest.java @@ -0,0 +1,109 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.logic.commands.CommandTestUtil.showEntryAtIndex; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; +import static seedu.ta.testutil.TypicalIndices.INDEX_SECOND; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.core.Messages; +import seedu.ta.commons.core.index.Index; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.entry.Entry; + +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code DeleteEntryCommand}. + */ +public class DeleteEntryCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + + @Test + public void execute_validIndexUnfilteredList_success() { + Entry entryToDelete = model.getFilteredEntryList().get(INDEX_FIRST.getZeroBased()); + DeleteEntryCommand deleteEntryCommand = new DeleteEntryCommand(INDEX_FIRST); + + String expectedMessage = String.format(DeleteEntryCommand.MESSAGE_DELETE_ENTRY_SUCCESS, entryToDelete); + + ModelManager expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); + expectedModel.deleteEntry(entryToDelete); + + assertCommandSuccess(deleteEntryCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexUnfilteredList_throwsCommandException() { + Index outOfBoundsIndex = Index.fromOneBased(model.getFilteredEntryList().size() + 1); + DeleteEntryCommand deleteEntryCommand = new DeleteEntryCommand(outOfBoundsIndex); + + assertCommandFailure(deleteEntryCommand, model, Messages.MESSAGE_INVALID_ENTRY_INDEX); + } + + @Test + public void execute_validIndexFilteredList_success() { + showEntryAtIndex(model, INDEX_FIRST); + + Entry entryToDelete = model.getFilteredEntryList().get(INDEX_FIRST.getZeroBased()); + DeleteEntryCommand deleteEntryCommand = new DeleteEntryCommand(INDEX_FIRST); + + String expectedMessage = String.format(DeleteEntryCommand.MESSAGE_DELETE_ENTRY_SUCCESS, entryToDelete); + + Model expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); + expectedModel.deleteEntry(entryToDelete); + showNoEntry(expectedModel); + + assertCommandSuccess(deleteEntryCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_invalidIndexFilteredList_throwsCommandException() { + showEntryAtIndex(model, INDEX_FIRST); + + Index outOfBoundIndex = INDEX_SECOND; + // ensures that outOfBoundIndex is still in bounds of the entry list + assertTrue(outOfBoundIndex.getZeroBased() < model.getTeachingAssistant().getEntryList().size()); + + DeleteEntryCommand deleteEntryCommand = new DeleteEntryCommand(outOfBoundIndex); + + assertCommandFailure(deleteEntryCommand, model, Messages.MESSAGE_INVALID_ENTRY_INDEX); + } + + @Test + public void equals() { + DeleteEntryCommand deleteFirstEntryCommand = new DeleteEntryCommand(INDEX_FIRST); + DeleteEntryCommand deleteSecondEntryCommand = new DeleteEntryCommand(INDEX_SECOND); + + //Same object -> true + assertTrue(deleteFirstEntryCommand.equals(deleteFirstEntryCommand)); + + //Same values -> true + DeleteEntryCommand deleteFirstEntryCommandCopy = new DeleteEntryCommand(INDEX_FIRST); + assertTrue(deleteFirstEntryCommand.equals(deleteFirstEntryCommandCopy)); + + //Different types -> false + assertFalse(deleteSecondEntryCommand.equals("2")); + + //null -> false + assertFalse(deleteSecondEntryCommand.equals(null)); + + //different entry -> false + assertFalse(deleteFirstEntryCommand.equals(deleteSecondEntryCommand)); + } + + /** + * Update the {@code model}'s filtered list to show no entry. + * @param model + */ + private void showNoEntry(Model model) { + model.updateFilteredEntryList(p -> false); + + assertTrue(model.getFilteredEntryList().isEmpty()); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/EditContactCommandTest.java b/src/test/java/seedu/ta/logic/commands/EditContactCommandTest.java new file mode 100644 index 00000000000..242280104d7 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/EditContactCommandTest.java @@ -0,0 +1,175 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.commons.core.Messages.MESSAGE_DUPLICATE_CONTACT; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX; +import static seedu.ta.logic.commands.CommandTestUtil.CONTACT_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.CONTACT_DESC_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandFailure; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.logic.commands.CommandTestUtil.showContactAtIndex; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; +import static seedu.ta.testutil.TypicalIndices.INDEX_SECOND; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.EditContactCommand.EditContactDescriptor; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.TeachingAssistant; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.contact.Contact; +import seedu.ta.testutil.ContactBuilder; +import seedu.ta.testutil.EditContactDescriptorBuilder; + +/** + * Contains integration tests (interaction with the Model) and unit tests for EditContactCommand. + */ + +public class EditContactCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + + @Test + public void execute_allFieldsSpecifiedUnfilteredList_success() { + Contact editedContact = new ContactBuilder().build(); + EditContactDescriptor descriptor = new EditContactDescriptorBuilder(editedContact).build(); + EditContactCommand editContactCommand = new EditContactCommand(INDEX_FIRST, descriptor); + + String expectedMessage = String.format(EditContactCommand.MESSAGE_EDIT_CONTACT_SUCCESS, editedContact); + + Model expectedModel = new ModelManager(new TeachingAssistant(model.getTeachingAssistant()), new UserPrefs()); + expectedModel.setContact(model.getFilteredContactList().get(0), editedContact); + + assertCommandSuccess(editContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_someFieldsSpecifiedUnfilteredList_success() { + Index indexLastContact = Index.fromOneBased(model.getFilteredContactList().size()); + Contact lastContact = model.getFilteredContactList().get(indexLastContact.getZeroBased()); + + ContactBuilder contactInList = new ContactBuilder(lastContact); + Contact editedContact = contactInList.withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB) + .withTags(VALID_TAG_HUSBAND).build(); + + EditContactDescriptor descriptor = new EditContactDescriptorBuilder().withContactName(VALID_NAME_BOB) + .withContactPhone(VALID_PHONE_BOB).withContactTags(VALID_TAG_HUSBAND).build(); + EditContactCommand editContactCommand = new EditContactCommand(indexLastContact, descriptor); + + String expectedMessage = String.format(EditContactCommand.MESSAGE_EDIT_CONTACT_SUCCESS, editedContact); + + Model expectedModel = new ModelManager(new TeachingAssistant(model.getTeachingAssistant()), new UserPrefs()); + expectedModel.setContact(lastContact, editedContact); + + assertCommandSuccess(editContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_noFieldSpecifiedUnfilteredList_success() { + EditContactCommand editContactCommand = + new EditContactCommand(INDEX_FIRST, new EditContactDescriptor()); + Contact editedContact = model.getFilteredContactList().get(INDEX_FIRST.getZeroBased()); + + String expectedMessage = String.format(EditContactCommand.MESSAGE_EDIT_CONTACT_SUCCESS, editedContact); + + Model expectedModel = new ModelManager(new TeachingAssistant(model.getTeachingAssistant()), new UserPrefs()); + + assertCommandSuccess(editContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_filteredList_success() { + showContactAtIndex(model, INDEX_FIRST); + + Contact contactInFilteredList = model.getFilteredContactList().get(INDEX_FIRST.getZeroBased()); + Contact editedContact = new ContactBuilder(contactInFilteredList).withName(VALID_NAME_BOB).build(); + EditContactCommand editContactCommand = new EditContactCommand(INDEX_FIRST, + new EditContactDescriptorBuilder().withContactName(VALID_NAME_BOB).build()); + + String expectedMessage = String.format(EditContactCommand.MESSAGE_EDIT_CONTACT_SUCCESS, editedContact); + + Model expectedModel = new ModelManager(new TeachingAssistant(model.getTeachingAssistant()), new UserPrefs()); + expectedModel.setContact(model.getFilteredContactList().get(0), editedContact); + + assertCommandSuccess(editContactCommand, model, expectedMessage, expectedModel); + } + + @Test + public void execute_duplicateContactUnfilteredList_failure() { + Contact firstContact = model.getFilteredContactList().get(INDEX_FIRST.getZeroBased()); + EditContactDescriptor descriptor = new EditContactDescriptorBuilder(firstContact).build(); + EditContactCommand editContactCommand = new EditContactCommand(INDEX_SECOND, descriptor); + + assertCommandFailure(editContactCommand, model, MESSAGE_DUPLICATE_CONTACT); + } + + @Test + public void execute_duplicateContactFilteredList_failure() { + showContactAtIndex(model, INDEX_FIRST); + + // edit Contact in filtered list into a duplicate in teaching assistant + Contact contactInList = model.getTeachingAssistant().getContactList().get(INDEX_SECOND.getZeroBased()); + EditContactCommand editContactCommand = new EditContactCommand(INDEX_FIRST, + new EditContactDescriptorBuilder(contactInList).build()); + + assertCommandFailure(editContactCommand, model, MESSAGE_DUPLICATE_CONTACT); + } + + @Test + public void execute_invalidContactIndexUnfilteredList_failure() { + Index outOfBoundIndex = Index.fromOneBased(model.getFilteredContactList().size() + 1); + EditContactDescriptor descriptor = new EditContactDescriptorBuilder().withContactName(VALID_NAME_BOB).build(); + EditContactCommand editContactCommand = new EditContactCommand(outOfBoundIndex, descriptor); + + assertCommandFailure(editContactCommand, model, MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX); + } + + /** + * Edit filtered list where index is larger than size of filtered list, + * but smaller than size of contact list in Teaching Assistant + */ + @Test + public void execute_invalidContactIndexFilteredList_failure() { + showContactAtIndex(model, INDEX_FIRST); + Index outOfBoundIndex = INDEX_SECOND; + // ensures that outOfBoundIndex is still in bounds of teaching assistant list + assertTrue(outOfBoundIndex.getZeroBased() < model.getTeachingAssistant().getContactList().size()); + + EditContactCommand editContactCommand = new EditContactCommand(outOfBoundIndex, + new EditContactDescriptorBuilder().withContactName(VALID_NAME_BOB).build()); + + assertCommandFailure(editContactCommand, model, MESSAGE_INVALID_CONTACT_DISPLAYED_INDEX); + } + + @Test + public void equals() { + final EditContactCommand standardCommand = new EditContactCommand(INDEX_FIRST, CONTACT_DESC_AMY); + + // same values -> returns true + EditContactDescriptor copyDescriptor = new EditContactDescriptor(CONTACT_DESC_AMY); + EditContactCommand commandWithSameValues = new EditContactCommand(INDEX_FIRST, 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 ClearCommand())); + + // different index -> returns false + assertFalse(standardCommand.equals(new EditContactCommand(INDEX_SECOND, CONTACT_DESC_AMY))); + + // different descriptor -> returns false + assertFalse(standardCommand.equals(new EditContactCommand(INDEX_FIRST, CONTACT_DESC_BOB))); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/EditContactDescriptorTest.java b/src/test/java/seedu/ta/logic/commands/EditContactDescriptorTest.java new file mode 100644 index 00000000000..4d964e1c5b5 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/EditContactDescriptorTest.java @@ -0,0 +1,54 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.logic.commands.CommandTestUtil.CONTACT_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.CONTACT_DESC_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.EditContactCommand.EditContactDescriptor; +import seedu.ta.testutil.EditContactDescriptorBuilder; + +public class EditContactDescriptorTest { + + @Test + public void equals() { + // same values -> returns true + EditContactDescriptor descriptorWithSameValues = new EditContactDescriptor(CONTACT_DESC_AMY); + assertTrue(CONTACT_DESC_AMY.equals(descriptorWithSameValues)); + + // same object -> returns true + assertTrue(CONTACT_DESC_AMY.equals(CONTACT_DESC_AMY)); + + // null -> returns false + assertFalse(CONTACT_DESC_AMY.equals(null)); + + // different types -> returns false + assertFalse(CONTACT_DESC_AMY.equals(5)); + + // different values -> returns false + assertFalse(CONTACT_DESC_AMY.equals(CONTACT_DESC_BOB)); + + // different contact name -> returns false + EditContactDescriptor editedAmy = new EditContactDescriptorBuilder(CONTACT_DESC_AMY) + .withContactName(VALID_NAME_BOB).build(); + assertFalse(CONTACT_DESC_AMY.equals(editedAmy)); + + // different contact phone -> returns false + editedAmy = new EditContactDescriptorBuilder(CONTACT_DESC_AMY).withContactPhone(VALID_PHONE_BOB).build(); + assertFalse(CONTACT_DESC_AMY.equals(editedAmy)); + + // different contact email -> returns false + editedAmy = new EditContactDescriptorBuilder(CONTACT_DESC_AMY).withContactEmail(VALID_EMAIL_BOB).build(); + assertFalse(CONTACT_DESC_AMY.equals(editedAmy)); + + // different contact tags -> returns false + editedAmy = new EditContactDescriptorBuilder(CONTACT_DESC_AMY).withContactTags(VALID_TAG_HUSBAND).build(); + assertFalse(CONTACT_DESC_AMY.equals(editedAmy)); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/EditEntryCommandTest.java b/src/test/java/seedu/ta/logic/commands/EditEntryCommandTest.java new file mode 100644 index 00000000000..441ae3338e1 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/EditEntryCommandTest.java @@ -0,0 +1,150 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.exceptions.CommandException; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.TemporaryEntry; + +/** + * Contains integration tests (interaction with the Model) for {@code EditEntryCommand}. + */ +public class EditEntryCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + + @Test + public void constructor_nullArguments_throwsNullPointerException() { + Index index = Index.fromZeroBased(0); + TemporaryEntry tempEntry = new TemporaryEntry(); + + assertThrows(NullPointerException.class, () -> new EditEntryCommand(index, null)); + assertThrows(NullPointerException.class, () -> new EditEntryCommand(null, tempEntry)); + assertThrows(NullPointerException.class, () -> new EditEntryCommand(null, null)); + } + + @Test + public void equals() { + Index index0 = Index.fromZeroBased(0); + Index index1 = Index.fromZeroBased(0); + Index index2 = Index.fromZeroBased(1); + TemporaryEntry tempEntry0 = new TemporaryEntry(); + TemporaryEntry tempEntry1 = new TemporaryEntry(); + + EditEntryCommand editEntryCommand0 = new EditEntryCommand(index0, tempEntry0); + EditEntryCommand editEntryCommand1 = new EditEntryCommand(index1, tempEntry0); + EditEntryCommand editEntryCommand2 = new EditEntryCommand(index1, tempEntry1); + EditEntryCommand editEntryCommand3 = new EditEntryCommand(index2, tempEntry0); + + assertEquals(editEntryCommand0, editEntryCommand1); + assertEquals(editEntryCommand0, editEntryCommand2); + assertNotEquals(editEntryCommand0, editEntryCommand3); + } + + @Test + public void execute_disjointEntries_commandSuccess() { + // CLASS_MEETING 2022-02-01 15:00 2022-02-01 16:30 + EntryDate entryDate13 = new EntryDate("2020-02-01 13:00"); + EntryDate entryDate14 = new EntryDate("2020-02-01 14:00"); + EntryDate entryDate15 = new EntryDate("2020-02-01 15:00"); + + // editing CONSULTATION + Index target = Index.fromZeroBased(0); + + // totally disjoint + TemporaryEntry tempEntry0 = new TemporaryEntry() + .setEntryStartDate(entryDate13) + .setEntryEndDate(entryDate14); + + try { + new EditEntryCommand(target, tempEntry0).execute(model); + } catch (CommandException e) { + throw new AssertionError("Command should be executed successfully!"); + } + + // one's end date is another's start date + TemporaryEntry tempEntry1 = new TemporaryEntry() + .setEntryEndDate(entryDate15); + + try { + new EditEntryCommand(target, tempEntry1).execute(model); + } catch (CommandException e) { + throw new AssertionError("Command should be executed successfully!"); + } + } + + @Test + public void execute_invalidEntryIndex_throwsCommandException() { + // CLASS_MEETING 2022-02-01 15:00 2022-02-01 16:30 + EntryDate entryDate13 = new EntryDate("2022-02-01 13:00"); + EntryDate entryDate14 = new EntryDate("2022-02-01 14:00"); + + // editing CONSULTATION + Index invalidTarget = Index.fromOneBased(9); + + TemporaryEntry tempEntry = new TemporaryEntry() + .setEntryStartDate(entryDate13) + .setEntryEndDate(entryDate14); + + assertThrows(CommandException.class, () -> new EditEntryCommand(invalidTarget, tempEntry).execute(model)); + } + + @Test + public void execute_invalidDateRange_throwsCommandException() { + // CLASS_MEETING 2022-02-01 15:00 2022-02-01 16:30 + EntryDate entryDate13 = new EntryDate("2022-02-01 13:00"); + EntryDate entryDate14 = new EntryDate("2022-02-01 14:00"); + + // editing CONSULTATION + Index target = Index.fromZeroBased(0); + + // same date + TemporaryEntry tempEntry = new TemporaryEntry() + .setEntryStartDate(entryDate14) + .setEntryEndDate(entryDate13); + + assertThrows(CommandException.class, () -> new EditEntryCommand(target, tempEntry).execute(model)); + } + + @Test + public void execute_sameStartAndEndDate_throwsCommandException() { + // CLASS_MEETING 2022-02-01 15:00 2022-02-01 16:30 + EntryDate entryDate14 = new EntryDate("2022-02-01 14:00"); + + // editing CONSULTATION + Index target = Index.fromZeroBased(0); + + // same date + TemporaryEntry tempEntry = new TemporaryEntry() + .setEntryStartDate(entryDate14) + .setEntryEndDate(entryDate14); + + assertThrows(CommandException.class, () -> new EditEntryCommand(target, tempEntry).execute(model)); + } + + @Test + public void execute_overlappingEntries_throwsCommandException() { + // CLASS_MEETING 2022-02-01 15:00 2022-02-01 16:30 + EntryDate entryDate16 = new EntryDate("2022-02-01 16:00"); + EntryDate entryDate17 = new EntryDate("2022-02-01 17:00"); + + // editing CONSULTATION + Index target = Index.fromZeroBased(0); + + // totally disjoint + TemporaryEntry tempEntry = new TemporaryEntry() + .setEntryStartDate(entryDate16) + .setEntryEndDate(entryDate17); + + assertThrows(CommandException.class, () -> new EditEntryCommand(target, tempEntry).execute(model)); + } +} diff --git a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java b/src/test/java/seedu/ta/logic/commands/ExitCommandTest.java similarity index 54% rename from src/test/java/seedu/address/logic/commands/ExitCommandTest.java rename to src/test/java/seedu/ta/logic/commands/ExitCommandTest.java index 9533c473875..f65a8fac6f5 100644 --- a/src/test/java/seedu/address/logic/commands/ExitCommandTest.java +++ b/src/test/java/seedu/ta/logic/commands/ExitCommandTest.java @@ -1,14 +1,18 @@ -package seedu.address.logic.commands; +package seedu.ta.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.logic.commands.ExitCommand.MESSAGE_EXIT_ACKNOWLEDGEMENT; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code ExitCommand}. + */ public class ExitCommandTest { + private Model model = new ModelManager(); private Model expectedModel = new ModelManager(); diff --git a/src/test/java/seedu/ta/logic/commands/FilterContactCommandTest.java b/src/test/java/seedu/ta/logic/commands/FilterContactCommandTest.java new file mode 100644 index 00000000000..e90013cc3f4 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/FilterContactCommandTest.java @@ -0,0 +1,92 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.ta.commons.core.Messages.MESSAGE_CONTACTS_LISTED_OVERVIEW; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.BEN; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.contact.ContactTagsContainKeywordsPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FilterContactCommand}. + */ +public class FilterContactCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + private Model expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); + + @Test + public void equals() { + ContactTagsContainKeywordsPredicate predicate0 = + new ContactTagsContainKeywordsPredicate(Collections.singletonList("a")); + ContactTagsContainKeywordsPredicate predicate1 = + new ContactTagsContainKeywordsPredicate(Collections.singletonList("a")); + ContactTagsContainKeywordsPredicate predicate2 = + new ContactTagsContainKeywordsPredicate(Collections.singletonList("b")); + + FilterContactCommand command0 = new FilterContactCommand(predicate0); + FilterContactCommand command1 = new FilterContactCommand(predicate1); + FilterContactCommand command2 = new FilterContactCommand(predicate2); + + assertEquals(command0, command0); + assertEquals(command0, command1); + assertNotEquals(command0, command2); + } + + @Test + public void execute_oneKeyword_noEntryFound() { + String expectedMessage = String.format(MESSAGE_CONTACTS_LISTED_OVERVIEW, 0); + ContactTagsContainKeywordsPredicate predicate = preparePredicate("a"); + FilterContactCommand command = new FilterContactCommand(predicate); + expectedModel.updateFilteredContactList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredContactList()); + } + + @Test + public void execute_oneKeyword_oneEntryFound() { + String expectedMessage = String.format(MESSAGE_CONTACTS_LISTED_OVERVIEW, 1); + ContactTagsContainKeywordsPredicate predicate = preparePredicate("owesMoney"); // case insensitive + FilterContactCommand command = new FilterContactCommand(predicate); + expectedModel.updateFilteredContactList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(BEN), model.getFilteredContactList()); + } + + @Test + public void execute_multipleKeywordsAndSearch_oneEntryFound() { + String expectedMessage = String.format(MESSAGE_CONTACTS_LISTED_OVERVIEW, 1); + ContactTagsContainKeywordsPredicate predicate = preparePredicate("friends owesMoney"); // case insensitive + FilterContactCommand command = new FilterContactCommand(predicate); + expectedModel.updateFilteredContactList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(BEN), model.getFilteredContactList()); + } + + @Test + public void execute_multipleKeywordsOrSearch_noEntryFound() { + String expectedMessage = String.format(MESSAGE_CONTACTS_LISTED_OVERVIEW, 0); + ContactTagsContainKeywordsPredicate predicate = preparePredicate("friends colleagues"); // case insensitive + FilterContactCommand command = new FilterContactCommand(predicate); + expectedModel.updateFilteredContactList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredContactList()); + } + + /** + * Parses {@code userInput} into a {@code ContactTagsContainKeywordsPredicate}. + */ + private ContactTagsContainKeywordsPredicate preparePredicate(String userInput) { + return new ContactTagsContainKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/FilterEntryCommandTest.java b/src/test/java/seedu/ta/logic/commands/FilterEntryCommandTest.java new file mode 100644 index 00000000000..10ef296918a --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/FilterEntryCommandTest.java @@ -0,0 +1,93 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.ta.commons.core.Messages.MESSAGE_ENTRIES_LISTED_OVERVIEW; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION; +import static seedu.ta.testutil.TypicalTeachingAssistant.DO_STUFF; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.entry.EntryTagsContainKeywordsPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FilterEntryCommand}. + */ +public class FilterEntryCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + + @Test + public void equals() { + EntryTagsContainKeywordsPredicate predicate0 = + new EntryTagsContainKeywordsPredicate(Collections.singletonList("a")); + EntryTagsContainKeywordsPredicate predicate1 = + new EntryTagsContainKeywordsPredicate(Collections.singletonList("a")); + EntryTagsContainKeywordsPredicate predicate2 = + new EntryTagsContainKeywordsPredicate(Collections.singletonList("b")); + + FilterEntryCommand command0 = new FilterEntryCommand(predicate0); + FilterEntryCommand command1 = new FilterEntryCommand(predicate1); + FilterEntryCommand command2 = new FilterEntryCommand(predicate2); + + assertEquals(command0, command0); + assertEquals(command0, command1); + assertNotEquals(command0, command2); + } + + @Test + public void execute_oneKeyword_noEntryFound() { + String expectedMessage = String.format(MESSAGE_ENTRIES_LISTED_OVERVIEW, 0); + EntryTagsContainKeywordsPredicate predicate = preparePredicate("a"); + FilterEntryCommand command = new FilterEntryCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredEntryList()); + } + + @Test + public void execute_oneKeyword_oneEntryFound() { + String expectedMessage = String.format(MESSAGE_ENTRIES_LISTED_OVERVIEW, 1); + EntryTagsContainKeywordsPredicate predicate = preparePredicate("history"); // case insensitive + FilterEntryCommand command = new FilterEntryCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(CONSULTATION), model.getFilteredEntryList()); + } + + @Test + public void execute_multipleKeywordsAndSearch_oneEntryFound() { + String expectedMessage = String.format(MESSAGE_ENTRIES_LISTED_OVERVIEW, 1); + EntryTagsContainKeywordsPredicate predicate = preparePredicate("important STUFF"); // case insensitive + FilterEntryCommand command = new FilterEntryCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(DO_STUFF), model.getFilteredEntryList()); + } + + @Test + public void execute_multipleKeywordsOrSearch_noEntryFound() { + String expectedMessage = String.format(MESSAGE_ENTRIES_LISTED_OVERVIEW, 0); + EntryTagsContainKeywordsPredicate predicate = preparePredicate("History Math"); // case insensitive + FilterEntryCommand command = new FilterEntryCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredEntryList()); + } + + /** + * Parses {@code userInput} into a {@code EntryTagsContainKeywordsPredicate}. + */ + private EntryTagsContainKeywordsPredicate preparePredicate(String userInput) { + return new EntryTagsContainKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/FindContactCommandTest.java b/src/test/java/seedu/ta/logic/commands/FindContactCommandTest.java new file mode 100644 index 00000000000..aacf17510e7 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/FindContactCommandTest.java @@ -0,0 +1,83 @@ +package seedu.ta.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.ta.commons.core.Messages.MESSAGE_CONTACTS_LISTED_OVERVIEW; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.contact.ContactNameContainsKeywordsPredicate; +import seedu.ta.testutil.TypicalTeachingAssistant; + +/** + * Contains integration tests (interaction with the Model) for {@code FindContactCommand}. + */ +public class FindContactCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + private Model expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); + + @Test + public void equals() { + ContactNameContainsKeywordsPredicate firstPredicate = + new ContactNameContainsKeywordsPredicate(Collections.singletonList("first")); + ContactNameContainsKeywordsPredicate secondPredicate = + new ContactNameContainsKeywordsPredicate(Collections.singletonList("second")); + + FindContactCommand findFirstCommand = new FindContactCommand(firstPredicate); + FindContactCommand findSecondCommand = new FindContactCommand(secondPredicate); + + // same object -> returns true + assertTrue(findFirstCommand.equals(findFirstCommand)); + + // same values -> returns true + FindContactCommand findFirstCommandCopy = new FindContactCommand(firstPredicate); + assertTrue(findFirstCommand.equals(findFirstCommandCopy)); + + // different types -> returns false + assertFalse(findFirstCommand.equals(1)); + + // null -> returns false + assertFalse(findFirstCommand.equals(null)); + + // different contact -> returns false + assertFalse(findFirstCommand.equals(findSecondCommand)); + } + + @Test + public void execute_oneKeyword_noContactFound() { + String expectedMessage = String.format(MESSAGE_CONTACTS_LISTED_OVERVIEW, 0); + ContactNameContainsKeywordsPredicate predicate = preparePredicate("Alison"); + FindContactCommand command = new FindContactCommand(predicate); + expectedModel.updateFilteredContactList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredContactList()); + } + + @Test + public void execute_oneKeyword_multipleContactsFound() { + String expectedMessage = String.format(MESSAGE_CONTACTS_LISTED_OVERVIEW, 2); + ContactNameContainsKeywordsPredicate predicate = preparePredicate("Meier"); + FindContactCommand command = new FindContactCommand(predicate); + expectedModel.updateFilteredContactList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(TypicalTeachingAssistant.BEN, TypicalTeachingAssistant.DAVID), + model.getFilteredContactList()); + } + + /** + * Parses {@code userInput} into a {@code ContactNameContainsKeywordsPredicate}. + */ + private ContactNameContainsKeywordsPredicate preparePredicate(String userInput) { + return new ContactNameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/FindEntryCommandTest.java b/src/test/java/seedu/ta/logic/commands/FindEntryCommandTest.java new file mode 100644 index 00000000000..74cff7c9df1 --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/FindEntryCommandTest.java @@ -0,0 +1,90 @@ +package seedu.ta.logic.commands; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static seedu.ta.commons.core.Messages.MESSAGE_ENTRIES_LISTED_OVERVIEW; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.EntryNameContainsKeywordsPredicate; +import seedu.ta.testutil.EntryBuilder; + +/** + * Contains integration tests (interaction with the Model) for {@code FindEntryCommand}. + */ +public class FindEntryCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + + @Test + public void equals() { + EntryNameContainsKeywordsPredicate predicate0 = + new EntryNameContainsKeywordsPredicate(Collections.singletonList("a")); + EntryNameContainsKeywordsPredicate predicate1 = + new EntryNameContainsKeywordsPredicate(Collections.singletonList("a")); + EntryNameContainsKeywordsPredicate predicate2 = + new EntryNameContainsKeywordsPredicate(Collections.singletonList("b")); + + FindEntryCommand command0 = new FindEntryCommand(predicate0); + FindEntryCommand command1 = new FindEntryCommand(predicate1); + FindEntryCommand command2 = new FindEntryCommand(predicate2); + + assertEquals(command0, command0); + assertEquals(command0, command1); + assertNotEquals(command0, command2); + } + + @Test + public void execute_oneKeyword_noEntryFound() { + String expectedMessage = String.format(MESSAGE_ENTRIES_LISTED_OVERVIEW, 0); + EntryNameContainsKeywordsPredicate predicate = preparePredicate("a"); + FindEntryCommand command = new FindEntryCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredEntryList()); + } + + @Test + public void execute_oneKeyword_oneEntryFound() { + String expectedMessage = String.format(MESSAGE_ENTRIES_LISTED_OVERVIEW, 1); + EntryNameContainsKeywordsPredicate predicate = preparePredicate("CONSULTATION"); + FindEntryCommand command = new FindEntryCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(CONSULTATION), model.getFilteredEntryList()); + } + + @Test + public void execute_oneKeyword_twoEntriesFound() { + Entry impostor = new EntryBuilder(CONSULTATION) + .withStartDate("2022-02-05 01:00") + .withEndDate("2022-02-05 02:00") + .build(); + model.addEntry(impostor); + expectedModel.addEntry(impostor); + String expectedMessage = String.format(MESSAGE_ENTRIES_LISTED_OVERVIEW, 2); + EntryNameContainsKeywordsPredicate predicate = preparePredicate("CONSULTATION"); + FindEntryCommand command = new FindEntryCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(CONSULTATION, impostor), model.getFilteredEntryList()); + } + + /** + * Parses {@code userInput} into a {@code EntryNameContainsKeywordsPredicate}. + */ + private EntryNameContainsKeywordsPredicate preparePredicate(String userInput) { + return new EntryNameContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+"))); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/FreeCommandTest.java b/src/test/java/seedu/ta/logic/commands/FreeCommandTest.java new file mode 100644 index 00000000000..09c3aeb513a --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/FreeCommandTest.java @@ -0,0 +1,89 @@ +package seedu.ta.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.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.CLASS_MEETING; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import java.util.Arrays; +import java.util.Collections; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.ListOccupyingEntryPredicate; + +/** + * Contains integration tests (interaction with the Model) for {@code FreeCommand}. + */ +public class FreeCommandTest { + + private Model model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + private Model expectedModel = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + + @Test + public void equals() { + EntryDate firstStartDate = new EntryDate("2022-02-01 13:00"); + EntryDate firstEndDate = new EntryDate("2022-02-01 15:30"); + EntryDate secondStartDate = new EntryDate("2022-02-02 14:00"); + EntryDate secondEndDate = new EntryDate("2022-02-02 15:00"); + ListOccupyingEntryPredicate firstPredicate = + new ListOccupyingEntryPredicate(firstStartDate, firstEndDate); + ListOccupyingEntryPredicate secondPredicate = + new ListOccupyingEntryPredicate(secondStartDate, secondEndDate); + + FreeCommand freeFirstCommand = new FreeCommand(firstPredicate); + FreeCommand freeSecondCommand = new FreeCommand(secondPredicate); + + // same object -> returns true + assertTrue(freeFirstCommand.equals(freeFirstCommand)); + + // same values -> returns true + FreeCommand freeFirstCommandCopy = new FreeCommand(firstPredicate); + assertTrue(freeFirstCommand.equals(freeFirstCommandCopy)); + + // different types -> returns false + assertFalse(freeFirstCommand.equals(1)); + + // null -> returns false + assertFalse(freeFirstCommand.equals(null)); + + // different contact -> returns false + assertFalse(freeFirstCommand.equals(freeSecondCommand)); + } + + @Test + public void execute_intervalNotOccupied_freeMessageShown() { + String expectedMessage = FreeCommand.MESSAGE_FREE; + ListOccupyingEntryPredicate predicate = preparePredicate("2022-02-03 13:00", "2022-02-03 15:30"); + FreeCommand command = new FreeCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Collections.emptyList(), model.getFilteredEntryList()); + } + + @Test + public void execute_intervalOccupied_notFreeMessageShown() { + String expectedMessage = FreeCommand.MESSAGE_NOT_FREE; + ListOccupyingEntryPredicate predicate = preparePredicate("2022-02-01 13:00", "2022-02-01 15:30"); + FreeCommand command = new FreeCommand(predicate); + expectedModel.updateFilteredEntryList(predicate); + assertCommandSuccess(command, model, expectedMessage, expectedModel); + assertEquals(Arrays.asList(CONSULTATION, CLASS_MEETING), model.getFilteredEntryList()); + } + + /** + * Parses {@code userInput} into a {@code ListOccupyingEntryPredicate}. + */ + private ListOccupyingEntryPredicate preparePredicate(String startDate, String endDate) { + EntryDate start = new EntryDate(startDate); + EntryDate end = new EntryDate(endDate); + return new ListOccupyingEntryPredicate(start, end); + } +} diff --git a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java b/src/test/java/seedu/ta/logic/commands/HelpCommandTest.java similarity index 54% rename from src/test/java/seedu/address/logic/commands/HelpCommandTest.java rename to src/test/java/seedu/ta/logic/commands/HelpCommandTest.java index 4904fc4352e..e5f8177e3b4 100644 --- a/src/test/java/seedu/address/logic/commands/HelpCommandTest.java +++ b/src/test/java/seedu/ta/logic/commands/HelpCommandTest.java @@ -1,14 +1,18 @@ -package seedu.address.logic.commands; +package seedu.ta.logic.commands; -import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess; -import static seedu.address.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.logic.commands.HelpCommand.SHOWING_HELP_MESSAGE; import org.junit.jupiter.api.Test; -import seedu.address.model.Model; -import seedu.address.model.ModelManager; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code HelpCommand}. + */ public class HelpCommandTest { + private Model model = new ModelManager(); private Model expectedModel = new ModelManager(); diff --git a/src/test/java/seedu/ta/logic/commands/ListContactCommandTest.java b/src/test/java/seedu/ta/logic/commands/ListContactCommandTest.java new file mode 100644 index 00000000000..c678173251e --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/ListContactCommandTest.java @@ -0,0 +1,39 @@ +package seedu.ta.logic.commands; + +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.logic.commands.CommandTestUtil.showContactAtIndex; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code ListContactCommand}. + */ +public class ListContactCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(new ListContactCommand(), model, ListContactCommand.MESSAGE_SUCCESS, expectedModel); + } + + @Test + public void execute_listIsFiltered_showsEverything() { + showContactAtIndex(model, INDEX_FIRST); + assertCommandSuccess(new ListContactCommand(), model, ListContactCommand.MESSAGE_SUCCESS, expectedModel); + } +} diff --git a/src/test/java/seedu/ta/logic/commands/ListEntryCommandTest.java b/src/test/java/seedu/ta/logic/commands/ListEntryCommandTest.java new file mode 100644 index 00000000000..40a2e2592cd --- /dev/null +++ b/src/test/java/seedu/ta/logic/commands/ListEntryCommandTest.java @@ -0,0 +1,36 @@ +package seedu.ta.logic.commands; + +import static seedu.ta.logic.commands.CommandTestUtil.ALL_PREDICATE; +import static seedu.ta.logic.commands.CommandTestUtil.assertCommandSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.core.Messages; +import seedu.ta.model.Model; +import seedu.ta.model.ModelManager; +import seedu.ta.model.UserPrefs; + +/** + * Contains integration tests (interaction with the Model) and unit tests for {@code ListEntryCommand}. + */ +public class ListEntryCommandTest { + + private Model model; + private Model expectedModel; + + @BeforeEach + public void setUp() { + model = new ModelManager(getTypicalTeachingAssistant(), new UserPrefs()); + expectedModel = new ModelManager(model.getTeachingAssistant(), new UserPrefs()); + + } + + @Test + public void execute_listIsNotFiltered_showsSameList() { + assertCommandSuccess(new ListEntryCommand(ALL_PREDICATE), model, + String.format(Messages.MESSAGE_ENTRIES_LISTED_OVERVIEW, + model.getFilteredEntryList().size()), expectedModel); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/AddContactCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/AddContactCommandParserTest.java new file mode 100644 index 00000000000..0c643b7349e --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/AddContactCommandParserTest.java @@ -0,0 +1,130 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.NAME_DESC_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.PHONE_DESC_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static seedu.ta.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.ta.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; +import static seedu.ta.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_NAME_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.AMY; +import static seedu.ta.testutil.TypicalTeachingAssistant.BOB; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.AddContactCommand; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.tag.Tag; +import seedu.ta.testutil.ContactBuilder; + +public class AddContactCommandParserTest { + + private final AddContactCommandParser parser = new AddContactCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Contact expectedContact = new ContactBuilder(BOB).withTags(VALID_TAG_FRIEND).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + TAG_DESC_FRIEND, new AddContactCommand(expectedContact)); + + // multiple names - last name accepted + assertParseSuccess(parser, NAME_DESC_AMY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + TAG_DESC_FRIEND, new AddContactCommand(expectedContact)); + + // multiple phones - last phone accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_AMY + PHONE_DESC_BOB + EMAIL_DESC_BOB + + TAG_DESC_FRIEND, new AddContactCommand(expectedContact)); + + // multiple emails - last email accepted + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_AMY + EMAIL_DESC_BOB + + TAG_DESC_FRIEND, new AddContactCommand(expectedContact)); + + // multiple tags - all accepted + Contact expectedContactMultipleTags = new ContactBuilder(BOB).withTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) + .build(); + assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddContactCommand(expectedContactMultipleTags)); + } + + @Test + public void parse_optionalFieldsMissing_success() { + // zero tags + Contact expectedContact = new ContactBuilder(AMY).withTags().build(); + assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY, + new AddContactCommand(expectedContact)); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddContactCommand.MESSAGE_USAGE); + + // missing name prefix + assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB, + expectedMessage); + + // missing phone prefix + assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB, + expectedMessage); + + // missing email prefix + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB, + expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB, + expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + String invalidUserInput = "INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_FRIEND"; + String expectedMsg = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddContactCommand.MESSAGE_USAGE); + // invalid name + assertParseFailure(parser, invalidUserInput, expectedMsg); + expectedMsg = String.format("Phone number given: %s\n%s", + INVALID_PHONE_DESC.substring(3), ContactPhone.MESSAGE_CONSTRAINTS); + + assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB + TAG_DESC_HUSBAND + + TAG_DESC_FRIEND, expectedMsg); + + expectedMsg = String.format("Email given: %s\n%s", + INVALID_EMAIL_DESC.substring(3), ContactEmail.MESSAGE_CONSTRAINTS); + + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, expectedMsg); + String invalidTestDesc = INVALID_TAG_DESC.substring(3) + "friend"; + expectedMsg = String.format("Tag given: %s\n%s", invalidTestDesc , Tag.MESSAGE_CONSTRAINTS); + + assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + INVALID_TAG_DESC + VALID_TAG_FRIEND, expectedMsg); + + expectedMsg = String.format("Name given: %s\n%s", + INVALID_NAME_DESC.substring(3), ContactName.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB, expectedMsg); + expectedMsg = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddContactCommand.MESSAGE_USAGE); + // non-empty preamble + assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, expectedMsg); + } +} + diff --git a/src/test/java/seedu/ta/logic/parser/AddEntryCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/AddEntryCommandParserTest.java new file mode 100644 index 00000000000..d3e683b90a4 --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/AddEntryCommandParserTest.java @@ -0,0 +1,142 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.commands.CommandTestUtil.END_DESC_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.END_DESC_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_END_DATE; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_START_DATE; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.NAME_DESC_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.NAME_DESC_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.PREAMBLE_NON_EMPTY; +import static seedu.ta.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE; +import static seedu.ta.logic.commands.CommandTestUtil.START_DESC_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.START_DESC_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.TAG_DESC_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.TAG_DESC_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_END_DATE_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_NAME_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_START_DATE_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_TAG_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_TAG_EXTRA; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION; +import static seedu.ta.testutil.TypicalTeachingAssistant.EXTRA_CLASS; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.AddEntryCommand; +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.tag.Tag; +import seedu.ta.testutil.EntryBuilder; + +/** + * Contains tests to make sure the parser instantiates the correct {@code AddEntryCommand}. + */ +public class AddEntryCommandParserTest { + + private final AddEntryCommandParser parser = new AddEntryCommandParser(); + + @Test + public void parse_allFieldsPresent_success() { + Entry expectedEntry = new EntryBuilder(EXTRA_CLASS).build(); + + // whitespace only preamble + assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_EXTRA + START_DESC_EXTRA + END_DESC_EXTRA + + TAG_DESC_EXTRA, new AddEntryCommand(expectedEntry)); + + // multiple names - last name accepted + assertParseSuccess(parser, NAME_DESC_CONSULTATION + NAME_DESC_EXTRA + START_DESC_EXTRA + END_DESC_EXTRA + + TAG_DESC_EXTRA, new AddEntryCommand(expectedEntry)); + + // multiple phones - last phone accepted + assertParseSuccess(parser, NAME_DESC_EXTRA + START_DESC_CONSULTATION + START_DESC_EXTRA + END_DESC_EXTRA + + TAG_DESC_EXTRA, new AddEntryCommand(expectedEntry)); + + // multiple emails - last email accepted + assertParseSuccess(parser, NAME_DESC_EXTRA + START_DESC_EXTRA + END_DESC_CONSULTATION + END_DESC_EXTRA + + TAG_DESC_EXTRA, new AddEntryCommand(expectedEntry)); + + // multiple addresses - last address accepted + assertParseSuccess(parser, NAME_DESC_EXTRA + START_DESC_EXTRA + END_DESC_EXTRA + + TAG_DESC_EXTRA, new AddEntryCommand(expectedEntry)); + + // multiple tags - all accepted + Entry expectedEntryMultipleTags = new EntryBuilder(EXTRA_CLASS) + .withTags(VALID_TAG_CONSULTATION, VALID_TAG_EXTRA) + .build(); + assertParseSuccess(parser, NAME_DESC_EXTRA + START_DESC_EXTRA + END_DESC_EXTRA + + TAG_DESC_CONSULTATION + TAG_DESC_EXTRA, new AddEntryCommand(expectedEntryMultipleTags)); + } + + @Test + public void parse_optionalFieldsMissing_success() { + // zero tags + Entry expectedEntry = new EntryBuilder(CONSULTATION).withTags().build(); + assertParseSuccess(parser, NAME_DESC_CONSULTATION + START_DESC_CONSULTATION + END_DESC_CONSULTATION, + new AddEntryCommand(expectedEntry)); + } + + @Test + public void parse_compulsoryFieldMissing_failure() { + String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddEntryCommand.MESSAGE_USAGE); + + // missing name prefix + assertParseFailure(parser, VALID_NAME_EXTRA + START_DESC_EXTRA + END_DESC_EXTRA, + expectedMessage); + + // missing phone prefix + assertParseFailure(parser, NAME_DESC_EXTRA + VALID_START_DATE_EXTRA + END_DESC_EXTRA, + expectedMessage); + + // missing email prefix + assertParseFailure(parser, NAME_DESC_EXTRA + START_DESC_EXTRA + VALID_END_DATE_EXTRA, + expectedMessage); + + // all prefixes missing + assertParseFailure(parser, VALID_NAME_EXTRA + VALID_START_DATE_EXTRA + VALID_END_DATE_EXTRA, + expectedMessage); + } + + @Test + public void parse_invalidValue_failure() { + String invalidUserInput = "INVALID_NAME_DESC + START_DESC_EXTRA + END_DESC_EXTRA + TAG_DESC_EXTRA"; + + // invalid name + String expectedMsg = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddEntryCommand.MESSAGE_USAGE); + assertParseFailure(parser, invalidUserInput, expectedMsg); + + // invalid start date + expectedMsg = String.format("Date given: %s\n%s", + INVALID_START_DATE.substring(4), EntryDate.DATE_CONSTRAINTS); + assertParseFailure(parser, NAME_DESC_EXTRA + INVALID_START_DATE + END_DESC_EXTRA + TAG_DESC_CONSULTATION + + TAG_DESC_EXTRA, expectedMsg); + + // invalid end date + expectedMsg = String.format("Date given: %s\n%s", + INVALID_END_DATE.substring(4), EntryDate.DATE_CONSTRAINTS); + assertParseFailure(parser, NAME_DESC_EXTRA + START_DESC_EXTRA + INVALID_END_DATE + TAG_DESC_CONSULTATION + + TAG_DESC_EXTRA, expectedMsg); + + // invalid tag + expectedMsg = String.format("Tag given: %s\n%s", + INVALID_TAG_DESC.substring(3) + "Math", Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, NAME_DESC_EXTRA + START_DESC_EXTRA + END_DESC_EXTRA + + INVALID_TAG_DESC + VALID_TAG_EXTRA, expectedMsg); + + // invalid entry name + expectedMsg = String.format("Name given: %s\n%s", + INVALID_NAME_DESC.substring(3), EntryName.NAME_CONSTRAINTS); + assertParseFailure(parser, INVALID_NAME_DESC + START_DESC_EXTRA + END_DESC_EXTRA, expectedMsg); + + // non-empty preamble + expectedMsg = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddEntryCommand.MESSAGE_USAGE); + assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_EXTRA + START_DESC_EXTRA + END_DESC_EXTRA + + TAG_DESC_CONSULTATION + TAG_DESC_EXTRA, expectedMsg); + } +} + diff --git a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java b/src/test/java/seedu/ta/logic/parser/ArgumentTokenizerTest.java similarity index 98% rename from src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java rename to src/test/java/seedu/ta/logic/parser/ArgumentTokenizerTest.java index c97308935f5..fac49c572de 100644 --- a/src/test/java/seedu/address/logic/parser/ArgumentTokenizerTest.java +++ b/src/test/java/seedu/ta/logic/parser/ArgumentTokenizerTest.java @@ -1,4 +1,4 @@ -package seedu.address.logic.parser; +package seedu.ta.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -7,6 +7,9 @@ import org.junit.jupiter.api.Test; +/** + * Contains tests to make sure the arguments are tokenized correctly. + */ public class ArgumentTokenizerTest { private final Prefix unknownPrefix = new Prefix("--u"); @@ -146,5 +149,4 @@ public void equalsMethod() { assertNotEquals(aaa, "aaa"); assertNotEquals(aaa, new Prefix("aab")); } - } diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/seedu/ta/logic/parser/CommandParserTestUtil.java similarity index 89% rename from src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java rename to src/test/java/seedu/ta/logic/parser/CommandParserTestUtil.java index e4c33515768..467a6d36961 100644 --- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java +++ b/src/test/java/seedu/ta/logic/parser/CommandParserTestUtil.java @@ -1,9 +1,9 @@ -package seedu.address.logic.parser; +package seedu.ta.logic.parser; import static org.junit.jupiter.api.Assertions.assertEquals; -import seedu.address.logic.commands.Command; -import seedu.address.logic.parser.exceptions.ParseException; +import seedu.ta.logic.commands.Command; +import seedu.ta.logic.parser.exceptions.ParseException; /** * Contains helper methods for testing command parsers. diff --git a/src/test/java/seedu/ta/logic/parser/DeleteContactCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/DeleteContactCommandParserTest.java new file mode 100644 index 00000000000..5097b5143bc --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/DeleteContactCommandParserTest.java @@ -0,0 +1,33 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.DeleteContactCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside of the DeleteContactCommand code. For example, inputs "1" and "1 abc" take the + * same path through the DeleteContactCommand, 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 DeleteContactCommandParserTest { + + private final DeleteContactCommandParser parser = new DeleteContactCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteContactCommand() { + assertParseSuccess(parser, "1", new DeleteContactCommand(INDEX_FIRST)); + } + + @Test + public void parse_invalidArgs_throwsParseException() { + String msg = DeleteContactCommand.MESSAGE_USAGE; + assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, msg)); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/DeleteEntryCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/DeleteEntryCommandParserTest.java new file mode 100644 index 00000000000..980255a34aa --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/DeleteEntryCommandParserTest.java @@ -0,0 +1,33 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.DeleteEntryCommand; + +/** + * As we are only doing white-box testing, our test cases do not cover path variations + * outside of the DeleteEntryCommand code. For example, inputs "1" and "1 abc" take the + * same path through the DeleteEntryCommand, 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 DeleteEntryCommandParserTest { + + private final DeleteEntryCommandParser parser = new DeleteEntryCommandParser(); + + @Test + public void parse_validArgs_returnsDeleteEntryCommand() { + assertParseSuccess(parser, "1", new DeleteEntryCommand(INDEX_FIRST)); + } + + @Test + public void parser_invalidArgs_throwsParseException() { + assertParseFailure(parser, "name", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteEntryCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/EditContactCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/EditContactCommandParserTest.java new file mode 100644 index 00000000000..5aca090d89e --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/EditContactCommandParserTest.java @@ -0,0 +1,198 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_NOT_EDITED; +import static seedu.ta.logic.commands.CommandTestUtil.EMAIL_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.EMAIL_DESC_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_PHONE_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.NAME_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.PHONE_DESC_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.PHONE_DESC_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.TAG_DESC_FRIEND; +import static seedu.ta.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_EMAIL_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_EMAIL_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_NAME_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_PHONE_AMY; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_PHONE_BOB; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_TAG_FRIEND; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; +import static seedu.ta.testutil.TypicalIndices.INDEX_THIRD; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.EditContactCommand; +import seedu.ta.logic.commands.EditContactCommand.EditContactDescriptor; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.tag.Tag; +import seedu.ta.testutil.EditContactDescriptorBuilder; + +/** + * Contains tests to make sure the parser instantiates the correct {@code EditContactCommand}. + */ +public class EditContactCommandParserTest { + + private static final String TAG_EMPTY = " " + PREFIX_TAG; + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditContactCommand.MESSAGE_USAGE); + + private final EditContactCommandParser parser = new EditContactCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified: cedit n/Amy Bee + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + + // no field specified: cedit Amy Bee + assertParseFailure(parser, "1", MESSAGE_NOT_EDITED); + + // no name and no field specified: cedit + assertParseFailure(parser, " ", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, + "1" + INVALID_NAME_DESC, + "Name given: James&\n" + ContactName.MESSAGE_CONSTRAINTS); // invalid name + assertParseFailure(parser, + "1" + INVALID_PHONE_DESC, + "Phone number given: 911a\n" + ContactPhone.MESSAGE_CONSTRAINTS); // invalid phone + assertParseFailure(parser, + "1" + INVALID_EMAIL_DESC, + "Email given: bob!yahoo\n" + ContactEmail.MESSAGE_CONSTRAINTS); // invalid email + assertParseFailure(parser, + "1" + INVALID_TAG_DESC, + "Tag given: hubby*\n" + Tag.MESSAGE_CONSTRAINTS); // invalid tag + + // invalid phone followed by valid email + assertParseFailure(parser, + "1" + INVALID_PHONE_DESC + EMAIL_DESC_AMY, + "Phone number given: 911a\n" + ContactPhone.MESSAGE_CONSTRAINTS); + + // valid phone followed by invalid phone. The test case for invalid phone followed by valid phone + // is tested at {@code parse_invalidValueFollowedByValidValue_success()} + assertParseFailure(parser, + "1" + PHONE_DESC_BOB + INVALID_PHONE_DESC, + "Phone number given: 911a\n" + ContactPhone.MESSAGE_CONSTRAINTS); + + // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Contact} being edited, + // parsing it together with a valid tag results in error + assertParseFailure(parser, + "1" + TAG_DESC_FRIEND + TAG_DESC_HUSBAND + TAG_EMPTY, + "Tag given: \n" + Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, + "1" + TAG_DESC_FRIEND + TAG_EMPTY + TAG_DESC_HUSBAND, + "Tag given: \n" + Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, + "1" + TAG_EMPTY + TAG_DESC_FRIEND + TAG_DESC_HUSBAND, + "Tag given: \n" + Tag.MESSAGE_CONSTRAINTS); + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_PHONE_AMY, + "Name given: James&\n" + ContactName.MESSAGE_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + String userInput = "1" + PHONE_DESC_BOB + TAG_DESC_HUSBAND + + EMAIL_DESC_AMY + NAME_DESC_AMY + TAG_DESC_FRIEND; + + EditContactDescriptor descriptor = new EditContactDescriptorBuilder().withContactName(VALID_NAME_AMY) + .withContactPhone(VALID_PHONE_BOB).withContactEmail(VALID_EMAIL_AMY) + .withContactTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build(); + EditContactCommand expectedCommand = new EditContactCommand(INDEX_FIRST, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + String userInput = "1" + PHONE_DESC_BOB + EMAIL_DESC_AMY; + + EditContactDescriptor descriptor = new EditContactDescriptorBuilder().withContactPhone(VALID_PHONE_BOB) + .withContactEmail(VALID_EMAIL_AMY).build(); + EditContactCommand expectedCommand = new EditContactCommand(INDEX_FIRST, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + // name + Index targetIndex = INDEX_THIRD; + String userInput = targetIndex.getOneBased() + NAME_DESC_AMY; + EditContactDescriptor descriptor = new EditContactDescriptorBuilder().withContactName(VALID_NAME_AMY).build(); + EditContactCommand expectedCommand = new EditContactCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // phone + userInput = targetIndex.getOneBased() + PHONE_DESC_AMY; + descriptor = new EditContactDescriptorBuilder().withContactPhone(VALID_PHONE_AMY).build(); + expectedCommand = new EditContactCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // email + userInput = targetIndex.getOneBased() + EMAIL_DESC_AMY; + descriptor = new EditContactDescriptorBuilder().withContactEmail(VALID_EMAIL_AMY).build(); + expectedCommand = new EditContactCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // tags + userInput = targetIndex.getOneBased() + TAG_DESC_FRIEND; + descriptor = new EditContactDescriptorBuilder().withContactTags(VALID_TAG_FRIEND).build(); + expectedCommand = new EditContactCommand(targetIndex, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + String userInput = "1" + PHONE_DESC_AMY + EMAIL_DESC_AMY + + TAG_DESC_FRIEND + PHONE_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND + + PHONE_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND; + + EditContactDescriptor descriptor = new EditContactDescriptorBuilder().withContactPhone(VALID_PHONE_BOB) + .withContactEmail(VALID_EMAIL_BOB).withContactTags(VALID_TAG_FRIEND, VALID_TAG_HUSBAND) + .build(); + EditContactCommand expectedCommand = new EditContactCommand(INDEX_FIRST, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValueFollowedByValidValue_success() { + // no other valid values specified + String userInput = "1" + INVALID_PHONE_DESC + PHONE_DESC_BOB; + EditContactDescriptor descriptor = new EditContactDescriptorBuilder().withContactPhone(VALID_PHONE_BOB).build(); + EditContactCommand expectedCommand = new EditContactCommand(INDEX_FIRST, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + + // other valid values specified + userInput = "1" + EMAIL_DESC_BOB + INVALID_PHONE_DESC + PHONE_DESC_BOB; + descriptor = new EditContactDescriptorBuilder() + .withContactPhone(VALID_PHONE_BOB).withContactEmail(VALID_EMAIL_BOB).build(); + expectedCommand = new EditContactCommand(INDEX_FIRST, descriptor); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_resetTags_success() { + String userInput = "1" + TAG_EMPTY; + + EditContactDescriptor descriptor = new EditContactDescriptorBuilder().withContactTags().build(); + EditContactCommand expectedCommand = new EditContactCommand(INDEX_FIRST, descriptor); + + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/EditEntryCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/EditEntryCommandParserTest.java new file mode 100644 index 00000000000..20270ae0042 --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/EditEntryCommandParserTest.java @@ -0,0 +1,180 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_NOT_EDITED; +import static seedu.ta.logic.commands.CommandTestUtil.END_DESC_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.END_DESC_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_END_DATE; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_NAME_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_START_DATE; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_TAG_DESC; +import static seedu.ta.logic.commands.CommandTestUtil.NAME_DESC_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.NAME_DESC_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.START_DESC_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.START_DESC_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.TAG_DESC_CONSULTATION; +import static seedu.ta.logic.commands.CommandTestUtil.TAG_DESC_EXTRA; +import static seedu.ta.logic.commands.CommandTestUtil.VALID_START_DATE_EXTRA; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; +import static seedu.ta.testutil.TypicalIndices.INDEX_THIRD; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION; +import static seedu.ta.testutil.TypicalTeachingAssistant.EXTRA_CLASS; + +import java.util.HashSet; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.core.index.Index; +import seedu.ta.logic.commands.EditEntryCommand; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.entry.TemporaryEntry; +import seedu.ta.model.tag.Tag; + +/** + * Contains tests to make sure the parser instantiates the correct {@code EditEntryCommand}. + */ +public class EditEntryCommandParserTest { + + private static final String TAG_EMPTY = " " + PREFIX_TAG; + + private static final String MESSAGE_INVALID_FORMAT = + String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditEntryCommand.MESSAGE_USAGE); + + private final EditEntryCommandParser parser = new EditEntryCommandParser(); + + @Test + public void parse_missingParts_failure() { + // no index specified: eedit n/event + assertParseFailure(parser, "", MESSAGE_INVALID_FORMAT); + + // no field specified: eedit event + assertParseFailure(parser, "1", MESSAGE_NOT_EDITED); + + // no name and no field specified: eedit + assertParseFailure(parser, " ", MESSAGE_INVALID_FORMAT); + } + + @Test + public void parse_invalidValue_failure() { + assertParseFailure(parser, + "1" + INVALID_NAME_DESC, + "Name given: James&\n" + EntryName.NAME_CONSTRAINTS); // invalid name + assertParseFailure(parser, + "1" + INVALID_START_DATE, + "Date given: 2020-02-30 10:00\n" + EntryDate.DATE_CONSTRAINTS); // invalid date + assertParseFailure(parser, + "1" + INVALID_END_DATE, + "Date given: 2021-10-10 24:30\n" + EntryDate.DATE_CONSTRAINTS); // invalid date + assertParseFailure(parser, + "1" + INVALID_TAG_DESC, + "Tag given: hubby*\n" + Tag.MESSAGE_CONSTRAINTS); // invalid tag + + // while parsing {@code PREFIX_TAG} alone will reset the tags of the {@code Entry} being edited, + // parsing it together with a valid tag results in error + assertParseFailure(parser, + "1" + TAG_DESC_CONSULTATION + TAG_DESC_EXTRA + TAG_EMPTY, + "Tag given: \n" + Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, + "1" + TAG_DESC_CONSULTATION + TAG_EMPTY + TAG_DESC_EXTRA, + "Tag given: \n" + Tag.MESSAGE_CONSTRAINTS); + assertParseFailure(parser, + "1" + TAG_EMPTY + TAG_DESC_CONSULTATION + TAG_DESC_EXTRA, + "Tag given: \n" + Tag.MESSAGE_CONSTRAINTS); + + // multiple invalid values, but only the first invalid value is captured + assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_END_DATE + VALID_START_DATE_EXTRA, + "Name given: James&\n" + EntryName.NAME_CONSTRAINTS); + } + + @Test + public void parse_allFieldsSpecified_success() { + String userInput = "1" + NAME_DESC_CONSULTATION + START_DESC_CONSULTATION + + END_DESC_CONSULTATION + TAG_DESC_CONSULTATION; + + TemporaryEntry tempEntry = new TemporaryEntry() + .setEntryName(CONSULTATION.getEntryName()) + .setEntryStartDate(CONSULTATION.getOriginalStartDate()) + .setEntryEndDate(CONSULTATION.getOriginalEndDate()) + .setTags(CONSULTATION.getTags()); + EditEntryCommand expectedCommand = new EditEntryCommand(INDEX_FIRST, tempEntry); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_someFieldsSpecified_success() { + String userInput = "1" + NAME_DESC_CONSULTATION + END_DESC_EXTRA; + + TemporaryEntry tempEntry = new TemporaryEntry() + .setEntryName(CONSULTATION.getEntryName()) + .setEntryEndDate(EXTRA_CLASS.getOriginalEndDate()); + EditEntryCommand expectedCommand = new EditEntryCommand(INDEX_FIRST, tempEntry); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_oneFieldSpecified_success() { + Index targetIndex = INDEX_THIRD; + + // name + String userInput = targetIndex.getOneBased() + NAME_DESC_CONSULTATION; + TemporaryEntry tempEntry = new TemporaryEntry().setEntryName(CONSULTATION.getEntryName()); + EditEntryCommand expectedCommand = new EditEntryCommand(targetIndex, tempEntry); + assertParseSuccess(parser, userInput, expectedCommand); + + // start date + userInput = targetIndex.getOneBased() + START_DESC_CONSULTATION; + tempEntry = new TemporaryEntry().setEntryStartDate(CONSULTATION.getOriginalStartDate()); + expectedCommand = new EditEntryCommand(targetIndex, tempEntry); + assertParseSuccess(parser, userInput, expectedCommand); + + // end date + userInput = targetIndex.getOneBased() + END_DESC_CONSULTATION; + tempEntry = new TemporaryEntry().setEntryEndDate(CONSULTATION.getOriginalEndDate()); + expectedCommand = new EditEntryCommand(targetIndex, tempEntry); + assertParseSuccess(parser, userInput, expectedCommand); + + // tags + userInput = targetIndex.getOneBased() + TAG_DESC_CONSULTATION; + tempEntry = new TemporaryEntry().setTags(CONSULTATION.getTags()); + expectedCommand = new EditEntryCommand(targetIndex, tempEntry); + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_multipleRepeatedFields_acceptsLast() { + String userInput = "1" + NAME_DESC_EXTRA + NAME_DESC_CONSULTATION; + + TemporaryEntry tempEntry = new TemporaryEntry().setEntryName(CONSULTATION.getEntryName()); + EditEntryCommand expectedCommand = new EditEntryCommand(INDEX_FIRST, tempEntry); + + assertParseSuccess(parser, userInput, expectedCommand); + } + + @Test + public void parse_invalidValuesPresent_throwsIllegalArgumentException() { + // invalid field + String userInput = "1" + INVALID_NAME_DESC + START_DESC_EXTRA; + assertParseFailure(parser, userInput, "Name given: James&\n" + EntryName.NAME_CONSTRAINTS); + + // still detects invalid value despite a valid field exists + userInput = "1" + NAME_DESC_EXTRA + INVALID_NAME_DESC + END_DESC_EXTRA; + assertParseFailure(parser, userInput, "Name given: James&\n" + EntryName.NAME_CONSTRAINTS); + } + + @Test + public void parse_resetTags_success() { + String userInput = "1" + TAG_EMPTY; + + TemporaryEntry tempEntry = new TemporaryEntry() + .setTags(new HashSet<>()); + EditEntryCommand expectedCommand = new EditEntryCommand(INDEX_FIRST, tempEntry); + + assertParseSuccess(parser, userInput, expectedCommand); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/FilterContactCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/FilterContactCommandParserTest.java new file mode 100644 index 00000000000..ed8485e971c --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/FilterContactCommandParserTest.java @@ -0,0 +1,37 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.FilterContactCommand; +import seedu.ta.model.contact.ContactTagsContainKeywordsPredicate; + +/** + * Contains tests to make sure the parser instantiates the correct {@code FilterContactCommand}. + */ +public class FilterContactCommandParserTest { + + private final FilterContactCommandParser parser = new FilterContactCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + FilterContactCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsFilterCommand() { + // no leading and trailing whitespaces + FilterContactCommand expectedFilterContactCommand = + new FilterContactCommand(new ContactTagsContainKeywordsPredicate(Arrays.asList("Alice", "Bob"))); + assertParseSuccess(parser, "Alice Bob", expectedFilterContactCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFilterContactCommand); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/FilterEntryCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/FilterEntryCommandParserTest.java new file mode 100644 index 00000000000..e66ee44df0b --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/FilterEntryCommandParserTest.java @@ -0,0 +1,37 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.FilterEntryCommand; +import seedu.ta.model.entry.EntryTagsContainKeywordsPredicate; + +/** + * Contains tests to make sure the parser instantiates the correct {@code FilterEntryCommand}. + */ +public class FilterEntryCommandParserTest { + + private final FilterEntryCommandParser parser = new FilterEntryCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FilterEntryCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsFilterEntryCommand() { + // no leading and trailing whitespaces + FilterEntryCommand expectedCommand = + new FilterEntryCommand(new EntryTagsContainKeywordsPredicate(Arrays.asList("Math", "History"))); + assertParseSuccess(parser, "Math History", expectedCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n Math \n \t History \t", expectedCommand); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/FindContactCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/FindContactCommandParserTest.java new file mode 100644 index 00000000000..d3609161ee0 --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/FindContactCommandParserTest.java @@ -0,0 +1,37 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.FindContactCommand; +import seedu.ta.model.contact.ContactNameContainsKeywordsPredicate; + +/** + * Contains tests to make sure the parser instantiates the correct {@code FindContactCommand}. + */ +public class FindContactCommandParserTest { + + private final FindContactCommandParser parser = new FindContactCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, + FindContactCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsFindContactCommand() { + // no leading and trailing whitespaces + FindContactCommand expectedFindContactCommand = + new FindContactCommand(new ContactNameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob"))); + assertParseSuccess(parser, "Alice Bob", expectedFindContactCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindContactCommand); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/FindEntryCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/FindEntryCommandParserTest.java new file mode 100644 index 00000000000..f0c6800c05a --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/FindEntryCommandParserTest.java @@ -0,0 +1,37 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.FindEntryCommand; +import seedu.ta.model.entry.EntryNameContainsKeywordsPredicate; + +/** + * Contains tests to make sure the parser instantiates the correct {@code FindEntryCommand}. + */ +public class FindEntryCommandParserTest { + + private final FindEntryCommandParser parser = new FindEntryCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindEntryCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_validArgs_returnsFindEntryCommand() { + // no leading and trailing whitespaces + FindEntryCommand expectedCommand = + new FindEntryCommand(new EntryNameContainsKeywordsPredicate(Arrays.asList("Party", "Consultation"))); + assertParseSuccess(parser, "Party Consultation", expectedCommand); + + // multiple whitespaces between keywords + assertParseSuccess(parser, " \n Party \n \t Consultation \t", expectedCommand); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/FreeCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/FreeCommandParserTest.java new file mode 100644 index 00000000000..4d1cdfcab23 --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/FreeCommandParserTest.java @@ -0,0 +1,35 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_ENTRY_START_DATE_IN_PAST; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_DATE_RANGE; +import static seedu.ta.logic.commands.CommandTestUtil.INVALID_DATE_RANGE; +import static seedu.ta.logic.commands.CommandTestUtil.PAST_DATE_INTERVAL; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.FreeCommand; + +/** + * Contains tests to make sure the parser instantiates the correct {@code FreeCommand}. + */ +public class FreeCommandParserTest { + + private final FreeCommandParser parser = new FreeCommandParser(); + + @Test + public void parse_emptyArg_throwsParseException() { + assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FreeCommand.MESSAGE_USAGE)); + } + + @Test + public void parse_startTimeAfterEndTime_throwsParseException() { + assertParseFailure(parser, INVALID_DATE_RANGE, MESSAGE_INVALID_DATE_RANGE); + } + + @Test + public void parse_pastInterval_throwsParseException() { + assertParseFailure(parser, PAST_DATE_INTERVAL, MESSAGE_ENTRY_START_DATE_IN_PAST); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/ListEntryCommandParserTest.java b/src/test/java/seedu/ta/logic/parser/ListEntryCommandParserTest.java new file mode 100644 index 00000000000..42664e60739 --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/ListEntryCommandParserTest.java @@ -0,0 +1,41 @@ +package seedu.ta.logic.parser; + +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.logic.commands.CommandTestUtil.ALL_PREDICATE; +import static seedu.ta.logic.commands.CommandTestUtil.DAY_PREDICATE; +import static seedu.ta.logic.commands.CommandTestUtil.WEEK_PREDICATE; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseFailure; +import static seedu.ta.logic.parser.CommandParserTestUtil.assertParseSuccess; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.ListEntryCommand; + +/** + * Contains tests to make sure the parser instantiates the correct {@code ListEntryCommand}. + */ +public class ListEntryCommandParserTest { + + private final ListEntryCommandParser parser = new ListEntryCommandParser(); + + @Test + public void parser_emptyInput_success() { + assertParseSuccess(parser, "", new ListEntryCommand(ALL_PREDICATE)); + } + + @Test + public void parse_dayInput_success() { + assertParseSuccess(parser, "day", new ListEntryCommand(DAY_PREDICATE)); + } + + @Test + public void parser_weekInput_success() { + assertParseSuccess(parser, "week", new ListEntryCommand(WEEK_PREDICATE)); + } + + @Test + public void parser_invalidInput_throwsParseException() { + assertParseFailure(parser, "lol", + String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListEntryCommand.MESSAGE_USAGE)); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/ParserUtilTest.java b/src/test/java/seedu/ta/logic/parser/ParserUtilTest.java new file mode 100644 index 00000000000..e40644376b3 --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/ParserUtilTest.java @@ -0,0 +1,248 @@ +package seedu.ta.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.logic.parser.ParserUtil.MESSAGE_INVALID_INDEX; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.tag.Tag; + +public class ParserUtilTest { + + private static final String INVALID_NAME_SYMBOLS = "R@chel"; + private static final String INVALID_NAME_LESS_THAN_2_CHARACTERS = "R"; + private static final String INVALID_NAME_START_WITH_NUMBER = "1Rachel"; + private static final String INVALID_NAME_END_WITH_NUMBER = "Rachel1"; + private static final String INVALID_PHONE_NOT_8_DIGITS = "123"; + private static final String INVALID_PHONE_STARTS_WITH_0 = "0123"; + private static final String INVALID_PHONE_HAS_SYMBOLS = "+123"; + private static final String INVALID_EMAIL_LOCAL_LESS_THAN_2_CHAR = "r@gmail"; + private static final String INVALID_EMAIL_LOCAL_INVALID_SYMBOLS = "[rachel@gmail"; + private static final String INVALID_EMAIL_DOMAIN_LESS_THAN_2_CHAR = "rachel@g"; + private static final String INVALID_EMAIL_DOMAIN_INVALID_SYMBOLS = "rachel@gmail+yahoo"; + private static final String INVALID_TAG = "#friend"; + private static final String INVALID_ENTRY_NAME_BLANK = " "; + private static final String INVALID_ENTRY_NAME_SYMBOLS = "$%^&*("; + private static final String INVALID_ENTRY_DATE_BLANK = " "; + private static final String INVALID_ENTRY_DATE_WRONG_DATE_FORMAT = "01-01-2022 12:12"; + private static final String INVALID_ENTRY_DATE_NO_TIME = "01-01-2022"; + private static final String INVALID_ENTRY_DATE_WRONG_TIME_FORMAT = "01-01-2022 12:12:12"; + private static final String INVALID_ENTRY_DATE_INVALID_TIME = "01-01-2022 12:12:12"; + + private static final String VALID_NAME = "Rachel Walker"; + private static final String VALID_NAME_WITH_HYPHEN = "Rachel Walker-Lim"; + private static final String VALID_NAME_AT_LEAST_2_CHARACTERS = "Ra"; + private static final String VALID_PHONE = "12345678"; + private static final String VALID_EMAIL = "rachel@example-gmail.com"; + private static final String VALID_TAG_1 = "friend"; + private static final String VALID_TAG_2 = "neighbour"; + private static final String VALID_ENTRY_NAME = "Consultation with teacher"; + private static final String VALID_ENTRY_DATE = "2022-01-01 12:12"; + + private static final String WHITESPACE = " \t\r\n"; + + @Test + public void parseIndex_invalidInput_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseIndex("10 a")); + } + + @Test + public void parseIndex_outOfRangeInput_throwsParseException() { + assertThrows(ParseException.class, + String.format("Index given: %s\n%s", Integer.MAX_VALUE + 1, MESSAGE_INVALID_INDEX), () + -> ParserUtil.parseIndex(Long.toString(Integer.MAX_VALUE + 1))); + } + + @Test + public void parseIndex_validInput_success() throws Exception { + // No whitespaces + assertEquals(INDEX_FIRST, ParserUtil.parseIndex("1")); + + // Leading and trailing whitespaces + assertEquals(INDEX_FIRST, ParserUtil.parseIndex(" 1 ")); + } + + @Test + public void parseContactName_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseContactName((String) null)); + } + + @Test + public void parseContactName_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseContactName(INVALID_NAME_SYMBOLS)); + assertThrows(ParseException.class, () -> ParserUtil.parseContactName(INVALID_NAME_LESS_THAN_2_CHARACTERS)); + assertThrows(ParseException.class, () -> ParserUtil.parseContactName(INVALID_NAME_START_WITH_NUMBER)); + assertThrows(ParseException.class, () -> ParserUtil.parseContactName(INVALID_NAME_END_WITH_NUMBER)); + } + + @Test + public void parseContactName_validValueWithoutWhitespace_returnsName() throws Exception { + ContactName expectedName = new ContactName(VALID_NAME); + assertEquals(expectedName, ParserUtil.parseContactName(VALID_NAME)); + + expectedName = new ContactName(VALID_NAME_WITH_HYPHEN); + assertEquals(expectedName, ParserUtil.parseContactName(VALID_NAME_WITH_HYPHEN)); + + expectedName = new ContactName(VALID_NAME_AT_LEAST_2_CHARACTERS); + assertEquals(expectedName, ParserUtil.parseContactName(VALID_NAME_AT_LEAST_2_CHARACTERS)); + } + + @Test + public void parseContactName_validValueWithWhitespace_returnsTrimmedName() throws Exception { + String nameWithWhitespace = WHITESPACE + VALID_NAME + WHITESPACE; + ContactName expectedName = new ContactName(VALID_NAME); + assertEquals(expectedName, ParserUtil.parseContactName(nameWithWhitespace)); + } + + @Test + public void parseContactPhone_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseContactPhone((String) null)); + } + + @Test + public void parseContactPhone_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseContactPhone(INVALID_PHONE_HAS_SYMBOLS)); + assertThrows(ParseException.class, () -> ParserUtil.parseContactPhone(INVALID_PHONE_NOT_8_DIGITS)); + assertThrows(ParseException.class, () -> ParserUtil.parseContactPhone(INVALID_PHONE_STARTS_WITH_0)); + } + + @Test + public void parseContactPhone_validValueWithoutWhitespace_returnsPhone() throws Exception { + ContactPhone expectedPhone = new ContactPhone(VALID_PHONE); + assertEquals(expectedPhone, ParserUtil.parseContactPhone(VALID_PHONE)); + } + + @Test + public void parseContactPhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exception { + String phoneWithWhitespace = WHITESPACE + VALID_PHONE + WHITESPACE; + ContactPhone expectedPhone = new ContactPhone(VALID_PHONE); + assertEquals(expectedPhone, ParserUtil.parseContactPhone(phoneWithWhitespace)); + } + + @Test + public void parseContactEmail_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseContactEmail((String) null)); + } + + @Test + public void parseContactEmail_invalidLocalPart_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseContactEmail(INVALID_EMAIL_LOCAL_LESS_THAN_2_CHAR)); + assertThrows(ParseException.class, () -> ParserUtil.parseContactEmail(INVALID_EMAIL_LOCAL_INVALID_SYMBOLS)); + } + + @Test + public void parseContactEmail_invalidDomainPart_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseContactEmail(INVALID_EMAIL_DOMAIN_LESS_THAN_2_CHAR)); + assertThrows(ParseException.class, () -> ParserUtil.parseContactEmail(INVALID_EMAIL_DOMAIN_INVALID_SYMBOLS)); + } + + @Test + public void parseContactEmail_validValueWithoutWhitespace_returnsEmail() throws Exception { + ContactEmail expectedEmail = new ContactEmail(VALID_EMAIL); + assertEquals(expectedEmail, ParserUtil.parseContactEmail(VALID_EMAIL)); + } + + @Test + public void parseContactEmail_validValueWithWhitespace_returnsTrimmedEmail() throws Exception { + String emailWithWhitespace = WHITESPACE + VALID_EMAIL + WHITESPACE; + ContactEmail expectedEmail = new ContactEmail(VALID_EMAIL); + assertEquals(expectedEmail, ParserUtil.parseContactEmail(emailWithWhitespace)); + } + + @Test + public void parseTag_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null)); + } + + @Test + public void parseTag_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG)); + } + + @Test + public void parseTag_validValueWithoutWhitespace_returnsTag() throws Exception { + Tag expectedTag = new Tag(VALID_TAG_1); + assertEquals(expectedTag, ParserUtil.parseTag(VALID_TAG_1)); + } + + @Test + public void parseTag_validValueWithWhitespace_returnsTrimmedTag() throws Exception { + String tagWithWhitespace = WHITESPACE + VALID_TAG_1 + WHITESPACE; + Tag expectedTag = new Tag(VALID_TAG_1); + assertEquals(expectedTag, ParserUtil.parseTag(tagWithWhitespace)); + } + + @Test + public void parseTags_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null)); + } + + @Test + public void parseTags_collectionWithInvalidTags_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG))); + } + + @Test + public void parseTags_emptyCollection_returnsEmptySet() throws Exception { + assertTrue(ParserUtil.parseTags(Collections.emptyList()).isEmpty()); + } + + @Test + public void parseTags_collectionWithValidTags_returnsTagSet() throws Exception { + Set actualTagSet = ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, VALID_TAG_2)); + Set expectedTagSet = new HashSet(Arrays.asList(new Tag(VALID_TAG_1), new Tag(VALID_TAG_2))); + + assertEquals(expectedTagSet, actualTagSet); + } + + @Test + public void parseEntryName_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseEntryName(null)); + } + + @Test + public void parseEntryName_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseEntryName(INVALID_ENTRY_NAME_BLANK)); + assertThrows(ParseException.class, () -> ParserUtil.parseEntryName(INVALID_ENTRY_NAME_SYMBOLS)); + } + + @Test + public void parseEntryName_validValueWithoutWhitespace_returnsEntryName() throws Exception { + EntryName expectedEntryName = new EntryName(VALID_ENTRY_NAME); + assertEquals(expectedEntryName, ParserUtil.parseEntryName(VALID_ENTRY_NAME)); + } + + @Test + public void parseEntryDate_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> ParserUtil.parseEntryDate(null)); + } + + @Test + public void parseEntryDate_invalidValue_throwsParseException() { + assertThrows(ParseException.class, () -> ParserUtil.parseEntryDate(INVALID_ENTRY_DATE_BLANK)); + assertThrows(ParseException.class, () -> ParserUtil.parseEntryDate(INVALID_ENTRY_DATE_WRONG_DATE_FORMAT)); + assertThrows(ParseException.class, () -> ParserUtil.parseEntryDate(INVALID_ENTRY_DATE_NO_TIME)); + assertThrows(ParseException.class, () -> ParserUtil.parseEntryDate(INVALID_ENTRY_DATE_WRONG_TIME_FORMAT)); + assertThrows(ParseException.class, () -> ParserUtil.parseEntryDate(INVALID_ENTRY_DATE_INVALID_TIME)); + } + + @Test + public void parseEntryDate_validValueWithoutWhitespace_returnsEntryDate() throws Exception { + EntryDate expectedEntryDate = new EntryDate(VALID_ENTRY_DATE); + assertEquals(expectedEntryDate, ParserUtil.parseEntryDate(VALID_ENTRY_DATE)); + } +} diff --git a/src/test/java/seedu/ta/logic/parser/TeachingAssistantParserTest.java b/src/test/java/seedu/ta/logic/parser/TeachingAssistantParserTest.java new file mode 100644 index 00000000000..2714af1e2ef --- /dev/null +++ b/src/test/java/seedu/ta/logic/parser/TeachingAssistantParserTest.java @@ -0,0 +1,101 @@ +package seedu.ta.logic.parser; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.ta.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalIndices.INDEX_FIRST; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.ta.logic.commands.AddContactCommand; +import seedu.ta.logic.commands.ClearCommand; +import seedu.ta.logic.commands.DeleteContactCommand; +import seedu.ta.logic.commands.EditContactCommand; +import seedu.ta.logic.commands.EditContactCommand.EditContactDescriptor; +import seedu.ta.logic.commands.ExitCommand; +import seedu.ta.logic.commands.FindContactCommand; +import seedu.ta.logic.commands.HelpCommand; +import seedu.ta.logic.commands.ListContactCommand; +import seedu.ta.logic.parser.exceptions.ParseException; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactNameContainsKeywordsPredicate; +import seedu.ta.testutil.ContactBuilder; +import seedu.ta.testutil.ContactUtil; +import seedu.ta.testutil.EditContactDescriptorBuilder; + +public class TeachingAssistantParserTest { + + private final TeachingAssistantParser parser = new TeachingAssistantParser(); + + @Test + public void parseCommand_add() throws Exception { + Contact contact = new ContactBuilder().build(); + AddContactCommand command = (AddContactCommand) parser.parseCommand(ContactUtil.getAddContactCommand(contact)); + assertEquals(new AddContactCommand(contact), 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 { + DeleteContactCommand command = (DeleteContactCommand) parser.parseCommand( + DeleteContactCommand.COMMAND_WORD + " " + INDEX_FIRST.getOneBased()); + assertEquals(new DeleteContactCommand(INDEX_FIRST), command); + } + + @Test + public void parseCommand_edit() throws Exception { + Contact contact = new ContactBuilder().build(); + EditContactDescriptor descriptor = new EditContactDescriptorBuilder(contact).build(); + EditContactCommand command = (EditContactCommand) parser.parseCommand(EditContactCommand.COMMAND_WORD + " " + + INDEX_FIRST.getOneBased() + " " + ContactUtil.getEditContactDescriptorDetails(descriptor)); + assertEquals(new EditContactCommand(INDEX_FIRST, 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"); + FindContactCommand command = (FindContactCommand) parser.parseCommand( + FindContactCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" "))); + assertEquals(new FindContactCommand(new ContactNameContainsKeywordsPredicate(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(ListContactCommand.COMMAND_WORD) instanceof ListContactCommand); + assertTrue(parser.parseCommand(ListContactCommand.COMMAND_WORD + " 3") instanceof ListContactCommand); + } + + @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/model/ModelManagerTest.java b/src/test/java/seedu/ta/model/ModelManagerTest.java similarity index 51% rename from src/test/java/seedu/address/model/ModelManagerTest.java rename to src/test/java/seedu/ta/model/ModelManagerTest.java index 2cf1418d116..5881ee30f32 100644 --- a/src/test/java/seedu/address/model/ModelManagerTest.java +++ b/src/test/java/seedu/ta/model/ModelManagerTest.java @@ -1,12 +1,12 @@ -package seedu.address.model; +package seedu.ta.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.model.Model.PREDICATE_SHOW_ALL_PERSONS; -import static seedu.address.testutil.Assert.assertThrows; -import static seedu.address.testutil.TypicalPersons.ALICE; -import static seedu.address.testutil.TypicalPersons.BENSON; +import static seedu.ta.model.Model.PREDICATE_SHOW_ALL_CONTACTS; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.AMY; +import static seedu.ta.testutil.TypicalTeachingAssistant.BEN; import java.nio.file.Path; import java.nio.file.Paths; @@ -14,10 +14,13 @@ import org.junit.jupiter.api.Test; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.person.NameContainsKeywordsPredicate; -import seedu.address.testutil.AddressBookBuilder; +import seedu.ta.commons.core.GuiSettings; +import seedu.ta.model.contact.ContactNameContainsKeywordsPredicate; +import seedu.ta.testutil.TeachingAssistantBuilder; +/** + * Contains unit tests for {@code ModelManager}. + */ public class ModelManagerTest { private ModelManager modelManager = new ModelManager(); @@ -26,7 +29,7 @@ public class ModelManagerTest { public void constructor() { assertEquals(new UserPrefs(), modelManager.getUserPrefs()); assertEquals(new GuiSettings(), modelManager.getGuiSettings()); - assertEquals(new AddressBook(), new AddressBook(modelManager.getAddressBook())); + assertEquals(new TeachingAssistant(), new TeachingAssistant(modelManager.getTeachingAssistant())); } @Test @@ -37,14 +40,14 @@ public void setUserPrefs_nullUserPrefs_throwsNullPointerException() { @Test public void setUserPrefs_validUserPrefs_copiesUserPrefs() { UserPrefs userPrefs = new UserPrefs(); - userPrefs.setAddressBookFilePath(Paths.get("address/book/file/path")); + userPrefs.setTeachingAssistantFilePath(Paths.get("address/book/file/path")); userPrefs.setGuiSettings(new GuiSettings(1, 2, 3, 4)); modelManager.setUserPrefs(userPrefs); assertEquals(userPrefs, modelManager.getUserPrefs()); // Modifying userPrefs should not modify modelManager's userPrefs UserPrefs oldUserPrefs = new UserPrefs(userPrefs); - userPrefs.setAddressBookFilePath(Paths.get("new/address/book/file/path")); + userPrefs.setTeachingAssistantFilePath(Paths.get("new/address/book/file/path")); assertEquals(oldUserPrefs, modelManager.getUserPrefs()); } @@ -61,47 +64,48 @@ public void setGuiSettings_validGuiSettings_setsGuiSettings() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.setAddressBookFilePath(null)); + public void setTeachingAssistantFilePath_nullPath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.setTeachingAssistantFilePath(null)); } @Test - public void setAddressBookFilePath_validPath_setsAddressBookFilePath() { + public void setTeachingAssistantFilePath_validPath_setsTeachingAssistantFilePath() { Path path = Paths.get("address/book/file/path"); - modelManager.setAddressBookFilePath(path); - assertEquals(path, modelManager.getAddressBookFilePath()); + modelManager.setTeachingAssistantFilePath(path); + assertEquals(path, modelManager.getTeachingAssistantFilePath()); } @Test - public void hasPerson_nullPerson_throwsNullPointerException() { - assertThrows(NullPointerException.class, () -> modelManager.hasPerson(null)); + public void hasContact_nullContact_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> modelManager.hasContact(null)); } @Test - public void hasPerson_personNotInAddressBook_returnsFalse() { - assertFalse(modelManager.hasPerson(ALICE)); + public void hasContact_contactNotInTeachingAssistant_returnsFalse() { + assertFalse(modelManager.hasContact(AMY)); } @Test - public void hasPerson_personInAddressBook_returnsTrue() { - modelManager.addPerson(ALICE); - assertTrue(modelManager.hasPerson(ALICE)); + public void hasContact_contactInTeachingAssistant_returnsTrue() { + modelManager.addContact(AMY); + assertTrue(modelManager.hasContact(AMY)); } @Test - public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException() { - assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0)); + public void getFilteredContactList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredContactList().remove(0)); } @Test public void equals() { - AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build(); - AddressBook differentAddressBook = new AddressBook(); + TeachingAssistant teachingAssistant = new TeachingAssistantBuilder().withContact(AMY).withContact(BEN) + .build(); + TeachingAssistant differentTeachingAssistant = new TeachingAssistant(); UserPrefs userPrefs = new UserPrefs(); // same values -> returns true - modelManager = new ModelManager(addressBook, userPrefs); - ModelManager modelManagerCopy = new ModelManager(addressBook, userPrefs); + modelManager = new ModelManager(teachingAssistant, userPrefs); + ModelManager modelManagerCopy = new ModelManager(teachingAssistant, userPrefs); assertTrue(modelManager.equals(modelManagerCopy)); // same object -> returns true @@ -113,20 +117,20 @@ public void equals() { // different types -> returns false assertFalse(modelManager.equals(5)); - // different addressBook -> returns false - assertFalse(modelManager.equals(new ModelManager(differentAddressBook, userPrefs))); + // different teachingAssistant -> returns false + assertFalse(modelManager.equals(new ModelManager(differentTeachingAssistant, userPrefs))); // different filteredList -> returns false - String[] keywords = ALICE.getName().fullName.split("\\s+"); - modelManager.updateFilteredPersonList(new NameContainsKeywordsPredicate(Arrays.asList(keywords))); - assertFalse(modelManager.equals(new ModelManager(addressBook, userPrefs))); + String[] keywords = AMY.getName().fullName.split("\\s+"); + modelManager.updateFilteredContactList(new ContactNameContainsKeywordsPredicate(Arrays.asList(keywords))); + assertFalse(modelManager.equals(new ModelManager(teachingAssistant, userPrefs))); // resets modelManager to initial state for upcoming tests - modelManager.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); + modelManager.updateFilteredContactList(PREDICATE_SHOW_ALL_CONTACTS); // different userPrefs -> returns false UserPrefs differentUserPrefs = new UserPrefs(); - differentUserPrefs.setAddressBookFilePath(Paths.get("differentFilePath")); - assertFalse(modelManager.equals(new ModelManager(addressBook, differentUserPrefs))); + differentUserPrefs.setTeachingAssistantFilePath(Paths.get("differentFilePath")); + assertFalse(modelManager.equals(new ModelManager(teachingAssistant, differentUserPrefs))); } } diff --git a/src/test/java/seedu/ta/model/TeachingAssistantTest.java b/src/test/java/seedu/ta/model/TeachingAssistantTest.java new file mode 100644 index 00000000000..6931a21b4e6 --- /dev/null +++ b/src/test/java/seedu/ta/model/TeachingAssistantTest.java @@ -0,0 +1,111 @@ +package seedu.ta.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.ta.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.AMY; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +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.ta.model.contact.Contact; +import seedu.ta.model.contact.exceptions.DuplicateContactException; +import seedu.ta.model.entry.Entry; +import seedu.ta.testutil.ContactBuilder; + +/** + * Contains unit tests for {@code TeachingAssistant}. + */ +public class TeachingAssistantTest { + + private final TeachingAssistant teachingAssistant = new TeachingAssistant(); + + @Test + public void constructor() { + assertEquals(Collections.emptyList(), teachingAssistant.getContactList()); + } + + @Test + public void resetData_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> teachingAssistant.resetData(null)); + } + + @Test + public void resetData_withValidReadOnlyTeachingAssistant_replacesData() { + TeachingAssistant newData = getTypicalTeachingAssistant(); + teachingAssistant.resetData(newData); + assertEquals(newData, teachingAssistant); + } + + @Test + public void resetData_withDuplicateContacts_throwsDuplicateContactException() { + // Two contacts with the same identity fields + Contact editedAmy = new ContactBuilder(AMY).withTags(VALID_TAG_HUSBAND) + .build(); + List newContacts = Arrays.asList(AMY, editedAmy); + TeachingAssistantStub newData = new TeachingAssistantStub(newContacts); + + assertThrows(DuplicateContactException.class, () -> teachingAssistant.resetData(newData)); + } + + @Test + public void hasContact_nullContact_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> teachingAssistant.hasContact(null)); + } + + @Test + public void hasContact_contactNotInTeachingAssistant_returnsFalse() { + assertFalse(teachingAssistant.hasContact(AMY)); + } + + @Test + public void hasContact_contactInTeachingAssistant_returnsTrue() { + teachingAssistant.addContact(AMY); + assertTrue(teachingAssistant.hasContact(AMY)); + } + + @Test + public void hasContact_contactWithSameIdentityFieldsInTeachingAssistant_returnsTrue() { + teachingAssistant.addContact(AMY); + Contact editedAlice = new ContactBuilder(AMY).withTags(VALID_TAG_HUSBAND) + .build(); + assertTrue(teachingAssistant.hasContact(editedAlice)); + } + + @Test + public void getContactList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> teachingAssistant.getContactList().remove(0)); + } + + /** + * A stub ReadOnlyTeachingAssistant whose contacts list can violate interface constraints. + */ + private static class TeachingAssistantStub implements ReadOnlyTeachingAssistant { + private final ObservableList contacts = FXCollections.observableArrayList(); + private final ObservableList entries = FXCollections.observableArrayList(); + + TeachingAssistantStub(Collection contacts) { + this.contacts.setAll(contacts); + } + + @Override + public ObservableList getContactList() { + return contacts; + } + + @Override + public ObservableList getEntryList() { + return entries; + } + + } +} diff --git a/src/test/java/seedu/address/model/UserPrefsTest.java b/src/test/java/seedu/ta/model/UserPrefsTest.java similarity index 68% rename from src/test/java/seedu/address/model/UserPrefsTest.java rename to src/test/java/seedu/ta/model/UserPrefsTest.java index b1307a70d52..9f237e0c1ec 100644 --- a/src/test/java/seedu/address/model/UserPrefsTest.java +++ b/src/test/java/seedu/ta/model/UserPrefsTest.java @@ -1,6 +1,6 @@ -package seedu.address.model; +package seedu.ta.model; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.ta.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; @@ -13,9 +13,8 @@ public void setGuiSettings_nullGuiSettings_throwsNullPointerException() { } @Test - public void setAddressBookFilePath_nullPath_throwsNullPointerException() { + public void setTeachingAssistantFilePath_nullPath_throwsNullPointerException() { UserPrefs userPrefs = new UserPrefs(); - assertThrows(NullPointerException.class, () -> userPrefs.setAddressBookFilePath(null)); + assertThrows(NullPointerException.class, () -> userPrefs.setTeachingAssistantFilePath(null)); } - } diff --git a/src/test/java/seedu/ta/model/contact/ContactEmailTest.java b/src/test/java/seedu/ta/model/contact/ContactEmailTest.java new file mode 100644 index 00000000000..b709b2ac164 --- /dev/null +++ b/src/test/java/seedu/ta/model/contact/ContactEmailTest.java @@ -0,0 +1,87 @@ +package seedu.ta.model.contact; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Contains unit tests for {@code ContactEmail}. + */ +public class ContactEmailTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new ContactEmail(null)); + } + + @Test + public void constructor_invalidEmail_throwsIllegalArgumentException() { + String invalidEmail = ""; + assertThrows(IllegalArgumentException.class, () -> new ContactEmail(invalidEmail)); + } + + @Test + public void isValidEmail() { + // null email + assertThrows(NullPointerException.class, () -> ContactEmail.isValidEmail(null)); + + // blank email + assertFalse(ContactEmail.isValidEmail("")); // empty string + assertFalse(ContactEmail.isValidEmail(" ")); // spaces only + + // missing parts + assertFalse(ContactEmail.isValidEmail("@example.com")); // missing local part + assertFalse(ContactEmail.isValidEmail("peterjackexample.com")); // missing '@' symbol + assertFalse(ContactEmail.isValidEmail("peterjack@")); // missing domain name + + // invalid parts + String email; + + email = "peterjack@-"; + assertFalse(ContactEmail.isValidEmail(email)); // invalid domain name + email = "peter jack@example.com"; + assertFalse(ContactEmail.isValidEmail(email)); // spaces in local part + email = "peterjack@exam ple.com"; + assertFalse(ContactEmail.isValidEmail(email)); // spaces in domain name + email = " peterjack@example.com"; + assertFalse(ContactEmail.isValidEmail(email)); // leading space + email = "peterjack@example.com "; + assertFalse(ContactEmail.isValidEmail(email)); // trailing space + email = "peterjack@@example.com"; + assertFalse(ContactEmail.isValidEmail(email)); // double '@' symbol + email = "peter@jack@example.com"; + assertFalse(ContactEmail.isValidEmail(email)); // '@' symbol in local part + email = "peterjack@example@com"; + assertFalse(ContactEmail.isValidEmail(email)); // '@' symbol in domain name + email = "peterjack@.example.com"; + assertFalse(ContactEmail.isValidEmail(email)); // domain name starts with a period + email = "peterjack@example.com."; + assertFalse(ContactEmail.isValidEmail(email)); // domain name ends with a period + email = "peterjack@-example.com"; + assertFalse(ContactEmail.isValidEmail(email)); // domain name starts with a hyphen + email = "peterjack@example.com-"; + assertFalse(ContactEmail.isValidEmail(email)); // domain name ends with a hyphen + email = "a!#$%&'*+/=?`{|}~^.-@example.org"; + assertFalse(ContactEmail.isValidEmail(email)); // trailing special characters + email = "!#$%&'*+/=?`{|}~^.-@example.org"; + assertFalse(ContactEmail.isValidEmail(email)); // leading special characters + + // valid email + email = "PeterJack_1190@example.com"; + assertTrue(ContactEmail.isValidEmail(email)); + email = "ad@bc"; + assertTrue(ContactEmail.isValidEmail(email)); // minimal + email = "test@localhost"; + assertTrue(ContactEmail.isValidEmail(email)); // alphabets only + email = "123@145"; + assertTrue(ContactEmail.isValidEmail(email)); // numeric local part and domain name + email = "a1+be!g@example1.com"; + assertTrue(ContactEmail.isValidEmail(email)); // mixture of alphanumeric and special characters + email = "peter_jack@very-very-very-long-example.com"; + assertTrue(ContactEmail.isValidEmail(email)); // long domain name + email = "peter_jack@very-very-very-long-example.com"; + assertTrue(ContactEmail.isValidEmail(email)); // long local part + } +} diff --git a/src/test/java/seedu/ta/model/contact/ContactNameContainsKeywordsPredicateTest.java b/src/test/java/seedu/ta/model/contact/ContactNameContainsKeywordsPredicateTest.java new file mode 100644 index 00000000000..910c8cb6346 --- /dev/null +++ b/src/test/java/seedu/ta/model/contact/ContactNameContainsKeywordsPredicateTest.java @@ -0,0 +1,79 @@ +package seedu.ta.model.contact; + +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.ta.testutil.ContactBuilder; + +/** + * Contains unit tests for {@code ContactNameContainsKeywordsPredicate}. + */ +public class ContactNameContainsKeywordsPredicateTest { + + @Test + public void equals() { + List firstPredicateKeywordList = Collections.singletonList("first"); + List secondPredicateKeywordList = Arrays.asList("first", "second"); + ContactNameContainsKeywordsPredicate firstPredicate = + new ContactNameContainsKeywordsPredicate(firstPredicateKeywordList); + ContactNameContainsKeywordsPredicate secondPredicate = + new ContactNameContainsKeywordsPredicate(secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + ContactNameContainsKeywordsPredicate firstPredicateCopy = + new ContactNameContainsKeywordsPredicate(firstPredicateKeywordList); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different contact -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_contactNameContainsKeywords_returnsTrue() { + ContactNameContainsKeywordsPredicate predicate; + + // Zero keywords + predicate = new ContactNameContainsKeywordsPredicate(Collections.emptyList()); + assertTrue(predicate.test(new ContactBuilder().withName("Alice").build())); + + // One keyword + predicate = new ContactNameContainsKeywordsPredicate(Collections.singletonList("Alice")); + assertTrue(predicate.test(new ContactBuilder().withName("Alice Bob").build())); + + // Multiple keywords + predicate = new ContactNameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")); + assertTrue(predicate.test(new ContactBuilder().withName("Alice Bob").build())); + + // Mixed-case keywords + predicate = new ContactNameContainsKeywordsPredicate(Arrays.asList("aLIce", "bOB")); + assertTrue(predicate.test(new ContactBuilder().withName("Alice Bob").build())); + } + + @Test + public void test_contactNameDoesNotContainKeywords_returnsFalse() { + ContactNameContainsKeywordsPredicate predicate; + + // Non-matching keyword + predicate = new ContactNameContainsKeywordsPredicate(Arrays.asList("Carol")); + assertFalse(predicate.test(new ContactBuilder().withName("Alice Bob").build())); + + // Only one matching keyword + predicate = new ContactNameContainsKeywordsPredicate(Arrays.asList("Bob", "Carol")); + assertFalse(predicate.test(new ContactBuilder().withName("Alice Carol").build())); + } +} diff --git a/src/test/java/seedu/ta/model/contact/ContactNameTest.java b/src/test/java/seedu/ta/model/contact/ContactNameTest.java new file mode 100644 index 00000000000..3319aa84c8d --- /dev/null +++ b/src/test/java/seedu/ta/model/contact/ContactNameTest.java @@ -0,0 +1,48 @@ +package seedu.ta.model.contact; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Contains unit tests for {@code ContactName}. + */ +public class ContactNameTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new ContactName(null)); + } + + @Test + public void constructor_invalidName_throwsIllegalArgumentException() { + String invalidName = ""; + assertThrows(IllegalArgumentException.class, () -> new ContactName(invalidName)); + } + + @Test + public void isValidName() { + // null name + assertThrows(NullPointerException.class, () -> ContactName.isValidName(null)); + + // invalid name + assertFalse(ContactName.isValidName("")); // empty string + assertFalse(ContactName.isValidName(" ")); // spaces only + assertFalse(ContactName.isValidName("^")); // only non-alphanumeric characters + assertFalse(ContactName.isValidName("peter*")); // contains non-alphanumeric characters + assertFalse(ContactName.isValidName("-peter")); // leading hyphen + assertFalse(ContactName.isValidName("peter-")); // leading hyphen + assertFalse(ContactName.isValidName("12345")); // numbers only + assertFalse(ContactName.isValidName("peter the 2nd")); // alphanumeric characters + assertFalse(ContactName.isValidName("David Roger Jackson Ray Jr 2nd")); // long names + + // valid name + assertTrue(ContactName.isValidName("peter")); // alphabets only + assertTrue(ContactName.isValidName("peter jack")); // alphabets and space only + assertTrue(ContactName.isValidName("Capital Tan")); // with capital letters + assertTrue(ContactName.isValidName("Stella Scarlett-Rose")); // with hyphens + } +} + diff --git a/src/test/java/seedu/ta/model/contact/ContactPhoneTest.java b/src/test/java/seedu/ta/model/contact/ContactPhoneTest.java new file mode 100644 index 00000000000..92d86e0e78f --- /dev/null +++ b/src/test/java/seedu/ta/model/contact/ContactPhoneTest.java @@ -0,0 +1,47 @@ +package seedu.ta.model.contact; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Contains unit tests for {@code ContactPhone}. + */ +public class ContactPhoneTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new ContactPhone(null)); + } + + @Test + public void constructor_invalidPhone_throwsIllegalArgumentException() { + String invalidPhone = ""; + assertThrows(IllegalArgumentException.class, () -> new ContactPhone(invalidPhone)); + } + + @Test + public void isValidPhone() { + // null phone number + assertThrows(NullPointerException.class, () -> ContactPhone.isValidPhone(null)); + + // invalid phone numbers + assertFalse(ContactPhone.isValidPhone("")); // empty string + assertFalse(ContactPhone.isValidPhone(" ")); // spaces only + assertFalse(ContactPhone.isValidPhone("91")); // less than 3 digits + assertFalse(ContactPhone.isValidPhone("impostor")); // non-numeric + assertFalse(ContactPhone.isValidPhone("9011p041")); // alphabets within digits + assertFalse(ContactPhone.isValidPhone("9312 1534")); // spaces within digits + assertFalse(ContactPhone.isValidPhone("9812748")); // 7 digits + assertFalse(ContactPhone.isValidPhone("09127382")); // 8 digits but leading zero + assertFalse(ContactPhone.isValidPhone("981274822")); // 9 digits + assertFalse(ContactPhone.isValidPhone("124293842033123")); // long phone numbers + + // valid phone numbers + assertTrue(ContactPhone.isValidPhone("93121534")); // exactly 8 digits + assertTrue(ContactPhone.isValidPhone("12345678")); // exactly 8 digits + + } +} diff --git a/src/test/java/seedu/ta/model/contact/ContactTagsContainKeywordsPredicateTest.java b/src/test/java/seedu/ta/model/contact/ContactTagsContainKeywordsPredicateTest.java new file mode 100644 index 00000000000..45434cff936 --- /dev/null +++ b/src/test/java/seedu/ta/model/contact/ContactTagsContainKeywordsPredicateTest.java @@ -0,0 +1,79 @@ +package seedu.ta.model.contact; + +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.ta.testutil.ContactBuilder; + +/** + * Contains unit tests for {@code ContactTagsContainKeywordsPredicate}. + */ +public class ContactTagsContainKeywordsPredicateTest { + + @Test + public void equals() { + List firstPredicateKeywordList = Collections.singletonList("first"); + List secondPredicateKeywordList = Arrays.asList("first", "second"); + ContactTagsContainKeywordsPredicate firstPredicate = + new ContactTagsContainKeywordsPredicate(firstPredicateKeywordList); + ContactTagsContainKeywordsPredicate secondPredicate = + new ContactTagsContainKeywordsPredicate(secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + ContactTagsContainKeywordsPredicate firstPredicateCopy = + new ContactTagsContainKeywordsPredicate(firstPredicateKeywordList); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different contact -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_contactTagsContainKeywords_returnsTrue() { + ContactTagsContainKeywordsPredicate predicate; + + // Zero keywords + predicate = new ContactTagsContainKeywordsPredicate(Collections.emptyList()); + assertTrue(predicate.test(new ContactBuilder().withTags("friends").build())); + + // One keyword + predicate = new ContactTagsContainKeywordsPredicate(Collections.singletonList("friends")); + assertTrue(predicate.test(new ContactBuilder().withTags("friends", "owesMoney").build())); + + // Multiple keywords + predicate = new ContactTagsContainKeywordsPredicate(Arrays.asList("friends", "owesMoney")); + assertTrue(predicate.test(new ContactBuilder().withTags("friends", "owesMoney").build())); + + // Mixed-case keywords + predicate = new ContactTagsContainKeywordsPredicate(Arrays.asList("fRieNds", "oWesmOneY")); + assertTrue(predicate.test(new ContactBuilder().withTags("friends", "owesMoney").build())); + } + + @Test + public void test_contactTagsDoesNotContainKeywords_returnsFalse() { + ContactTagsContainKeywordsPredicate predicate; + + // Non-matching keyword + predicate = new ContactTagsContainKeywordsPredicate(Arrays.asList("colleagues")); + assertFalse(predicate.test(new ContactBuilder().withTags("friends", "owesMoney").build())); + + // Only one matching keyword + predicate = new ContactTagsContainKeywordsPredicate(Arrays.asList("colleagues", "friends")); + assertFalse(predicate.test(new ContactBuilder().withTags("colleagues", "owesMoney").build())); + } +} diff --git a/src/test/java/seedu/ta/model/contact/ContactTest.java b/src/test/java/seedu/ta/model/contact/ContactTest.java new file mode 100644 index 00000000000..d749ec53410 --- /dev/null +++ b/src/test/java/seedu/ta/model/contact/ContactTest.java @@ -0,0 +1,55 @@ +package seedu.ta.model.contact; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +import seedu.ta.testutil.ContactBuilder; + +/** + * Contains unit tests for {@code Contact}. + */ +public class ContactTest { + + @Test + public void asObservableList_modifyList_throwsUnsupportedOperationException() { + Contact contact = new ContactBuilder().build(); + assertThrows(UnsupportedOperationException.class, () -> contact.getTags().remove(0)); + } + + @Test + public void isSameContact() { + Contact alice = new ContactBuilder().withName("Alice").withPhone("87654321").withEmail("alice@email.com") + .withTags("student", "junior").build(); + Contact bob = new ContactBuilder().withName("Bob").withPhone("98765432").withEmail("bob@email.com") + .withTags("student", "senior").build(); + + // same object -> returns true + assertTrue(alice.isSameContact(alice)); + + // null -> returns false + assertFalse(alice.isSameContact(null)); + + // totally different attributes -> returns false + assertFalse(alice.isSameContact(bob)); + + // same name, all other attributes different -> returns false + bob = new ContactBuilder(bob).withName("Alice").build(); + assertFalse(alice.isSameContact(bob)); + + // different name, all other attributes same -> returns false + bob = new ContactBuilder(alice).withName("Bob").build(); + assertFalse(alice.isSameContact(bob)); + + // name differs in case, all other attributes same -> returns false + bob = new ContactBuilder(alice).withName("bob").build(); + assertFalse(alice.isSameContact(bob)); + + // attributes differ in case -> returns true + bob = new ContactBuilder().withName("alice").withPhone("87654321").withEmail("alice@EMAIL.COM") + .withTags("student", "JUNIOR").build(); + assertTrue(alice.isSameContact(bob)); + } +} diff --git a/src/test/java/seedu/ta/model/contact/UniqueContactListTest.java b/src/test/java/seedu/ta/model/contact/UniqueContactListTest.java new file mode 100644 index 00000000000..378fb1b0683 --- /dev/null +++ b/src/test/java/seedu/ta/model/contact/UniqueContactListTest.java @@ -0,0 +1,175 @@ +package seedu.ta.model.contact; + +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.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.AMY; +import static seedu.ta.testutil.TypicalTeachingAssistant.BEN; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.contact.exceptions.ContactNotFoundException; +import seedu.ta.model.contact.exceptions.DuplicateContactException; +import seedu.ta.testutil.ContactBuilder; + +/** + * Contains unit tests for {@code UniqueContactList}. + */ +public class UniqueContactListTest { + + private final UniqueContactList uniqueContactList = new UniqueContactList(); + + @Test + public void contains_nullContact_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueContactList.contains(null)); + } + + @Test + public void contains_contactNotInList_returnsFalse() { + assertFalse(uniqueContactList.contains(AMY)); + } + + @Test + public void contains_contactInList_returnsTrue() { + uniqueContactList.add(AMY); + assertTrue(uniqueContactList.contains(AMY)); + } + + @Test + public void contains_copiedContact_returnsTrue() { + uniqueContactList.add(new ContactBuilder(AMY).build()); + assertTrue(uniqueContactList.contains(AMY)); + } + + @Test + public void contains_contactWithDifferentAttribute_returnsFalse() { + uniqueContactList.add(AMY); + Contact impostor = new ContactBuilder(AMY).withName("Impostor").build(); + assertFalse(uniqueContactList.contains(impostor)); + } + + @Test + public void add_nullContact_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueContactList.add(null)); + } + + @Test + public void add_duplicateContact_throwsDuplicateContactException() { + uniqueContactList.add(AMY); + assertThrows(DuplicateContactException.class, () -> uniqueContactList.add(AMY)); + } + + @Test + public void setContact_nullTargetContact_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueContactList.setContact(null, AMY)); + } + + @Test + public void setContact_nullEditedContact_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueContactList.setContact(AMY, null)); + } + + @Test + public void setContact_targetContactNotInList_throwsContactNotFoundException() { + assertThrows(ContactNotFoundException.class, () -> uniqueContactList.setContact(AMY, AMY)); + } + + @Test + public void setContact_editedContactIsSameContact_success() { + uniqueContactList.add(AMY); + uniqueContactList.setContact(AMY, AMY); + UniqueContactList expectedUniqueContactList = new UniqueContactList(); + expectedUniqueContactList.add(AMY); + assertEquals(expectedUniqueContactList, uniqueContactList); + } + + @Test + public void setContact_editedContactHasSameIdentity_success() { + uniqueContactList.add(AMY); + Contact copiedAmy = new ContactBuilder(AMY).build(); + uniqueContactList.setContact(AMY, copiedAmy); + UniqueContactList expectedUniqueContactList = new UniqueContactList(); + expectedUniqueContactList.add(copiedAmy); + assertEquals(expectedUniqueContactList, uniqueContactList); + } + + @Test + public void setContact_editedContactHasDifferentIdentity_success() { + uniqueContactList.add(AMY); + uniqueContactList.setContact(AMY, BEN); + UniqueContactList expectedUniqueContactList = new UniqueContactList(); + expectedUniqueContactList.add(BEN); + assertEquals(expectedUniqueContactList, uniqueContactList); + } + + @Test + public void setContact_editedContactHasNonUniqueIdentity_throwsDuplicateContactException() { + uniqueContactList.add(AMY); + uniqueContactList.add(BEN); + assertThrows(DuplicateContactException.class, () -> uniqueContactList.setContact(AMY, BEN)); + } + + @Test + public void remove_nullContact_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueContactList.remove(null)); + } + + @Test + public void remove_contactDoesNotExist_throwsContactNotFoundException() { + assertThrows(ContactNotFoundException.class, () -> uniqueContactList.remove(AMY)); + } + + @Test + public void remove_existingContact_removesContact() { + uniqueContactList.add(AMY); + uniqueContactList.remove(AMY); + UniqueContactList expectedUniqueContactList = new UniqueContactList(); + assertEquals(expectedUniqueContactList, uniqueContactList); + } + + @Test + public void setContacts_nullUniqueContactList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueContactList.setContacts((UniqueContactList) null)); + } + + @Test + public void setContacts_uniqueContactList_replacesOwnListWithProvidedUniqueContactList() { + uniqueContactList.add(AMY); + UniqueContactList expectedUniqueContactList = new UniqueContactList(); + expectedUniqueContactList.add(BEN); + uniqueContactList.setContacts(expectedUniqueContactList); + assertEquals(expectedUniqueContactList, uniqueContactList); + } + + @Test + public void setContacts_nullList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> uniqueContactList.setContacts((List) null)); + } + + @Test + public void setContacts_list_replacesOwnListWithProvidedList() { + uniqueContactList.add(AMY); + List contactList = Collections.singletonList(BEN); + uniqueContactList.setContacts(contactList); + UniqueContactList expectedUniqueContactList = new UniqueContactList(); + expectedUniqueContactList.add(BEN); + assertEquals(expectedUniqueContactList, uniqueContactList); + } + + @Test + public void setContacts_listWithDuplicateContacts_throwsDuplicateContactException() { + List listWithDuplicateContacts = Arrays.asList(AMY, AMY); + assertThrows(DuplicateContactException.class, () -> uniqueContactList.setContacts(listWithDuplicateContacts)); + } + + @Test + public void asUnmodifiableObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () + -> uniqueContactList.asUnmodifiableObservableList().remove(0)); + } +} diff --git a/src/test/java/seedu/ta/model/entry/EntryDateTest.java b/src/test/java/seedu/ta/model/entry/EntryDateTest.java new file mode 100644 index 00000000000..84baaa54c2f --- /dev/null +++ b/src/test/java/seedu/ta/model/entry/EntryDateTest.java @@ -0,0 +1,97 @@ +package seedu.ta.model.entry; + +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.ta.model.entry.EntryDate.DEFAULT_FORMATTER; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.Test; + +/** + * Contains unit tests for {@code EntryDate}. + */ +public class EntryDateTest { + + private String validDateString = "2021-01-01 12:12"; + private LocalDateTime validLocalDateTime = LocalDateTime.parse(validDateString, DEFAULT_FORMATTER); + private EntryDate validEntryDate = new EntryDate(validDateString); + + @Test + public void testGetDate() { + assertEquals(validLocalDateTime, validEntryDate.getDate()); + } + + @Test + public void testToString() { + assertEquals("2021-01-01 12:12", validEntryDate.toString()); + } + + @Test + public void isAfter_entryDateIsAfter_returnTrue() { + assertTrue(validEntryDate.isAfter(new EntryDate("2020-12-31 12:12"))); + assertTrue(validEntryDate.isAfter(new EntryDate("2021-01-01 12:11"))); + } + + @Test + public void isAfter_entryDateIsNotAfter_returnFalse() { + assertFalse(validEntryDate.isAfter(new EntryDate("2021-01-02 12:12"))); + assertFalse(validEntryDate.isAfter(new EntryDate("2021-01-01 12:13"))); + } + + @Test + public void isValidDate_validDate_returnTrue() { + assertTrue(EntryDate.isValidDate("2021-01-01 12:12")); + assertTrue(EntryDate.isValidDate("2021-01-01 00:00")); + assertTrue(EntryDate.isValidDate("2024-02-29 12:12")); + } + + @Test + public void isValidDate_invalidMonth_returnFalse() { + assertFalse(EntryDate.isValidDate("2021-13-01 12:12")); + assertFalse(EntryDate.isValidDate("2021-0-01 12:12")); + } + + @Test + public void isValidDate_invalidDay_returnFalse() { + assertFalse(EntryDate.isValidDate("2021-01-32 12:12")); + assertFalse(EntryDate.isValidDate("2021-02-0 12:12")); + assertFalse(EntryDate.isValidDate("2021-02-29 12:12")); + } + + @Test + public void isValidDate_invalidTime_returnFalse() { + assertFalse(EntryDate.isValidDate("2021-01-01 25:00")); + assertFalse(EntryDate.isValidDate("2021-01-01 23:60")); + } + + @Test + public void isValidDate_invalidFormat_returnFalse() { + assertFalse(EntryDate.isValidDate("01-13-2021 12:12")); + assertFalse(EntryDate.isValidDate("01-13-2021 12:12:12")); + assertFalse(EntryDate.isValidDate("01132021 12:12")); + } + + @Test + public void equals() { + EntryDate firstEntryDate = new EntryDate("2021-01-01 12:12"); + EntryDate secondEntryDate = new EntryDate("2021-02-02 13:13"); + + // same object -> returns true + assertTrue(firstEntryDate.equals(firstEntryDate)); + + // same values -> returns true + EntryDate firstEntryDateCopy = new EntryDate("2021-01-01 12:12"); + assertTrue(firstEntryDate.equals(firstEntryDateCopy)); + + // different types -> returns false + assertFalse(firstEntryDate.equals(1)); + + // null -> returns false + assertFalse(firstEntryDate.equals(null)); + + // different contact -> returns false + assertFalse(firstEntryDate.equals(secondEntryDate)); + } +} diff --git a/src/test/java/seedu/ta/model/entry/EntryNameContainsKeywordsPredicateTest.java b/src/test/java/seedu/ta/model/entry/EntryNameContainsKeywordsPredicateTest.java new file mode 100644 index 00000000000..02e19b43672 --- /dev/null +++ b/src/test/java/seedu/ta/model/entry/EntryNameContainsKeywordsPredicateTest.java @@ -0,0 +1,74 @@ +package seedu.ta.model.entry; + +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.ta.testutil.EntryBuilder; + +/** + * Contains unit tests for {@code EntryNameContainsKeywordsPredicate}. + */ +public class EntryNameContainsKeywordsPredicateTest { + + @Test + public void equals() { + List firstPredicateKeywordList = Collections.singletonList("first"); + List secondPredicateKeywordList = Arrays.asList("first", "second"); + + EntryNameContainsKeywordsPredicate firstPredicate = + new EntryNameContainsKeywordsPredicate(firstPredicateKeywordList); + EntryNameContainsKeywordsPredicate secondPredicate = + new EntryNameContainsKeywordsPredicate(secondPredicateKeywordList); + + // same object -> returns true + assertTrue(firstPredicate.equals(firstPredicate)); + + // same values -> returns true + EntryNameContainsKeywordsPredicate firstPredicateCopy = + new EntryNameContainsKeywordsPredicate(firstPredicateKeywordList); + assertTrue(firstPredicate.equals(firstPredicateCopy)); + + // different types -> returns false + assertFalse(firstPredicate.equals(1)); + + // null -> returns false + assertFalse(firstPredicate.equals(null)); + + // different contact -> returns false + assertFalse(firstPredicate.equals(secondPredicate)); + } + + @Test + public void test_entryNameContainsKeywords_returnsTrue() { + // One keyword + EntryNameContainsKeywordsPredicate predicate = new + EntryNameContainsKeywordsPredicate(Collections.singletonList("Consultation")); + assertTrue(predicate.test(new EntryBuilder().build())); + + // Multiple keywords + predicate = new EntryNameContainsKeywordsPredicate(Arrays.asList("Final", "Exam")); + assertTrue(predicate.test(new EntryBuilder().withEntryName("Final Exam lol").build())); + + // Mixed-case keywords + predicate = new EntryNameContainsKeywordsPredicate(Arrays.asList("final", "exam")); + assertTrue(predicate.test(new EntryBuilder().withEntryName("Final Exam now").build())); + } + + @Test + public void test_entryNameDoesNotContainKeywords_returnFalse() { + // Non-matching keyword + EntryNameContainsKeywordsPredicate predicate = new + EntryNameContainsKeywordsPredicate(Collections.singletonList("Consultation")); + assertFalse(predicate.test(new EntryBuilder().withEntryName("Nope").build())); + + // Only one matching keyword + predicate = new EntryNameContainsKeywordsPredicate(Arrays.asList("Final", "Exam")); + assertFalse(predicate.test(new EntryBuilder().withEntryName("Final quiz").build())); + } +} diff --git a/src/test/java/seedu/ta/model/entry/EntryNameTest.java b/src/test/java/seedu/ta/model/entry/EntryNameTest.java new file mode 100644 index 00000000000..e2d32588f5b --- /dev/null +++ b/src/test/java/seedu/ta/model/entry/EntryNameTest.java @@ -0,0 +1,44 @@ +package seedu.ta.model.entry; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +/** + * Contains unit tests for {@code EntryName}. + */ +public class EntryNameTest { + + @Test + public void constructor_null_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> new EntryName(null)); + } + + @Test + public void constructor_invalidEntryName_throwsIllegalArgumentException() { + String invalidName = ""; + assertThrows(IllegalArgumentException.class, () -> new EntryName(invalidName)); + } + + @Test + public void isValidName() { + // null name + assertThrows(NullPointerException.class, () -> EntryName.isValidName(null)); + + // invalid name + assertFalse(EntryName.isValidName("")); + assertFalse(EntryName.isValidName(" ")); + assertFalse(EntryName.isValidName("@")); + assertFalse(EntryName.isValidName("cl@ass")); + assertFalse(EntryName.isValidName("class *")); + + // valid name + assertTrue(EntryName.isValidName("consultation")); // alphabets only + assertTrue(EntryName.isValidName("12345")); // numbers only + assertTrue(EntryName.isValidName("consultation the 2nd")); // alphanumeric characters + assertTrue(EntryName.isValidName("Consultation")); // with capital letters + assertTrue(EntryName.isValidName("Important class consultation today lol")); // long names + } +} diff --git a/src/test/java/seedu/ta/model/entry/EntryTagsContainKeywordsPredicateTest.java b/src/test/java/seedu/ta/model/entry/EntryTagsContainKeywordsPredicateTest.java new file mode 100644 index 00000000000..33583b1023c --- /dev/null +++ b/src/test/java/seedu/ta/model/entry/EntryTagsContainKeywordsPredicateTest.java @@ -0,0 +1,77 @@ +package seedu.ta.model.entry; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.ta.testutil.EntryBuilder; + +/** + * Contains unit tests for {@code EntryTagsContainKeywordsPredicate}. + */ +public class EntryTagsContainKeywordsPredicateTest { + + @Test + public void test_entryTagIsEmpty_returnFalse() { + List keywords = Arrays.asList(EntryBuilder.DEFAULT_TAG); + EntryTagsContainKeywordsPredicate predicate = new EntryTagsContainKeywordsPredicate(keywords); + assertFalse(predicate.test(new EntryBuilder().withTags().build())); + } + + @Test + public void test_entryTagsContainsAllKeywords_returnTrue() { + List keywords = Arrays.asList("ALl", "Key", "Words"); + EntryTagsContainKeywordsPredicate predicate = new EntryTagsContainKeywordsPredicate(keywords); + assertTrue(predicate.test(new EntryBuilder().withTags("All", "Key", "Words").build())); + } + + @Test + public void test_entryTagsDoesNotFullyMatchAllKeywords_returnFalse() { + List keywords = Arrays.asList("Not", "All"); + EntryTagsContainKeywordsPredicate predicate = new EntryTagsContainKeywordsPredicate(keywords); + assertFalse(predicate.test(new EntryBuilder().withTags("All").build())); + } + + @Test + public void test_entryTagsContainsKeywordsAndMore_returnTrue() { + List keywords = Arrays.asList("All", "More"); + EntryTagsContainKeywordsPredicate predicate = new EntryTagsContainKeywordsPredicate(keywords); + assertTrue(predicate.test(new EntryBuilder().withTags("All", "And", "More").build())); + } + + @Test + public void test_entryTagMatchesKeywordPartially_returnFalse() { + List keywords = Arrays.asList("Match"); + EntryTagsContainKeywordsPredicate predicate = new EntryTagsContainKeywordsPredicate(keywords); + assertFalse(predicate.test(new EntryBuilder().withTags("Mat").build())); + } + + @Test + public void test_entryTagKeywordCasingMismatch_returnTrue() { + List keywords = Arrays.asList("MiSmaTCh"); + EntryTagsContainKeywordsPredicate predicate = new EntryTagsContainKeywordsPredicate(keywords); + assertTrue(predicate.test(new EntryBuilder().withTags("mismatch").build())); + } + + @Test + public void equals() { + List keywords = Arrays.asList("Test", "equals"); + EntryTagsContainKeywordsPredicate testPredicate = new EntryTagsContainKeywordsPredicate(keywords); + + // same object -> return true + assertTrue(testPredicate.equals(testPredicate)); + + // null -> return false + assertFalse(testPredicate.equals(null)); + + // different object, same keywords -> return true + assertTrue(testPredicate.equals(new EntryTagsContainKeywordsPredicate(keywords))); + + // different object, different keywords -> return false + assertFalse(testPredicate.equals(new EntryTagsContainKeywordsPredicate(Arrays.asList("lol")))); + } +} diff --git a/src/test/java/seedu/ta/model/entry/EntryTest.java b/src/test/java/seedu/ta/model/entry/EntryTest.java new file mode 100644 index 00000000000..743e0eef414 --- /dev/null +++ b/src/test/java/seedu/ta/model/entry/EntryTest.java @@ -0,0 +1,121 @@ +package seedu.ta.model.entry; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.testutil.Assert.assertThrows; + +import org.junit.jupiter.api.Test; + +import seedu.ta.testutil.EntryBuilder; + +/** + * Contains unit tests for {@code Entry}. + */ +public class EntryTest { + + private final Entry defaultEntry = new EntryBuilder().build(); + + @Test + public void asObservableList_modifyList_throwsUnsupportedOperationException() { + assertThrows(UnsupportedOperationException.class, () -> defaultEntry.getTags().remove(0)); + } + + @Test + public void isSameEntry() { + + // same object -> returns true + assertTrue(defaultEntry.isSameEntry(defaultEntry)); + + // null -> returns false + assertFalse(defaultEntry.isSameEntry(null)); + + // same attributes except name -> return false + assertFalse(defaultEntry.isSameEntry(new EntryBuilder().withEntryName("Different Name") + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate(EntryBuilder.DEFAULT_END_DATE) + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except name with trailing whitespaces-> return false + assertFalse(defaultEntry.isSameEntry(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME + " ") + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate(EntryBuilder.DEFAULT_END_DATE) + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except start date -> return false + assertFalse(defaultEntry.isSameEntry(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME) + .withStartDate("2020-01-01 00:00").withEndDate(EntryBuilder.DEFAULT_END_DATE) + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except end date -> return false + assertFalse(defaultEntry.isSameEntry(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME) + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate("2025-01-01 00:00") + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except tag -> return false + assertFalse(defaultEntry.isSameEntry(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME) + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate("2025-01-01 00:00") + .withTags("DifferentTag").build())); + } + + @Test + public void isOverlapping() { + + // date ranges do not coincide -> return false + assertFalse(defaultEntry.overlapsWith(new EntryBuilder().withEntryName("Second Entry") + .withStartDate("2222-01-01 00:00").withEndDate("2222-01-01 12:00").build())); + + // start date of second entry is equal to end date of default entry -> return false + assertFalse(defaultEntry.overlapsWith(new EntryBuilder().withEntryName("Second Entry") + .withStartDate("2021-04-05 19:00").withEndDate("2222-01-01 00:00").build())); + + // start date of second entry is in between the start date and end date of default entry -> return true + assertTrue(defaultEntry.overlapsWith(new EntryBuilder().withEntryName("Second Entry") + .withStartDate("2021-04-05 17:01").withEndDate("2021-04-05 20:00").build())); + + // start date and end date of second entry is in between start date and end date of default entry -> return true + assertTrue(defaultEntry.overlapsWith(new EntryBuilder().withEntryName("Second Entry") + .withStartDate("2021-04-05 17:01").withEndDate("2021-04-05 18:59").build())); + + // end date of second entry is in between start date and end date of default entry -> return true + assertTrue(defaultEntry.overlapsWith(new EntryBuilder().withEntryName("Second Entry") + .withStartDate("2020-01-01 00:00").withEndDate("2021-04-05 17:01").build())); + } + + @Test + public void equals() { + + // same object -> returns true + assertTrue(defaultEntry.equals(defaultEntry)); + + // null -> returns false + assertFalse(defaultEntry.equals(null)); + + // same attributes -> return True + assertTrue(defaultEntry.equals(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME) + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate(EntryBuilder.DEFAULT_END_DATE) + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except name -> return false + assertFalse(defaultEntry.equals(new EntryBuilder().withEntryName("Different Name") + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate(EntryBuilder.DEFAULT_END_DATE) + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except name with trailing whitespaces-> return false + assertFalse(defaultEntry.equals(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME + " ") + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate(EntryBuilder.DEFAULT_END_DATE) + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except start date -> return false + assertFalse(defaultEntry.equals(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME) + .withStartDate("2020-01-01 00:00").withEndDate(EntryBuilder.DEFAULT_END_DATE) + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except end date -> return false + assertFalse(defaultEntry.equals(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME) + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate("2025-01-01 00:00") + .withTags(EntryBuilder.DEFAULT_TAG).build())); + + // same attributes except tag -> return false + assertFalse(defaultEntry.equals(new EntryBuilder().withEntryName(EntryBuilder.DEFAULT_ENTRY_NAME) + .withStartDate(EntryBuilder.DEFAULT_START_DATE).withEndDate("2025-01-01 00:00") + .withTags("DifferentTag").build())); + } +} diff --git a/src/test/java/seedu/ta/model/entry/ListEntryFormatPredicateTest.java b/src/test/java/seedu/ta/model/entry/ListEntryFormatPredicateTest.java new file mode 100644 index 00000000000..078fca3c120 --- /dev/null +++ b/src/test/java/seedu/ta/model/entry/ListEntryFormatPredicateTest.java @@ -0,0 +1,160 @@ +package seedu.ta.model.entry; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static seedu.ta.model.entry.EntryDate.DEFAULT_FORMATTER; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.Test; + +import seedu.ta.testutil.EntryBuilder; + +/** + * Contains unit tests for {@code ListEntryFormatPredicate}. + */ +public class ListEntryFormatPredicateTest { + + @Test void test_emptyPredicate_returnTrue() { + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate(""); + assertTrue(predicate.test(new EntryBuilder().build())); + } + + @Test + public void test_dayPredicateEntryStartToday_returnTrue() { + String testStartDate = LocalDateTime.now().format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().plusMinutes(1).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("day"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertTrue(predicate.test(todayEntry)); + } + + @Test + public void test_dayPredicateEntryStartAfter_returnFalse() { + String testStartDate = LocalDateTime.now().plusDays(1).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().plusDays(2).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("day"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertFalse(predicate.test(todayEntry)); + } + + @Test + public void test_dayPredicateEntryEndBefore_returnFalse() { + String testStartDate = LocalDateTime.now().minusDays(2).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().minusDays(1).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("day"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertFalse(predicate.test(todayEntry)); + } + + @Test + public void test_dayPredicateEntryStartBeforeEndToday_returnTrue() { + String testStartDate = LocalDateTime.now().minusDays(1).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("day"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertTrue(predicate.test(todayEntry)); + } + + @Test + public void test_dayPredicateEntryStartBeforeEndAfter_returnTrue() { + String testStartDate = LocalDateTime.now().minusDays(2).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().plusDays(2).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("day"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertTrue(predicate.test(todayEntry)); + } + + @Test + public void test_weekPredicateEntryStartThisWeek_returnTrue() { + String testStartDate = LocalDateTime.now().plusDays(6).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().plusDays(10).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("week"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertTrue(predicate.test(todayEntry)); + } + + @Test + public void test_weekPredicateEntryEndThisWeek_returnTrue() { + String testStartDate = LocalDateTime.now().minusDays(10).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().plusDays(6).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("week"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertTrue(predicate.test(todayEntry)); + + } + + @Test + public void test_weekPredicateEntryStartBeforeEndAfter_returnTrue() { + String testStartDate = LocalDateTime.now().minusDays(10).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().plusDays(10).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("week"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertTrue(predicate.test(todayEntry)); + } + + @Test + public void test_weekPredicateEntryStartAfterWeek_returnFalse() { + String testStartDate = LocalDateTime.now().plusDays(10).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().plusDays(11).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("week"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertFalse(predicate.test(todayEntry)); + } + + @Test + public void test_weekPredicateEntryEndBefore_returnFalse() { + String testStartDate = LocalDateTime.now().minusDays(10).format(DEFAULT_FORMATTER); + String testEndDate = LocalDateTime.now().minusDays(8).format(DEFAULT_FORMATTER); + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("week"); + + Entry todayEntry = new EntryBuilder().withStartDate(testStartDate) + .withEndDate(testEndDate).build(); + + assertFalse(predicate.test(todayEntry)); + } + + @Test + public void equals() { + ListEntryFormatPredicate predicate = new ListEntryFormatPredicate("day"); + + // same object -> return true + assertTrue(predicate.equals(predicate)); + + // same keyword -> return true + assertTrue(predicate.equals(new ListEntryFormatPredicate("day"))); + + // null -> return false + assertFalse(predicate.equals(null)); + + // different keyword -> return false + assertFalse(predicate.equals(new ListEntryFormatPredicate("week"))); + } +} diff --git a/src/test/java/seedu/ta/model/entry/ListOccupyingEntryPredicateTest.java b/src/test/java/seedu/ta/model/entry/ListOccupyingEntryPredicateTest.java new file mode 100644 index 00000000000..174dadb1d7d --- /dev/null +++ b/src/test/java/seedu/ta/model/entry/ListOccupyingEntryPredicateTest.java @@ -0,0 +1,35 @@ +package seedu.ta.model.entry; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +import seedu.ta.testutil.EntryBuilder; + +public class ListOccupyingEntryPredicateTest { + + @Test + void test_intervalOccupiedFully_returnTrue() { + EntryDate start = new EntryDate("2021-04-05 17:00"); + EntryDate end = new EntryDate("2021-04-05 19:00"); + ListOccupyingEntryPredicate predicate = new ListOccupyingEntryPredicate(start, end); + assertTrue(predicate.test(new EntryBuilder().build())); + } + + @Test + void test_intervalOccupiedPartially_returnTrue() { + EntryDate start = new EntryDate("2021-04-05 17:30"); + EntryDate end = new EntryDate("2021-04-05 19:30"); + ListOccupyingEntryPredicate predicate = new ListOccupyingEntryPredicate(start, end); + assertTrue(predicate.test(new EntryBuilder().build())); + } + + @Test + void test_intervalNotOccupied_returnFalse() { + EntryDate start = new EntryDate("2021-04-05 13:00"); + EntryDate end = new EntryDate("2021-04-05 14:00"); + ListOccupyingEntryPredicate predicate = new ListOccupyingEntryPredicate(start, end); + assertFalse(predicate.test(new EntryBuilder().build())); + } +} diff --git a/src/test/java/seedu/ta/model/entry/NonOverlappingEntryListTest.java b/src/test/java/seedu/ta/model/entry/NonOverlappingEntryListTest.java new file mode 100644 index 00000000000..7ead2dd5de0 --- /dev/null +++ b/src/test/java/seedu/ta/model/entry/NonOverlappingEntryListTest.java @@ -0,0 +1,152 @@ +package seedu.ta.model.entry; + +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.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.CLASS_MEETING; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION; +import static seedu.ta.testutil.TypicalTeachingAssistant.NON_OVERDUE_ENTRY; +import static seedu.ta.testutil.TypicalTeachingAssistant.OVERDUE_ENTRY; + +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import seedu.ta.model.entry.exceptions.EntryNotFoundException; +import seedu.ta.model.entry.exceptions.OverlappingEntryException; + +/** + * Contains unit tests for {@code NonOverlappingEntryList}. + */ +public class NonOverlappingEntryListTest { + + private final NonOverlappingEntryList nonOverlappingEntryList = new NonOverlappingEntryList(); + + @Test + public void contains_nullEntry_throwNullPointerException() { + assertThrows(NullPointerException.class, () -> nonOverlappingEntryList.contains(null)); + } + + @Test + public void contains_entryNotInList_returnFalse() { + assertFalse(nonOverlappingEntryList.contains(CONSULTATION)); + } + + @Test + public void contains_entryInList_returnTrue() { + nonOverlappingEntryList.add(CONSULTATION); + assertTrue(nonOverlappingEntryList.contains(CONSULTATION)); + } + + @Test + public void overlapsWith_nullEntryToCheck_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> nonOverlappingEntryList.overlapsWith(null)); + } + + @Test + public void overlapsWith_noOverlaps_success() { + assertFalse(nonOverlappingEntryList.overlapsWith(CONSULTATION)); + } + + @Test + public void overlapsWith_overlapFound_success() { + nonOverlappingEntryList.add(CONSULTATION); + assertTrue(nonOverlappingEntryList.overlapsWith(CONSULTATION)); + } + + @Test + public void add_nullEntry_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> nonOverlappingEntryList.add(null)); + } + + @Test + public void add_overlappingEntry_throwsOverlappingEntryException() { + nonOverlappingEntryList.add(CONSULTATION); + assertThrows(OverlappingEntryException.class, () -> nonOverlappingEntryList.add(CONSULTATION)); + } + + @Test + public void setEntry_nullTargetEntry_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> nonOverlappingEntryList.setEntry(null, CONSULTATION)); + } + + @Test + public void setEntry_nullEditedEntry_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> nonOverlappingEntryList.setEntry(CONSULTATION, null)); + } + + @Test + public void setEntry_targetNotInList_throwsEntryNotFoundException() { + assertThrows(EntryNotFoundException.class, () -> nonOverlappingEntryList.setEntry(CONSULTATION, CONSULTATION)); + } + + @Test + public void setEntry_overlappingTargetEntry_throwsOverlappingEntryException() { + nonOverlappingEntryList.add(CONSULTATION); + nonOverlappingEntryList.add(CLASS_MEETING); + assertThrows(OverlappingEntryException.class, () -> nonOverlappingEntryList + .setEntry(CLASS_MEETING, CONSULTATION)); + } + + @Test + public void setEntry_success() { + NonOverlappingEntryList expectedList = new NonOverlappingEntryList(); + expectedList.add(CLASS_MEETING); + nonOverlappingEntryList.add(CONSULTATION); + nonOverlappingEntryList.setEntry(CONSULTATION, CLASS_MEETING); + assertEquals(expectedList, nonOverlappingEntryList); + } + + @Test + public void remove_nullTargetEntry_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> nonOverlappingEntryList.remove(null)); + } + + @Test + public void remove_entryNotInList_throwsEntryNotFoundException() { + assertThrows(EntryNotFoundException.class, () -> nonOverlappingEntryList.remove(CONSULTATION)); + } + + @Test + public void remove_success() { + NonOverlappingEntryList expectedList = new NonOverlappingEntryList(); + nonOverlappingEntryList.add(CONSULTATION); + nonOverlappingEntryList.remove(CONSULTATION); + assertEquals(expectedList, nonOverlappingEntryList); + } + + @Test + public void setEntries_nullList_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> nonOverlappingEntryList + .setEntries((NonOverlappingEntryList) null)); + } + + @Test + public void setEntries_overlappingEntriesList_throwsOverlappingEntriesException() { + List listWithOverlappingEntries = Arrays.asList(CONSULTATION, CONSULTATION); + assertThrows(OverlappingEntryException.class, () -> nonOverlappingEntryList + .setEntries(listWithOverlappingEntries)); + } + + @Test + public void setEntries_success() { + List validList = Arrays.asList(CONSULTATION, CLASS_MEETING); + NonOverlappingEntryList expectedList = new NonOverlappingEntryList(); + expectedList.add(CONSULTATION); + expectedList.add(CLASS_MEETING); + nonOverlappingEntryList.setEntries(validList); + assertEquals(expectedList, nonOverlappingEntryList); + } + + @Test + public void clearOverDueEntries_success() { + List validList = Arrays.asList(OVERDUE_ENTRY, NON_OVERDUE_ENTRY); + NonOverlappingEntryList expectedList = new NonOverlappingEntryList(); + expectedList.add(NON_OVERDUE_ENTRY); + nonOverlappingEntryList.setEntries(validList); + nonOverlappingEntryList.clearOverdueEntries(); + assertEquals(expectedList, nonOverlappingEntryList); + } +} diff --git a/src/test/java/seedu/address/model/tag/TagTest.java b/src/test/java/seedu/ta/model/tag/TagTest.java similarity index 82% rename from src/test/java/seedu/address/model/tag/TagTest.java rename to src/test/java/seedu/ta/model/tag/TagTest.java index 64d07d79ee2..515be1cc323 100644 --- a/src/test/java/seedu/address/model/tag/TagTest.java +++ b/src/test/java/seedu/ta/model/tag/TagTest.java @@ -1,9 +1,12 @@ -package seedu.address.model.tag; +package seedu.ta.model.tag; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.ta.testutil.Assert.assertThrows; import org.junit.jupiter.api.Test; +/** + * Contains unit tests for {@code Tag}. + */ public class TagTest { @Test @@ -22,5 +25,4 @@ public void isValidTagName() { // null tag name assertThrows(NullPointerException.class, () -> Tag.isValidTagName(null)); } - } diff --git a/src/test/java/seedu/ta/storage/JsonAdaptedContactTest.java b/src/test/java/seedu/ta/storage/JsonAdaptedContactTest.java new file mode 100644 index 00000000000..bc2bbbab20f --- /dev/null +++ b/src/test/java/seedu/ta/storage/JsonAdaptedContactTest.java @@ -0,0 +1,72 @@ +package seedu.ta.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.BEN; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.exceptions.IllegalValueException; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; + +public class JsonAdaptedContactTest { + + public static final String VALID_CONTACT_NAME = BEN.getName().fullName; + public static final String VALID_CONTACT_PHONE = BEN.getPhone().value; + public static final String VALID_CONTACT_EMAIL = BEN.getEmail().value; + public static final List VALID_CONTACT_TAGS = BEN.getTags().stream() + .map(JsonAdaptedTag::new).collect(Collectors.toList()); + + public static final String INVALID_CONTACT_NAME = "@@ron"; + public static final String INVALID_CONTACT_PHONE = "8888"; + public static final String INVALID_CONTACT_EMAIL = "carls email"; + public static final String INVALID_CONTACT_TAG = "%%"; + + @Test + public void toModelType_validContactDetails_returnContact() throws Exception { + JsonAdaptedContact contact = new JsonAdaptedContact(BEN); + assertEquals(BEN, contact.toModelType()); + } + + @Test + public void toModelType_invalidContactName_throwsIllegalValueException() { + JsonAdaptedContact contact = + new JsonAdaptedContact(INVALID_CONTACT_NAME, VALID_CONTACT_PHONE, + VALID_CONTACT_EMAIL, VALID_CONTACT_TAGS); + String expectedMessage = ContactName.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, contact::toModelType); + } + + @Test + public void toModelType_invalidContactPhone_throwsIllegalValueException() { + JsonAdaptedContact contact = + new JsonAdaptedContact(VALID_CONTACT_NAME, INVALID_CONTACT_PHONE, + VALID_CONTACT_EMAIL, VALID_CONTACT_TAGS); + String expectedMessage = ContactPhone.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, contact::toModelType); + } + + @Test + public void toModelType_invalidContactEmail_throwsIllegalValueException() { + JsonAdaptedContact contact = + new JsonAdaptedContact(VALID_CONTACT_NAME, VALID_CONTACT_PHONE, + INVALID_CONTACT_EMAIL, VALID_CONTACT_TAGS); + String expectedMessage = ContactEmail.MESSAGE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, contact::toModelType); + } + + @Test + public void toModelType_invalidTags_throwsIllegalValueException() { + List invalidTags = new ArrayList<>(VALID_CONTACT_TAGS); + invalidTags.add(new JsonAdaptedTag(INVALID_CONTACT_TAG)); + JsonAdaptedContact contact = + new JsonAdaptedContact(VALID_CONTACT_NAME, VALID_CONTACT_PHONE, VALID_CONTACT_EMAIL, invalidTags); + assertThrows(IllegalValueException.class, contact::toModelType); + } +} diff --git a/src/test/java/seedu/ta/storage/JsonAdaptedEntryTest.java b/src/test/java/seedu/ta/storage/JsonAdaptedEntryTest.java new file mode 100644 index 00000000000..568a5a56c7e --- /dev/null +++ b/src/test/java/seedu/ta/storage/JsonAdaptedEntryTest.java @@ -0,0 +1,103 @@ +package seedu.ta.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.ta.storage.JsonAdaptedEntry.MISSING_FIELD_MESSAGE_FORMAT; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.core.Messages; +import seedu.ta.commons.exceptions.IllegalValueException; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; + +public class JsonAdaptedEntryTest { + + public static final String INVALID_ENTRY_NAME = "consultation!!!!!!!!"; + public static final String INVALID_START_DATE = "2021 02 31 18:00"; + public static final String INVALID_END_DATE = "2021-02-31 1800"; + public static final String INVALID_TAG_NOT_SINGLE_WORD = "hello world"; + + private static final String VALID_ENTRY_NAME = CONSULTATION.getEntryName().toString(); + private static final String VALID_START_DATE = CONSULTATION.getOriginalStartDate().toString(); + private static final String VALID_END_DATE = CONSULTATION.getOriginalEndDate().toString(); + private static final List VALID_TAGS = CONSULTATION.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList()); + + @Test + public void toModelType_validEntryDetails_returnsEntry() throws Exception { + JsonAdaptedEntry entry = new JsonAdaptedEntry(CONSULTATION); + assertEquals(CONSULTATION, entry.toModelType()); + } + + @Test + public void toModelType_invalidEntryName_throwsIllegalValueException() { + JsonAdaptedEntry entry = + new JsonAdaptedEntry(INVALID_ENTRY_NAME, VALID_START_DATE, VALID_END_DATE, VALID_TAGS); + String expectedMessage = EntryName.NAME_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, entry::toModelType); + } + + @Test + public void toModelType_nullEntryName_throwsIllegalValueException() { + JsonAdaptedEntry entry = + new JsonAdaptedEntry(null, VALID_START_DATE, VALID_END_DATE, VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, EntryName.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, entry::toModelType); + } + + @Test + public void toModelType_invalidStartDate_throwsIllegalValueException() { + JsonAdaptedEntry entry = + new JsonAdaptedEntry(VALID_ENTRY_NAME, INVALID_START_DATE, VALID_END_DATE, VALID_TAGS); + String expectedMessage = EntryDate.DATE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, entry::toModelType); + } + + @Test + public void toModelType_nullStartDate_throwsIllegalValueException() { + JsonAdaptedEntry entry = + new JsonAdaptedEntry(VALID_ENTRY_NAME, null, VALID_END_DATE, VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, EntryDate.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, entry::toModelType); + } + + @Test + public void toModelType_invalidEndDate_throwsIllegalValueException() { + JsonAdaptedEntry entry = + new JsonAdaptedEntry(VALID_ENTRY_NAME, VALID_START_DATE, INVALID_END_DATE, VALID_TAGS); + String expectedMessage = EntryDate.DATE_CONSTRAINTS; + assertThrows(IllegalValueException.class, expectedMessage, entry::toModelType); + } + + @Test + public void toModelType_nullEndDate_throwsIllegalValueException() { + JsonAdaptedEntry entry = + new JsonAdaptedEntry(VALID_ENTRY_NAME, VALID_START_DATE, null, VALID_TAGS); + String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, EntryDate.class.getSimpleName()); + assertThrows(IllegalValueException.class, expectedMessage, entry::toModelType); + } + + @Test + public void toModelType_invalidDateRange_throwsIllegalValueException() { + JsonAdaptedEntry entry = + new JsonAdaptedEntry(VALID_ENTRY_NAME, VALID_END_DATE, VALID_START_DATE, VALID_TAGS); + String expectedMessage = Messages.MESSAGE_INVALID_DATE_RANGE; + assertThrows(IllegalValueException.class, expectedMessage, entry::toModelType); + } + + @Test + public void toModelType_invalidTags_throwsIllegalValueException() { + List invalidTags = new ArrayList<>(VALID_TAGS); + invalidTags.add(new JsonAdaptedTag(INVALID_TAG_NOT_SINGLE_WORD)); + JsonAdaptedEntry entry = + new JsonAdaptedEntry(VALID_ENTRY_NAME, VALID_START_DATE, VALID_END_DATE, invalidTags); + assertThrows(IllegalValueException.class, entry::toModelType); + } +} diff --git a/src/test/java/seedu/ta/storage/JsonSerializableTeachingAssistantTest.java b/src/test/java/seedu/ta/storage/JsonSerializableTeachingAssistantTest.java new file mode 100644 index 00000000000..1a950667529 --- /dev/null +++ b/src/test/java/seedu/ta/storage/JsonSerializableTeachingAssistantTest.java @@ -0,0 +1,58 @@ +package seedu.ta.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static seedu.ta.testutil.Assert.assertThrows; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.junit.jupiter.api.Test; + +import seedu.ta.commons.exceptions.IllegalValueException; +import seedu.ta.commons.util.JsonUtil; +import seedu.ta.model.TeachingAssistant; +import seedu.ta.testutil.TypicalTeachingAssistant; + +public class JsonSerializableTeachingAssistantTest { + + private static final Path TEST_DATA_FOLDER = Paths + .get("src", "test", "data", "JsonSerializableTeachingAssistantTest"); + private static final Path INVALID_CONTACT_FILE = TEST_DATA_FOLDER.resolve("invalidContactTeachingAssistant.json"); + private static final Path DUPLICATE_CONTACT_FILE = TEST_DATA_FOLDER.resolve( + "duplicateContactTeachingAssistant.json"); + private static final Path TYPICAL_TEACHING_ASSISTANT_FILE = TEST_DATA_FOLDER.resolve( + "typicalTeachingAssistant.json"); + private static final Path OVERLAPPING_ENTRY_FILE = TEST_DATA_FOLDER.resolve( + "overlappingEntryTeachingAssistant.json"); + @Test + public void toModelType_typicalTeachingAssistantFile_success() throws Exception { + JsonSerializableTeachingAssistant dataFromFile = JsonUtil.readJsonFile(TYPICAL_TEACHING_ASSISTANT_FILE, + JsonSerializableTeachingAssistant.class).get(); + TeachingAssistant teachingAssistantFromFile = dataFromFile.toModelType(); + TeachingAssistant typicalTeachingAssistant = TypicalTeachingAssistant.getTypicalTeachingAssistant(); + assertEquals(teachingAssistantFromFile, typicalTeachingAssistant); + } + + @Test + public void toModelType_overlappingEntries_throwsIllegalValueException() throws Exception { + JsonSerializableTeachingAssistant dataFromFile = JsonUtil.readJsonFile(OVERLAPPING_ENTRY_FILE, + JsonSerializableTeachingAssistant.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableTeachingAssistant.MESSAGE_OVERLAPPING_ENTRY, + dataFromFile::toModelType); + } + + @Test + public void toModelType_duplicateContacts_throwsIllegalValueException() throws Exception { + JsonSerializableTeachingAssistant dataFromFile = JsonUtil.readJsonFile(DUPLICATE_CONTACT_FILE, + JsonSerializableTeachingAssistant.class).get(); + assertThrows(IllegalValueException.class, JsonSerializableTeachingAssistant.MESSAGE_DUPLICATE_CONTACT, + dataFromFile::toModelType); + } + + @Test + public void toModelType_invalidContact_throwsIllegalValueException() throws Exception { + JsonSerializableTeachingAssistant dataFromFile = JsonUtil.readJsonFile(INVALID_CONTACT_FILE, + JsonSerializableTeachingAssistant.class).get(); + assertThrows(IllegalValueException.class, dataFromFile::toModelType); + } +} diff --git a/src/test/java/seedu/ta/storage/JsonTeachingAssistantStorageTest.java b/src/test/java/seedu/ta/storage/JsonTeachingAssistantStorageTest.java new file mode 100644 index 00000000000..96819cdd403 --- /dev/null +++ b/src/test/java/seedu/ta/storage/JsonTeachingAssistantStorageTest.java @@ -0,0 +1,130 @@ +package seedu.ta.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static seedu.ta.testutil.Assert.assertThrows; +import static seedu.ta.testutil.TypicalTeachingAssistant.AVA; +import static seedu.ta.testutil.TypicalTeachingAssistant.CONSULTATION_MATH; +import static seedu.ta.testutil.TypicalTeachingAssistant.HANNAH; +import static seedu.ta.testutil.TypicalTeachingAssistant.IVAN; +import static seedu.ta.testutil.TypicalTeachingAssistant.REMEDIAL; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; + +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.ta.commons.exceptions.DataConversionException; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.TeachingAssistant; + +public class JsonTeachingAssistantStorageTest { + + private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "JsonTeachingAssistantStorageTest"); + + @TempDir + public Path testFolder; + + @Test + public void readTeachingAssistant_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> readTeachingAssistant(null)); + } + + private java.util.Optional readTeachingAssistant(String filePath) throws Exception { + return new JsonTeachingAssistantStorage(Paths.get(filePath)) + .readTeachingAssistant(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(readTeachingAssistant("NonExistentFile.json").isPresent()); + } + + @Test + public void read_notJsonFormat_exceptionThrown() { + assertThrows(DataConversionException.class, () -> readTeachingAssistant("notJsonFormatTeachingAssistant.json")); + } + + @Test + public void readTeachingAssistant_invalidContactTeachingAssistant_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTeachingAssistant( + "invalidContactTeachingAssistant.json")); + } + + @Test + public void readTeachingAssistant_invalidAndValidContactTeachingAssistant_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTeachingAssistant( + "invalidAndValidTeachingAssistant.json")); + } + + @Test + public void readTeachingAssistant_invalidEntryTeachingAssistant_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> readTeachingAssistant( + "invalidEntryTeachingAssistant.json")); + } + + @Test + public void readTeachingAssistant_invalidAndValidEntryTeachingAssistant_throwDataConversionException() { + assertThrows(DataConversionException.class, () -> + readTeachingAssistant("invalidAndValidEntryTeachingAssistant.json")); + } + + @Test + public void readAndSaveTeachingAssistant_allInOrder_success() throws Exception { + Path filePath = testFolder.resolve("TempTeachingAssistant.json"); + TeachingAssistant original = getTypicalTeachingAssistant(); + JsonTeachingAssistantStorage jsonTeachingAssistantStorage = new JsonTeachingAssistantStorage(filePath); + + // Save in new file and read back + jsonTeachingAssistantStorage.saveTeachingAssistant(original, filePath); + ReadOnlyTeachingAssistant readBack = jsonTeachingAssistantStorage.readTeachingAssistant(filePath).get(); + assertEquals(original, new TeachingAssistant(readBack)); + + // Modify data, overwrite exiting file, and read back + original.addContact(HANNAH); + original.removeContact(AVA); + original.addEntry(REMEDIAL); + jsonTeachingAssistantStorage.saveTeachingAssistant(original, filePath); + readBack = jsonTeachingAssistantStorage.readTeachingAssistant(filePath).get(); + assertEquals(original, new TeachingAssistant(readBack)); + + // Save and read without specifying file path + original.addContact(IVAN); + original.addEntry(CONSULTATION_MATH); + jsonTeachingAssistantStorage.saveTeachingAssistant(original); // file path not specified + readBack = jsonTeachingAssistantStorage.readTeachingAssistant().get(); // file path not specified + assertEquals(original, new TeachingAssistant(readBack)); + + } + + @Test + public void saveTeachingAssistant_nullTeachingAssistant_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveTeachingAssistant(null, "SomeFile.json")); + } + + /** + * Saves {@code teachingAssistant} at the specified {@code filePath}. + */ + private void saveTeachingAssistant(ReadOnlyTeachingAssistant teachingAssistant, String filePath) { + try { + new JsonTeachingAssistantStorage(Paths.get(filePath)) + .saveTeachingAssistant(teachingAssistant, addToTestDataPathIfNotNull(filePath)); + } catch (IOException ioe) { + throw new AssertionError("There should not be an error writing to the file.", ioe); + } + } + + @Test + public void saveTeachingAssistant_nullFilePath_throwsNullPointerException() { + assertThrows(NullPointerException.class, () -> saveTeachingAssistant(new TeachingAssistant(), null)); + } +} diff --git a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java b/src/test/java/seedu/ta/storage/JsonUserPrefsStorageTest.java similarity index 93% rename from src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java rename to src/test/java/seedu/ta/storage/JsonUserPrefsStorageTest.java index 16f33f4a6bb..9b6b1a25a80 100644 --- a/src/test/java/seedu/address/storage/JsonUserPrefsStorageTest.java +++ b/src/test/java/seedu/ta/storage/JsonUserPrefsStorageTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package seedu.ta.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.ta.testutil.Assert.assertThrows; import java.io.IOException; import java.nio.file.Path; @@ -12,9 +12,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.commons.exceptions.DataConversionException; -import seedu.address.model.UserPrefs; +import seedu.ta.commons.core.GuiSettings; +import seedu.ta.commons.exceptions.DataConversionException; +import seedu.ta.model.UserPrefs; public class JsonUserPrefsStorageTest { @@ -73,7 +73,7 @@ public void readUserPrefs_extraValuesInFile_extraValuesIgnored() throws DataConv private UserPrefs getTypicalUserPrefs() { UserPrefs userPrefs = new UserPrefs(); userPrefs.setGuiSettings(new GuiSettings(1000, 500, 300, 100)); - userPrefs.setAddressBookFilePath(Paths.get("addressbook.json")); + userPrefs.setTeachingAssistantFilePath(Paths.get("teachingassistant.json")); return userPrefs; } @@ -119,5 +119,4 @@ public void saveUserPrefs_allInOrder_success() throws DataConversionException, I readBack = jsonUserPrefsStorage.readUserPrefs().get(); assertEquals(original, readBack); } - } diff --git a/src/test/java/seedu/address/storage/StorageManagerTest.java b/src/test/java/seedu/ta/storage/StorageManagerTest.java similarity index 58% rename from src/test/java/seedu/address/storage/StorageManagerTest.java rename to src/test/java/seedu/ta/storage/StorageManagerTest.java index 99a16548970..c1bde58ef00 100644 --- a/src/test/java/seedu/address/storage/StorageManagerTest.java +++ b/src/test/java/seedu/ta/storage/StorageManagerTest.java @@ -1,8 +1,8 @@ -package seedu.address.storage; +package seedu.ta.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook; +import static seedu.ta.testutil.TypicalTeachingAssistant.getTypicalTeachingAssistant; import java.nio.file.Path; @@ -10,10 +10,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import seedu.address.commons.core.GuiSettings; -import seedu.address.model.AddressBook; -import seedu.address.model.ReadOnlyAddressBook; -import seedu.address.model.UserPrefs; +import seedu.ta.commons.core.GuiSettings; +import seedu.ta.model.ReadOnlyTeachingAssistant; +import seedu.ta.model.TeachingAssistant; +import seedu.ta.model.UserPrefs; public class StorageManagerTest { @@ -24,9 +24,9 @@ public class StorageManagerTest { @BeforeEach public void setUp() { - JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(getTempFilePath("ab")); + JsonTeachingAssistantStorage teachingAssistantStorage = new JsonTeachingAssistantStorage(getTempFilePath("ab")); JsonUserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(getTempFilePath("prefs")); - storageManager = new StorageManager(addressBookStorage, userPrefsStorage); + storageManager = new StorageManager(teachingAssistantStorage, userPrefsStorage); } private Path getTempFilePath(String fileName) { @@ -48,21 +48,20 @@ public void prefsReadSave() throws Exception { } @Test - public void addressBookReadSave() throws Exception { + public void teachingAssistantReadSave() throws Exception { /* * Note: This is an integration test that verifies the StorageManager is properly wired to the - * {@link JsonAddressBookStorage} class. - * More extensive testing of UserPref saving/reading is done in {@link JsonAddressBookStorageTest} class. + * {@link JsonTeachingAssistantStorage} class. + * More extensive testing of UserPref saving/reading is done in {@link JsonTeachingAssistantStorageTest} class. */ - AddressBook original = getTypicalAddressBook(); - storageManager.saveAddressBook(original); - ReadOnlyAddressBook retrieved = storageManager.readAddressBook().get(); - assertEquals(original, new AddressBook(retrieved)); + TeachingAssistant original = getTypicalTeachingAssistant(); + storageManager.saveTeachingAssistant(original); + ReadOnlyTeachingAssistant retrieved = storageManager.readTeachingAssistant().get(); + assertEquals(original, new TeachingAssistant(retrieved)); } @Test - public void getAddressBookFilePath() { - assertNotNull(storageManager.getAddressBookFilePath()); + public void getTeachingAssistantFilePath() { + assertNotNull(storageManager.getTeachingAssistantFilePath()); } - } diff --git a/src/test/java/seedu/address/testutil/Assert.java b/src/test/java/seedu/ta/testutil/Assert.java similarity index 97% rename from src/test/java/seedu/address/testutil/Assert.java rename to src/test/java/seedu/ta/testutil/Assert.java index 9863093bd6e..9a3c86ba746 100644 --- a/src/test/java/seedu/address/testutil/Assert.java +++ b/src/test/java/seedu/ta/testutil/Assert.java @@ -1,4 +1,4 @@ -package seedu.address.testutil; +package seedu.ta.testutil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.function.Executable; diff --git a/src/test/java/seedu/ta/testutil/ContactBuilder.java b/src/test/java/seedu/ta/testutil/ContactBuilder.java new file mode 100644 index 00000000000..2e130914c5b --- /dev/null +++ b/src/test/java/seedu/ta/testutil/ContactBuilder.java @@ -0,0 +1,84 @@ +package seedu.ta.testutil; + +import java.util.HashSet; +import java.util.Set; + +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.tag.Tag; +import seedu.ta.model.util.SampleDataUtil; + +/** + * A utility class to help with building contact objects. + */ +public class ContactBuilder { + + public static final String DEFAULT_NAME = "Amy Bee"; + public static final String DEFAULT_PHONE = "85355255"; + public static final String DEFAULT_EMAIL = "amy@gmail.com"; + + private ContactName name; + private ContactPhone contactPhone; + private ContactEmail email; + private Set tags; + + /** + * Creates a {@code ContactBuilder} with the default details. + */ + public ContactBuilder() { + name = new ContactName(DEFAULT_NAME); + contactPhone = new ContactPhone(DEFAULT_PHONE); + email = new ContactEmail(DEFAULT_EMAIL); + tags = new HashSet<>(); + } + + /** + * Initializes the ContactBuilder with the data of {@code contactToCopy}. + */ + public ContactBuilder(Contact contactToCopy) { + name = contactToCopy.getName(); + contactPhone = contactToCopy.getPhone(); + email = contactToCopy.getEmail(); + tags = new HashSet<>(contactToCopy.getTags()); + } + + /** + * Sets the contact's name as the given string. + */ + public ContactBuilder withName(String name) { + this.name = new ContactName(name); + return this; + } + + /** + * Sets the contact's phone as the given string. + */ + public ContactBuilder withPhone(String phone) { + this.contactPhone = new ContactPhone(phone); + return this; + } + + /** + * Sets the contact's email as the given string. + */ + public ContactBuilder withEmail(String email) { + this.email = new ContactEmail(email); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set} and sets it as the contact's tags. + */ + public ContactBuilder withTags(String ... tags) { + this.tags = SampleDataUtil.getTagSet(tags); + return this; + } + + public Contact build() { + return new Contact(name, contactPhone, email, tags); + } + +} + diff --git a/src/test/java/seedu/ta/testutil/ContactUtil.java b/src/test/java/seedu/ta/testutil/ContactUtil.java new file mode 100644 index 00000000000..c68bd266677 --- /dev/null +++ b/src/test/java/seedu/ta/testutil/ContactUtil.java @@ -0,0 +1,59 @@ +package seedu.ta.testutil; + +import static seedu.ta.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_NAME; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_PHONE; +import static seedu.ta.logic.parser.CliSyntax.PREFIX_TAG; + +import java.util.Set; + +import seedu.ta.logic.commands.AddContactCommand; +import seedu.ta.logic.commands.EditContactCommand.EditContactDescriptor; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.tag.Tag; + +/** + * A utility class for Contact. + */ +public class ContactUtil { + + /** + * Returns an add command string for adding the {@code contact}. + */ + public static String getAddContactCommand(Contact contact) { + return AddContactCommand.COMMAND_WORD + " " + getContactDetails(contact); + } + + /** + * Returns the part of command string for the given {@code contact}'s details. + */ + public static String getContactDetails(Contact contact) { + StringBuilder sb = new StringBuilder(); + sb.append(PREFIX_NAME + contact.getName().fullName + " "); + sb.append(PREFIX_PHONE + contact.getPhone().value + " "); + sb.append(PREFIX_EMAIL + contact.getEmail().value + " "); + contact.getTags().stream().forEach( + s -> sb.append(PREFIX_TAG + s.tagName + " ") + ); + return sb.toString(); + } + + /** + * Returns the part of command string for the given {@code EditContactDescriptor}'s details. + */ + public static String getEditContactDescriptorDetails(EditContactDescriptor descriptor) { + StringBuilder sb = new StringBuilder(); + descriptor.getName().ifPresent(name -> sb.append(PREFIX_NAME).append(name.fullName).append(" ")); + descriptor.getPhone().ifPresent(phone -> sb.append(PREFIX_PHONE).append(phone.value).append(" ")); + descriptor.getEmail().ifPresent(email -> sb.append(PREFIX_EMAIL).append(email.value).append(" ")); + if (descriptor.getTags().isPresent()) { + Set tags = descriptor.getTags().get(); + if (tags.isEmpty()) { + sb.append(PREFIX_TAG); + } else { + tags.forEach(s -> sb.append(PREFIX_TAG).append(s.tagName).append(" ")); + } + } + return sb.toString(); + } +} diff --git a/src/test/java/seedu/ta/testutil/EditContactDescriptorBuilder.java b/src/test/java/seedu/ta/testutil/EditContactDescriptorBuilder.java new file mode 100644 index 00000000000..67484ce052a --- /dev/null +++ b/src/test/java/seedu/ta/testutil/EditContactDescriptorBuilder.java @@ -0,0 +1,77 @@ +package seedu.ta.testutil; + +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import seedu.ta.logic.commands.EditContactCommand.EditContactDescriptor; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.contact.ContactEmail; +import seedu.ta.model.contact.ContactName; +import seedu.ta.model.contact.ContactPhone; +import seedu.ta.model.tag.Tag; + +/** + * A utility class to help with building EditContactDescriptor objects. + */ +public class EditContactDescriptorBuilder { + + private EditContactDescriptor descriptor; + + public EditContactDescriptorBuilder() { + descriptor = new EditContactDescriptor(); + } + + public EditContactDescriptorBuilder(EditContactDescriptor descriptor) { + this.descriptor = new EditContactDescriptor(descriptor); + } + + /** + * Returns an {@code EditContactDescriptor} with fields containing {@code contact}'s details + */ + public EditContactDescriptorBuilder(Contact contact) { + descriptor = new EditContactDescriptor(); + descriptor.setName(contact.getName()); + descriptor.setPhone(contact.getPhone()); + descriptor.setEmail(contact.getEmail()); + descriptor.setTags(contact.getTags()); + } + + /** + * Sets the {@code ContactName} of the {@code EditContactDescriptor} that we are building. + */ + public EditContactDescriptorBuilder withContactName(String name) { + descriptor.setName(new ContactName(name)); + return this; + } + + /** + * Sets the {@code ContactPhone} of the {@code EditContactDescriptor} that we are building. + */ + public EditContactDescriptorBuilder withContactPhone(String phone) { + descriptor.setPhone(new ContactPhone(phone)); + return this; + } + + /** + * Sets the {@code ContactEmail} of the {@code EditContactDescriptor} that we are building. + */ + public EditContactDescriptorBuilder withContactEmail(String email) { + descriptor.setEmail(new ContactEmail(email)); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set} and set it to the {@code EditContactDescriptor} + * that we are building. + */ + public EditContactDescriptorBuilder withContactTags(String... tags) { + Set tagSet = Stream.of(tags).map(Tag::new).collect(Collectors.toSet()); + descriptor.setTags(tagSet); + return this; + } + + public EditContactDescriptor build() { + return descriptor; + } +} diff --git a/src/test/java/seedu/ta/testutil/EntryBuilder.java b/src/test/java/seedu/ta/testutil/EntryBuilder.java new file mode 100644 index 00000000000..be2b517a859 --- /dev/null +++ b/src/test/java/seedu/ta/testutil/EntryBuilder.java @@ -0,0 +1,81 @@ +package seedu.ta.testutil; + +import java.util.Set; + +import seedu.ta.model.entry.Entry; +import seedu.ta.model.entry.EntryDate; +import seedu.ta.model.entry.EntryName; +import seedu.ta.model.tag.Tag; +import seedu.ta.model.util.SampleDataUtil; + +/** + * A utility class to help with building entry objects. + */ +public class EntryBuilder { + + public static final String DEFAULT_ENTRY_NAME = "Consultation"; + public static final String DEFAULT_START_DATE = "2021-04-05 17:00"; + public static final String DEFAULT_END_DATE = "2021-04-05 19:00"; + public static final String DEFAULT_TAG = "Physics"; + + private EntryName entryName; + private EntryDate startDate; + private EntryDate endDate; + private Set tags; + + /** + * Creates a {@code EntryBuilder} with the default details. + */ + public EntryBuilder() { + entryName = new EntryName(DEFAULT_ENTRY_NAME); + startDate = new EntryDate(DEFAULT_START_DATE); + endDate = new EntryDate(DEFAULT_END_DATE); + tags = SampleDataUtil.getTagSet(DEFAULT_TAG); + } + + /** + * Initializes the EntryBuilder with the data if {@code entryToCopy}. + */ + public EntryBuilder(Entry entryToCopy) { + entryName = entryToCopy.getEntryName(); + startDate = entryToCopy.getOriginalStartDate(); + endDate = entryToCopy.getOriginalEndDate(); + tags = entryToCopy.getTags(); + } + + /** + * Sets the entry's entryName as the given string. + */ + public EntryBuilder withEntryName(String entryName) { + this.entryName = new EntryName(entryName); + return this; + } + + /** + * Sets the entry's startDate as the given string. + */ + public EntryBuilder withStartDate(String startDate) { + this.startDate = new EntryDate(startDate); + return this; + } + + /** + * Sets the entry's endDate as the given string. + */ + public EntryBuilder withEndDate(String endDate) { + this.endDate = new EntryDate(endDate); + return this; + } + + /** + * Parses the {@code tags} into a {@code Set + * {@code TeachingAssistant ab = new TeachingAssistantBuilder().withContact("John", "Doe").build();} + */ +public class TeachingAssistantBuilder { + + private TeachingAssistant teachingAssistant; + + public TeachingAssistantBuilder() { + teachingAssistant = new TeachingAssistant(); + } + + public TeachingAssistantBuilder(TeachingAssistant teachingAssistant) { + this.teachingAssistant = teachingAssistant; + } + + /** + * Adds a new {@code Contact} to the {@code TeachingAssistant} that we are building. + */ + public TeachingAssistantBuilder withContact(Contact contact) { + teachingAssistant.addContact(contact); + return this; + } + + public TeachingAssistant build() { + return teachingAssistant; + } +} diff --git a/src/test/java/seedu/address/testutil/TestUtil.java b/src/test/java/seedu/ta/testutil/TestUtil.java similarity index 58% rename from src/test/java/seedu/address/testutil/TestUtil.java rename to src/test/java/seedu/ta/testutil/TestUtil.java index 896d103eb0b..1ef1c7252f8 100644 --- a/src/test/java/seedu/address/testutil/TestUtil.java +++ b/src/test/java/seedu/ta/testutil/TestUtil.java @@ -1,13 +1,13 @@ -package seedu.address.testutil; +package seedu.ta.testutil; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import seedu.address.commons.core.index.Index; -import seedu.address.model.Model; -import seedu.address.model.person.Person; +import seedu.ta.commons.core.index.Index; +import seedu.ta.model.Model; +import seedu.ta.model.contact.Contact; /** * A utility class for test cases. @@ -33,23 +33,23 @@ public static Path getFilePathInSandboxFolder(String fileName) { } /** - * Returns the middle index of the person in the {@code model}'s person list. + * Returns the middle index of the contact in the {@code model}'s contact list. */ public static Index getMidIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size() / 2); + return Index.fromOneBased(model.getFilteredContactList().size() / 2); } /** - * Returns the last index of the person in the {@code model}'s person list. + * Returns the last index of the contact in the {@code model}'s contact list. */ public static Index getLastIndex(Model model) { - return Index.fromOneBased(model.getFilteredPersonList().size()); + return Index.fromOneBased(model.getFilteredContactList().size()); } /** - * Returns the person in the {@code model}'s person list at {@code index}. + * Returns the contact in the {@code model}'s contact list at {@code index}. */ - public static Person getPerson(Model model, Index index) { - return model.getFilteredPersonList().get(index.getZeroBased()); + public static Contact getContact(Model model, Index index) { + return model.getFilteredContactList().get(index.getZeroBased()); } } diff --git a/src/test/java/seedu/ta/testutil/TypicalIndices.java b/src/test/java/seedu/ta/testutil/TypicalIndices.java new file mode 100644 index 00000000000..01d97853107 --- /dev/null +++ b/src/test/java/seedu/ta/testutil/TypicalIndices.java @@ -0,0 +1,12 @@ +package seedu.ta.testutil; + +import seedu.ta.commons.core.index.Index; + +/** + * A utility class containing a list of {@code Index} objects to be used in tests. + */ +public class TypicalIndices { + public static final Index INDEX_FIRST = Index.fromOneBased(1); + public static final Index INDEX_SECOND = Index.fromOneBased(2); + public static final Index INDEX_THIRD = Index.fromOneBased(3); +} diff --git a/src/test/java/seedu/ta/testutil/TypicalTeachingAssistant.java b/src/test/java/seedu/ta/testutil/TypicalTeachingAssistant.java new file mode 100644 index 00000000000..1a70d7b8ecf --- /dev/null +++ b/src/test/java/seedu/ta/testutil/TypicalTeachingAssistant.java @@ -0,0 +1,103 @@ +package seedu.ta.testutil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import seedu.ta.model.TeachingAssistant; +import seedu.ta.model.contact.Contact; +import seedu.ta.model.entry.Entry; + +public class TypicalTeachingAssistant { + + // === Entries === + + public static final Entry CONSULTATION = new EntryBuilder().withEntryName("Consultation") + .withStartDate("2022-02-01 13:00").withEndDate("2022-02-01 14:30").withTags("History").build(); + + public static final Entry CLASS_MEETING = new EntryBuilder().withEntryName("Class Meeting") + .withStartDate("2022-02-01 15:00").withEndDate("2022-02-01 16:30").withTags("21S07").build(); + + public static final Entry EXTRA_CLASS = new EntryBuilder().withEntryName("Extra class") + .withStartDate("2022-02-02 17:00").withEndDate("2022-02-02 18:30").withTags("Math").build(); + + public static final Entry SHORT_QUIZ = new EntryBuilder().withEntryName("Short Geo Quiz") + .withStartDate("2022-02-02 14:30").withEndDate("2022-02-02 15:15").withTags("Geography", "21A01").build(); + + public static final Entry DO_STUFF = new EntryBuilder().withEntryName("Go do something important") + .withStartDate("2022-02-04 09:00").withEndDate("2022-02-04 09:30").withTags("Important", "Stuff").build(); + + public static final Entry REMEDIAL = new EntryBuilder().withEntryName("Remedial") + .withStartDate("2022-02-05 10:00").withEndDate("2022-02-05 12:00").withTags("Geography", "21A07").build(); + + public static final Entry CONSULTATION_MATH = new EntryBuilder().withEntryName("Consultation") + .withStartDate("2022-02-05 17:00").withEndDate("2022-02-05 18:00").withTags("Math", "Alice").build(); + + public static final Entry NON_OVERDUE_ENTRY = new EntryBuilder().withEntryName("This entry will not be overdue") + .withStartDate("2030-02-04 09:00").withEndDate("2030-02-04 09:30").withTags("Important", "Stuff").build(); + + public static final Entry OVERDUE_ENTRY = new EntryBuilder().withEntryName("This entry is overdue") + .withStartDate("2010-02-04 09:00").withEndDate("2010-02-04 09:30").withTags("Important", "Stuff").build(); + + // === Contacts === + + public static final Contact AMY = new ContactBuilder().withName("Amy Bee") + .withEmail("amy@example.com").withPhone("11111111").withTags("friends").build(); + + public static final Contact BOB = new ContactBuilder().withName("Bob Choo") + .withEmail("bob@example.com").withPhone("22222222").withTags("owesMoney", "friends").build(); + + public static final Contact AVA = new ContactBuilder().withName("Ava Pauline") + .withEmail("alice@example.com").withPhone("94351253").withTags("friends").build(); + + public static final Contact BEN = new ContactBuilder().withName("Ben Meier") + .withEmail("johnd@example.com").withPhone("98765432").withTags("owesMoney", "friends").build(); + + public static final Contact CLAIRE = new ContactBuilder().withName("Claire Kurz") + .withPhone("95352563").withEmail("heinz@example.com").withTags("colleagues").build(); + + public static final Contact DAVID = new ContactBuilder().withName("David Meier") + .withPhone("87652533").withEmail("cornelia@example.com").withTags("friends").build(); + + public static final Contact ELLIE = new ContactBuilder().withName("Ellie Meyer") + .withPhone("94822244").withEmail("werner@example.com").build(); + + public static final Contact FINN = new ContactBuilder().withName("Finn Kunz") + .withPhone("94824277").withEmail("lydia@example.com").build(); + + public static final Contact GABRIEL = new ContactBuilder().withName("Gabriel Best") + .withPhone("94824422").withEmail("anna@example.com").build(); + + public static final Contact HANNAH = new ContactBuilder().withName("Hannah Meier") + .withPhone("84824244").withEmail("stefan@example.com").build(); + + public static final Contact IVAN = new ContactBuilder().withName("Ivan Mueller") + .withPhone("84821311").withEmail("hans@example.com").build(); + + private TypicalTeachingAssistant() {} // prevents instantiation + + public static TeachingAssistant getEmptyTypicalTeachingAssistant() { + TeachingAssistant ab = new TeachingAssistant(); + return ab; + } + + public static TeachingAssistant getTypicalTeachingAssistant() { + TeachingAssistant ab = new TeachingAssistant(); + for (Contact contact : getTypicalContacts()) { + ab.addContact(contact); + } + + for (Entry entry : getTypicalEntries()) { + ab.addEntry(entry); + } + return ab; + } + + public static List getTypicalEntries() { + return new ArrayList<>(Arrays.asList(CONSULTATION, CLASS_MEETING, EXTRA_CLASS, SHORT_QUIZ, DO_STUFF)); + } + + public static List getTypicalContacts() { + return new ArrayList<>(Arrays.asList(AVA, BEN, CLAIRE, DAVID, ELLIE, FINN, GABRIEL)); + } +} diff --git a/src/test/java/seedu/address/ui/TestFxmlObject.java b/src/test/java/seedu/ta/ui/TestFxmlObject.java similarity index 96% rename from src/test/java/seedu/address/ui/TestFxmlObject.java rename to src/test/java/seedu/ta/ui/TestFxmlObject.java index 5ecd82656f2..2cf9108efdd 100644 --- a/src/test/java/seedu/address/ui/TestFxmlObject.java +++ b/src/test/java/seedu/ta/ui/TestFxmlObject.java @@ -1,4 +1,4 @@ -package seedu.address.ui; +package seedu.ta.ui; import javafx.beans.DefaultProperty; diff --git a/src/test/java/seedu/address/ui/UiPartTest.java b/src/test/java/seedu/ta/ui/UiPartTest.java similarity index 97% rename from src/test/java/seedu/address/ui/UiPartTest.java rename to src/test/java/seedu/ta/ui/UiPartTest.java index 33d82d911b8..c0937bc6745 100644 --- a/src/test/java/seedu/address/ui/UiPartTest.java +++ b/src/test/java/seedu/ta/ui/UiPartTest.java @@ -1,8 +1,8 @@ -package seedu.address.ui; +package seedu.ta.ui; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static seedu.address.testutil.Assert.assertThrows; +import static seedu.ta.testutil.Assert.assertThrows; import java.net.URL; import java.nio.file.Path; @@ -11,7 +11,7 @@ import org.junit.jupiter.api.io.TempDir; import javafx.fxml.FXML; -import seedu.address.MainApp; +import seedu.ta.MainApp; public class UiPartTest { diff --git a/src/test/resources/view/UiPartTest/validFile.fxml b/src/test/resources/view/UiPartTest/validFile.fxml index bab836af0db..457c0b46959 100644 --- a/src/test/resources/view/UiPartTest/validFile.fxml +++ b/src/test/resources/view/UiPartTest/validFile.fxml @@ -1,4 +1,4 @@ - + Hello World! diff --git a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml index 151e09ce926..8d40e306d07 100644 --- a/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml +++ b/src/test/resources/view/UiPartTest/validFileWithFxRoot.fxml @@ -1,6 +1,6 @@ - + Hello World!