diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000000..0cd51c5fb97
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,25 @@
+name: MarkBind Action
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ build_and_deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Install Graphviz
+ run: sudo apt-get install graphviz
+ - name: Install Java
+ uses: actions/setup-java@v3
+ with:
+ java-version: '11'
+ distribution: 'temurin'
+ - name: Build & Deploy MarkBind site
+ uses: MarkBind/markbind-action@v2
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ rootDirectory: './docs'
+ baseUrl: '/tp' # assuming your repo name is tp
+ version: '^5.2.0'
diff --git a/.gitignore b/.gitignore
index 284c4ca7cd9..a0908714673 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,6 @@ src/test/data/sandbox/
# MacOS custom attributes files created by Finder
.DS_Store
docs/_site/
+docs/_markbind/logs/
+
+htmlReport
diff --git a/README.md b/README.md
index 13f5c77403f..11c462b6d13 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,14 @@
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
-
-![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.
+[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/AY2324S2-CS2103T-F11-3/tp/actions)
+
+![Ui](docs/images/UpdatedUi.png)
+
+Tether is a professional Applicant Tracking System (ATS) for hiring managers in small to mid range startups.
+
+Hiring managers can use this application to keep track of the following:
+1. Applicant and Interviewer contacts
+2. Interview datetimes
+3. Application statuses
+
+For usage instructions, refer to the [user guide](https://ay2324s2-cs2103t-f11-3.github.io/tp/UserGuide.html).
+
+This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
diff --git a/build.gradle b/build.gradle
index a2951cc709e..7f49c7221d4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -25,6 +25,10 @@ test {
finalizedBy jacocoTestReport
}
+run {
+ enableAssertions = true
+}
+
task coverage(type: JacocoReport) {
sourceDirectories.from files(sourceSets.main.allSource.srcDirs)
classDirectories.from files(sourceSets.main.output)
@@ -66,7 +70,7 @@ dependencies {
}
shadowJar {
- archiveFileName = 'addressbook.jar'
+ archiveFileName = 'tether.jar'
}
defaultTasks 'clean', 'test'
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000000..1748e487fbd
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,23 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+_markbind/logs/
+
+# Dependency directories
+node_modules/
+
+# Production build files (change if you output the build to a different directory)
+_site/
+
+# Env
+.env
+.env.local
+
+# IDE configs
+.vscode/
+.idea/*
+*.iml
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..59067de664e 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,59 +1,68 @@
---
-layout: page
-title: About Us
+ layout: default.md
+ title: "About Us"
---
+# About Us
+
We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg).
-You can reach us at the email `seer[at]comp.nus.edu.sg`
+You can reach us at our respective emails.
## Project team
-### John Doe
+### Yashwit Polapragada
-
+
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[homepage](https://yashwit.com)]
+[[github](https://github.com/yashpola)]
+[[portfolio](team/yashpola.md)]
+[email: yashwit@u.nus.edu]
-* Role: Project Advisor
+* Role: Developer
+* Responsibilites: UI, Docs, Coding
-### Jane Doe
+### Ryan Chiang
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/macareonie)]
+[[portfolio](team/macareonie)]
+[email: e0958490@u.nus.edu]
-* Role: Team Lead
-* Responsibilities: UI
+* Role: Developer
+* Responsibilities: UI, Docs, Coding
-### Johnny Doe
+### Jiang Bowei
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](http://github.com/gingerbreaf)]
+[[portfolio](team/jiangbowei.md)]
+[email: boweij@u.nus.edu]
* Role: Developer
-* Responsibilities: Data
+* Responsibilities: UI, Docs, Coding
-### Jean Doe
+### Ong Zhi Kai
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/zhikaiong2001)]
+[[portfolio](team/zhikaiong2001.md)]
+[email: e0968830@u.nus.edu]
* Role: Developer
-* Responsibilities: Dev Ops + Threading
+* Responsibilities: Ui, Docs, Coding
-### James Doe
+### Wesley Weishen Yu
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](https://github.com/headcube1)]
+[[portfolio](team/wesleyyu.md)]
+[email: e0959950@u.nus.edu]
* Role: Developer
-* Responsibilities: UI
+* Responsibilities: UI, Docs, Coding
diff --git a/docs/Configuration.md b/docs/Configuration.md
index 13cf0faea16..32f6255f3b9 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -1,6 +1,8 @@
---
-layout: page
-title: Configuration guide
+ layout: default.md
+ title: "Configuration guide"
---
+# Configuration guide
+
Certain properties of the application can be controlled (e.g user preferences file location, logging level) through the configuration file (default: `config.json`).
diff --git a/docs/DevOps.md b/docs/DevOps.md
index d2fd91a6001..8228c845e86 100644
--- a/docs/DevOps.md
+++ b/docs/DevOps.md
@@ -1,12 +1,15 @@
---
-layout: page
-title: DevOps guide
+ layout: default.md
+ title: "DevOps guide"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# DevOps guide
---------------------------------------------------------------------------------------------------------------------
+
+
+
+
## Build automation
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 1b56bb5d31b..5ad511213cd 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,15 +1,19 @@
---
-layout: page
-title: Developer Guide
+ layout: default.md
+ title: "Developer Guide"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+
+# Tether Developer Guide
+
+
+
--------------------------------------------------------------------------------------------------------------------
## **Acknowledgements**
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+This project is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org).
--------------------------------------------------------------------------------------------------------------------
@@ -21,14 +25,9 @@ Refer to the guide [_Setting up and getting started_](SettingUp.md).
## **Design**
-
-
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document `docs/diagrams` folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
-
-
### Architecture
-
+
The ***Architecture Diagram*** given above explains the high-level design of the App.
@@ -53,7 +52,7 @@ The bulk of the app's work is done by the following four components:
The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`.
-
+
Each of the four main components (also shown in the diagram above),
@@ -62,7 +61,7 @@ Each of the four main components (also shown in the diagram above),
For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below.
-
+
The sections below give more details of each component.
@@ -70,9 +69,9 @@ The sections below give more details of each component.
The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java)
-![Structure of the UI Component](images/UiClassDiagram.png)
+
-The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
+The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter`, `InterviewListPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI.
The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml)
@@ -81,7 +80,7 @@ The `UI` component,
* executes user commands using the `Logic` component.
* listens for changes to `Model` data so that the UI can be updated with the modified data.
* keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands.
-* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`.
+* depends on some classes in the `Model` component, as it displays `Person` and `Interview` objects residing in the `Model`.
### Logic component
@@ -89,14 +88,16 @@ The `UI` component,
Here's a (partial) class diagram of the `Logic` component:
-
+
The sequence diagram below illustrates the interactions within the `Logic` component, taking `execute("delete 1")` API call as an example.
-![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png)
+
-
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
-
+
+
+**Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline continues till the end of diagram.
+
How the `Logic` component works:
@@ -108,7 +109,7 @@ How the `Logic` component works:
Here are the other classes in `Logic` (omitted from the class diagram above) that are used for parsing a user command:
-
+
How the parsing works:
* When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object.
@@ -117,7 +118,7 @@ How the parsing works:
### Model component
**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java)
-
+
The `Model` component,
@@ -125,20 +126,24 @@ The `Model` component,
* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object).
* stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
* stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects.
+* stores the address book data i.e., all `Interview` objects (which are contained in a `UniqueInterviewList` object).
+* stores the currently 'selected' `Interview` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change.
* does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components)
-
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
+
+**Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
-
+
-
+
### Storage component
**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java)
-
+
The `Storage` component,
* can save both address book data and user preference data in JSON format, and read them back into corresponding objects.
@@ -155,94 +160,292 @@ Classes used by multiple components are in the `seedu.addressbook.commons` packa
This section describes some noteworthy details on how certain features are implemented.
-### \[Proposed\] Undo/redo feature
+### Interviews
+#### Implementation
+
+An interview contains the following fields: Date, Start time, End time, Description, and two Person instances representing the applicant and interviewer.
+
+
+### Find feature
+
+#### Implementation
+
+The find mechanism is facilitated by 3 classes `FindEmailCommand`, `FindNameCommand` and `FindPhoneCommand`. They all extend `FindCommand` with their corresponding `COMMAND_WORD` : `find_email`, `find_name` and `find_phone` respectively, as well as a corresponding `predicate` variable of type `EmailContainsKeywordsPredicate`, `NameContainsKeywordsPredicate` and `PhoneContainsKeywordsPredicate` respectively.
+`EmailContainsKeywordsPredicate`, `NameContainsKeywordsPredicate` and `PhoneContainsKeywordsPredicate` extend `Predicate` from the `java.util.function` package and override the `test` function to match their respective criteria of matching `Email`, `Name` and `Phone` values respectively.
+
+These `Predicate` objects allow for matching of multiple substrings, facilitating searching for multiple persons in the application simultaneously. This is done by providing multiple keyword arguments after the `find_[email/name/phone]` command word. However, this only applies to keywords for the same criteria.
+
+
+
+Example: Keyword arguments after `find_name` will be matched only to the `Name` values of persons in application data, and not theie `Email` or `Phone` values.
+* `FindEmailCommand#execute()` — Searches for persons based on the specified email keywords.
+* `FindNameCommand#execute()` — Searches for persons based on the specified name keywords.
+* `FindPhoneCommand#execute()` — Searches for persons based on the specified phone keywords.history.
+
+The above `execute` operations utilise `ModelManager#updateFilteredPersonList()` implemented from the `Model` interface to update the GUI to display the persons that match the criteria provided as arguments to the `FindCommand` variant.
+
+The following class diagram summarizes the organisation of the `FindCommand` variant classes.
+
+
+
+Given below is an example usage scenario and how the find mechanism behaves at each step. All 3 variants behave in the same way, just with their keywords being of different types.
+
+Step 1. The user launches the application which loads in data from the previous session. Current data in the application include 2 `Applicant` objects, one with `Name = "Ryan"` and the other with `Name = "Wesley"`.
+
+Step 2. The user executes `find_name` command to find a person with the name Ryan in the application. The `find_name` command calls `FindNameCommandParser#parse()`, creating a new `FindNameCommand` object initialised with a `NameContainsKeywordsPredicate` object that is created with an array of the keywords passed as arguments with the `find_name` command. When `FindNameCommand#execute()` is called, the list displayed on the GUI will be updated to show only the entry of `Ryan:Applicant`.
+
+
+
+
+**Note:**
+* The command expects at least 1 argument following the `find_name` command word and will result in an `ParseException` indicating invalid command format otherwise.
+* Use the command `list_persons` to display the original list of all persons on the GUI
+* There is no need to return back to the original list before executing another `find_[email/name/phone]` command
+
+
+
+
+The following sequence diagram shows how a find operation, specifically `find_name`, goes through the `Logic` component. `find_phone` and `find_email` also behave in a similar way.
+
+
+
+
+#### Design considerations:
+
+**Aspect: How find is implemented:**
+
+* **Alternative 1 (current choice):** 3 separate commands for phone, email, and name.
+ * Pros: Easy and straightforward to implement.
+ * Cons: Uses 3 separate command words resulting in 3 separate CommandParser classes.
-#### Proposed Implementation
+* **Alternative 2:** Command word remains as `find` and the first argument after will determine the criteria to search for: `email`, `name` or `phone`.
+ * Pros: Less repeating of similar code. Only 1 command word required.
+ * Cons: More changes to parsing is required for identification of criteria and potential errors with mixing up keywords with criteria word.
-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:
+### Applicant status feature
-* `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.
+#### Implementation
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+##### Applicant Status
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+The applicant status mechanism is facilitated by `AddApplicantStatusCommand`. It extends `Command` with its own `status` field, stored internally as `ApplicantStatus`.
+`ApplicantStatus` encapsulates statuses (enumerated in `ApplicantState`) in a `value` field.
+`AddApplicantStatusCommand` implements the following operations:
-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.
+* `AddApplicantStatusCommand#execute()` — Adds on the encapsulated `currentStatus` to the applicant in question.
-![UndoRedoState0](images/UndoRedoState0.png)
+`ApplicantStatus` also enables the following functionality in `Applicant`:
-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.
+* `Applicant#updateCurrentStatusToReflectScheduledInterview()` — Updates the `currentStatus` of the applicant to "pending interview".
+* `Applicant#revertCurrentStatus()` — Reverts the `currentStatus` of the applicant to "resume review".
+* `Applicant#getCurrentStatus()` — Returns the stringified version of the encapsulated `currentStatus` which is simply its `value`"
-![UndoRedoState1](images/UndoRedoState1.png)
-Step 3. The user executes `add n/David …` to add a new person. The `add` command also calls `Model#commitAddressBook()`, causing another modified address book state to be saved into the `addressBookStateList`.
+##### Interviewer Status
+Unlike for applicant statuses, there is no `AddInterviewerStatusCommand` available for users. interviewer statuses are managed only internally between `Interview`, `InterviewerStatus` and `Interviewer`.
-![UndoRedoState2](images/UndoRedoState2.png)
+Another important difference is that while an `Applicant` contains at most 1 `currentStatus`, an `Interviewer` contains a variable-size list of `upcomingInterviews` containing objects of type `InterviewerStatus` in the form of "interview with [applicant name]".
-
: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`.
+The interviewer status mechanism is facilitated by `InterviewerStatus`. It extends `Status` and
+encapsulates statuses (enumerated in `InterviewerState`) in a `value` field represented as a `String`.
-
+`InterviewerStatus` enables the following functionality in `Interviewer`:
-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.
+* `Interviewer#updateCurrentStatusToReflectScheduledInterview()` — Appends a new "interview with..." status to the list of `upcomingInterviews` of the interviewer.
+* `Interviewer#updateCurrentStatusToReflectDeletedInterview()()` — Removed the specific "interview with..." status of from the list of `upcomingInterviews` of the interviewer.
+* `Interviewer#getCurrentStatus()` — Returns the stringified version of the encapsulated `upcomingInterviews` list by retrieving the stringified version of each of the `InterviewStatus` in the `upcomingInterviews` list and separating them with a newline.
+ * **Note** that if an interviewer has no upcoming interviews, then instead of storing an `InterviewerStatus` of "free" inside `upcomingInterviews`, the list is left empty and `getCurrentStatus` just returns the String "free" instead.
-![UndoRedoState3](images/UndoRedoState3.png)
+##### General Notes
-
: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 following class diagram shows the overall structure of `AddApplicantStatusCommand`, `ApplicantStatus`, `InterviewerStatus`:
-The following sequence diagram shows how an undo operation goes through the `Logic` component:
+
-![UndoSequenceDiagram](images/UndoSequenceDiagram-Logic.png)
+Given below is an example usage scenario and how the applicant/interviewer status mechanism behaves at each step.
-
:information_source: **Note:** The lifeline for `UndoCommand` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+Step 1. The user launches the application for the first time and executes `add_applicant n/Yash p/98362254 e/yashwit@u.nus.edu`. An applicant _Yash_ is initialised with default `currentStatus` "resume review".
-
+Step 2. The user executes `add_interviewer n/Ryan p/12345678 e/ryan@u.nus.edu`. An interviewer _Ryan_ is initialised with default `upcomingInterviews` .
-Similarly, how an undo operation goes through the `Model` component is shown below:
+Step 3. The user executes `add_interview....a/98362254 i/12345678` to create an interview between _Yash_ and _Ryan_. The `add_interview` command makes a call to `updateCurrentStatusToReflectScheduledInterview` in _Yash_, which updates _Yash's_ `currentStatus` to "pending interview". Similarly, a call is made to `updateCurrentStatusToReflectInterview` in _Ryan_ and _Ryan's_ `upcomingInterviews` is appended with "interview with Yash". The `updateCurrentStatusToReflectScheduledInterview` methods in _Ryan_ and _Yash_ in-turn call the `setPerson` list of the current `Model` for the status change to be reflected immediately.
-![UndoSequenceDiagram](images/UndoSequenceDiagram-Model.png)
+
-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.
+**Note:** If the `add_interview` command fails its execution, it will not call `updateCurrentStatusToReflectScheduledInterview`, so the address book state will not be modified.
-
:information_source: **Note:** If the `currentStatePointer` is at index `addressBookStateList.size() - 1`, pointing to the latest address book state, then there are no undone AddressBook states to restore. The `redo` command uses `Model#canRedoAddressBook()` to check if this is the case. If so, it will return an error to the user rather than attempting to perform the redo.
+
-
+Step 4. The user now decides that she wants to edit `Yash`'s status to "completed interview" manually, and executes the `applicant_status 98362254 s/completed interview` command. The `applicant_status` command will call `AddApplicantStatusCommandParser#parse()`, which will verify the validity of the status through `ApplicantStatus#isValidStatus()` through `ParserUtil` before creating an `ApplicantStatus` and then an `AddApplicantStatusCommand`.
-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.
+
-![UndoRedoState4](images/UndoRedoState4.png)
+**Note:** If the status passed by the user matches with none of the statuses enumerated in `ApplicantState`, a new `ApplicantStatus` is not created and consequently neither is an `AddApplicantStatusCommand`.
-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.
+
-![UndoRedoState5](images/UndoRedoState5.png)
+The following sequence diagram illustrates step 4:
-The following activity diagram summarizes what happens when a user executes a new command:
+
-
+#### Design considerations:
+
+**Aspect: Customisability of statuses for users:**
+
+* **Alternative 1 (current choice):** Users can only set those applicant statuses enumerated in `ApplicantState`.
+ * Pros: Less prone to user inputs that may crash the application or interfere with data integrity.
+ * Cons: Less freedom for users, especially those whose companies have their own set of business rules.
+
+* **Alternative 2:** Custom statuses.
+ * Pros: Can switch to pre-existing `Tag` architecture to implement statuses since they are very similar and there is more leeway in setting tags.
+ * Cons: Lose a bit of control over status management and degrade separation of concerns. More prone to errors when parsing user input.
+
+### Add Interview Feature
+#### Implementation
+
+The add interview mechanism is facilitated by `AddInterviewCommand`. They all extend the `Command` with fields called `description`, `applicant` phone number,
+`interviewer` phone number, `date` of interview, `startTime` as well as `endTime`. An `Interview` is created then added to the list.
+AddInterviewCommand implements the following operations:
+
+* `AddInterviewCommand#execute()` — Adds the encapsulated `Interview` to the list.
+
+The above `execute` operation utilises `ModelManager#updateFilteredPersonList()` implemented from the Model interface to obtain the list of `applicant` and `interviewer` phone numbers.
+This is followed by checking the validity of the phone numbers before creating an `Interview` object to be added into the interview list. The operation `execute` then utilises
+`ModelManager#addInterview()` implemented from the Model to add the `Interview` to the list. The operation `execute` also utilises `ModelManager#sortInterview()` to the `interview` objects by `date`, `startTime` and `endTime`.
+
+The following class diagram summarizes the organisation of the `AddInterviewCommand`:
+
+
+Given below is an example usage scenario and how the mechanism behaves at each step.
+
+Step 1. The user launches the application for the first time and executes `add_applicant n/Wesley p/81159858 e/ywesley16@gmail.com`. An applicant `Wesley` is initialised.
+
+Step 2. The user executes `add_interviewer n/Yash p/98362254 e/yashwit@u.nus.edu`. An interviewer `Yash` is initialised.
+
+Step 3. The user executes `add_interview desc/technical interview date/2024-03-28 st/10:00 et/11:00 a/81159858 i/98362254`. The `add_interview` command calls the `AddInterviewCommandParser#parse()`,
+creating a new `AddInterviewCommand` object initialised with the respective fields. When the `AddInterviewCommand#excute()` is called, a check is conducted to determine whether the `Interview` is already scheduled.
+This is then followed by creating `Interview` object and adding it into the list. The list of interviews will then be sorted. The GUI will display the interviews under the interview column.
+
+Note:
+
+* The command expects all arguments to be filled and will result in `ParseException` indicating invalid command format otherwise.
+* When a duplicate interview is entered, it will result in a `CommandException` indicating a duplicate interview has been entered.
+* When incorrect phone numbers are entered, it will result in a `CommandException` indicating which phone number is incorrect.
+
+The following sequence diagram shows demonstrates step 3:
+
+
#### Design considerations:
-**Aspect: How undo & redo executes:**
+**Aspect: How interviews are added:**
+
+* **Alternative 1(current choice):** Creates the `Interview` inside `AddInterviewCommand`.
+ * Pros: Easier to implement and straight forward.
+ * Cons: Exposes the fields and tedious to recreate `Interview`.
+
+* **Alternative 2:** `Interview` is created under `AddInterviewCommandParser`.
+ * Pros: Better encapsulation.
+ * Cons: Harder to conduct the necessary checks for validity for phone numbers.
+
+### Saving of interviews/persons feature
+
+#### Implementation
+The interview and person saving mechanism in the application is powered through the coordination of seven key components:
+`JsonSerializableAddressBook`, `JsonAdaptedStorageBook` `JsonAdaptedPerson`, `JsonAdaptedTag`, `JsonAdaptedInterview`, `LogicManager`, and `StorageManager`.
+
+The `LogicManager` triggers data persistence post-command execution by invoking `StorageManager`'s `saveAddressBook` function
+which in turn triggers `JsonAddressBookStorage`'s saveAddressBook command.
+This process generates a new `JsonSerializableAddressBook` object, encapsulating lists for both persons and
+interviews.
+
+The `Model` class harbors an `addressBook` with distinct
+lists (`UniquePersonList` and `UniqueInterviewList`) for storing `Person`
+and `Interview` entities. During the serialization phase, these entities are transformed into
+JSON format, utilizing `JsonAdaptedPerson` and `JsonAdaptedInterview` for accurate mapping.
+
+Each `JsonAdaptedPerson` retains `Person` attributes as `@JsonProperty` and encapsulates tags
+as `JsonAdaptedTag` collections. Likewise, `JsonAdaptedInterview` conserves `Interview` attributes,
+additionally incorporating applicant and interviewer information as `JsonAdaptedPerson` instances.
+
+The following sequence diagram illustrates a simplified version of the above process.
+
+
+
+#### Design considerations:
+
+**Aspect: How interviews/persons are stored:**
+
+Converting person and interview data to JSON format facilitates easy data storage, retrieval, and
+manipulation. JSON, being lightweight and text-based, is highly compatible across different systems,
+making data sharing and application scaling more efficient. It also supports hierarchical data structures,
+which is useful for representing complex data relationships. JSON's human-readable format simplifies
+debugging and development processes, enhancing overall productivity.
+
+* **Alternative 1(current choice)**: Save interviews/persons in JSON format.
+ * Pros: Human-readable format simplifies debugging and development processes, compatible with web APIs and databases.
-* **Alternative 1 (current choice):** Saves the entire address book.
- * Pros: Easy to implement.
- * Cons: May have performance issues in terms of memory usage.
+ * Cons: Harder to implement for a beginner developer.
+
+* **Alternative 2:** Save interview/person directly as strings.
+ * Pros: Easy to implement.
+ * Cons: Does not follow existing format, needs to reformat how saving for persons is done which could be very time-consuming, may not be compatible with exisitng APIs and databases.
-* **Alternative 2:** Individual command knows how to undo/redo by
- itself.
- * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted).
- * Cons: We must ensure that the implementation of each individual command are correct.
-_{more aspects and alternatives to be added}_
+### List Persons and List interview Features
+#### Implementation
-### \[Proposed\] Data archiving
+The listing mechanism is facilitated by `ListCommand` and `ListInterviewsCommand`. They all extend the `Command` class. `ListCommand` is responsible for listing persons while `ListInterviewsCommand` is for listing interviews.
+The command words for `ListCommand` and `ListInterviewsCommand` are `list_persons` and `list_interviews` respectively.
-_{Explain here how the data archiving feature will be implemented}_
+Both `ListCommand` and `ListInterviewsCommand` implements the following operations:
+* `ListCommand / ListInterviewsCommand #execute()` - Updates the list in `Model` with the original list.
+
+The above `execute` operation utilises `ModelManager#updateFilteredPersonList()` or `ModelManager#updateFilteredInterviewList()` respectively implemented from the Model interface, with the `PREDICATE_SHOW_ALL_PERSONS` Predicate, to update the list in `Model` with the full list.
+
+This change is then reflected in the UI list of persons / Interviews.
+
+The following class diagram summarizes the organisation of the two different list commands:
+
+
+
+Given below is an example usage scenario for interviews and how the mechanism behaves at each step.
+
+* Step 1. The user launches the application with existing interviewers, applicants, and interviews. The interviews' date are all different.
+
+* Step 2. The user executes the command `filter_interviews_by_date 2024-02-03`. Only interviews on the date `2024-02-03` will show up on the list of interviews on right side of the application.
+
+* Step 3. Now the user wants to see the original list of interviews in step 1 (eg. All interviews, regardless of date). The user can enter the command `list_interviews`. Now the list on the right side of the application will show the full list of interviews as in Step 1.
+
+The scenario for persons is also similar to interviews.
+
+Note:
+* Both `list_persons` and `list_interviews` commands have no arguments.
+
+
+### Delete Interview and Delete Persons feature
+#### Implementation
+
+The deleting mechanism for persons and interview are facilitated by `DeleteCommand` and `DeleteInterviewCommand`. They both extends from `Command` class. `DeleteCommand` is responsible for deleting a person while `DeleteInterviewCommand` is for deleting an interview.
+The command words are `delete_person` and `delete_interview` respectively. `DeleteCommand` takes in a phone number to identify the person to delete, while `DeleteInterviewsCommand` takes in the index of the interview to delete.
+
+Both `DeleteCommand` and `DeleteInterviewsCommand` implements the following operations:
+
+* `DeleteCommand / DeleteInterviewsCommand #execute()` - Removes the corresponding person or interview from the list in `Model`.
+
+The above `execute` operation utilises `ModelManager#deletePerson()` or `ModelManager#deleteInterview()` respectively implemented from the Model interface,to remove the corresponding person or interview from the list in `Model`.
+
+This change is then reflected in the UI list of persons / Interviews.
+
+The following class diagram summarizes the organisation of the two different delete commands:
+
+
+
+Note:
+* If the argument entered (phone number or index) references a interview or person that is not in the current list, it will result in a `CommandException` indicating an out of bounds or invalid phone number error.
+* If there is no argument provided, it will result in `ParseException` indicating invalid command format.
--------------------------------------------------------------------------------------------------------------------
@@ -260,44 +463,187 @@ _{Explain here how the data archiving feature will be implemented}_
### Product scope
-**Target user profile**:
-
-* has a need to manage a significant number of contacts
+**Target user profile**:
+Hiring manager who:
+* cannot afford a professional Applicant Tracking System (ATS)
+* has a need to manage a significant number of job applicants and their interview details
* prefer desktop apps over other types
* can type fast
* prefers typing to mouse interactions
* is reasonably comfortable using CLI apps
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+**Value proposition**:
+Free alternative for tracking interview datetimes, applicant contacts and their application statuses.
### User stories
Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
-| Priority | As a … | I want to … | So that I can… |
-| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- |
-| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App |
-| `* * *` | user | add a new person | |
-| `* * *` | user | delete a person | remove entries that I no longer need |
-| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list |
-| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident |
-| `*` | user with many persons in the address book | sort persons by name | locate a person easily |
+| Priority | As a … | I want to … | So that I can… |
+|----------|---------------------------------------------------------------------------------|---------------------------------------------------------------|---------------------------------------------------------------------------|
+| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the Tether |
+| `* * *` | user | add a new person (applicant/interviewer) | |
+| `* * *` | user | delete a person (applicant/interviewer) | remove person entries that I no longer need |
+| `* * *` | user | add a new interview | |
+| `* * *` | user | delete an interviewer | remove interview entries that I no longer need |
+| `* *` | user with many persons in Tether | find a person by name/email | locate details of a person without having to go through the entire list |
+| `* *` | user with many interviews in Tether | filter interviews by date | locate details of interviews without having to go through the entire list |
+| `* *` | user with many applicants of varying application status in Tether | record applicants' statuses | identify applicant's application progress |
+| `* *` | user with many interviewers of varying availabilities in the company | have a record of interviewer's scheduled interviews | identify which interviewers are occupied or available for interviews |
+| `* *` | user with many persons of varying application status in Tether | filter persons by status | contact all persons within a specific status group if necessary |
+| `* ` | user collaborating with other Tether users | share an applicant's details | update other hiring managers on applicant details |
+| `* ` | user who does not want to clutter local hard drive with files | store applicant's resume | view applicant's resume in Tether |
+| `*` | user with multiple ongoing interviews and many applicants/interviewers to track | view overall statistics of applicants/interviewers/interviews | keep abreast of the overall situation at all times |
*{More to be added}*
### 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 `Tether` and the **Actor** is the `Hiring Manager`, unless specified otherwise)
+
+**Use case: UC01 - List persons**
+
+**MSS**
+
+1. User requests to list persons
+2. System displays list of persons
+
+ Use case ends.
+
+**Use case: UC02 - Add a person with name, email and phone number**
+
+**MSS**
+
+1. User requests to list persons (UC01)
+2. User requests to add a new person to the list
+3. System adds the person and updates the displayed list
-**Use case: Delete a person**
+ Use case ends.
+
+**Extensions**
+
+* 2a. Any of the given name, email, phone number are invalid.
+
+ * 2a1. System shows an error message.
+
+ Use case resumes at step 1.
+
+**Use case: UC03 - Delete a person by phone number**
**MSS**
-1. User requests to list persons
-2. AddressBook shows a list of persons
-3. User requests to delete a specific person in the list
-4. AddressBook deletes the person
+1. User requests to list persons (UC01)
+2. User requests to delete a specific person in the list
+3. System deletes the person
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The given phone number is invalid or doesn't exist.
+
+ * 2a1. System shows an error message.
+
+ Use case resumes at step 1.
+
+**Use case: UC04 - Find a person by name/email/phone number**
+
+**MSS**
+
+1. User requests to list persons (UC01)
+2. User requests to find a specific person in the list by their name/email/phone number
+3. System updates the list to only display the requested person
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The given name/email/phone number is invalid.
+
+ * 2a1. Tether shows an error message.
+
+ Use case resumes at step 1.
+
+**Use case: UC05 - Update an applicant's status**
+
+Precondition: There is at least 1 applicant in the system.
+
+**MSS**
+
+1. User requests to list persons (UC01).
+2. User requests to update a specific applicant's status
+3. System changes the applicant's status
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The given applicant phone number is invalid or doesn't exist.
+
+ * 2a1. System shows an error message.
+
+ Use case resumes at step 1.
+
+
+* 2b. The given status is an invalid applicant status.
+
+ * 2a1. System shows an error message.
+
+ Use case resumes at step 1.
+
+**Use case: UC06 - Filter by status**
+
+Precondition: There is at least 1 applicant or interviewer in the system.
+
+**MSS**
+
+1. User requests to list persons (UC01).
+2. User requests to filter by a given status
+3. System updates the filtered list
+
+**Extensions**
+
+* 2a. The given status is neither a valid applicant nor interviewer status.
+
+ * 2a1. System shows an error message.
+
+ Use case resumes at step 1.
+
+**Use case: UC07 - List interviews**
+
+**MSS**
+
+1. User requests to list interviews
+2. System shows a list of interviews
+
+ Use case ends.
+
+**Use case: UC08 - Add an interview**
+
+**MSS**
+
+1. User requests to list interviews (UC07)
+2. User requests to add a new interview to the list
+3. System adds the interview and updates the displayed list
+
+ Use case ends.
+
+**Extensions**
+
+* 3a. Any of the given description, date, time, phone number are invalid.
+
+ * 3a1. System shows an error message.
+
+ Use case resumes at step 1.
+
+**Use case: UC09 - Delete an interview**
+
+**MSS**
+
+1. User requests to list interviews (UC07)
+2. User requests to delete a specific interview in the list
+3. System deletes the interview
Use case ends.
@@ -305,78 +651,362 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
* 2a. The list is empty.
- Use case ends.
+ * 2a1. System shows an error message.
+
+ Use case resumes at step 1.
* 3a. The given index is invalid.
- * 3a1. AddressBook shows an error message.
+ * 3a1. System shows an error message.
+
+ Use case resumes at step 1.
+
- Use case resumes at step 2.
+**Use case: UC10 - Filtering interviews by date**
+
+**MSS**
+
+1. User requests to list interviews (UC07)
+2. User requests to filter interview by a specified date
+3. System updates the list to only display interviews that match the specified date
+
+ Use case ends.
+
+**Extensions**
+
+* 2a. The given date is invalid.
+
+ * 2a1. Tether shows an error message.
+
+ Use case resumes at step 1.
+* 2b. There are no interviews on the specified date.
+
+ * 2b1. Tether shows a no interviews found message.
+
+ Use case resumes at step 1.
+
+**Use case: UC11 - View overall statistics**
+
+**MSS**
+
+1. User requests to list persons (UC01).
+2. User requests to view overall statistics
+3. System displays number of applicants (total, and by status), number of interviewers (total, and by status), and number of interviews
+
+ Use case ends.
*{More to be added}*
### Non-Functional Requirements
-1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed.
+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.
+3. Should be able to display lists of persons and interviews without a noticeable sluggishness in performance for typical usage.
+4. Should be responsive in all functionality, especially updating and displaying the list after each request.
+5. Should be able to reliably preserve application data across multiple sessions without risk of data loss/corruption.
+6. Should not leak person details, especially email and phone number, outside the application.
+7. Should provide specific error messages to guide users on intended usage of features.
+8. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
*{More to be added}*
### Glossary
+* **Application Status**: These statuses comprise resume review, pending interview, completed interview, accepted, rejected and waiting list.
+* **Application Tracking System**: A software application used by organizations to manage and streamline the recruitment and hiring process
* **Mainstream OS**: Windows, Linux, Unix, MacOS
+* **Person**: A person can refer to either an `Applicant` or an `Interviewer`
* **Private contact detail**: A contact detail that is not meant to be shared with others
+
--------------------------------------------------------------------------------------------------------------------
## **Appendix: Instructions for manual testing**
Given below are instructions to test the app manually.
-
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
+
+
+**Note:** These instructions only provide a starting point for testers to work on;
testers are expected to do more *exploratory* testing.
-
+
### Launch and shutdown
1. Initial launch
- 1. Download the jar file and copy into an empty folder
+ 1. Download `tether.jar` and copy into an empty folder
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+ 2. Double-click the jar file or run `java -jar tether.jar`.
+ Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
-1. Saving window preferences
+2. Saving window preferences
1. Resize the window to an optimum size. Move the window to a different location. Close the window.
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+ 2. Re-launch the app by double-clicking the jar file or running `java -jar tether.jar`.
+ Expected: The most recent window size and location is retained.
-1. _{ more test cases … }_
+3. Closing the app using the `exit` command
+
+ 1. Type the command exit into the command line and hit enter.
+ Expected: The app window closes.
+
+4. Closing the app by clicking on the close button
+
+ 1. Click the close button in the top right hand corner of the app window.
+ Expected: The app window closes.
+
+5. Closing the app by clicking on the `Exit` button in the `File` tab
+
+ 1. Click on the `File` tab in the top left corner of the app window.
+
+ 2. Click on the `Exit` button that is displayed under the `File` tab.
+ Expected: The app window closes.
### Deleting a person
1. Deleting a person while all persons are being shown
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+ 1. Prerequisites: List all persons using the `list_persons` command. Multiple persons in the list, specifically there exists a person with phone number `123` and there is no existing person with phone number `456`.
+
+ 2. Test case: `delete 123`
+ Expected: Contact with phone number `123` is deleted from the list. Details of the deleted contact shown in the status message. Timestamp in the status bar is updated.
+
+ 3. Test case: `delete 456`
+ Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+
+ 4. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is an invalid phone number)
+ Expected: Similar to previous.
+
+### Finding a person
+
+1. Finding a person by name
+
+ 1. Prerequisites: Tether already contains multiple persons in the list, specifically there exists a person with name `Alice` and there is no existing person with phone number `Bob`.
+
+ 2. Test case: `find_name Alice`
+ Expected: Contact with name `Alice` is displayed on the list.
+
+ 3. Test case: `find_name Bob`
+ Expected: An empty list is displayed.
+
+2. Finding a person by phone number
+
+ 1. Prerequisites: Tether already contains multiple persons in the list, specifically there exists a person with phone number `123` and there is no existing person with phone number `456`.
+
+ 2. Test case: `find_phone 123`
+ Expected: Contact with phone number `123` is displayed on the list.
+
+ 3. Test case: `find_phone 456`
+ Expected: An empty list is displayed.
+
+3. Finding a person by email
+
+ 1. Prerequisites: Tether already contains multiple persons in the list, specifically there exists a person with phone number `alice@email.com` and there is no existing person with phone number `bob@email.com`.
+
+ 2. Test case: `find_email alice@email.com`
+ Expected: Contact with email `alice@email.com` is displayed on the list.
+
+ 3. Test case: `find_email bob@email.com`
+ Expected: An empty list is displayed.
+
+### Updating applicant status
+
+1. Updating an applicant's status
+
+ 1. Prerequisites: List all persons using the `list_persons` command. Multiple applicants in the list, specifically there exists an applicant with phone number `123` and an interviewer with phone number `321`.
+
+ 2. Test case: `applicant_status 123 s/accepted`
+ Expected: Status field of applicant with phone number `123` is updated with "accepted". Details of the applicant with their status is shown in the result message.
+
+ 3. Test case: `applicant_status 321 s/accepted`
+ Expected: Status field of interviewer with phone number `321` does not change. Error message displayed to user stating that only applicants can be given this status.
+
+ 4. Test case: `applicant_status 123 s/free`
+ Expected: Status field of applicant with phone number `123` does not change. Error message displayed to user stating that applicant status can only be given one of a given set of statuses.
+
+ 5. Test case: `applicant_status hello s/free`
+ Expected: Error message displayed to user stating that either the command format or parameters are invalid.
+
+ 6. Test case: `applicant_status 123 s/accepted s/rejected`
+ Expected: Status field of applicant with phone number `123` is updated with "rejected". Details of the applicant with their status is shown in the result message.
+
+ 7. Test case: `applicant_status 123 s/accepted rejected`
+ Expected: Status field of applicant with phone number `123` does not change. Error message displayed to user stating that applicant status can only be given one of a given set of statuses.
+
+### Filtering persons by status
+
+1. Filtering persons by status
+
+ 1. Prerequisites: List all persons using the `list_persons` command. 4 applicants in the list, specifically there exists 2 applicants with status `resume review` and `pending interview` respectively, and 2 interviewers with status `free` and `interview with applicantOneName` respectively.
+
+ 2. Test case: `filter_by_status resume review`
+ Expected: Displayed list of persons is updated to show only 1 applicant with status `resume review`.
+
+ 3. Test case: `filter_by_status pending interview`
+ Expected: Displayed list of persons is updated to show only 1 applicant with status `pending interview`.
+
+ 4. Test case: `filter_by_status free`
+ Expected: Displayed list of persons is updated to show only 1 interviewer with status `free`.
+
+ 5. Test case: `filter_by_status interview with applicantOneName`
+ Expected: Displayed list of persons is updated to show only 1 interviewer with status `interview with applicantOneName`.
+
+ 6. Test case: `filter_by_status 1`
+ Expected: Displayed list of persons doesn't change. Error message is displayed stating invalid command format or parameters.
+
+ 7. Test case: `filter_by_status accepted`
+ Expected: Displayed list of persons doesn't change. Message is displayed stating no persons found with the given status.
+
+### Deleting an interview
+
+1. Deleting an interview while all interviews are being shown
+
+ 1. Prerequisites: List all interviews using the `list_interviews` command. One interview in the list.
+
+ 2. Test case: `delete 1`
+ Expected: The only interview is deleted from the list. Details of the deleted interview shown in the status message. Timestamp in the status bar is updated.
+
+ 3. Test case: `delete 5`
+ Expected: No interview is deleted. Error details shown in the status message. Status bar remains the same.
+
+ 4. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is an invalid phone number)
+ Expected: Similar to previous.
+
+### Filtering interviews by date
+
+1. Filtering interviews by date
+
+ 1. Prerequisites: Tether already contains multiple interviews in the list, specifically there exists multiple interviews with date `2024-05-05` and there is no existing interview with date `2024-06-06`. Additionally, ensure that the original interview list is displayed by using the `list_interviews` command.
+
+ 2. Test case: `filter_interviews_by_date 2024-05-05`
+ Expected: Interviews with date `2024-05-05` is displayed on the list.
+
+ 3. Test case: `filter_interviews_by_date 2024-06-06`
+ Expected: A message indicating that no interviews are found is displayed, and the displayed interview list does not change.
+
+### View overall statistics
+
+1. View overall statistics
+
+ 1. Prerequisites: List all persons using the `list_persons` command. 4 applicants in the list, specifically there exists 2 applicants with status `resume review` and `pending interview` respectively, and 2 interviewers with status `free` and `interview with applicantOneName` respectively. List all interviews using the `list_interviews` command. 1 interview in the list, between the same interviewer whose status is `interview with applicantOneName` and the same applicant whose status is `pending interview`.
+
+ 2. Test case: `view_overall_statistics`
+ Expected: Message is displayed stating 2 applicants total (1 in resume review and 1 in pending interview), 2 interviewers total (1 free and 1 busy), and 1 interview total.
+
+ 3. Test case: `view_overall_statistics 1`
+ Expected: Same result as Test Case 2. Extraneous parameters are ignored
+
+### Saving and Loading data from data file
+
+1. Saving and loading data from data file.
+
+ 1. Prerequisites: The data file exists and is located at `data/addressbook.json`. Data in data file is valid.
+
+ 2. Perform commands that changes data in Tether (Example: add an applicant using `add_applicant n/Alice p/123 e/alice@email.com`)
+
+ 3. Close and launch Tether again.
+ Expected: Tether correctly displays the updated data.
+
+2. Dealing with corrupted data file.
+
+ 1. Prerequisites: The data file exists and is located at `data/addressbook.json`.
+
+ 2. Add an invalid character to the `addressbook.json` (e.g. add a # to the start of file).
+
+ 3. Close and launch Tether again.
+ Expected: Tether does not display any data and an error message is shown in the terminal.
+
+3. Dealing with missing data file.
+
+ 1. Prerequisites: The data file exists and is located at `data/addressbook.json`.
+
+ 2. Delete the data file located at `data/addressbook.json`.
+
+ 3. Close and launch Tether again.
+ Expected: New data file created at `data/addressbook.json` containing some sample data.
- 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.
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous.
+## **Appendix: Planned Enhancements**
-1. _{ more test cases … }_
+**Team Size**: 5 members
-### Saving data
+Given below are the planned enhancements we plan to implement for our application in the future:
-1. Dealing with missing/corrupted data files
+1. Add group/panel interviews
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+ - **Description:** Currently, each interview can only have one applicant and one interviewer. This can be problematic since it is common for group interviews to occur where there are multiple interviewers/applicants in one interview.
+ - **Enhancement:** To resolve this, we plan to allow the `add_interview` command to take in more than one applicants/interviewers phone number.
+ - **Example:** The user can add two applicants and 2 interviewers to an interview by executing`add_interview desc/Group interview date/2024-05-05 st/13:00 et/15:00 a/11111111 a/22222222 i/33333333 i/44444444`.
+
+
+2. `AddApplicantStatusCommand` to accommodate custom statuses
+ * Description: Currently, the `AddApplicantStatusCommand` strictly checks if the status given by the user matches any of 5 preset statuses. This can be problematic since different companies may have different conventions on applicant statuses and thus require adding applicant statuses to be more flexible.
+ * Enhancement: To resolve this, we plan to change the `AddApplicantStatusCommand` to accommodate custom statuses so long as they still adhere to some basic constraints such as being alphanumeric or ASCII-characters.
+ * Example: A user may choose to execute `applicant_status 12345678 s/Stage1` instead of `applicant_status 12345678 s/resume review`, depending on their specific label for the first stage of the hiring pipeline.
+
+3. Shorthand for commands
+ - **Description:** Currently, some commands can be very long and take some to type out. This can affect the efficiency of the hiring manager.
+ - **Enhancement:** To resolve this, we plan to add shorter versions of existing commands by modifying the parser classes to take in these shorter commands.
+ - **Example:** User can filter interviews by `fibd 2024-05-05` instead of typing out the whole `filter_interviews_by_date 2024-05-05`.
+
+
+4. `PersonCard` to accommodate dynamic sizing
+ * Description: Currently, the `PersonCard` encapsulating all the fields of a person (`name`, `email`, `status`...) spills out of bounds when the fields have too long values or when statuses stack up vertically (for interviewers). This can be problematic since it impedes the user view of the application and undermines their interaction with it.
+ * Enhancement: To resolve this, we plan to calibrate the `PersonCard`s to be more dynamically sized i.e. resize themselves when the information they encapsulate grows out of the current dimensions.
+
+
+5. Edit parameters of persons
+ - **Description:** Currently, the only way to edit a person’s details is to delete the person and re-add the person with the corrected details. This can be problematic as users have to execute multiple commands with repeated parameters just to make a small change.
+ - **Enhancement:** We plan to abstract these steps into a single edit command which allows users to change specific parameters of a selected person.
+ - **Example:** Assuming the first person in the list has name Alice and the user wants to change the person’s name to Bob, the command `edit 1 n/Bob` can be executed.
+
+
+6. Tagging persons
+ - **Description:** Currently, applicant and interviewer tags are automatically added when a person object is created and cannot be modified. This can be inconvenient for users who may want to manually append additional information in the tags of existing people, but are unable to do so in the current iteration of Tether.
+ - **Enhancement:** To resolve this, we plan to modify the currently private add tag functionality into a command that users can input in the command box to add custom tags to persons.
+ - **Example:** Users can now execute `tag 1 late` to add a late tag to the first person in the list.
+
+
+7. More specific error messages
+ - **Description:** Some of the commands currently are not the most specific when it comes to error messages. Users who enter the parameters wrongly might not be able to tell from a glance what went wrong. One example would be for the add_interview command if the user accidentally swapped the interviewer and applicant’s phone numbers.
+ - **Enhancement:** We can create a new exception for when the user swaps the interviewer and applicant’s phone numbers.
+ - **Example:** If the phone numbers are swapped, an error message will be shown: “You seemed to have swapped the interviewer and applicant’s phone numbers”.
+
+
+8. Separate Applicant/Interviewer list
+ - **Description:** Currently, all persons (regardless of applicant or interviewer) are packed in the persons list, with their respective tags. These two different types of persons are not sorted or separated. This might not be convenient when you only want to look at either applicants or interviewers but not both.
+ - **Enhancement:** We can modify the UI to have a third list, of Interviewers, in the middle. The leftmost list will be list of Applicants, while the rightmost will remain to be the list of interviews. The two persons list will be labelled Applicants and interviewers respectively.
+ - **Example:** The two different lists will be as described, then the list_persons command will reset all filters for both lists. find_name/phone/email command will also work for both. The Applicant and Interviewer Tag is no longer shown.
+
+
+9. Delete Interviews when deleting a person
+ - **Description:** Currently, when the user tries to delete a person, the system checks whether that person is involved in any existing interviews (be it as an applicant or interviewer). If this is false, the command executes as normal. However, if it is true, the command execution will be blocked and the user will receive a system message stating that he or she will need to delete all interviews corresponding to the person before he or she can delete the person. This might be troublesome if there are many interviews to be deleted.
+ - **Enhancement:** We plan to add a cascading function that will automatically search for all existing interviews corresponding to the person and delete them, as well as the person. We will also ask the user to confirm this action before proceeding.
+ - **Example:** If applicant A has 3 existing interviews, when the user deletes this applicant, the system will ask for confirmation first. After confirming, the application will delete applicant A together with all 3 interviews corresponding to A.
+
+
+10. Delete multiple people at once
+ - **Description:** Currently, the user can only delete one person at a time. This may be problematic because the user may want to delete many people at once and this can take up quite some time.
+ - **Enhancement:** We plan allow the `delete_person` command to take in multiple phone numbers at once.
+ - **Example:** The user can execute `delete_person 11111111 22222222 33333333` to delete persons with the respective phone numbers.
+
+
+## **Appendix: Effort**
+
+**Challenges** :
+* Designing and implementing the `addInterviewCommand` from scratch.
+* Getting the GUI to display a separate list for interviews and ensuring it did not distort other current GUI components.
+* Saving of `Interview` and `Person` together in the same JSON file with no issues.
+* Implementing multi status for `Interviewer`.
+
+**Effort Required and Difficulty** :
+* 2 new sub-entity types (`Applicant`, `Interviewer`) extending AB3's 1 entity type (`Person`).
+* 1 new entity type (`Interview`).
+* 3 new data type (`Remark`, `Type` , `Status`).
+* 12 new commands (AB3 only had 8 commands of which we removed 2 and updated 3 commands).
+* Updated UI to accommodate to `Person` and `Interview` - 2 list panels compared to 1 in AB3.
-1. _{ more test cases … }_
diff --git a/docs/Documentation.md b/docs/Documentation.md
index 3e68ea364e7..082e652d947 100644
--- a/docs/Documentation.md
+++ b/docs/Documentation.md
@@ -1,29 +1,21 @@
---
-layout: page
-title: Documentation guide
+ layout: default.md
+ title: "Documentation guide"
+ pageNav: 3
---
-**Setting up and maintaining the project website:**
-
-* We use [**Jekyll**](https://jekyllrb.com/) to manage documentation.
-* The `docs/` folder is used for documentation.
-* To learn how set it up and maintain the project website, follow the guide [_[se-edu/guides] **Using Jekyll for project documentation**_](https://se-education.org/guides/tutorials/jekyll.html).
-* Note these points when adapting the documentation to a different project/product:
- * The 'Site-wide settings' section of the page linked above has information on how to update site-wide elements such as the top navigation bar.
- * :bulb: In addition to updating content files, you might have to update the config files `docs\_config.yml` and `docs\_sass\minima\_base.scss` (which contains a reference to `AB-3` that comes into play when converting documentation pages to PDF format).
-* If you are using Intellij for editing documentation files, you can consider enabling 'soft wrapping' for `*.md` files, as explained in [_[se-edu/guides] **Intellij IDEA: Useful settings**_](https://se-education.org/guides/tutorials/intellijUsefulSettings.html#enabling-soft-wrapping)
+# Documentation Guide
+* We use [**MarkBind**](https://markbind.org/) to manage documentation.
+* The `docs/` folder contains the source files for the documentation website.
+* To learn how set it up and maintain the project website, follow the guide [[se-edu/guides] Working with Forked MarkBind sites](https://se-education.org/guides/tutorials/markbind-forked-sites.html) for project documentation.
**Style guidance:**
* Follow the [**_Google developer documentation style guide_**](https://developers.google.com/style).
+* Also relevant is the [_se-edu/guides **Markdown coding standard**_](https://se-education.org/guides/conventions/markdown.html).
-* Also relevant is the [_[se-edu/guides] **Markdown coding standard**_](https://se-education.org/guides/conventions/markdown.html)
-
-**Diagrams:**
-
-* See the [_[se-edu/guides] **Using PlantUML**_](https://se-education.org/guides/tutorials/plantUml.html)
-**Converting a document to the PDF format:**
+**Converting to PDF**
-* See the guide [_[se-edu/guides] **Saving web documents as PDF files**_](https://se-education.org/guides/tutorials/savingPdf.html)
+* See the guide [_se-edu/guides **Saving web documents as PDF files**_](https://se-education.org/guides/tutorials/savingPdf.html).
diff --git a/docs/Gemfile b/docs/Gemfile
deleted file mode 100644
index c8385d85874..00000000000
--- a/docs/Gemfile
+++ /dev/null
@@ -1,10 +0,0 @@
-# frozen_string_literal: true
-
-source "https://rubygems.org"
-
-git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
-
-gem 'jekyll'
-gem 'github-pages', group: :jekyll_plugins
-gem 'wdm', '~> 0.1.0' if Gem.win_platform?
-gem 'webrick'
diff --git a/docs/Logging.md b/docs/Logging.md
index 5e4fb9bc217..589644ad5c6 100644
--- a/docs/Logging.md
+++ b/docs/Logging.md
@@ -1,8 +1,10 @@
---
-layout: page
-title: Logging guide
+ layout: default.md
+ title: "Logging guide"
---
+# Logging guide
+
* We are using `java.util.logging` package for logging.
* The `LogsCenter` class is used to manage the logging levels and logging destinations.
* The `Logger` for a class can be obtained using `LogsCenter.getLogger(Class)` which will log messages according to the specified logging level.
diff --git a/docs/SettingUp.md b/docs/SettingUp.md
index 275445bd551..03df0295bd2 100644
--- a/docs/SettingUp.md
+++ b/docs/SettingUp.md
@@ -1,27 +1,32 @@
---
-layout: page
-title: Setting up and getting started
+ layout: default.md
+ title: "Setting up and getting started"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# Setting up and getting started
+
+
--------------------------------------------------------------------------------------------------------------------
## Setting up the project in your computer
-
:exclamation: **Caution:**
+
+**Caution:**
Follow the steps in the following guide precisely. Things will not work out if you deviate in some steps.
-
+
First, **fork** this repo, and **clone** the fork into your computer.
If you plan to use Intellij IDEA (highly recommended):
1. **Configure the JDK**: Follow the guide [_[se-edu/guides] IDEA: Configuring the JDK_](https://se-education.org/guides/tutorials/intellijJdk.html) to to ensure Intellij is configured to use **JDK 11**.
-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. **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.
+
+ 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 tests](Testing.md) to ensure they all pass.
@@ -34,10 +39,11 @@ If you plan to use Intellij IDEA (highly recommended):
If using IDEA, follow the guide [_[se-edu/guides] IDEA: Configuring the code style_](https://se-education.org/guides/tutorials/intellijCodeStyle.html) to set up IDEA's coding style to match ours.
-
:bulb: **Tip:**
+
+ **Tip:**
Optionally, you can follow the guide [_[se-edu/guides] Using Checkstyle_](https://se-education.org/guides/tutorials/checkstyle.html) to find how to use the CheckStyle within IDEA e.g., to report problems _as_ you write code.
-
+
1. **Set up CI**
diff --git a/docs/Testing.md b/docs/Testing.md
index 8a99e82438a..78ddc57e670 100644
--- a/docs/Testing.md
+++ b/docs/Testing.md
@@ -1,12 +1,15 @@
---
-layout: page
-title: Testing guide
+ layout: default.md
+ title: "Testing guide"
+ pageNav: 3
---
-* Table of Contents
-{:toc}
+# Testing guide
---------------------------------------------------------------------------------------------------------------------
+
+
+
+
## Running tests
@@ -19,8 +22,10 @@ There are two ways to run tests.
* **Method 2: Using Gradle**
* Open a console and run the command `gradlew clean test` (Mac/Linux: `./gradlew clean test`)
-
:link: **Link**: Read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html) to learn more about using Gradle.
-
+
+
+**Link**: Read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html) to learn more about using Gradle.
+
--------------------------------------------------------------------------------------------------------------------
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 7abd1984218..e2014aefb66 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,198 +1,494 @@
----
-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.
-
-* Table of Contents
-{:toc}
+# Tether User Guide
+
+_What if you could enjoy the dynamic nature of professional Applicant Tracking Systems combined with the timeless
+simplicity of tracking tools like Excel?_
+
+Welcome to Tether, a free offline desktop application built for the everyday Hiring Manager (HM) such as yourself! Our application enables you to record applicant and interviewer details, schedule interviews, and access a high-level overview of applicant/interviewer/interview statistics. Tether is optimised for the HM that prefers a core set of features supported by a minimal, but smart, interface with no dependency on the internet.
+
+If you have found yourself here in this user guide, it probably means you need some help setting up and/or using our application. Thus, our user guide serves to
+* Provide you with some context on what our application is and how it is different from traditional desktop applications,
+* Help you install and run Tether,
+* Orientate you through Tether's interface,
+* Enumerate and explain the features available to you and,
+* List any planned enhancements to keep you appraised of how we're improving Tether.
+
+There is also a List of Contents at the start, and a Glossary and FAQ section at the end to ease and expedite your perusal of our user guide.
+
+Ready to make hiring management easy for yourself? Let's get started!
+
+
+
+
+-----------------------------------------------------------------------------------------------------------------
+
+# List of Contents
+- [Getting Started](#getting-started)
+ - [Context on Tether](#context-on-tether)
+ - [Quick Start](#quick-start)
+ - [Gathering Your Bearings](#gathering-your-bearings)
+- [Features](#features)
+ - [Using Features Error-Free](#using-features-error-free)
+ - [Adding an Applicant](#adding-an-applicant)
+ - [Adding a Status to an Applicant](#adding-a-status-to-an-applicant)
+ - [Adding an Interviewer](#adding-an-interviewer)
+ - [Viewing the Status of an Interviewer](#viewing-the-status-of-an-interviewer)
+ - [Adding a Remark to a Person](#adding-a-remark-to-a-person)
+ - [Adding an Interview](#adding-an-interview)
+ - [Listing All Persons](#listing-all-persons)
+ - [Listing All the Interviews](#listing-all-interviews)
+ - [Finding Persons](#finding-persons)
+ - [Filtering Persons by Status](#filtering-persons-by-status)
+ - [Filtering Interviews by Date](#filtering-interviews-by-date)
+ - [Deleting a Person](#deleting-a-person)
+ - [Deleting an Interview](#deleting-an-interview)
+ - [View Overall Statistics](#view-overall-statistics)
+ - [Clear All Existing Data](#clear-all-existing-data)
+ - [Exiting the Program](#exiting-the-program)
+- [Saving your Data](#saving-your-data)
+- [Planned Enhancements and Known Limitations](#planned-enhancements-and-known-limitations)
+- [Glossary](#glossary)
+- [FAQ](#faq)
+- [Command Summary](#command-summary)
--------------------------------------------------------------------------------------------------------------------
+# Getting Started
+
+This crucial section is where your journey with Tether starts. Here, you may learn the context for Tether, how to set up and run Tether, the structure of the application, and where to find help quickly if you need it. For terms which we feel may be a little technical or require further contextualising, we add numbering like so **[0]** beside them and then define those terms in our [Glossary](#glossary).
+
+## Context on Tether
+
+Tether is not your typical application that you may download off an app-store and directly launch from your desktop. It is an offline application that launches from and runs entirely through your computer's [_Command Line Interface_ (CLI)](https://aws.amazon.com/what-is/cli/#:~:text=A%20command%20line%20interface%20operating%20system%20using%20your%20keyboard) **[1]** which is a medium through which you directly interact with your computer system.
+
+The way Tether works is, once you set up and launch the application from the CLI (as you will learn below in [Quick Start](#quick-start)), you type-in commands into a [_Graphical User Interface_ (GUI)](https://www.britannica.com/technology/graphical-user-interface) **[2]** to use the application. We employ this modus operandi to cut down on visual-noise such as buttons and click-away popups, and allow you to focus your hands only on the keyboard.
+
+In short, **if you can type fast**, Tether can get your hiring management tasks done faster than traditional GUI apps.
+
+Further, the data you create on Tether will not be saved in any online platform, but locally in your computer in a [json](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/JSON) **[3]** file. You may even directly edit your data in this file if need be and the changes will be reflected when you (re)launch Tether. The benefit of this is that if you need a quicker way of fixing or updating your data than using commands on our application, the json file is at your disposal.
+
+Below you will see a listing and explanation of the exact commands you will need to use our available [features](#features).
+
+
+## Quick Start
+
+1. [Ensure](https://www.baeldung.com/java-check-is-installed) you have Java 11 or above installed in your Computer. This is a crucial step as Java, one of the world's most prominent programming languages, is the backbone of how our application operates.
+ - If you do not have Java 11, download it from [here](https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html)
+ - For **Mac** users, you are recommended to download from [here](https://www.azul.com/downloads/?version=java-11-lts&os=macos&architecture=arm-64-bit&package=jdk-fx) to prevent any issues running the `tether.jar` file.
+ - Help with configuring correct Java version: [Windows](https://www.happycoders.eu/java/how-to-switch-multiple-java-versions-windows/) | [MacOS](https://stackoverflow.com/questions/21964709/how-to-set-or-change-the-default-java-jdk-version-on-macos) | [Linux](https://www.baeldung.com/linux/java-choose-default-version)
+
+
-## Quick start
+2. Click to download the latest `tether.jar` from [here](https://github.com/AY2324S2-CS2103T-F11-3/tp/releases/tag/v1.4).
-1. Ensure you have Java `11` or above installed in your Computer.
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+3. Open your _file manager_ (search for File Explorer in your computer for Windows, or Finder for Mac), and navigate to your _downloads_ section
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
-1. Open a command terminal, `cd` into the folder you put the jar file in, and use the `java -jar addressbook.jar` command to run the application.
- A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- ![Ui](images/Ui.png)
+4. Copy the downloaded `tether.jar` to a new folder that you will use as the _home folder_ for Tether (from here on, let _tether_folder_ refer to this home folder). You may choose to create this new folder in the downloads section as well, for later convenience.
-1. Type the command in the command box and press Enter to execute it. e.g. typing **`help`** and pressing Enter will open the help window.
- Some example commands you can try:
- * `list` : Lists all contacts.
+5. Open a _command line interface_ (Command Prompt in Windows or Terminal in Mac) and follow these steps:
+ * Once you open the interface, you will notice an almost blank screen with a single line and a cursor waiting for input. This means you are now in your _home directory_ of your computer.
+ * Type in the command `cd` ("change directory") to navigate to the folder you put the jar file in.
+ * Example: if your home directory starts at "~" and you wish to navigate to _downloads_, first type `cd downloads` and press enter to navigate into _downloads_ and then type `cd tether_folder` to navigate into your _tether_folder_
+ * **Tip** for Mac Users: when you first open the CLI and start at your home directory, type in `cd` and then simply drag and drop the _tether_folder_ into the terminal. Press enter and you will directly find yourself in the _tether_folder_.
+ * Now that you are in the _tether_folder_, enter `java -jar tether.jar` command and press enter to run the application. A GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
- * `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.
+![img_4.png](img_4.png)
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+--------------------------------------------------------------------------------------------------------------------
+
+# Gathering your bearings ##
+Before delving into the features, we want you to give you a brief tour on how to use our application.
+
+## The structure of Tether
- * `clear` : Deletes all contacts.
+![img_3.png](images/tetherOrientation.png)
- * `exit` : Exits the app.
+1. Taskbar: Where you may click on either the _File_ or _Help_ buttons.
+2. Command Box: Where you enter commands (which are later forwarded to the CLI for execution).
+3. Result Box: Where success or error messages, if any, for executed commands are displayed
+ * Note that the Result Box may not necessarily refresh everytime a new command is executed. This may happen if, as an example, a command incurs a fatal error before it can generate a result.
+4. Person/Interview Card: Where the details of each person **[4]** and interview you add are displayed
+5. Path to saved data: The location (i.e. the place you created the _tether_folder_ from earlier) of the folder _data_ in which your person/interview data file _addressbook.json_ is saved. If you followed the instructions in the [Quick Start](#quick-start) closely, when you first run and use the application using `java -jar`, the _data_ folder should be automatically created in the same _tether_folder_ that you created to store the `tether.jar` file.
+ * **Note**: If you move the _data_ folder or the enclosed _addressbook.json_ file to some other location, the application will **not** load with your existing data. This is because the application searches for the data folder in the **same** location as your `tether.jar` file. The path to saved data will **not** update itself if you move the _data_ folder.
+ * To summarize, just do not touch the _data_ folder or the _addressbook.json_ file as doing so would cause issues with your existing data.
-1. Refer to the [Features](#features) below for details of each command.
+## Help yourself!
+
+Simply execute `help` or press the _Help_ button in the taskbar to launch a help window that shows the list of commands.
--------------------------------------------------------------------------------------------------------------------
-## Features
+# Features
+
+Now that we've gone over the basics, let's dive into how you may use Tether for your most essential hiring management tasks!
+
+
+## Using Features Error-Free
+
+**Notes about the command format:**
+
+* Most commands are in the format `commandname [parameters]`.
+
+
+* Take note of parameters **[5]** that have special prefixes such as `n/` for name and `e/` for email.
+
+
+* Command names are case-sensitive.
+ e.g if you type in `ADD_APPLICANT` instead of `add_applicant`, it is interpreted as an invalid command.
+
+
+* Tether will give appropriate feedback if any parameter constraints for commands are violated. However, this feedback may not always be very specific as to exactly which parameters are violated and in what way. To help you in this matter, we do list parameter constraints
+ along with the commands that first use them (i.e. the same parameter constraints will not be
+ explained everytime the parameter is used, only the first time).
+
+
+* Tether will ignore leading and trailing whitespaces for all parameters except for the `filter_by_status` command.
+
+
+* Tether will ignore extraneous parameters for commands that do not take in any parameters (`help`, `clear`, `exit`, `view_overall_statistics`). For example, if you attempt to execute `help 123`, it will be interpreted as `help`.
+
+
+* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines
+ as space characters surrounding line-breaks may be omitted when copied over to the application.
+
+
+**Consequences of adding invalid values for fields:**
+
+* The application may not load any data at all upon launch.
+ * If you make invalid edits to the _addressbook.json_ file such as changing a person's name to be an emoji, Tether will launch with no data until invalid fields are rectified.
+ * The definition of an invalid edit is far from exhaustive, but a good indicator is that if the edit violates basic parameter constraints of the fields (which are explained below), then it is likely to be invalid.
+
+
+
+* The application may fill up with incoherent data.
+ * Tether may not always check against invalid edits. If you attempt to add illogical data such as editing all person emails in the _addressbook.json_ to be duplicated or add interviews whose dates precede the current date, the application will allow you to do so at your expense.
+ * To be safe, do check what parameters you use for commands and be careful editing the _addressbook.json_ file.
+
+### Adding an Applicant:
+
+One of the first steps in the hiring pipeline is when an applicant submits their name for consideration.
+
+To record an applicant and their contact details in Tether, simply execute `add_applicant n/NAME p/PHONE e/EMAIL` and the applicant will appear under the _Persons_ column as seen via the example usage below.
+
+**Example Usage**:
+* `add_applicant n/Wesley Yu p/88889999 e/wesleyyu@gmail.com`
+
+![img_3.png](img_3.png)
+
+**Parameter Constraints**:
+* Names can only contain alphanumeric **[6]** characters and spaces, and should not be blank.
+* Phone numbers must be at least 3 digits long and strictly only contain numbers (i.e. no spaces or dashes).
+* Emails should be of the format local-part@domain and adhere to the following constraints:
+ * The local-part should only contain alphanumeric characters and the special characters (+_.-), excluding the parentheses. The local-part may not start or end with any special characters.
+ * This is followed by a '@' and then a domain name. The domain name is made up of domain labels separated by periods.
+ The domain name must:
+ - End with a domain label at least 2 characters long.
+ - Have each domain label start and end with alphanumeric characters.
+ - Have each domain label consist of alphanumeric characters, separated only by hyphens, if any.
+
+**Notes**:
+* No two applicants can have the same email or phone number.
+ * For two applicants to be considered different, they must have different emails **and** different phone numbers.
+ * If you are a discrete mathematics enthusiast, this is equivalent to `~(sameEmail || samePhone) = ~sameEmail && ~ samePhone = differentEmail && differentPhone`.
+* Applicants' remark field will be empty by default and can only be edited later with the `remark` command (described below).
+* An applicant will have an _Applicant_ tag (as pictured above, below the name _Wesley_) by default. Customising this tag or adding additional tags is not currently possible.
-
+### Adding a Status to an Applicant:
-**:information_source: Notes about the command format:**
+Now that you know how to add an applicant, it would be nice to record their position in your hiring pipeline at any given time for later review. This is where tagging applicants by status is handy.
-* 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`.
+Simply execute `applicant_status PHONE s/STATUS` where PHONE is the target applicant's phone number and STATUS may **only** be any one of:
+- "Resume review": for when an applicant has only just entered your hiring pool. Note that this is the default status an applicant receives when first added.
+- "Pending interview": for when you are satisfied with an applicant's potential and have set up or are in the process of scheduling an interview for them.
+- "Completed interview": as the natural successor to the previous status.
+- "Waiting list": if you - or more specifically the interviewer - are not quite sure about an applicant's future.
+- "Accepted": in the case that an applicant has impressed their interviewer enough for you to send a happy email as soon as possible.
+- "Rejected": for the unfortunate case....
-* 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`.
+**Example Usage**
-* 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.
+* `applicant_status 98362254 s/accepted`.
-* 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.
+![img_3.png](images/addApplicantStatusExample.png)
-* 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`.
+**Parameter Constraints**
+* A STATUS may strictly only be any of the statuses enumerated above. However, STATUS is case-insensitive i.e. `s/accepted` is as valid as `s/AcCepTed`.
+* If multiple valid status parameters are used, such as `add_applicant PHONE s/accepted s/rejected`, then only the last status will be considered i.e, the applicant's status will become _rejected_.
+* If multiple valid statuses are given through one status parameter, such as `add_applicant PHONE s/accepted rejected`, then an error will be displayed since "accepted rejected" is not any one of the valid statuses. Simply put, the clause immediately following an `s/` prefix is considered as one status.
-* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
-
+**Notes**:
-### Viewing help : `help`
+* The `applicant_status` command **overwrites** the applicant's current status. Ultimately, we want you to be able to pivot your applicants to any stage of the hiring pipeline.
+* If you schedule an interview with a particular applicant, the applicant's status will change automatically from _resume review_ to _pending interview_. Conversely, if you delete an interview involving an applicant, their status will revert to _resume review_ regardless of what their previous status was.
-Shows a message explaning how to access the help page.
+### Adding an Interviewer:
+Recording all potential applicants is one thing, but to meaningfully schedule interviews for them, you also need a host of interviewers and their details.
-![help message](images/helpMessage.png)
+To record an interviewer and their contact details in Tether, simply execute `add_interviewer n/NAME p/PHONE e/EMAIL` and the interviewer will appear under the _Persons_ column as seen via the example usage below.
-Format: `help`
+**Example Usage**
+* `add_interviewer n/Yash p/99998888 e/yash@gmail.com`
+![img_2.png](img_2.png)
-### Adding a person: `add`
+**Notes**:
+* Similar to applicants, no two interviewers can have the same email or phone number.
+* Interviewers' remark field will be empty by default and can only be edited later with the `remark` command (described below).
+* An interviewer will have an _Interviewer_ tag (as pictured above, below the name _Yash_) by default. Customising this tag or adding additional tags is not currently possible.
-Adds a person to the address book.
-Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…`
+### Viewing the Status of an Interviewer:
-
:bulb: **Tip:**
-A person can have any number of tags (including 0)
-
+Now that you know how to add an interviewer, it would be nice to record their availabilities at any given time for subsequent interview scheduling. Happily however, there are no commands here to manage interviewer statuses manually!
-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`
+Tether is capable of tagging an interviewer with a status _automatically_ when an interview concerning the respective interviewer is added. As a specific example, if an interview is scheduled between applicant Wesley and interviewer Yash, then Yash is given a status of _interview with wesley_. If an interviewer has no scheduled interviews, then their status will be _free_.
-### Listing all persons : `list`
+At any time, an interviewer's status can only either be _free_ or a list of _interview with..._ for all the applicants they are interviewing.
-Shows a list of all persons in the address book.
+As more interviews are added, the interviewer's statuses stack on top of each other like so:
-Format: `list`
+![img_3.png](images/addInterviewerStatusExample.png)
-### Editing a person : `edit`
+Conversely, if an interview is deleted, the respective status is automatically removed from the interviewer's status stack.
-Edits an existing person in the address book.
+### Adding a Remark to a Person:
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+Once you have Persons in Tether, wouldn't it be nice to annotate them with helpful remarks?
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+Simply execute `remark INDEX r/REMARK` where INDEX is the serial number of the person in the list and the REMARK will appear under the respective person.
-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 Usage**
-### Locating persons by name: `find`
+* `remark 1 r/Confident` would add the
+_Confident_ remark to the person at number 1.
-Finds persons whose names contain any of the given keywords.
+**Parameter Constraints**
+* The INDEX of the person to be removed has to be valid, meaning it should correspond to an existing person in the current list. For example, executing `remark -2 r/Confident` when there can't be a negative amount of people, or `remark 100 r/Confident` when there's only 20 people, will both lead to errors.
-Format: `find KEYWORD [MORE_KEYWORDS]`
+**Notes**
+* If you only execute `remark INDEX` without any parameters, at all the remark of the person at that index will be removed.
-* 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`
-Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png)
+### Adding an interview:
-### Deleting a person : `delete`
+Now comes a very meaty part of hiring management - scheduling interviews. And to do so, simply execute `add_interview desc/DESCRIPTION date/DATE st/START_TIME et/END_TIME a/APPLICANT_PHONE_NUMBER i/INTERVIEWER_PHONE_NUMBER`
-Deletes the specified person from the address book.
+The following example usage demonstrates the effect of adding an interview in this manner:
-Format: `delete INDEX`
+**Example Usage**
+* `add_interview desc/Technical Round date/2024-11-11 st/12:00 et/15:00 a/12345678 i/87654321`.
-* 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, …
+![img.png](images/addInterviewExample.png)
-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.
+**Parameter Constraints**
+* If the interview does not require a description, simply use an empty parameter`desc/`.
+* Dates must be in `YYYY-MM-DD` format.
+* Start and end times must be in either `HH:MM` or `HH:MM:SS` format, and the start-time cannot precede the end-time. Timings follow the 24-hour format.
-### Clearing all entries : `clear`
+**Notes**:
+* You can still schedule new interviews for applicants who have already been rejected or accepted. This is simply to accommodate, amongst other possibilities, the possibility of follow-up interviews.
+* Scheduling interviews on dates that have already lapsed are allowed, to support you retroactively recording interviews.
+* If you attempt to use an interviewer or applicant phone number that does not exist to schedule an interview, tether will tell you that the _Person you are looking for does not exist_.
+* If you use a valid interviewer and applicant phone number but use them in the opposite order in which they are required (i.e. using an interviewer phone number after `a/` or an applicant phone number after `i/`), Tether will indicate to you that the phone number you keyed in is not a valid applicant or interviewer phone number.
-Clears all entries from the address book.
+### Listing all Persons:
-Format: `clear`
+When you've added several applicants or interviewers to Tether, it's helpful to see them all together with their details.
-### Exiting the program : `exit`
+The list of persons and their details is typically shown on the left side of the screen in the GUI, and it updates automatically whenever new persons are added. However, there are also commands like `find` and `filter` (explained below) that can change the list shown. If you've used any of these commands and want to go back to seeing the original, unfiltered list of persons, just use the command `list_persons`.
-Exits the program.
+### Listing all Interviews:
-Format: `exit`
+Similar to persons, after adding several interviews to Tether, it's useful to see them all together with their details.
-### Saving the data
+The list of interviews and their details is usually displayed on the right side of the GUI, and it updates automatically when new interviews are added. However, there is a `filter` command (explained below) that can change the list displayed. If you've used this command and want to return to seeing the original, unfiltered list of interviews, simply use the command `list_interviews`.
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+### Finding Persons:
-### Editing the data file
+After adding multiple persons into Tether, you may find yourself having to manually scroll to locate a specific person.
+The ```find``` command is useful here to save you time in locating such persons provided you already know at least one of
+the following 3 details of the person: their email, name or phone number.
-AddressBook data are saved automatically as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file.
+```find``` can also be used to find multiple persons at once. You can provide multiple parameters after the initial
+`find_email`, `find_phone` or `find_name` and all persons that match any of the parameters for these commands will be displayed.
-
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-Furthermore, certain edits can cause the AddressBook to behave in unexpected ways (e.g., if a value entered is outside of the acceptable range). Therefore, edit the data file only if you are confident that you can update it correctly.
-
+To find a person or persons, execute `find_email/name/phone PARAMETER1 PARAMETER2...` such as in the example usages illustrated below:
-### Archiving data files `[coming in v2.0]`
+**Example Usage**:
-_Details coming soon ..._
+**Single Parameter:**
+Executing ```find_name Alice``` will list all persons containing the name ```Alice```:
+
+![img.png](images/find-command/base_to_success.png)
+
+**Multiple Parameters:**
+Executing ```find_phone 123 456``` will list all persons with the phone number `123` or `456` as seen below:
+
+![img.png](images/find-command/base_to_multiplesuccess.png)
+
+**Notes**:
+* The ```find``` command queries the original unfiltered list of persons each time, meaning that sequentially executed ```find``` commands are not applied on top of the previous ones.
+* If you use email or phone number, note that they have to match exactly to locate the person entry if it exists.
+* If you use name, a full name is not required but the name provided should be complete. Otherwise, there will also be no matching persons. For example, `find_name Alice` will bring up _Alice_ and _Alice Yeoh_ but `find_name Ali` will not return any matches.
+* The command accepts any parameters, including invalid ones. However, no matching persons will be displayed in such cases. An example of an invalid parameter is `find_email Alice`.
+ * However, for multiple parameters, all the parameters should at least be of the same type. For example, ```find_phone``` should only be followed by valid phone number(s), not a mix of phones, emails and names.
+
+### Filtering Persons by Status:
+
+What if you have no information about an applicant or interviewer's name, phone or email though? Fret not, for the `filter_by_status` command enables you to narrow down the current list of persons on the basis of their current status.
+
+Simply execute `filter_by_status STATUS`, and the displayed list will update to show only persons with the required STATUS.
+
+**Example Usage**
+* `filter_by_status free` will display all interviewers with status _free_.
+* `filter_by_status resume review` will display all applicants with status _resume review_.
+
+**Parameter Constraints**
+* STATUS may only be any one of the valid statuses enumerated in [adding a status to an applicant](#adding-a-status-to-an-applicant) and [viewing the status of an interviewer](#viewing-the-status-of-an-interviewer) above.
+
+**Notes**
+* As of now, there is only support for a single status parameter.
+* If you want to apply filters one-after-the-other seamlessly, you will have to do this manually. For example, after applying `filter_by_status free`, you will need to `list_persons` again before applying `filter_by_status interview with wesley`.
+ * If you don't execute `list_persons`, then the filters will stack on top of the other and you will be searching for interviewers with status _interview with wesley_ among interviewers with status _free_, which will logically result in finding no interviewers at all.
+* If there are no persons with the given status, Tether will simply inform you that there are no persons found and the displayed list will remain unchanged.
+* Statuses must be matched exactly i.e. `filter_by_status interview with wes` will not match an interviewer with status _interview with wesley_.
+
+### Filtering Interviews by Date:
+
+Navigating through a dense schedule to find specific interviews can be daunting.
+Tether simplifies this with the `filter_interviews_by_date DATE` command, allowing you to instantly locate all interviews scheduled for the given DATE.
+
+To view your full interview schedule, simply use the `list_interviews` command.
+
+**Example Usage**
+* For instance, to see all interviews on May 5th, 2024, you would use `filter_interviews_by_date 2024-05-05`.
+
+**Parameter Constraints**
+* DATE must be in YYYY-MM-DD format and must be a valid date.
+
+**Notes**
+* If there are no interviews with the given date, Tether will simply inform you that there are no interviews found and the displayed list will remain unchanged.
+* If you want to apply filters one-after-the-other seamlessly, you will have to do this manually. For example, after applying `filter_interviews_by_date 2024-05-05`, and if there exists interviews on that date, you will need to `list_interviews` again before applying `filter_interviews_by_date 2024-06-06`.
+
+### Deleting a Person:
+
+If you're confident in removing a person from Tether if - for example - an interviewer leaves your company or an applicant is out of the hiring process, then execute ```delete_person PHONE``` and the person with the given PHONE will be removed **permanently**.
+
+**Example Usage**:
+
+* ```delete_person 81239123``` would delete that
+ person.
+
+### Deleting an Interview:
+
+If you're confident in unscheduling an interview, perhaps due to changed availability or an unexpected clash, then execute ```delete_interview INDEX``` and the interview numbered at INDEX will be removed **permanently**.
+
+**Example Usage**:
+
+* ```delete_interview 1``` would delete the first interview in the list.
+
+### View Overall Statistics:
+
+Core features such as adding, finding, status management and deleting are likely going to be your bread and butter as a hiring manager. However, often you may be tasked to analyse and report certain statistics that may be tedious to compute manually.
+
+Tether has some basic support for viewing statistics, particularly:
+- Total number of applicants as well as applicant numbers by status
+- Total number of interviewers as well as interviewer numbers by status
+- Total number of interviews
+
+A simple example usage would be `view_overall_statistics` to get a result as such:
+
+![img.png](images/viewOverallStatisticsExample.png)
+
+### Clear all existing data:
+
+If you made multiple mistakes and wish to rebuild your hiring data from scratch, the ```clear``` command deletes all existing data in Tether, giving you the fresh start you require.
+
+**Notes**
+1. This action is irreversible. The moment you enter the command and see the success message _Addressbook has been cleared!_, **all** your data will be deleted permanently. Do exercise caution with this particular command by making an independent copy of your data before clearing it.
+
+### Exiting the program:
+
+If you're clocking out for the day, either execute ```exit``` directly or press the _File_ button in the taskbar and then press _Exit_ to quit Tether.
--------------------------------------------------------------------------------------------------------------------
-## FAQ
+## Saving Your Data
+
+Tether's person and interview data are saved in the hard disk **[7]** automatically after any command that changes the data.
+
+There is **no need** to save manually.
-**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.
+--------------------------------------------------------------------------------------------------------------------
+
+## Planned Enhancements and Known Limitations
+
+Tether is always a Work-In-Progress as we are constantly refining our application for your needs. Listed below are some of our planned enhancements as well as current limitations, to keep you abreast of the current state of our application.
+
+### Planned Enhancements
+1. Managing Persons/Interviews
+ * An `edit` command to amend any of the fields of a person or interview.
+ * A `tag` command and `t/` parameter to add and edit custom tags for persons/interviews.
+ * `undo` and `redo` for commands.
+ * Separate lists in the GUI for Applicants and Interviewers as opposed to displaying them together in one Persons list.
+ * Custom statuses for applicants.
+ * Delete multiple persons at once.
+
+
+3. Interviews
+ * Delete interviews automatically once an applicant or interviewer is deleted.
+ * Schedule interview involving multiple applicants or interviewers.
+
+### Known Limitations
+1. User Display
+ * In general, we focused on a clean interface with minimal moving parts. For this reason, there are many possible areas for improvement in our UI that we are ready and open to work on
+ * Currently, adding multiple interviews may cause the interviewer's card to expand beyond its bounds.
+ * Adding extremely long fields for a person/interview may cause the field in the respective cards to spill out of bounds.
+
+
+2. Error Messaging
+ * Some of our error messages could be more specific in why the command triggered an error and how to rectify this. For now, we wanted to minimise the risk of information overload in lieu of very detailed feedback.
--------------------------------------------------------------------------------------------------------------------
-## Known issues
+## Glossary
+1. Command Line Interface (CLI) : It is a software mechanism you use to interact with your computer using your keyboard.
+2. Graphical User Interface (GUI) : It is a user interface that allows users to interact with graphical icons like buttons.
+3. JSON: A text-based format for representing structured data involving specific fields and their values.
+3. Person : Refers to an applicant or an interviewer.
+4. Parameter : Information to be supplied by the user into commands.
+6. Alphanumeric : Alphabets `a-z` (both lower and uppercase) and numbers `0-9`.
+7. Hard disk : The storage component of your computer.
+--------------------------------------------------------------------------------------------------------------------
+
+## FAQ
-1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
+**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 Tether 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`
-**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`
+| Action | Format, Examples |
+|-------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Add Applicant** | `add_applicant n/NAME p/PHONE e/EMAIL` e.g., `add_applicant n/John Doe p/81239123 e/johndoe123@gmail.com` |
+| **Change Applicant Status** | `applicant_status PHONE s/STATUS` e.g., `applicant_status 81239123 s/accepted` |
+| **Add Interviewer** | `add_interviewer n/NAME p/PHONE e/EMAIL` e.g., `add_interviewer n/Jane Doe p/81239123 e/janed@example.com` |
+| **Change Interviewer Status** | `interviewer_status PHONE s/STATUS` e.g., `interviewer_status 81239123 s/free` |
+| **Add a Remark to Person** | `remark INDEX r/REMARK` e.g, `remark 1 r/Confident` |
+| **Add Interview** | `add_interview desc/DESCRIPTION date/DATE st/START_TIME et/END_TIME a/APPLICANT_PHONE i/INTERVIEWER_PHONE` e.g., `add_interview desc/Interview with John date/2024-11-11 st/10:00 et/11:00 a/81239123 i/91238123` |
+| **Delete Person** | `delete_person PHONE` e.g., `delete_person 81239123` |
+| **Delete Interview** | `delete_interview INDEX` e.g., `delete_interview 1` |
+| **List Interviews** | `list_interviews` |
+| **List Persons** | `list_persons` |
+| **Find Persons** | `find_email/name/phone PARAMETER1 PARAMETER2...` e.g., `find_name Alice` or `find_phone 123 456 789` |
+| **Filter Persons by Status** | `filter_by_status STATUS` e.g., `filter_by_status free` |
+| **Filter Interviews by date** | `filter_interviews_by_date DATE` e.g., `filter_interviews_by_date 2024-05-05` |
+| **View Overall Statistics** | `view_overall_statistics` |
+| **Clear All Existing Data** | `clear` |
+| **Exit** | `exit` |
+| **Help** | `help` |
diff --git a/docs/_config.yml b/docs/_config.yml
deleted file mode 100644
index 6bd245d8f4e..00000000000
--- a/docs/_config.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-title: "AB-3"
-theme: minima
-
-header_pages:
- - UserGuide.md
- - DeveloperGuide.md
- - AboutUs.md
-
-markdown: kramdown
-
-repository: "se-edu/addressbook-level3"
-github_icon: "images/github-icon.png"
-
-plugins:
- - jemoji
diff --git a/docs/_data/projects.yml b/docs/_data/projects.yml
deleted file mode 100644
index 8f3e50cb601..00000000000
--- a/docs/_data/projects.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-- name: "AB-1"
- url: https://se-edu.github.io/addressbook-level1
-
-- name: "AB-2"
- url: https://se-edu.github.io/addressbook-level2
-
-- name: "AB-3"
- url: https://se-edu.github.io/addressbook-level3
-
-- name: "AB-4"
- url: https://se-edu.github.io/addressbook-level4
-
-- name: "Duke"
- url: https://se-edu.github.io/duke
-
-- name: "Collate"
- url: https://se-edu.github.io/collate
-
-- name: "Book"
- url: https://se-edu.github.io/se-book
-
-- name: "Resources"
- url: https://se-edu.github.io/resources
diff --git a/docs/_includes/custom-head.html b/docs/_includes/custom-head.html
deleted file mode 100644
index 8559a67ffad..00000000000
--- a/docs/_includes/custom-head.html
+++ /dev/null
@@ -1,6 +0,0 @@
-{% comment %}
- Placeholder to allow defining custom head, in principle, you can add anything here, e.g. favicons:
-
- 1. Head over to https://realfavicongenerator.net/ to add your own favicons.
- 2. Customize default _includes/custom-head.html in your source directory and insert the given code snippet.
-{% endcomment %}
diff --git a/docs/_includes/head.html b/docs/_includes/head.html
deleted file mode 100644
index 83ac5326933..00000000000
--- a/docs/_includes/head.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
- {%- include custom-head.html -%}
-
- {{page.title}}
-
-
diff --git a/docs/_includes/header.html b/docs/_includes/header.html
deleted file mode 100644
index 33badcd4f99..00000000000
--- a/docs/_includes/header.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
+
-:information_source: Don’t forget to update `AddressBookParser` to use our new `RemarkCommandParser`!
+Don’t forget to update `AddressBookParser` to use our new `RemarkCommandParser`!
-
+
If you are stuck, check out the sample
[here](https://github.com/se-edu/addressbook-level3/commit/dc6d5139d08f6403da0ec624ea32bd79a2ae0cbf#diff-8bf239e8e9529369b577701303ddd96af93178b4ed6735f91c2d8488b20c6b4a).
@@ -244,7 +247,7 @@ Simply add the following to [`seedu.address.ui.PersonCard`](https://github.com/s
**`PersonCard.java`:**
-``` java
+```java
@FXML
private Label remark;
```
@@ -276,11 +279,11 @@ We change the constructor of `Person` to take a `Remark`. We will also need to d
Unfortunately, a change to `Person` will cause other commands to break, you will have to modify these commands to use the updated `Person`!
-
+
-:bulb: Use the `Find Usages` feature in IntelliJ IDEA on the `Person` class to find these commands.
+Use the `Find Usages` feature in IntelliJ IDEA on the `Person` class to find these commands.
-
+
Refer to [this commit](https://github.com/se-edu/addressbook-level3/commit/ce998c37e65b92d35c91d28c7822cd139c2c0a5c) and check that you have got everything in order!
@@ -291,11 +294,11 @@ AddressBook stores data by serializing `JsonAdaptedPerson` into `json` with the
While the changes to code may be minimal, the test data will have to be updated as well.
-
+
-:exclamation: You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
+You must delete AddressBook’s storage file located at `/data/addressbook.json` before running it! Not doing so will cause AddressBook to default to an empty address book!
-
+
Check out [this commit](https://github.com/se-edu/addressbook-level3/commit/556cbd0e03ff224d7a68afba171ad2eb0ce56bbf)
to see what the changes entail.
@@ -308,7 +311,7 @@ Just add [this one line of code!](https://github.com/se-edu/addressbook-level3/c
**`PersonCard.java`:**
-``` java
+```java
public PersonCard(Person person, int displayedIndex) {
//...
remark.setText(person.getRemark().value);
@@ -328,7 +331,7 @@ save it with `Model#setPerson()`.
**`RemarkCommand.java`:**
-``` java
+```java
//...
public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s";
public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s";
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..c73bd379e5e 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -1,8 +1,11 @@
---
-layout: page
-title: "Tutorial: Removing Fields"
+ layout: default.md
+ title: "Tutorial: Removing Fields"
+ pageNav: 3
---
+# Tutorial: Removing Fields
+
> Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.
>
> — Antoine de Saint-Exupery
@@ -10,17 +13,17 @@ title: "Tutorial: Removing Fields"
When working on an existing code base, you will most likely find that some features that are no longer necessary.
This tutorial aims to give you some practice on such a code 'removal' activity by removing the `address` field from `Person` class.
-
+
**If you have done the [Add `remark` command tutorial](AddRemark.html) already**, you should know where the code had to be updated to add the field `remark`. From that experience, you can deduce where the code needs to be changed to _remove_ that field too. The removing of the `address` field can be done similarly.
However, if you have no such prior knowledge, removing a field can take a quite a bit of detective work. This tutorial takes you through that process. **At least have a read even if you don't actually do the steps yourself.**
-
+
-* Table of Contents
-{:toc}
+
+
## Safely deleting `Address`
@@ -50,10 +53,10 @@ Let’s try removing references to `Address` in `EditPersonDescriptor`.
1. Remove the usages of `address` and select `Do refactor` when you are done.
-
+
- :bulb: **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Person` class will require you to modify its constructor.
-
+ **Tip:** Removing usages may result in errors. Exercise discretion and fix them. For example, removing the `address` field from the `Person` class will require you to modify its constructor.
+
1. Repeat the steps for the remaining usages of `Address`
@@ -71,7 +74,7 @@ A quick look at the `PersonCard` class and its `fxml` file quickly reveals why i
**`PersonCard.java`**
-``` java
+```java
...
@FXML
private Label address;
diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..2b1b0f2d6b7 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -1,26 +1,30 @@
---
-layout: page
-title: "Tutorial: Tracing code"
+ layout: default.md
+ title: "Tutorial: Tracing code"
+ pageNav: 3
---
+# Tutorial: Tracing code
+
+
> Indeed, the ratio of time spent reading versus writing is well over 10 to 1. We are constantly reading old code as part of the effort to write new code. …\[Therefore,\] making it easy to read makes it easier to write.
>
> — Robert C. Martin Clean Code: A Handbook of Agile Software Craftsmanship
When trying to understand an unfamiliar code base, one common strategy used is to trace some representative execution path through the code base. One easy way to trace an execution path is to use a debugger to step through the code. In this tutorial, you will be using the IntelliJ IDEA’s debugger to trace the execution path of a specific user command.
-* Table of Contents
-{:toc}
+
+
## Before we start
Before we jump into the code, it is useful to get an idea of the overall structure and the high-level behavior of the application. This is provided in the 'Architecture' section of the developer guide. In particular, the architecture diagram (reproduced below), tells us that the App consists of several components.
-![ArchitectureDiagram](../images/ArchitectureDiagram.png)
+
It also has a sequence diagram (reproduced below) that tells us how a command propagates through the App.
-
+
Note how the diagram shows only the execution flows _between_ the main components. That is, it does not show details of the execution path *inside* each component. By hiding those details, the diagram aims to inform the reader about the overall execution path of a command without overwhelming the reader with too much details. In this tutorial, you aim to find those omitted details so that you get a more in-depth understanding of how the code works.
@@ -37,16 +41,16 @@ As you know, the first step of debugging is to put in a breakpoint where you wan
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 you saw earlier (and repeated above for reference), the `UI` component yields control to the `Logic` component through a method named `execute`. Searching through the code base for an `execute()` method that belongs to the `Logic` component yields a promising candidate in `seedu.address.logic.Logic`.
-
+
-:bulb: **Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
-
+**Intellij Tip:** The ['**Search Everywhere**' feature](https://www.jetbrains.com/help/idea/searching-everywhere.html) can be used here. In particular, the '**Find Symbol**' ('Symbol' here refers to methods, variables, classes etc.) variant of that feature is quite useful here as we are looking for a _method_ named `execute`, not simply the text `execute`.
+
A quick look at the `seedu.address.logic.Logic` (an extract given below) confirms that this indeed might be what we’re looking for.
@@ -67,14 +71,14 @@ public interface Logic {
But apparently, this is an interface, not a concrete implementation.
That should be fine because the [Architecture section of the Developer Guide](../DeveloperGuide.html#architecture) tells us that components interact through interfaces. Here's the relevant diagram:
-
+
Next, let's find out which statement(s) in the `UI` code is calling this method, thus transferring control from the `UI` to the `Logic`.
-
+
-:bulb: **Intellij Tip:** The ['**Find Usages**' feature](https://www.jetbrains.com/help/idea/find-highlight-usages.html#find-usages) can find from which parts of the code a class/method/variable is being used.
-
+**Intellij Tip:** The ['**Find Usages**' feature](https://www.jetbrains.com/help/idea/find-highlight-usages.html#find-usages) can find from which parts of the code a class/method/variable is being used.
+
![`Find Usages` tool window. `Edit` \> `Find` \> `Find Usages`.](../images/tracing/FindUsages.png)
@@ -87,10 +91,10 @@ Now let’s set the breakpoint. First, double-click the item to reach the corres
Recall from the User Guide that the `edit` command has the format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…` For this tutorial we will be issuing the command `edit 1 n/Alice Yeoh`.
-
+
-:bulb: **Tip:** Over the course of the debugging session, you will encounter every major component in the application. Try to keep track of what happens inside the component and where the execution transfers to another component.
-
+**Tip:** Over the course of the debugging session, you will encounter every major component in the application. Try to keep track of what happens inside the component and where the execution transfers to another component.
+
1. To start the debugging session, simply `Run` \> `Debug Main`
@@ -110,7 +114,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
**LogicManager\#execute().**
- ``` java
+ ```java
@Override
public CommandResult execute(String commandText)
throws CommandException, ParseException {
@@ -142,7 +146,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
![StepOver](../images/tracing/StepOver.png)
1. _Step into_ the line where user input in parsed from a String to a Command, which should bring you to the `AddressBookParser#parseCommand()` method (partial code given below):
- ``` java
+ ```java
public Command parseCommand(String userInput) throws ParseException {
...
final String commandWord = matcher.group("commandWord");
@@ -157,7 +161,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Stepping through the `switch` block, we end up at a call to `EditCommandParser().parse()` as expected (because the command we typed is an edit command).
- ``` java
+ ```java
...
case EditCommand.COMMAND_WORD:
return new EditCommandParser().parse(arguments);
@@ -166,8 +170,10 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Let’s see what `EditCommandParser#parse()` does by stepping into it. You might have to click the 'step into' button multiple times here because there are two method calls in that statement: `EditCommandParser()` and `parse()`.
-
:bulb: **Intellij Tip:** Sometimes, you might end up stepping into functions that are not of interest. Simply use the `step out` button to get out of them!
-
+
+
+ **Intellij Tip:** Sometimes, you might end up stepping into functions that are not of interest. Simply use the `step out` button to get out of them!
+
1. Stepping through the method shows that it calls `ArgumentTokenizer#tokenize()` and `ParserUtil#parseIndex()` to obtain the arguments and index required.
@@ -175,17 +181,17 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
![EditCommand](../images/tracing/EditCommand.png)
1. As you just traced through some code involved in parsing a command, you can take a look at this class diagram to see where the various parsing-related classes you encountered fit into the design of the `Logic` component.
-
+
1. Let’s continue stepping through until we return to `LogicManager#execute()`.
The sequence diagram below shows the details of the execution path through the Logic component. Does the execution path you traced in the code so far match the diagram?
- ![Tracing an `edit` command through the Logic component](../images/tracing/LogicSequenceDiagram.png)
+
1. Now, step over until you read the statement that calls the `execute()` method of the `EditCommand` object received, and step into that `execute()` method (partial code given below):
**`EditCommand#execute()`:**
- ``` java
+ ```java
@Override
public CommandResult execute(Model model) throws CommandException {
...
@@ -205,25 +211,28 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
* it uses the `updateFilteredPersonList` method to ask the `Model` to populate the 'filtered list' with _all_ persons.
FYI, The 'filtered list' is the list of persons resulting from the most recent operation that will be shown to the user immediately after. For the `edit` command, we populate it with all the persons so that the user can see the edited person along with all other persons. If this was a `find` command, we would be setting that list to contain the search results instead.
To provide some context, given below is the class diagram of the `Model` component. See if you can figure out where the 'filtered list' of persons is being tracked.
-
+
* :bulb: This may be a good time to read through the [`Model` component section of the DG](../DeveloperGuide.html#model-component)
1. As you step through the rest of the statements in the `EditCommand#execute()` method, you'll see that it creates a `CommandResult` object (containing information about the result of the execution) and returns it.
Advancing the debugger by one more step should take you back to the middle of the `LogicManager#execute()` method.
1. Given that you have already seen quite a few classes in the `Logic` component in action, see if you can identify in this partial class diagram some of the classes you've encountered so far, and see how they fit into the class structure of the `Logic` component:
-
+
+
* :bulb: This may be a good time to read through the [`Logic` component section of the DG](../DeveloperGuide.html#logic-component)
1. Similar to before, you can step over/into statements in the `LogicManager#execute()` method to examine how the control is transferred to the `Storage` component and what happens inside that component.
-
:bulb: **Intellij Tip:** When trying to step into a statement such as `storage.saveAddressBook(model.getAddressBook())` which contains multiple method calls, Intellij will let you choose (by clicking) which one you want to step into.
-
+
+
+ **Intellij Tip:** When trying to step into a statement such as `storage.saveAddressBook(model.getAddressBook())` which contains multiple method calls, Intellij will let you choose (by clicking) which one you want to step into.
+
-1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
+1. As you step through the code inside the `Storage` component, you will eventually arrive at the `JsonAddressBook#saveAddressBook()` method which calls the `JsonSerializableAddressBook` constructor, to create an object that can be _serialized_ (i.e., stored in storage medium) in JSON format. That constructor is given below (with added line breaks for easier readability):
**`JsonSerializableAddressBook` constructor:**
- ``` java
+ ```java
/**
* Converts a given {@code ReadOnlyAddressBook} into this class for Jackson use.
*
@@ -243,7 +252,8 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
This is because regular Java objects need to go through an _adaptation_ for them to be suitable to be saved in JSON format.
1. While you are stepping through the classes in the `Storage` component, here is the component's class diagram to help you understand how those classes fit into the structure of the component.
-
+
+
* :bulb: This may be a good time to read through the [`Storage` component section of the DG](../DeveloperGuide.html#storage-component)
1. We can continue to step through until you reach the end of the `LogicManager#execute()` method and return to the `MainWindow#executeCommand()` method (the place where we put the original breakpoint).
@@ -251,7 +261,7 @@ Recall from the User Guide that the `edit` command has the format: `edit INDEX [
1. Stepping into `resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());`, we end up in:
**`ResultDisplay#setFeedbackToUser()`**
- ``` java
+ ```java
public void setFeedbackToUser(String feedbackToUser) {
requireNonNull(feedbackToUser);
resultDisplay.setText(feedbackToUser);
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 3d6bd06d5af..4ee98a72ccf 100644
--- a/src/main/java/seedu/address/MainApp.java
+++ b/src/main/java/seedu/address/MainApp.java
@@ -36,7 +36,7 @@
*/
public class MainApp extends Application {
- public static final Version VERSION = new Version(0, 2, 2, true);
+ public static final Version VERSION = new Version(1, 3, 0, true);
private static final Logger logger = LogsCenter.getLogger(MainApp.class);
@@ -48,7 +48,7 @@ public class MainApp extends Application {
@Override
public void init() throws Exception {
- logger.info("=============================[ Initializing AddressBook ]===========================");
+ logger.info("=============================[ Initializing Tether ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -170,7 +170,7 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting Tether " + MainApp.VERSION);
ui.start(primaryStage);
}
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..7326988b700 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -8,6 +8,7 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
/**
@@ -32,6 +33,8 @@ public interface Logic {
/** Returns an unmodifiable view of the filtered list of persons */
ObservableList getFilteredPersonList();
+ /** Returns an unmodifiable view of the filtered list of interviews */
+ ObservableList getFilteredInterviewList();
/**
* Returns the user prefs' address book file path.
diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java
index 5aa3b91c7d0..10628c46997 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -15,6 +15,7 @@
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
import seedu.address.storage.Storage;
@@ -71,6 +72,11 @@ public ObservableList getFilteredPersonList() {
return model.getFilteredPersonList();
}
+ @Override
+ public ObservableList getFilteredInterviewList() {
+ return model.getFilteredInterviewList();
+ }
+
@Override
public Path getAddressBookFilePath() {
return model.getAddressBookFilePath();
diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java
index ecd32c31b53..b9a44617912 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -5,6 +5,7 @@
import java.util.stream.Stream;
import seedu.address.logic.parser.Prefix;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
/**
@@ -13,12 +14,33 @@
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_COMMAND = "Invalid command format or invalid parameters! \n%1$s";
public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
+ public static final String MESSAGE_PERSON_NOT_IN_LIST = "The person you are looking for is not on the list";
+ public static final String MESSAGE_PERSON_HAS_INTERVIEW = "The applicant or interviewer that you are "
+ + "trying to delete has existing scheduled interview(s). Please delete the corresponding "
+ + "interviews before deleting this person.";
+ public static final String MESSAGE_INCORRECT_APPLICANT_PHONE_NUMBER = "The phone number you have keyed is is "
+ + "not an applicant phone number, please check the input phone number";
+ public static final String MESSAGE_INCORRECT_INTERVIEWER_PHONE_NUMBER = "The phone number you have keyed is is "
+ + "not an interviewer phone number, please check the input phone number";
+ public static final String MESSAGE_INCORRECT_INTERVIEWER_AND_APPLICANT_PHONE_NUMBER =
+ "The phone number you have keyed is is not an applicant nor an interviewer phone number. "
+ + "Please check the input phone numbers";
+ public static final String MESSAGE_INCORRECT_STATUS_APPLICANT = "Only applicants can be given this status";
+ public static final String MESSAGE_INCORRECT_STATUS_INTERVIEWER = "Only interviewers can be given this status";
public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
+ public static final String MESSAGE_INVALID_END_TIME = "The end time is before the start time!";
+ public static final String MESSAGE_INTERVIEW_NOT_IN_LIST = "The interview you are looking for is not on the list";
public static final String MESSAGE_DUPLICATE_FIELDS =
"Multiple values specified for the following single-valued field(s): ";
+ public static final String MESSAGE_NOT_INTEGER = "The provided argument is not an integer";
+ public static final String MESSAGE_NOT_DATE = "The provided argument is not a valid date";
+ public static final String INVALID_DATE = "Invalid date format!";
+ public static final String INVALID_TIME = "Invalid time format!";
+ public static final String MESSAGE_NOT_TIME = "The provided argument is not a valid time";
+
/**
* Returns an error message indicating the duplicate prefixes.
*/
@@ -41,11 +63,27 @@ public static String format(Person person) {
.append(person.getPhone())
.append("; Email: ")
.append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
.append("; Tags: ");
person.getTags().forEach(builder::append);
return builder.toString();
}
+ /**
+ * Formats the {@code interview} for display to the user.
+ */
+ public static String formatInterview(Interview interview) {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("Applicant: ")
+ .append(interview.getApplicant().getName())
+ .append(" Interviewer: ")
+ .append(interview.getInterviewer().getName())
+ .append(" Date: ")
+ .append(interview.getDate())
+ .append(" From: ")
+ .append(interview.getStartTime())
+ .append(" to: ")
+ .append(interview.getEndTime());;
+ return builder.toString();
+ }
+
}
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddApplicantPersonCommand.java
similarity index 60%
rename from src/main/java/seedu/address/logic/commands/AddCommand.java
rename to src/main/java/seedu/address/logic/commands/AddApplicantPersonCommand.java
index 5d7185a9680..be10ea3cb6d 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddApplicantPersonCommand.java
@@ -1,7 +1,6 @@
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;
@@ -14,38 +13,30 @@
import seedu.address.model.person.Person;
/**
- * Adds a person to the address book.
+ * Adds an applicant to Tether.
*/
-public class AddCommand extends Command {
+public class AddApplicantPersonCommand extends AddPersonCommand {
- public static final String COMMAND_WORD = "add";
+ public static final String COMMAND_WORD = AddPersonCommand.COMMAND_WORD + "_applicant";
- 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"
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an applicant to Tether. "
+ + AddPersonCommand.MESSAGE_USAGE
+ "Example: " + COMMAND_WORD + " "
+ PREFIX_NAME + "John Doe "
+ PREFIX_PHONE + "98765432 "
+ PREFIX_EMAIL + "johnd@example.com "
- + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 "
- + PREFIX_TAG + "friends "
- + PREFIX_TAG + "owesMoney";
+ + PREFIX_TAG + "friends ";
- 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;
+ public static final String MESSAGE_SUCCESS = "New applicant added: %1$s";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in Tether.";
+
/**
- * Creates an AddCommand to add the specified {@code Person}
+ * Creates an AddApplicantCommand to add the specified {@code Person}
*/
- public AddCommand(Person person) {
- requireNonNull(person);
- toAdd = person;
+ public AddApplicantPersonCommand(Person person) {
+ super(person);
}
@Override
@@ -67,12 +58,12 @@ public boolean equals(Object other) {
}
// instanceof handles nulls
- if (!(other instanceof AddCommand)) {
+ if (!(other instanceof AddApplicantPersonCommand)) {
return false;
}
- AddCommand otherAddCommand = (AddCommand) other;
- return toAdd.equals(otherAddCommand.toAdd);
+ AddPersonCommand otherAddPersonCommand = (AddApplicantPersonCommand) other;
+ return toAdd.equals(otherAddPersonCommand.toAdd);
}
@Override
diff --git a/src/main/java/seedu/address/logic/commands/AddApplicantStatusCommand.java b/src/main/java/seedu/address/logic/commands/AddApplicantStatusCommand.java
new file mode 100644
index 00000000000..6cdecbc47b8
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddApplicantStatusCommand.java
@@ -0,0 +1,105 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.ApplicantStatus;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.enums.Type;
+
+/**
+ * Parses input arguments and creates a new AddApplicantCommandStatus object
+ */
+public class AddApplicantStatusCommand extends Command {
+
+ public static final String COMMAND_WORD = "applicant_status";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the status of the applicant identified "
+ + "by the phone number used in the command. "
+ + "Existing status will be overwritten by the input.\n"
+ + "Parameters: [APPLICANT PHONE] (must be an existing valid phone) "
+ + PREFIX_STATUS + "[STATUS] (must be one of \"resume review\", \"pending interview\", "
+ + "\"completed interview\", \"waiting list\", \"accepted\", or \"rejected\")\n"
+ + "Example usage: " + COMMAND_WORD + " 98362254 "
+ + PREFIX_STATUS + "accepted";
+
+ public static final String MESSAGE_ADD_STATUS_SUCCESS = "Added status to Applicant: %1$s";
+
+ private final Phone phone;
+
+ private final ApplicantStatus status;
+
+ /**
+ * @param phone of the person in the filtered person list to edit the status
+ * @param status of the person to be updated to
+ */
+ public AddApplicantStatusCommand(Phone phone, ApplicantStatus status) {
+ requireAllNonNull(phone, status);
+
+ this.phone = phone;
+ this.status = status;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ List lastShownList = model.getFilteredPersonList();
+
+ Person personToEdit;
+ try {
+ personToEdit = lastShownList
+ .stream()
+ .parallel()
+ .filter(person -> person.getPhone().equals(phone))
+ .reduce((person, prev) -> person).get();
+ } catch (NoSuchElementException e) {
+ throw new CommandException(Messages.MESSAGE_INCORRECT_APPLICANT_PHONE_NUMBER);
+ }
+
+ if (!personToEdit.getPersonType().equals(Type.APPLICANT.toString())) {
+ throw new CommandException(Messages.MESSAGE_INCORRECT_STATUS_APPLICANT);
+ }
+
+ Applicant editedPerson = new Applicant(personToEdit.getName(), personToEdit.getPhone(), personToEdit.getEmail(),
+ personToEdit.getRemark(), status, personToEdit.getTags());
+
+ model.setPerson(personToEdit, editedPerson);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+
+ return new CommandResult(generateSuccessMessage(editedPerson));
+ }
+
+ /**
+ * Generates a command execution success message for adding the status
+ * {@code personToEdit}.
+ */
+ private String generateSuccessMessage(Person personToEdit) {
+ return String.format(MESSAGE_ADD_STATUS_SUCCESS, personToEdit);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddApplicantStatusCommand)) {
+ return false;
+ }
+
+ // state check
+ AddApplicantStatusCommand e = (AddApplicantStatusCommand) other;
+ return phone.equals(e.phone)
+ && status.equals(e.status);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddInterviewCommand.java b/src/main/java/seedu/address/logic/commands/AddInterviewCommand.java
new file mode 100644
index 00000000000..b70010dfeb3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddInterviewCommand.java
@@ -0,0 +1,200 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_END_TIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_APPLICANT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEWER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_START_TIME;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.List;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.interview.Interview;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.enums.Type;
+
+/**
+ * Adds an Interview to Tether.
+ */
+public class AddInterviewCommand extends Command {
+
+ public static final String COMMAND_WORD = AddPersonCommand.COMMAND_WORD + "_interview";
+
+ public static final String MESSAGE_INFORMATION = "Parameters: "
+ + PREFIX_DESCRIPTION + "DESCRIPTION "
+ + PREFIX_DATE + "DATE "
+ + PREFIX_START_TIME + "START_TIME "
+ + PREFIX_END_TIME + "END_TIME "
+ + PREFIX_APPLICANT + "APPLICANT_PHONE_NUMBER "
+ + PREFIX_INTERVIEWER + "INTERVIEWER_PHONE_NUMBER " + "\n"
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_DESCRIPTION + "technical interview "
+ + PREFIX_DATE + "2022-11-11 "
+ + PREFIX_START_TIME + "10:00 "
+ + PREFIX_END_TIME + "11:00 "
+ + PREFIX_APPLICANT + "88888888 "
+ + PREFIX_INTERVIEWER + "88889999";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an interview to Tether. "
+ + MESSAGE_INFORMATION;
+
+
+ public static final String MESSAGE_SUCCESS = "New Interview added: %1$s";
+ public static final String MESSAGE_DUPLICATE_INTERVIEW = "This Interview already exists in Tether.";
+
+ private String description;
+ private Phone applicant;
+ private Phone interviewer;
+
+ // Format YYYY-MM-DD
+ private LocalDate date;
+ // Format HH:mm
+ private LocalTime startTime;
+ private LocalTime endTime;
+ private Interview interview;
+ private Person applicantSearch;
+ private Person interviewerSearch;
+
+ /**
+ * Creates an AddInterviewCommand to add the specified {@code Person}
+ */
+ public AddInterviewCommand(String description, Phone applicant, Phone interviewer, LocalDate date,
+ LocalTime startTime, LocalTime endTime) {
+ this.description = description;
+ this.applicant = applicant;
+ this.interviewer = interviewer;
+ this.date = date;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ Result result = getResult(model);
+ if (startTime.isAfter(endTime)) {
+ throw new CommandException(MESSAGE_INVALID_END_TIME);
+ }
+ phoneNumberCheck(result.isFoundApplicant, result.isFoundInterviewer, result.isIncorrectApplicantPhone,
+ result.isIncorrectInterviewerPhone);
+ this.interview = new Interview(applicantSearch, interviewerSearch, date, startTime, endTime, description);
+ if (model.hasInterview(interview)) {
+ throw new CommandException(MESSAGE_DUPLICATE_INTERVIEW);
+ }
+ model.addInterview(interview);
+ model.sortInterview();
+ applicantSearch.updateCurrentStatusToReflectScheduledInterview(model);
+ interviewerSearch.updateCurrentStatusToReflectScheduledInterview(model, applicantSearch);
+ return new CommandResult(String.format(MESSAGE_SUCCESS, "\n" + Messages.formatInterview(interview)));
+ }
+
+ private Result getResult(Model model) {
+ List lastShownList = model.getFilteredPersonList();
+ boolean isFoundApplicant = false;
+ boolean isFoundInterviewer = false;
+ boolean isIncorrectApplicantPhone = true;
+ boolean isIncorrectInterviewerPhone = true;
+ for (Person p : lastShownList) {
+ if (p.getPhone().equals(applicant)) {
+ isFoundApplicant = true;
+ }
+ if (isFoundApplicant) {
+ if (p.getPersonType().equals(Type.APPLICANT.toString())) {
+ isIncorrectApplicantPhone = false;
+ }
+ applicantSearch = p;
+ break;
+ }
+ }
+ for (Person p : lastShownList) {
+ if (p.getPhone().equals(interviewer)) {
+ isFoundInterviewer = true;
+ }
+ if (isFoundInterviewer) {
+ if (p.getPersonType().equals(Type.INTERVIEWER.toString())) {
+ isIncorrectInterviewerPhone = false;
+ }
+ interviewerSearch = p;
+ break;
+ }
+ }
+ Result result = new Result(isFoundApplicant, isFoundInterviewer,
+ isIncorrectApplicantPhone, isIncorrectInterviewerPhone);
+ return result;
+ }
+
+ private static class Result {
+ public final boolean isFoundApplicant;
+ public final boolean isFoundInterviewer;
+ public final boolean isIncorrectApplicantPhone;
+ public final boolean isIncorrectInterviewerPhone;
+
+ public Result(boolean isFoundApplicant, boolean isFoundInterviewer, boolean isIncorrectApplicantPhone,
+ boolean isIncorrectInterviewerPhone) {
+ this.isFoundApplicant = isFoundApplicant;
+ this.isFoundInterviewer = isFoundInterviewer;
+ this.isIncorrectApplicantPhone = isIncorrectApplicantPhone;
+ this.isIncorrectInterviewerPhone = isIncorrectInterviewerPhone;
+ }
+ }
+
+ private static void phoneNumberCheck(boolean isFoundApplicant,
+ boolean isFoundInterviewer,
+ boolean isIncorrectApplicantPhone,
+ boolean isIncorrectInterviewerPhone) throws CommandException {
+ if (!isFoundApplicant || !isFoundInterviewer) {
+ throw new CommandException(Messages.MESSAGE_PERSON_NOT_IN_LIST);
+ }
+ if (isIncorrectApplicantPhone && isIncorrectInterviewerPhone) {
+ throw new CommandException(Messages.MESSAGE_INCORRECT_INTERVIEWER_AND_APPLICANT_PHONE_NUMBER);
+ }
+ if (isIncorrectApplicantPhone) {
+ throw new CommandException(Messages.MESSAGE_INCORRECT_APPLICANT_PHONE_NUMBER);
+ }
+ if (isIncorrectInterviewerPhone) {
+ throw new CommandException(Messages.MESSAGE_INCORRECT_INTERVIEWER_PHONE_NUMBER);
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddInterviewCommand)) {
+ return false;
+ }
+
+ if (sameInputs((AddInterviewCommand) other)) {
+ return true;
+ }
+ AddInterviewCommand otherInterviewCommmand = (AddInterviewCommand) other;
+ return interview.equals(otherInterviewCommmand.interview);
+ }
+
+ private boolean sameInputs(AddInterviewCommand other) {
+ return this.description.equals((other).description)
+ && this.applicant.equals((other).applicant)
+ && this.interviewer.equals((other).interviewer)
+ && this.date.equals((other).date)
+ && this.startTime.equals((other).startTime)
+ && this.endTime.equals((other).endTime);
+ }
+
+ @Override
+ public String toString() {
+ return "Description: " + description + " applicant: " + applicant.toString() + " interviewer: "
+ + interviewer.toString() + " date: " + date.toString() + " start: " + startTime.toString() + " end: "
+ + endTime.toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddInterviewerPersonCommand.java b/src/main/java/seedu/address/logic/commands/AddInterviewerPersonCommand.java
new file mode 100644
index 00000000000..e52d6c57834
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddInterviewerPersonCommand.java
@@ -0,0 +1,73 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+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.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+
+/**
+ * Adds an interviewer to Tether.
+ */
+public class AddInterviewerPersonCommand extends AddPersonCommand {
+
+ public static final String COMMAND_WORD = AddPersonCommand.COMMAND_WORD + "_interviewer";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an interviewer to Tether. "
+ + AddPersonCommand.MESSAGE_USAGE
+ + "Example: " + COMMAND_WORD + " "
+ + PREFIX_NAME + "John Doe "
+ + PREFIX_PHONE + "98765432 "
+ + PREFIX_EMAIL + "johnd@example.com "
+ + PREFIX_TAG + "friends ";
+
+ public static final String MESSAGE_SUCCESS = "New interviewer added: %1$s";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in Tether.";
+
+ /**
+ * Creates an AddInterviewerCommand to add the specified {@code Person}
+ */
+ public AddInterviewerPersonCommand(Person person) {
+ super(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, Messages.format(toAdd)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AddInterviewerPersonCommand)) {
+ return false;
+ }
+
+ AddPersonCommand otherAddPersonCommand = (AddPersonCommand) other;
+ return toAdd.equals(otherAddPersonCommand.toAdd);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("toAdd", toAdd)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/AddPersonCommand.java b/src/main/java/seedu/address/logic/commands/AddPersonCommand.java
new file mode 100644
index 00000000000..8055627cc81
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AddPersonCommand.java
@@ -0,0 +1,34 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+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 seedu.address.model.person.Person;
+
+/**
+ * Adds a person to Tether.
+ */
+public abstract class AddPersonCommand extends Command {
+
+ public static final String COMMAND_WORD = "add";
+
+ public static final String MESSAGE_USAGE = "Parameters: "
+ + PREFIX_NAME + "NAME "
+ + PREFIX_PHONE + "PHONE "
+ + PREFIX_EMAIL + "EMAIL ";
+
+ 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";
+
+ protected final Person toAdd;
+
+ /**
+ * Creates an AddCommand to add the specified {@code Person}
+ */
+ public AddPersonCommand(Person person) {
+ requireNonNull(person);
+ toAdd = person;
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 1135ac19b74..2c5bb5379f5 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -4,43 +4,67 @@
import java.util.List;
-import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.ToStringBuilder;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.Model;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
/**
* 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 COMMAND_WORD = "delete_person";
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";
+ + ": Deletes the person identified by the phone number used in the displayed person list.\n"
+ + "Parameters: Phone\n"
+ + "Example: " + COMMAND_WORD + " 87652533";
public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
- private final Index targetIndex;
+ private final Phone targetPhone;
- public DeleteCommand(Index targetIndex) {
- this.targetIndex = targetIndex;
+ public DeleteCommand(Phone targetPhone) {
+ this.targetPhone = targetPhone;
}
@Override
public CommandResult execute(Model model) throws CommandException {
requireNonNull(model);
List lastShownList = model.getFilteredPersonList();
+ List interviews = model.getFilteredInterviewList();
- if (targetIndex.getZeroBased() >= lastShownList.size()) {
- throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ boolean personFound = false;
+ Person personToDelete = null;
+
+ for (Person p : lastShownList) {
+ if (p.getPhone().equals(targetPhone)) {
+ personToDelete = p;
+ personFound = true;
+ break;
+ }
+ }
+
+ if (!personFound) {
+ throw new CommandException(Messages.MESSAGE_PERSON_NOT_IN_LIST);
+ }
+
+ boolean personFoundInInterviews = false;
+
+ for (Interview i: interviews) {
+ if (i.containsPerson(personToDelete)) {
+ personFoundInInterviews = true;
+ }
+ }
+
+ if (personFoundInInterviews) {
+ throw new CommandException(Messages.MESSAGE_PERSON_HAS_INTERVIEW);
}
- Person personToDelete = lastShownList.get(targetIndex.getZeroBased());
model.deletePerson(personToDelete);
return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, Messages.format(personToDelete)));
}
@@ -57,13 +81,13 @@ public boolean equals(Object other) {
}
DeleteCommand otherDeleteCommand = (DeleteCommand) other;
- return targetIndex.equals(otherDeleteCommand.targetIndex);
+ return targetPhone.equals(otherDeleteCommand.targetPhone);
}
@Override
public String toString() {
return new ToStringBuilder(this)
- .add("targetIndex", targetIndex)
+ .add("targetPhone", targetPhone)
.toString();
}
}
diff --git a/src/main/java/seedu/address/logic/commands/DeleteInterviewCommand.java b/src/main/java/seedu/address/logic/commands/DeleteInterviewCommand.java
new file mode 100644
index 00000000000..cf48b639e07
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/DeleteInterviewCommand.java
@@ -0,0 +1,73 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.interview.Interview;
+
+
+/**
+ * Deletes a person identified using it's displayed index from the address book.
+ */
+public class DeleteInterviewCommand extends Command {
+
+ public static final String COMMAND_WORD = "delete_interview";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Deletes the Interview identified by the index in the displayed interview list.\n"
+ + "Parameters: Index\n"
+ + "Example: " + COMMAND_WORD + " 2";
+
+ public static final String MESSAGE_DELETE_INTERVIEW_SUCCESS = "Interview Deleted: %1$s";
+
+ private final Integer targetInt;
+
+
+ public DeleteInterviewCommand(int targetInt) {
+ this.targetInt = targetInt;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredInterviewList();
+ Interview interview;
+ try {
+ interview = lastShownList.get(targetInt);
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException(Messages.MESSAGE_INTERVIEW_NOT_IN_LIST);
+ }
+
+ interview.getApplicant().revertCurrentStatus(model);
+ interview.getInterviewer().updateCurrentStatusToReflectDeletedInterview(model, interview.getApplicant());
+ model.deleteInterview(interview);
+ return new CommandResult(String.format(MESSAGE_DELETE_INTERVIEW_SUCCESS, Messages.formatInterview(interview)));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof DeleteInterviewCommand)) {
+ return false;
+ }
+
+ DeleteInterviewCommand otherDeleteInterviewCommand = (DeleteInterviewCommand) other;
+ return targetInt.equals(otherDeleteInterviewCommand.targetInt);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetInt", targetInt)
+ .toString();
+ }
+}
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 4b581c7331e..00000000000
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ /dev/null
@@ -1,242 +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.Objects;
-import java.util.Optional;
-import java.util.Set;
-
-import seedu.address.commons.core.index.Index;
-import seedu.address.commons.util.CollectionUtil;
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.logic.commands.exceptions.CommandException;
-import seedu.address.model.Model;
-import seedu.address.model.person.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, Messages.format(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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditCommand)) {
- return false;
- }
-
- EditCommand otherEditCommand = (EditCommand) other;
- return index.equals(otherEditCommand.index)
- && editPersonDescriptor.equals(otherEditCommand.editPersonDescriptor);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("index", index)
- .add("editPersonDescriptor", editPersonDescriptor)
- .toString();
- }
-
- /**
- * 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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof EditPersonDescriptor)) {
- return false;
- }
-
- EditPersonDescriptor otherEditPersonDescriptor = (EditPersonDescriptor) other;
- return Objects.equals(name, otherEditPersonDescriptor.name)
- && Objects.equals(phone, otherEditPersonDescriptor.phone)
- && Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
- && Objects.equals(tags, otherEditPersonDescriptor.tags);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
- }
- }
-}
diff --git a/src/main/java/seedu/address/logic/commands/FilterCommand.java b/src/main/java/seedu/address/logic/commands/FilterCommand.java
new file mode 100644
index 00000000000..c3b79393cd7
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FilterCommand.java
@@ -0,0 +1,14 @@
+package seedu.address.logic.commands;
+
+/**
+ * Filters the displayed interview or person list based on the date or status provided.
+ */
+public abstract class FilterCommand extends Command {
+
+ public static final String COMMAND_WORD = "filter";
+
+ public static final String MESSAGE_SUCCESS = "Listed all";
+
+ public static final String MESSAGE_USAGE = "use either filter_by_interview date or filter_by_status";
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/FilterInterviewsByDateCommand.java b/src/main/java/seedu/address/logic/commands/FilterInterviewsByDateCommand.java
new file mode 100644
index 00000000000..073833869aa
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FilterInterviewsByDateCommand.java
@@ -0,0 +1,57 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.time.LocalDate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.Model;
+
+/**
+ * Filters the displayed interview list based on the date provided.
+ */
+public class FilterInterviewsByDateCommand extends FilterCommand {
+
+ public static final String COMMAND_WORD = "filter_interviews_by_date";
+
+ public static final String MESSAGE_SUCCESS = "Listed all interviews on";
+
+ private LocalDate targetDate;
+
+ public FilterInterviewsByDateCommand(LocalDate targetDate) {
+ this.targetDate = targetDate;
+ }
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ boolean existsMatchingInterviews = model.getFilteredInterviewList().stream()
+ .anyMatch(interview -> interview.getDate().equals(targetDate));
+ if (!existsMatchingInterviews) {
+ return new CommandResult("No interviews found on " + targetDate);
+ }
+ model.updateFilteredInterviewList(interview -> interview.getDate().equals(targetDate));
+ return new CommandResult(MESSAGE_SUCCESS + " " + targetDate);
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof FilterInterviewsByDateCommand)) {
+ return false;
+ }
+
+ FilterInterviewsByDateCommand other = (FilterInterviewsByDateCommand) object;
+ return this.targetDate.equals(other.targetDate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetDate", targetDate)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/FilterPersonsByStatusCommand.java b/src/main/java/seedu/address/logic/commands/FilterPersonsByStatusCommand.java
new file mode 100644
index 00000000000..95171fb3a8b
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FilterPersonsByStatusCommand.java
@@ -0,0 +1,70 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.Model;
+import seedu.address.model.person.Status;
+
+/**
+ * Filters the displayed person list based on the status provided.
+ */
+public class FilterPersonsByStatusCommand extends FilterCommand {
+
+ public static final String COMMAND_WORD = FilterCommand.COMMAND_WORD + "_by_status";
+
+ public static final String MESSAGE_SUCCESS = FilterCommand.MESSAGE_SUCCESS + " persons with status";
+ private final Status targetStatus;
+
+ /**
+ * Creates a FilterPersonsByStatusCommand to filter persons by status.
+ *
+ * @param targetStatus The {@code Status} to be matched
+ */
+ public FilterPersonsByStatusCommand(Status targetStatus) {
+ this.targetStatus = targetStatus;
+ }
+
+ /**
+ * This method wraps the {@code updateFilteredPersonList} method of the {@code ModelManager} class
+ * with a preliminary check on whether any persons exist with the {@code targetStatus}.
+ *
+ * @param model {@code Model} which the command should operate on.
+ * @return A command result indicating success
+ */
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+
+ boolean existsMatchingPersons = model.getFilteredPersonList().stream()
+ .anyMatch(person -> person.getCurrentStatus().equals(targetStatus.toString()));
+ if (!existsMatchingPersons) {
+ return new CommandResult("No persons found with status: " + targetStatus.toString());
+ }
+
+ model.updateFilteredPersonList(person -> person.getCurrentStatus().equals(targetStatus.toString()));
+ return new CommandResult(MESSAGE_SUCCESS + ": " + targetStatus.toString());
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+
+ if (!(object instanceof FilterPersonsByStatusCommand)) {
+ return false;
+ }
+
+ FilterPersonsByStatusCommand other = (FilterPersonsByStatusCommand) object;
+ return this.targetStatus.equals(other.targetStatus);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetStatus", targetStatus)
+ .toString();
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index 72b9eddd3a7..0a3e1161ad4 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -1,58 +1,17 @@
package seedu.address.logic.commands;
-import static java.util.Objects.requireNonNull;
-
-import seedu.address.commons.util.ToStringBuilder;
-import seedu.address.logic.Messages;
-import seedu.address.model.Model;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
-
/**
- * Finds and lists all persons in address book whose name contains any of the argument keywords.
+ * Finds and lists all persons in Tether 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 abstract class FindCommand extends Command {
- 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"
+ public static final String MESSAGE_USAGE = "find_[field]: Finds all persons whose names/phone/email 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;
- }
+ + "Examples: " + "find_name alice bob charlie \n find_phone 123 456 789 \n find_email alice@gmail.com";
- @Override
- public CommandResult execute(Model model) {
- requireNonNull(model);
- model.updateFilteredPersonList(predicate);
- return new CommandResult(
- String.format(Messages.MESSAGE_PERSONS_LISTED_OVERVIEW, model.getFilteredPersonList().size()));
+ public FindCommand() {
}
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof FindCommand)) {
- return false;
- }
-
- FindCommand otherFindCommand = (FindCommand) other;
- return predicate.equals(otherFindCommand.predicate);
- }
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .add("predicate", predicate)
- .toString();
- }
}
diff --git a/src/main/java/seedu/address/logic/commands/FindEmailCommand.java b/src/main/java/seedu/address/logic/commands/FindEmailCommand.java
new file mode 100644
index 00000000000..75a09486322
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindEmailCommand.java
@@ -0,0 +1,58 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.person.EmailContainsKeywordsPredicate;
+
+/**
+ * Finds and lists all persons in Tether whose email contains any of the argument keywords.
+ * Keyword matching is case insensitive.
+ */
+public class FindEmailCommand extends FindCommand {
+
+ public static final String COMMAND_WORD = "find_email";
+
+ private final EmailContainsKeywordsPredicate predicate;
+
+ /**
+ * Creates a FindEmailCommand with given {@code Predicate} to filter for specific {@code Person}s.
+ */
+ public FindEmailCommand(EmailContainsKeywordsPredicate predicate) {
+ super();
+ 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) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindEmailCommand)) {
+ return false;
+ }
+
+ FindEmailCommand otherFindEmailCommand = (FindEmailCommand) other;
+ return predicate.equals(otherFindEmailCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/FindNameCommand.java b/src/main/java/seedu/address/logic/commands/FindNameCommand.java
new file mode 100644
index 00000000000..9a69434f817
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindNameCommand.java
@@ -0,0 +1,58 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.person.NameContainsKeywordsPredicate;
+
+/**
+ * Finds and lists all persons in Tether whose name contains any of the argument keywords.
+ * Keyword matching is case insensitive.
+ */
+public class FindNameCommand extends FindCommand {
+
+ public static final String COMMAND_WORD = "find_name";
+
+ private final NameContainsKeywordsPredicate predicate;
+
+ /**
+ * Creates a FindNameCommand with given {@code Predicate} to filter for specific {@code Person}s.
+ */
+ public FindNameCommand(NameContainsKeywordsPredicate predicate) {
+ super();
+ 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) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindNameCommand)) {
+ return false;
+ }
+
+ FindNameCommand otherFindNameCommand = (FindNameCommand) other;
+ return predicate.equals(otherFindNameCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/FindPhoneCommand.java b/src/main/java/seedu/address/logic/commands/FindPhoneCommand.java
new file mode 100644
index 00000000000..ce4f0f98f23
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/FindPhoneCommand.java
@@ -0,0 +1,57 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.person.PhoneContainsKeywordsPredicate;
+
+/**
+ * Finds and lists all persons in Tether whose phone number contains any of the argument keywords.
+ */
+public class FindPhoneCommand extends FindCommand {
+
+ public static final String COMMAND_WORD = "find_phone";
+
+ private final PhoneContainsKeywordsPredicate predicate;
+
+ /**
+ * Creates a FindPhoneCommand with given {@code Predicate} to filter for specific {@code Person}s.
+ */
+ public FindPhoneCommand(PhoneContainsKeywordsPredicate predicate) {
+ super();
+ 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) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof FindPhoneCommand)) {
+ return false;
+ }
+
+ FindPhoneCommand otherFindPhoneCommand = (FindPhoneCommand) other;
+ return predicate.equals(otherFindPhoneCommand.predicate);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("predicate", predicate)
+ .toString();
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
index 84be6ad2596..f01860636c7 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ListCommand.java
@@ -10,7 +10,7 @@
*/
public class ListCommand extends Command {
- public static final String COMMAND_WORD = "list";
+ public static final String COMMAND_WORD = "list_persons";
public static final String MESSAGE_SUCCESS = "Listed all persons";
diff --git a/src/main/java/seedu/address/logic/commands/ListInterviewsCommand.java b/src/main/java/seedu/address/logic/commands/ListInterviewsCommand.java
new file mode 100644
index 00000000000..b459978c961
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ListInterviewsCommand.java
@@ -0,0 +1,24 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_INTERVIEWS;
+
+import seedu.address.model.Model;
+
+/**
+ * Lists all persons in the address book to the user.
+ */
+public class ListInterviewsCommand extends Command {
+
+ public static final String COMMAND_WORD = "list_interviews";
+
+ public static final String MESSAGE_SUCCESS = "Listed all interviews";
+
+
+ @Override
+ public CommandResult execute(Model model) {
+ requireNonNull(model);
+ model.updateFilteredInterviewList(PREDICATE_SHOW_ALL_INTERVIEWS);
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/RemarkCommand.java b/src/main/java/seedu/address/logic/commands/RemarkCommand.java
new file mode 100644
index 00000000000..cc563353fe6
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/RemarkCommand.java
@@ -0,0 +1,114 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.ApplicantStatus;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Interviewer;
+import seedu.address.model.person.InterviewerStatus;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.Remark;
+import seedu.address.model.person.enums.Type;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Changes the remark of an existing person in the address book.
+ */
+public class RemarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "remark";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the remark of the person identified "
+ + "by the index number used in the last person listing. "
+ + "Existing remark will be overwritten by the input.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + PREFIX_REMARK + "[REMARK]\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_REMARK + "Likes to swim.";
+
+ public static final String MESSAGE_ADD_REMARK_SUCCESS = "Added remark to Person: %1$s";
+ public static final String MESSAGE_DELETE_REMARK_SUCCESS = "Removed remark from Person: %1$s";
+
+ private final Index index;
+ private final Remark remark;
+
+ /**
+ * @param index of the person in the filtered person list to edit the remark
+ * @param remark of the person to be updated to
+ */
+ public RemarkCommand(Index index, Remark remark) {
+ requireAllNonNull(index, remark);
+
+ this.index = index;
+ this.remark = remark;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToEdit = lastShownList.get(index.getZeroBased());
+ Name name = personToEdit.getName();
+ Phone phone = personToEdit.getPhone();
+ Email email = personToEdit.getEmail();
+ Set tags = personToEdit.getTags();
+ String type = personToEdit.getPersonType();
+ String status = personToEdit.getCurrentStatus();
+
+ Person editedPerson;
+ if (type.equals(Type.APPLICANT.toString())) {
+ editedPerson = new Applicant(name, phone, email, remark, new ApplicantStatus(status), tags);
+ } else if (type.equals(Type.INTERVIEWER.toString())) {
+ editedPerson = new Interviewer(name, phone, email, remark, new InterviewerStatus(status), tags);
+ } else {
+ editedPerson = new Person(name, phone, email, remark, tags);
+ }
+
+ model.setPerson(personToEdit, editedPerson);
+ model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+
+ return new CommandResult(generateSuccessMessage(editedPerson));
+ }
+
+ /**
+ * Generates a command execution success message based on whether the remark is added to or removed from
+ * {@code personToEdit}.
+ */
+ private String generateSuccessMessage(Person personToEdit) {
+ String message = !remark.value.isEmpty() ? MESSAGE_ADD_REMARK_SUCCESS : MESSAGE_DELETE_REMARK_SUCCESS;
+ return String.format(message, personToEdit);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof RemarkCommand)) {
+ return false;
+ }
+
+ // state check
+ RemarkCommand e = (RemarkCommand) other;
+ return index.equals(e.index)
+ && remark.equals(e.remark);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ViewOverallStatisticsCommand.java b/src/main/java/seedu/address/logic/commands/ViewOverallStatisticsCommand.java
new file mode 100644
index 00000000000..273998ce456
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewOverallStatisticsCommand.java
@@ -0,0 +1,103 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.enums.ApplicantState;
+import seedu.address.model.person.enums.InterviewerState;
+import seedu.address.model.person.enums.Type;
+
+/**
+ * Displays preselected person and interview statistics for the currently filtered person and interview lists.
+ */
+public class ViewOverallStatisticsCommand extends Command {
+
+ public static final String COMMAND_WORD = "view_overall_statistics";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Displays overall statistics for the currently "
+ + "displayed list.";
+
+ /**
+ * Creates a ViewOverallStatisticsCommand
+ */
+ public ViewOverallStatisticsCommand() {
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ String statisticsOverview = retrieveStatistics(model);
+ return new CommandResult(statisticsOverview);
+ }
+
+ private String retrieveStatistics(Model model) {
+ // String counts of applicants, interviewers, pending interviews
+ String numberOfApplicants = getFilteredPersonListCount(model, person ->
+ person.getPersonType().equals(Type.APPLICANT.toString()));
+ String numberOfInterviewers = getFilteredPersonListCount(model, person ->
+ person.getPersonType().equals(Type.INTERVIEWER.toString()));
+ String numberOfInterviews = "" + model.getAddressBook().getInterviewList().size();
+
+ // String count of interviewers by status
+ String numberOfFreeInterviewers = getFilteredPersonListCount(model, person ->
+ person.getCurrentStatus().equals(InterviewerState.FREE.toString()));
+ String numberOfOccupiedInterviewers = "" + (Integer.parseInt(numberOfInterviewers)
+ - Integer.parseInt(numberOfFreeInterviewers));
+
+ // String of applicants by status
+ String numberOfApplicantsByStatus = retrieveApplicantStatusStatistics(model);
+
+ return "Number of applicants: "
+ + numberOfApplicants + " (" + numberOfApplicantsByStatus + ")"
+ + "\nNumber of interviewers: "
+ + numberOfInterviewers + " (Free: " + numberOfFreeInterviewers + ", Busy: "
+ + numberOfOccupiedInterviewers + ")\nNumber of interviews: "
+ + numberOfInterviews;
+ }
+
+ private String getFilteredPersonListCount(Model model, Predicate predicate) {
+ return "" + model.getAddressBook().getPersonList().stream().parallel().filter(predicate).count();
+ }
+
+ private String retrieveApplicantStatusStatistics(Model model) {
+ String numberOfReviewableApplicants = getFilteredPersonListCount(model, person ->
+ person.getCurrentStatus().equals(ApplicantState.STAGE_ONE.toString()));
+ String numberOfApplicantsPendingIntv = getFilteredPersonListCount(model, person ->
+ person.getCurrentStatus().equals(ApplicantState.STAGE_TWO.toString()));
+ String numberOfApplicantsCompletedIntv = getFilteredPersonListCount(model, person ->
+ person.getCurrentStatus().equals(ApplicantState.STAGE_THREE.toString()));
+ String numberOfApplicantsWaitlisted = getFilteredPersonListCount(model, person ->
+ person.getCurrentStatus().equals(ApplicantState.OUTCOME_ONE.toString()));
+ String numberOfApplicantsAccepted = getFilteredPersonListCount(model, person ->
+ person.getCurrentStatus().equals(ApplicantState.OUTCOME_TWO.toString()));
+ String numberOfApplicantsRejected = getFilteredPersonListCount(model, person ->
+ person.getCurrentStatus().equals(ApplicantState.OUTCOME_THREE.toString()));
+
+ return "Resume review: " + numberOfReviewableApplicants
+ + ", Pending interview: " + numberOfApplicantsPendingIntv
+ + ", Completed interview: " + numberOfApplicantsCompletedIntv
+ + ", Waiting list: " + numberOfApplicantsWaitlisted
+ + ", Accepted: " + numberOfApplicantsAccepted
+ + ", Rejected: " + numberOfApplicantsRejected;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ return other instanceof ViewOverallStatisticsCommand;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddApplicantCommandParser.java
similarity index 63%
rename from src/main/java/seedu/address/logic/parser/AddCommandParser.java
rename to src/main/java/seedu/address/logic/parser/AddApplicantCommandParser.java
index 4ff1a97ed77..7421105c08a 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddApplicantCommandParser.java
@@ -1,53 +1,57 @@
package seedu.address.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
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.HashSet;
import java.util.Set;
import java.util.stream.Stream;
-import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AddApplicantPersonCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.ApplicantStatus;
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.person.Remark;
+import seedu.address.model.person.enums.ApplicantState;
import seedu.address.model.tag.Tag;
/**
- * Parses input arguments and creates a new AddCommand object
+ * Parses input arguments and creates a new AddApplicantCommand object
*/
-public class AddCommandParser implements Parser {
+public class AddApplicantCommandParser implements Parser {
/**
* Parses the given {@code String} of arguments in the context of the AddCommand
* and returns an AddCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
- public AddCommand parse(String args) throws ParseException {
+ public AddApplicantPersonCommand parse(String args) throws ParseException {
ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_TAG);
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
+ if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL)
|| !argMultimap.getPreamble().isEmpty()) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND,
+ AddApplicantPersonCommand.MESSAGE_USAGE));
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL);
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));
+ Remark remark = new Remark(""); // add command does not allow adding remarks straight away
+ ApplicantStatus status = new ApplicantStatus(ApplicantState.STAGE_ONE.toString());
+ Set tagList = new HashSet<>();
- Person person = new Person(name, phone, email, address, tagList);
+ Applicant applicant = new Applicant(name, phone, email, remark, status, tagList);
- return new AddCommand(person);
+ return new AddApplicantPersonCommand(applicant);
}
/**
diff --git a/src/main/java/seedu/address/logic/parser/AddApplicantStatusCommandParser.java b/src/main/java/seedu/address/logic/parser/AddApplicantStatusCommandParser.java
new file mode 100644
index 00000000000..2473ceee98e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddApplicantStatusCommandParser.java
@@ -0,0 +1,39 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.AddApplicantStatusCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.ApplicantStatus;
+import seedu.address.model.person.Phone;
+
+/**
+ * Parses input arguments and creates a new {@code AddApplicantStatusCommand} object
+ */
+public class AddApplicantStatusCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the {@code AddApplicantStatusCommand}
+ * and returns a {@code AddApplicantStatusCommand} object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AddApplicantStatusCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_STATUS);
+
+ Phone phone;
+ try {
+ phone = ParserUtil.parsePhone(argMultimap.getPreamble());
+ } catch (IllegalValueException ive) {
+ throw new ParseException(String.format(Messages.MESSAGE_INVALID_COMMAND,
+ AddApplicantStatusCommand.MESSAGE_USAGE), ive);
+ }
+
+ ApplicantStatus applicantStatus = ParserUtil.parseApplicantStatus(argMultimap
+ .getValue(PREFIX_STATUS).orElse(""));
+
+ return new AddApplicantStatusCommand(phone, applicantStatus);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddInterviewCommandParser.java b/src/main/java/seedu/address/logic/parser/AddInterviewCommandParser.java
new file mode 100644
index 00000000000..b9a38d3ce8c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddInterviewCommandParser.java
@@ -0,0 +1,80 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+import static seedu.address.logic.Messages.MESSAGE_NOT_DATE;
+import static seedu.address.logic.Messages.MESSAGE_NOT_TIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_APPLICANT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEWER;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_START_TIME;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.logging.Logger;
+import java.util.stream.Stream;
+
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.commands.AddInterviewCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Phone;
+
+
+/**
+ * Parses input arguments and creates a new AddInterviewCommand object
+ */
+public class AddInterviewCommandParser implements Parser {
+
+ private final Logger logger = LogsCenter.getLogger(getClass());
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the AddInterviewCommand
+ * and returns an AddInterviewCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public AddInterviewCommand parse(String args) throws ParseException {
+ ArgumentMultimap argMultimap =
+ ArgumentTokenizer.tokenize(args, PREFIX_DESCRIPTION, PREFIX_DATE, PREFIX_START_TIME, PREFIX_END_TIME,
+ PREFIX_APPLICANT, PREFIX_INTERVIEWER);
+
+ if (!arePrefixesPresent(argMultimap, PREFIX_DESCRIPTION, PREFIX_DATE, PREFIX_START_TIME, PREFIX_END_TIME,
+ PREFIX_APPLICANT, PREFIX_INTERVIEWER)
+ || !argMultimap.getPreamble().isEmpty()) {
+ logger.info("An error has occurred while parsing AddInterviewCommand: " + MESSAGE_INVALID_COMMAND);
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND, AddInterviewCommand.MESSAGE_USAGE));
+ }
+
+ String description = argMultimap.getValue(PREFIX_DESCRIPTION).get();
+ LocalDate date = parseDate(argMultimap.getValue(PREFIX_DATE).get());
+ LocalTime startTime = parseTime(argMultimap.getValue(PREFIX_START_TIME).get());
+ LocalTime endTime = parseTime(argMultimap.getValue(PREFIX_END_TIME).get());
+ Phone applicantPhone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_APPLICANT).get());
+ Phone interviewerPhone = ParserUtil.parsePhone(argMultimap.getValue(PREFIX_INTERVIEWER).get());
+
+ return new AddInterviewCommand(description, applicantPhone, interviewerPhone, date, startTime, endTime);
+ }
+
+ private static boolean arePrefixesPresent(ArgumentMultimap argumentMultimap, Prefix... prefixes) {
+ return Stream.of(prefixes).allMatch(prefix -> argumentMultimap.getValue(prefix).isPresent());
+ }
+
+ private static LocalDate parseDate(String date) throws ParseException {
+ try {
+ return LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE);
+ } catch (DateTimeParseException e) {
+ throw new ParseException(MESSAGE_NOT_DATE);
+ }
+ }
+
+ private static LocalTime parseTime(String time) throws ParseException {
+ try {
+ return LocalTime.parse(time, DateTimeFormatter.ISO_LOCAL_TIME);
+ } catch (DateTimeParseException e) {
+ throw new ParseException(MESSAGE_NOT_TIME);
+ }
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/parser/AddInterviewerCommandParser.java b/src/main/java/seedu/address/logic/parser/AddInterviewerCommandParser.java
new file mode 100644
index 00000000000..3bab0d5a444
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AddInterviewerCommandParser.java
@@ -0,0 +1,65 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+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.HashSet;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import seedu.address.logic.commands.AddInterviewerPersonCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Interviewer;
+import seedu.address.model.person.InterviewerStatus;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.Remark;
+import seedu.address.model.person.enums.InterviewerState;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Parses input arguments and creates a new AddInterviewerCommand object
+ */
+public class AddInterviewerCommandParser 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 AddInterviewerPersonCommand 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,
+ AddInterviewerPersonCommand.MESSAGE_USAGE));
+ }
+
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL);
+ 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());
+ Remark remark = new Remark(""); // add command does not allow adding remarks straight away
+ InterviewerStatus status = new InterviewerStatus(InterviewerState.FREE.toString());
+ Set tagList = new HashSet<>();
+
+ Interviewer person = new Interviewer(name, phone, email, remark, status, tagList);
+
+ return new AddInterviewerPersonCommand(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
index 3149ee07e0b..3184602354b 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -1,22 +1,30 @@
package seedu.address.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
-import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import seedu.address.commons.core.LogsCenter;
-import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AddApplicantPersonCommand;
+import seedu.address.logic.commands.AddApplicantStatusCommand;
+import seedu.address.logic.commands.AddInterviewCommand;
+import seedu.address.logic.commands.AddInterviewerPersonCommand;
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.DeleteInterviewCommand;
import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FilterInterviewsByDateCommand;
+import seedu.address.logic.commands.FilterPersonsByStatusCommand;
+import seedu.address.logic.commands.FindEmailCommand;
+import seedu.address.logic.commands.FindNameCommand;
+import seedu.address.logic.commands.FindPhoneCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.ListInterviewsCommand;
+import seedu.address.logic.commands.RemarkCommand;
+import seedu.address.logic.commands.ViewOverallStatisticsCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -28,7 +36,6 @@ public class AddressBookParser {
* Used for initial separation of command word and args.
*/
private static final Pattern BASIC_COMMAND_FORMAT = Pattern.compile("(?\\S+)(?.*)");
- private static final Logger logger = LogsCenter.getLogger(AddressBookParser.class);
/**
* Parses user input into command for execution.
@@ -40,37 +47,61 @@ public class AddressBookParser {
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));
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND, HelpCommand.MESSAGE_USAGE));
}
final String commandWord = matcher.group("commandWord");
final String arguments = matcher.group("arguments");
-
- // Note to developers: Change the log level in config.json to enable lower level (i.e., FINE, FINER and lower)
- // log messages such as the one below.
- // Lower level log messages are used sparingly to minimize noise in the code.
- logger.fine("Command word: " + commandWord + "; Arguments: " + arguments);
-
switch (commandWord) {
- case AddCommand.COMMAND_WORD:
- return new AddCommandParser().parse(arguments);
+ case AddInterviewerPersonCommand.COMMAND_WORD:
+ return new AddInterviewerCommandParser().parse(arguments);
+
+ case AddApplicantPersonCommand.COMMAND_WORD:
+ return new AddApplicantCommandParser().parse(arguments);
- case EditCommand.COMMAND_WORD:
- return new EditCommandParser().parse(arguments);
+ case AddInterviewCommand.COMMAND_WORD:
+ return new AddInterviewCommandParser().parse(arguments);
case DeleteCommand.COMMAND_WORD:
return new DeleteCommandParser().parse(arguments);
+ case DeleteInterviewCommand.COMMAND_WORD:
+ return new DeleteInterviewCommandParser().parse(arguments);
+
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
- case FindCommand.COMMAND_WORD:
- return new FindCommandParser().parse(arguments);
+ case FindEmailCommand.COMMAND_WORD:
+ return new FindEmailCommandParser().parse(arguments);
+
+ case FindNameCommand.COMMAND_WORD:
+ return new FindNameCommandParser().parse(arguments);
+
+ case FindPhoneCommand.COMMAND_WORD:
+ return new FindPhoneCommandParser().parse(arguments);
+
+ case RemarkCommand.COMMAND_WORD:
+ return new RemarkCommandParser().parse(arguments);
+
+ case AddApplicantStatusCommand.COMMAND_WORD:
+ return new AddApplicantStatusCommandParser().parse(arguments);
case ListCommand.COMMAND_WORD:
return new ListCommand();
+ case ListInterviewsCommand.COMMAND_WORD:
+ return new ListInterviewsCommand();
+
+ case FilterInterviewsByDateCommand.COMMAND_WORD:
+ return new FilterInterviewsByDateCommandParser().parse(arguments);
+
+ case FilterPersonsByStatusCommand.COMMAND_WORD:
+ return new FilterPersonsByStatusCommandParser().parse(arguments);
+
+ case ViewOverallStatisticsCommand.COMMAND_WORD:
+ return new ViewOverallStatisticsCommand();
+
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
@@ -78,7 +109,6 @@ public Command parseCommand(String userInput) throws ParseException {
return new HelpCommand();
default:
- logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
}
}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..307e7b282b7 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -9,7 +9,15 @@ public class CliSyntax {
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_REMARK = new Prefix("r/");
public static final Prefix PREFIX_TAG = new Prefix("t/");
+ public static final Prefix PREFIX_DESCRIPTION = new Prefix("desc/");
+ public static final Prefix PREFIX_DATE = new Prefix("date/");
+ public static final Prefix PREFIX_START_TIME = new Prefix("st/");
+ public static final Prefix PREFIX_END_TIME = new Prefix("et/");
+ public static final Prefix PREFIX_APPLICANT = new Prefix("a/");
+ public static final Prefix PREFIX_INTERVIEWER = new Prefix("i/");
+
+ public static final Prefix PREFIX_STATUS = new Prefix("s/");
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
index 3527fe76a3e..23ce9b54706 100644
--- a/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/DeleteCommandParser.java
@@ -1,10 +1,10 @@
package seedu.address.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
-import seedu.address.commons.core.index.Index;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Phone;
/**
* Parses input arguments and creates a new DeleteCommand object
@@ -18,11 +18,11 @@ public class DeleteCommandParser implements Parser {
*/
public DeleteCommand parse(String args) throws ParseException {
try {
- Index index = ParserUtil.parseIndex(args);
- return new DeleteCommand(index);
+ Phone phone = ParserUtil.parsePhone(args);
+ return new DeleteCommand(phone);
} catch (ParseException pe) {
throw new ParseException(
- String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE), pe);
+ String.format(MESSAGE_INVALID_COMMAND, DeleteCommand.MESSAGE_USAGE), pe);
}
}
diff --git a/src/main/java/seedu/address/logic/parser/DeleteInterviewCommandParser.java b/src/main/java/seedu/address/logic/parser/DeleteInterviewCommandParser.java
new file mode 100644
index 00000000000..97ddb1f0b15
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/DeleteInterviewCommandParser.java
@@ -0,0 +1,29 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+
+import seedu.address.logic.commands.DeleteInterviewCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new DeleteInterviewCommand object
+ */
+public class DeleteInterviewCommandParser 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 DeleteInterviewCommand parse(String args) throws ParseException {
+ try {
+ int x = Integer.parseInt(args.trim());
+ x -= 1;
+ return new DeleteInterviewCommand(x);
+ } catch (NumberFormatException e) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND, DeleteInterviewCommand.MESSAGE_USAGE), e);
+ }
+ }
+
+}
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 46b3309a78b..00000000000
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package seedu.address.logic.parser;
-
-import static java.util.Objects.requireNonNull;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_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);
- }
-
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
-
- 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/FilterInterviewsByDateCommandParser.java b/src/main/java/seedu/address/logic/parser/FilterInterviewsByDateCommandParser.java
new file mode 100644
index 00000000000..2b20d2d2490
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FilterInterviewsByDateCommandParser.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_NOT_DATE;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+
+import seedu.address.logic.commands.FilterInterviewsByDateCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+
+/**
+ * Parses input arguments and creates a new FilterInterviewsByDateCommand object
+ */
+public class FilterInterviewsByDateCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of FilterInterviewByDateCommand
+ * and returns a FilterInterviewByDateCommand object for execution.
+ * @throws ParseException if the user does not conform the expected date format
+ */
+ public FilterInterviewsByDateCommand parse(String args) throws ParseException {
+ try {
+ LocalDate date = LocalDate.parse(args.trim());
+ return new FilterInterviewsByDateCommand(date);
+ } catch (DateTimeParseException e) {
+ throw new ParseException(String.format(MESSAGE_NOT_DATE));
+ }
+
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/FilterPersonsByStatusCommandParser.java b/src/main/java/seedu/address/logic/parser/FilterPersonsByStatusCommandParser.java
new file mode 100644
index 00000000000..4ddb59dfe00
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FilterPersonsByStatusCommandParser.java
@@ -0,0 +1,47 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.commands.FilterPersonsByStatusCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.ApplicantStatus;
+import seedu.address.model.person.InterviewerStatus;
+import seedu.address.model.person.Status;
+
+
+/**
+ * Parses input arguments and creates a new FilterPersonsByStatusCommandParser object
+ */
+public class FilterPersonsByStatusCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of FilterPersonsByStatusCommand
+ * and returns a FilterPersonsByStatusCommand object for execution.
+ * @throws ParseException if the given status is invalid
+ */
+ public FilterPersonsByStatusCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_STATUS);
+
+ Status status;
+ try {
+ String statusArgument = argMultimap.getPreamble().trim().toLowerCase();
+ if (ApplicantStatus.isValidStatus(statusArgument)) {
+ status = new ApplicantStatus(statusArgument);
+ } else if (InterviewerStatus.isValidStatus(statusArgument)) {
+ status = new InterviewerStatus(statusArgument);
+ } else {
+ throw new ParseException(Status.MESSAGE_USAGE);
+ }
+ } catch (IllegalValueException ive) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND,
+ Status.MESSAGE_USAGE), ive);
+ }
+
+ return new FilterPersonsByStatusCommand(status);
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/parser/FindEmailCommandParser.java b/src/main/java/seedu/address/logic/parser/FindEmailCommandParser.java
new file mode 100644
index 00000000000..fbe971821a0
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindEmailCommandParser.java
@@ -0,0 +1,33 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.FindEmailCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.EmailContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new FindEmailCommand object
+ */
+public class FindEmailCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the FindEmailCommand
+ * and returns a FindEmailCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FindEmailCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND, FindEmailCommand.MESSAGE_USAGE));
+ }
+
+ String[] emailKeywords = trimmedArgs.split("\\s+");
+
+ return new FindEmailCommand(new EmailContainsKeywordsPredicate(Arrays.asList(emailKeywords)));
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/FindCommandParser.java b/src/main/java/seedu/address/logic/parser/FindNameCommandParser.java
similarity index 53%
rename from src/main/java/seedu/address/logic/parser/FindCommandParser.java
rename to src/main/java/seedu/address/logic/parser/FindNameCommandParser.java
index 2867bde857b..77c9b97d9a8 100644
--- a/src/main/java/seedu/address/logic/parser/FindCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/FindNameCommandParser.java
@@ -1,33 +1,33 @@
package seedu.address.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
import java.util.Arrays;
-import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindNameCommand;
import seedu.address.logic.parser.exceptions.ParseException;
import seedu.address.model.person.NameContainsKeywordsPredicate;
/**
- * Parses input arguments and creates a new FindCommand object
+ * Parses input arguments and creates a new FindNameCommand object
*/
-public class FindCommandParser implements Parser {
+public class FindNameCommandParser implements Parser {
/**
- * Parses the given {@code String} of arguments in the context of the FindCommand
- * and returns a FindCommand object for execution.
+ * Parses the given {@code String} of arguments in the context of the FindNameCommand
+ * and returns a FindNameCommand object for execution.
* @throws ParseException if the user input does not conform the expected format
*/
- public FindCommand parse(String args) throws ParseException {
+ public FindNameCommand 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.format(MESSAGE_INVALID_COMMAND, FindNameCommand.MESSAGE_USAGE));
}
String[] nameKeywords = trimmedArgs.split("\\s+");
- return new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
+ return new FindNameCommand(new NameContainsKeywordsPredicate(Arrays.asList(nameKeywords)));
}
}
diff --git a/src/main/java/seedu/address/logic/parser/FindPhoneCommandParser.java b/src/main/java/seedu/address/logic/parser/FindPhoneCommandParser.java
new file mode 100644
index 00000000000..9164a5d4464
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/FindPhoneCommandParser.java
@@ -0,0 +1,33 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.FindPhoneCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.PhoneContainsKeywordsPredicate;
+
+/**
+ * Parses input arguments and creates a new FindPhoneCommand object
+ */
+public class FindPhoneCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the FindPhoneCommand
+ * and returns a FindPhoneCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public FindPhoneCommand parse(String args) throws ParseException {
+ String trimmedArgs = args.trim();
+ if (trimmedArgs.isEmpty()) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND, FindPhoneCommand.MESSAGE_USAGE));
+ }
+
+ String[] phoneKeywords = trimmedArgs.split("\\s+");
+
+ return new FindPhoneCommand(new PhoneContainsKeywordsPredicate(Arrays.asList(phoneKeywords)));
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..57ddd5fa83f 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -9,7 +9,7 @@
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.ApplicantStatus;
import seedu.address.model.person.Email;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
@@ -65,21 +65,6 @@ public static Phone parsePhone(String phone) throws ParseException {
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.
@@ -121,4 +106,19 @@ public static Set parseTags(Collection tags) throws ParseException
}
return tagSet;
}
+
+ /**
+ * Parses a {@code String status} into a {@code ApplicantStatus}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code status} is invalid.
+ */
+ public static ApplicantStatus parseApplicantStatus(String applicantStatus) throws ParseException {
+ requireNonNull(applicantStatus);
+ String trimmedApplicantStatus = applicantStatus.trim().toLowerCase();
+ if (!ApplicantStatus.isValidStatus(trimmedApplicantStatus)) {
+ throw new ParseException(ApplicantStatus.MESSAGE_CONSTRAINTS);
+ }
+ return new ApplicantStatus(trimmedApplicantStatus);
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java b/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java
new file mode 100644
index 00000000000..9b8aefbcd7d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/RemarkCommandParser.java
@@ -0,0 +1,37 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.commands.RemarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Remark;
+
+/**
+ * Parses input arguments and creates a new {@code RemarkCommand} object
+ */
+public class RemarkCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the {@code RemarkCommand}
+ * and returns a {@code RemarkCommand} object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public RemarkCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_REMARK);
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (IllegalValueException ive) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND, RemarkCommand.MESSAGE_USAGE), ive);
+ }
+
+ String remark = argMultimap.getValue(PREFIX_REMARK).orElse("");
+
+ return new RemarkCommand(index, new Remark(remark));
+ }
+}
diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java
index 73397161e84..150c4a5a5fb 100644
--- a/src/main/java/seedu/address/model/AddressBook.java
+++ b/src/main/java/seedu/address/model/AddressBook.java
@@ -6,6 +6,8 @@
import javafx.collections.ObservableList;
import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.interview.Interview;
+import seedu.address.model.interview.UniqueInterviewList;
import seedu.address.model.person.Person;
import seedu.address.model.person.UniquePersonList;
@@ -16,6 +18,8 @@
public class AddressBook implements ReadOnlyAddressBook {
private final UniquePersonList persons;
+ private final UniqueInterviewList interviews;
+
/*
* The 'unusual' code block below is a non-static initialization block, sometimes used to avoid duplication
@@ -26,6 +30,7 @@ public class AddressBook implements ReadOnlyAddressBook {
*/
{
persons = new UniquePersonList();
+ interviews = new UniqueInterviewList();
}
public AddressBook() {}
@@ -39,6 +44,7 @@ public AddressBook(ReadOnlyAddressBook toBeCopied) {
}
//// list overwrite operations
+ // Person Operations
/**
* Replaces the contents of the person list with {@code persons}.
@@ -53,8 +59,8 @@ public void setPersons(List persons) {
*/
public void resetData(ReadOnlyAddressBook newData) {
requireNonNull(newData);
-
setPersons(newData.getPersonList());
+ setInterviews(newData.getInterviewList());
}
//// person-level operations
@@ -94,6 +100,40 @@ public void removePerson(Person key) {
persons.remove(key);
}
+ // Interview Operations
+
+ /**
+ * Adds an interview to the address book.
+ * The interview must not already exist in the address book.
+ */
+ public void addInterview(Interview interview) {
+ interviews.add(interview);
+ }
+
+ /**
+ * Returns true if an interview with the same identity as {@code interview} exists in the address book.
+ */
+ public boolean hasInterview(Interview interview) {
+ requireNonNull(interview);
+ return interviews.contains(interview);
+ }
+
+
+ /**
+ * Removes {@code key} from this {@code AddressBook}.
+ * {@code key} must exist in the address book.
+ */
+ public void removeInterview(Interview key) {
+ interviews.remove(key);
+ }
+
+ public void setInterviews(List interviews) {
+ this.interviews.setInterviews(interviews);
+ }
+
+ public void sortInterviews() {
+ interviews.sortInterviewsByDate();
+ }
//// util methods
@Override
@@ -108,6 +148,11 @@ public ObservableList getPersonList() {
return persons.asUnmodifiableObservableList();
}
+ @Override
+ public ObservableList getInterviewList() {
+ return interviews.asUnmodifiableObservableList();
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..b6749e02375 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -5,6 +5,7 @@
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
/**
@@ -13,6 +14,7 @@
public interface Model {
/** {@code Predicate} that always evaluate to true */
Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ Predicate PREDICATE_SHOW_ALL_INTERVIEWS = unused -> true;
/**
* Replaces user prefs data with the data in {@code userPrefs}.
@@ -84,4 +86,15 @@ public interface Model {
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate predicate);
+ void updateFilteredInterviewList(Predicate predicate);
+
+ void addInterview(Interview interview);
+
+ void sortInterview();
+
+ boolean hasInterview(Interview interview);
+
+ void deleteInterview(Interview interview);
+
+ ObservableList getFilteredInterviewList();
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..d73667a4cce 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -11,6 +11,7 @@
import javafx.collections.transformation.FilteredList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
/**
@@ -23,6 +24,8 @@ public class ModelManager implements Model {
private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
+ private final FilteredList filteredInterviews;
+
/**
* Initializes a ModelManager with the given addressBook and userPrefs.
*/
@@ -34,6 +37,7 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.addressBook = new AddressBook(addressBook);
this.userPrefs = new UserPrefs(userPrefs);
filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ filteredInterviews = new FilteredList<>(this.addressBook.getInterviewList());
}
public ModelManager() {
@@ -111,6 +115,28 @@ public void setPerson(Person target, Person editedPerson) {
addressBook.setPerson(target, editedPerson);
}
+ @Override
+ public boolean hasInterview(Interview interview) {
+ requireNonNull(interview);
+ return addressBook.hasInterview(interview);
+ }
+
+ @Override
+ public void addInterview(Interview interview) {
+ addressBook.addInterview(interview);
+ updateFilteredInterviewList(PREDICATE_SHOW_ALL_INTERVIEWS);
+ }
+
+ @Override
+ public void sortInterview() {
+ addressBook.sortInterviews();
+ }
+
+ @Override
+ public void deleteInterview(Interview interview) {
+ addressBook.removeInterview(interview);
+ }
+
//=========== Filtered Person List Accessors =============================================================
/**
@@ -127,6 +153,18 @@ public void updateFilteredPersonList(Predicate predicate) {
requireNonNull(predicate);
filteredPersons.setPredicate(predicate);
}
+ @Override
+ public void updateFilteredInterviewList(Predicate predicate) {
+ requireNonNull(predicate);
+ filteredInterviews.setPredicate(predicate);
+ }
+
+ //=========== Filtered Interview List Accessors =============================================================
+
+ @Override
+ public ObservableList getFilteredInterviewList() {
+ return filteredInterviews;
+ }
@Override
public boolean equals(Object other) {
diff --git a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
index 6ddc2cd9a29..0ca19c72cb9 100644
--- a/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
+++ b/src/main/java/seedu/address/model/ReadOnlyAddressBook.java
@@ -1,6 +1,7 @@
package seedu.address.model;
import javafx.collections.ObservableList;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
/**
@@ -14,4 +15,6 @@ public interface ReadOnlyAddressBook {
*/
ObservableList getPersonList();
+ ObservableList getInterviewList();
+
}
diff --git a/src/main/java/seedu/address/model/interview/Interview.java b/src/main/java/seedu/address/model/interview/Interview.java
new file mode 100644
index 00000000000..26360470eb2
--- /dev/null
+++ b/src/main/java/seedu/address/model/interview/Interview.java
@@ -0,0 +1,102 @@
+package seedu.address.model.interview;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+import seedu.address.model.person.Person;
+
+/**
+ * Represents an Interview in the Tether.
+ */
+public class Interview {
+ private Person applicant;
+ private Person interviewer;
+ private LocalDate date;
+ private LocalTime startTime;
+ private LocalTime endTime;
+ private String description;
+
+ /**
+ * Represents a Interview in the address book.
+ */
+ public Interview(Person applicant, Person interviewer, LocalDate date, LocalTime startTime,
+ LocalTime endTime, String description) {
+ this.applicant = applicant;
+ this.interviewer = interviewer;
+ this.date = date;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.description = description;
+ }
+
+ public Person getApplicant() {
+ return applicant;
+ }
+
+ public Person getInterviewer() {
+ return interviewer;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public LocalTime getStartTime() {
+ return startTime;
+ }
+
+ public LocalTime getEndTime() {
+ return endTime;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns true if both interviews of the same name have at least one other identity field that is the same.
+ * This defines a weaker notion of equality between two interviews.
+ */
+ public boolean isSameInterview(Interview otherInterview) {
+ boolean applicantMatch = otherInterview.applicant.equals(this.applicant);
+ boolean interviewerMatch = otherInterview.interviewer.equals(this.interviewer);
+ boolean dateMatch = otherInterview.date.equals(this.date);
+ boolean timeMatch = otherInterview.startTime.equals(this.startTime)
+ && otherInterview.endTime.equals(this.endTime);
+
+ return applicantMatch && interviewerMatch && dateMatch && timeMatch;
+ }
+
+ /**
+ * Returns true if both Interviews have the same fields.
+ * This defines a stronger notion of equality between two Interviews.
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof Interview)) {
+ return false;
+ }
+
+ Interview otherInterview = (Interview) other;
+
+ boolean applicantMatch = otherInterview.applicant.equals(this.applicant);
+ boolean interviewerMatch = otherInterview.interviewer.equals(this.interviewer);
+
+ return applicantMatch && interviewerMatch;
+ }
+
+ @Override
+ public String toString() {
+ return " ------Interview------" + '\n' + "Applicant: " + applicant.getName() + "\nInterviewer: "
+ + interviewer.getName() + "\nDate: " + date.toString() + "\nStart: " + startTime.toString() + " End: "
+ + endTime.toString() + "\nDescription: " + description;
+ }
+
+ public boolean containsPerson(Person person) {
+ return this.interviewer.isSamePerson(person) || this.applicant.isSamePerson(person);
+ }
+}
diff --git a/src/main/java/seedu/address/model/interview/UniqueInterviewList.java b/src/main/java/seedu/address/model/interview/UniqueInterviewList.java
new file mode 100644
index 00000000000..19432c27c13
--- /dev/null
+++ b/src/main/java/seedu/address/model/interview/UniqueInterviewList.java
@@ -0,0 +1,168 @@
+package seedu.address.model.interview;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.interview.exceptions.DuplicateInterviewException;
+import seedu.address.model.interview.exceptions.InterviewNotFoundException;
+
+/**
+ * A list of interviews that enforces uniqueness between its elements and does not allow nulls.
+ * An interview is considered unique by comparing using {@code Interview#isSameInterview(Interview)}. As such,
+ * adding and updating of interviews uses Interview#isSameInterview(Interview) for equality so as to ensure that
+ * the interview being added or updated is unique in terms of identity in the UniqueInterviewList.
+ * However, the removal of an interview uses Interview#equals(Object) so
+ * as to ensure that the interview with exactly the same fields will be removed.
+ *
+ * Supports a minimal set of list operations.
+ *
+ * @see Interview#isSameInterview(Interview)
+ */
+public class UniqueInterviewList implements Iterable {
+
+ private final ObservableList internalList = FXCollections.observableArrayList();
+ private final ObservableList internalUnmodifiableList =
+ FXCollections.unmodifiableObservableList(internalList);
+
+ /**
+ * Returns true if the list contains an equivalent interview as the given argument.
+ */
+ public boolean contains(Interview toCheck) {
+ requireNonNull(toCheck);
+ return internalList.stream().anyMatch(toCheck::isSameInterview);
+ }
+
+ /**
+ * Adds an interview to the list.
+ * The interview must not already exist in the list.
+ */
+ public void add(Interview toAdd) {
+ requireNonNull(toAdd);
+ if (contains(toAdd)) {
+ throw new DuplicateInterviewException();
+ }
+ internalList.add(toAdd);
+ }
+
+ /**
+ * Replaces the interview {@code target} in the list with {@code editedInterview}.
+ * {@code target} must exist in the list.
+ * The interview identity of {@code editedInterview} must not be the same as another existing interview in the list.
+ */
+ public void setInterview(Interview target, Interview editedInterview) throws DuplicateInterviewException,
+ InterviewNotFoundException {
+ requireAllNonNull(target, editedInterview);
+
+ int index = internalList.indexOf(target);
+ if (index == -1) {
+ throw new InterviewNotFoundException();
+ }
+
+ if (!target.isSameInterview(editedInterview) && contains(editedInterview)) {
+ throw new DuplicateInterviewException();
+ }
+
+ internalList.set(index, editedInterview);
+ }
+
+ /**
+ * Sorts the interviews by date and time.
+ */
+ public void sortInterviewsByDate() {
+ Comparator dateSorter = (interview1, interview2) -> {
+ if (interview1.getDate().isBefore(interview2.getDate())) {
+ return -1;
+ } else if (interview1.getDate().isEqual(interview2.getDate())) {
+ if (interview1.getStartTime().isBefore(interview2.getStartTime())) {
+ return -1;
+ } else if (interview1.getStartTime().isAfter(interview2.getStartTime())) {
+ return 1;
+ } else if (interview1.getStartTime().equals(interview2.getStartTime())) {
+ if (interview1.getEndTime().isAfter(interview2.getEndTime())) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ } else {
+ return 1;
+ }
+ };
+ internalList.sort(dateSorter);
+ }
+
+ /**
+ * Removes the equivalent interview from the list.
+ * The interview must exist in the list.
+ */
+ public void remove(Interview toRemove) {
+ requireNonNull(toRemove);
+ if (!internalList.remove(toRemove)) {
+ throw new InterviewNotFoundException();
+ }
+ }
+
+ public void setInterviews(UniqueInterviewList replacement) {
+ requireNonNull(replacement);
+ internalList.setAll(replacement.internalList);
+ }
+
+ /**
+ * Replaces the contents of this list with {@code interviews}.
+ * {@code interviews} must not contain duplicate interviews.
+ */
+ public void setInterviews(List interviews) throws DuplicateInterviewException {
+ requireAllNonNull(interviews);
+ if (!interviewsAreUnique(interviews)) {
+ throw new DuplicateInterviewException();
+ }
+
+ internalList.setAll(interviews);
+ }
+
+ /**
+ * 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 || (other instanceof UniqueInterviewList
+ && internalList.equals(((UniqueInterviewList) other).internalList));
+ }
+
+ @Override
+ public int hashCode() {
+ return internalList.hashCode();
+ }
+
+ /**
+ * Returns true if {@code interviews} contains only unique interviews.
+ */
+ private boolean interviewsAreUnique(List interviews) {
+ for (int i = 0; i < interviews.size() - 1; i++) {
+ for (int j = i + 1; j < interviews.size(); j++) {
+ if (interviews.get(i).isSameInterview(interviews.get(j))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+}
+
diff --git a/src/main/java/seedu/address/model/interview/exceptions/DuplicateInterviewException.java b/src/main/java/seedu/address/model/interview/exceptions/DuplicateInterviewException.java
new file mode 100644
index 00000000000..a554c939972
--- /dev/null
+++ b/src/main/java/seedu/address/model/interview/exceptions/DuplicateInterviewException.java
@@ -0,0 +1,11 @@
+package seedu.address.model.interview.exceptions;
+
+/**
+ * Signals that the operation will result in duplicate Interviews (Interviews are considered duplicates if they have
+ * the same identity).
+ */
+public class DuplicateInterviewException extends RuntimeException {
+ public DuplicateInterviewException() {
+ super("Operation would result in duplicate interviews");
+ }
+}
diff --git a/src/main/java/seedu/address/model/interview/exceptions/InterviewNotFoundException.java b/src/main/java/seedu/address/model/interview/exceptions/InterviewNotFoundException.java
new file mode 100644
index 00000000000..21e7cc0b11c
--- /dev/null
+++ b/src/main/java/seedu/address/model/interview/exceptions/InterviewNotFoundException.java
@@ -0,0 +1,10 @@
+package seedu.address.model.interview.exceptions;
+
+/**
+ * Signals that the operation is unable to find the specified interview.
+ */
+public class InterviewNotFoundException extends RuntimeException {
+ public InterviewNotFoundException() {
+ super("Interview not found!");
+ }
+}
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 469a2cc9a1e..00000000000
--- a/src/main/java/seedu/address/model/person/Address.java
+++ /dev/null
@@ -1,65 +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) {
- if (other == this) {
- return true;
- }
-
- // instanceof handles nulls
- if (!(other instanceof Address)) {
- return false;
- }
-
- Address otherAddress = (Address) other;
- return value.equals(otherAddress.value);
- }
-
- @Override
- public int hashCode() {
- return value.hashCode();
- }
-
-}
diff --git a/src/main/java/seedu/address/model/person/Applicant.java b/src/main/java/seedu/address/model/person/Applicant.java
new file mode 100644
index 00000000000..a2bf80f41e3
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Applicant.java
@@ -0,0 +1,77 @@
+package seedu.address.model.person;
+
+import java.util.Set;
+
+import seedu.address.model.Model;
+import seedu.address.model.person.enums.ApplicantState;
+import seedu.address.model.person.enums.Type;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Represents an Applicant in the Tether.
+ * Guarantees: details are present and not null, field values are validated, immutable.
+ */
+public class Applicant extends Person {
+
+ private final Type type = Type.APPLICANT;
+ private ApplicantStatus currentStatus;
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Applicant(Name name, Phone phone, Email email, Remark remark, ApplicantStatus status, Set tags) {
+ super(name, phone, email, remark, tags);
+ this.tags.add(new Tag("Applicant"));
+ this.currentStatus = status;
+ }
+
+ @Override
+ public boolean isSamePerson(Person otherPerson) {
+ return super.isSamePerson(otherPerson);
+ }
+
+ @Override
+ public String getPersonType() {
+ return type.toString();
+ }
+
+ /**
+ * Changes the status of this applicant to pending interview.
+ *
+ * @param model the location of the applicant to be edited
+ */
+ @Override
+ public void updateCurrentStatusToReflectScheduledInterview(Model model) {
+ currentStatus = new ApplicantStatus(ApplicantState.STAGE_TWO.toString());
+ /*
+ Need to find this applicant by reference equality and replace them for the change in status to reflect in
+ the gui immediately
+ */
+ model.setPerson(this, this);
+ }
+
+ /**
+ * Rolls back the status of this applicant.
+ *
+ * @param model the location of the applicant to be edited
+ */
+ @Override
+ public void revertCurrentStatus(Model model) {
+ currentStatus = new ApplicantStatus(ApplicantState.STAGE_ONE.toString());
+ /*
+ Need to find this applicant by reference equality and replace them for the change in status to reflect in
+ the gui immediately
+ */
+ model.setPerson(this, this);
+ }
+
+ @Override
+ public String getCurrentStatus() {
+ return currentStatus.toString().toLowerCase();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString() + "\nStatus: " + currentStatus.toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/ApplicantStatus.java b/src/main/java/seedu/address/model/person/ApplicantStatus.java
new file mode 100644
index 00000000000..c6fcca6dad6
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/ApplicantStatus.java
@@ -0,0 +1,87 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Represents an Applicant's status in the Tether.
+ * Guarantee: is valid as declared in {@link #isValidStatus(String)}
+ */
+public class ApplicantStatus extends Status {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Applicant status can only be one of \"resume review\", \"pending interview\", \"completed interview\","
+ + "\"waiting list\", \"accepted\", or \"rejected\"";
+
+ public final String value;
+
+ /**
+ * Constructs a {@code Status}.
+ *
+ * @param status A status.
+ */
+ public ApplicantStatus(String status) {
+ requireNonNull(status);
+ checkArgument(isValidStatus(status.toLowerCase()), MESSAGE_CONSTRAINTS);
+ value = status.toLowerCase();
+ }
+
+ /**
+ * Checks and returns the status if it is valid.
+ */
+ public static boolean isValidStatus(String status) {
+ Pattern patternResumeReview = Pattern.compile("^resume review$");
+ Matcher matcherResumeReview = patternResumeReview.matcher(status);
+
+ Pattern patternPendingIntv = Pattern.compile("^pending interview$");
+ Matcher matcherPendingIntv = patternPendingIntv.matcher(status);
+
+ Pattern patternCompletedIntv = Pattern.compile("^completed interview$");
+ Matcher matcherCompletedIntv = patternCompletedIntv.matcher(status);
+
+ Pattern patternWaitingList = Pattern.compile("^waiting list$");
+ Matcher matcherWaitingList = patternWaitingList.matcher(status);
+
+ Pattern patternAccepted = Pattern.compile("^accepted$");
+ Matcher matcherAccepted = patternAccepted.matcher(status);
+
+ Pattern patternRejected = Pattern.compile("^rejected$");
+ Matcher matcherRejected = patternRejected.matcher(status.toLowerCase());
+
+ return (matcherResumeReview.matches()
+ || matcherPendingIntv.matches()
+ || matcherCompletedIntv.matches()
+ || matcherWaitingList.matches()
+ || matcherAccepted.matches()
+ || matcherRejected.matches());
+
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ApplicantStatus)) {
+ return false;
+ }
+
+ ApplicantStatus otherApplicantStatus = (ApplicantStatus) other;
+ return value.equals(otherApplicantStatus.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/EmailContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/EmailContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..411c2f18ec3
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/EmailContainsKeywordsPredicate.java
@@ -0,0 +1,44 @@
+package seedu.address.model.person;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code Person}'s {@code Email} matches any of the keywords given.
+ */
+public class EmailContainsKeywordsPredicate implements Predicate {
+ private final List keywords;
+
+ public EmailContainsKeywordsPredicate(List keywords) {
+ this.keywords = keywords;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ return keywords.stream()
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getEmail().value, keyword));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof EmailContainsKeywordsPredicate)) {
+ return false;
+ }
+
+ EmailContainsKeywordsPredicate otherEmailContainsKeywordsPredicate = (EmailContainsKeywordsPredicate) other;
+ return keywords.equals(otherEmailContainsKeywordsPredicate.keywords);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("keywords", keywords).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Interviewer.java b/src/main/java/seedu/address/model/person/Interviewer.java
new file mode 100644
index 00000000000..f655a2bc06b
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Interviewer.java
@@ -0,0 +1,108 @@
+package seedu.address.model.person;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import seedu.address.model.Model;
+import seedu.address.model.person.enums.InterviewerState;
+import seedu.address.model.person.enums.Type;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Represents an Interviewer in Tether.
+ * Guarantees: details are present and not null, field values are validated, immutable.
+ */
+public class Interviewer extends Person {
+
+ private final Type type = Type.INTERVIEWER;
+
+ private final List upcomingInterviews = new ArrayList<>();
+
+ /**
+ * Every field must be present and not null.
+ */
+ public Interviewer(Name name, Phone phone, Email email, Remark remark, InterviewerStatus status, Set tags) {
+ super(name, phone, email, remark, tags);
+ this.tags.add(new Tag("Interviewer"));
+ if (!status.value.equals(InterviewerState.FREE.toString())) {
+ parseAndAddStatus(status);
+ }
+ }
+
+ @Override
+ public String getPersonType() {
+ return type.toString();
+ }
+
+ private void parseAndAddStatus(InterviewerStatus status) {
+ String[] newlineSeparatedStatusArray = status.value.split("\n");
+ for (String individualStatus : newlineSeparatedStatusArray) {
+ upcomingInterviews.add(new InterviewerStatus(individualStatus));
+ }
+ }
+
+ /**
+ * Changes the status of this interviewer to free only if they had an interview before.
+ *
+ * @param model the location of the interviewer to be edited.
+ */
+ public void updateCurrentStatusToReflectDeletedInterview(Model model, Person applicantScheduled) {
+ String scheduledApplicantName = applicantScheduled.getName().toString().toLowerCase();
+ for (Iterator iterator = upcomingInterviews.iterator(); iterator.hasNext();) {
+ String status = iterator.next().toString();
+ if (status.contains(scheduledApplicantName)) {
+ iterator.remove();
+ break;
+ }
+ }
+ /*
+ * Need to find this interviewer by reference equality and replace them for the change in status to reflect
+ * in the gui immediately
+ */
+ model.setPerson(this, this);
+ }
+
+ /**
+ * Changes the status of this interviewer to interview with [applicant name] only if the status is free currently.
+ *
+ * @param model the location of the interviewer to be edited.
+ * @param applicantScheduled the applicant whom this interviewer is scheduled with.
+ */
+ public void updateCurrentStatusToReflectScheduledInterview(Model model, Person applicantScheduled) {
+ upcomingInterviews.add(new InterviewerStatus(InterviewerState.OCCUPIED + " " + applicantScheduled.getName()));
+ /*
+ Need to find this interviewer by reference equality and replace them for the change in status to reflect
+ in the gui immediately
+ */
+ model.setPerson(this, this);
+ }
+
+ @Override
+ public String getCurrentStatus() {
+ if (upcomingInterviews.isEmpty()) {
+ return InterviewerState.FREE.toString();
+ } else {
+ return stringifyInterviewStatuses();
+ }
+ }
+
+ private String stringifyInterviewStatuses() {
+ StringBuilder interviewStatusesString = new StringBuilder();
+ int numberOfScheduledInterviews = upcomingInterviews.size();
+ for (int i = 0; i < numberOfScheduledInterviews; i++) {
+ if (i == numberOfScheduledInterviews - 1) {
+ interviewStatusesString.append(upcomingInterviews.get(i));
+ } else {
+ interviewStatusesString.append(upcomingInterviews.get(i)).append("\n");
+ }
+ }
+ return interviewStatusesString.toString();
+ }
+
+ @Override
+ public String toString() {
+ return super.toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/InterviewerStatus.java b/src/main/java/seedu/address/model/person/InterviewerStatus.java
new file mode 100644
index 00000000000..468fe8b7c8f
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/InterviewerStatus.java
@@ -0,0 +1,67 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Represents an Interviewer's status in the Tether.
+ * Guarantee: is valid as declared in {@link #isValidStatus(String)}
+ */
+public class InterviewerStatus extends Status {
+ public static final String MESSAGE_CONSTRAINTS =
+ "Interviewer status can only be either \"free\" or \"interview with [applicant phone]\"";
+
+ public final String value;
+
+ /**
+ * Constructs a {@code ApplicantStatus}.
+ *
+ * @param status A status.
+ */
+ public InterviewerStatus(String status) {
+ requireNonNull(status);
+ checkArgument(isValidStatus(status.toLowerCase()), MESSAGE_CONSTRAINTS);
+ value = status.toLowerCase();
+ }
+
+ /**
+ * Checks and returns the status if it is valid.
+ */
+ public static boolean isValidStatus(String status) {
+ Pattern patternFree = Pattern.compile("^free$");
+ Matcher matcherFree = patternFree.matcher(status);
+
+ Pattern patternOccupied = Pattern.compile("(?s)^interview with .*");
+ Matcher matcherOccupied = patternOccupied.matcher(status);
+
+ return matcherFree.matches() || matcherOccupied.matches();
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof InterviewerStatus)) {
+ return false;
+ }
+
+ InterviewerStatus otherInterviewerStatus = (InterviewerStatus) other;
+ return value.equals(otherInterviewerStatus.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
index abe8c46b535..f799c592344 100644
--- a/src/main/java/seedu/address/model/person/Person.java
+++ b/src/main/java/seedu/address/model/person/Person.java
@@ -7,7 +7,8 @@
import java.util.Objects;
import java.util.Set;
-import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.Model;
+import seedu.address.model.person.enums.Type;
import seedu.address.model.tag.Tag;
/**
@@ -16,24 +17,27 @@
*/
public class Person {
+ protected Set tags = new HashSet<>();
// 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<>();
+ private final Remark remark;
+
+ // Type field
+ private final Type type = Type.PERSON;
/**
* Every field must be present and not null.
*/
- public Person(Name name, Phone phone, Email email, Address address, Set tags) {
- requireAllNonNull(name, phone, email, address, tags);
+ public Person(Name name, Phone phone, Email email, Remark remark, Set tags) {
+ requireAllNonNull(name, phone, email, tags);
this.name = name;
this.phone = phone;
this.email = email;
- this.address = address;
+ this.remark = remark;
this.tags.addAll(tags);
}
@@ -49,8 +53,8 @@ public Email getEmail() {
return email;
}
- public Address getAddress() {
- return address;
+ public Remark getRemark() {
+ return remark;
}
/**
@@ -61,8 +65,28 @@ public Set getTags() {
return Collections.unmodifiableSet(tags);
}
+ public String getPersonType() {
+ return type.toString();
+ }
+
+ public void revertCurrentStatus(Model model) {
+ }
+
+ public void updateCurrentStatusToReflectDeletedInterview(Model model, Person applicantScheduled) {
+ }
+
+ public void updateCurrentStatusToReflectScheduledInterview(Model model) {
+ }
+
+ public void updateCurrentStatusToReflectScheduledInterview(Model model, Person applicantScheduled) {
+ }
+
+ public String getCurrentStatus() {
+ return "";
+ }
+
/**
- * Returns true if both persons have the same name.
+ * Returns true if both persons of the have either email or phone number field that is the same.
* This defines a weaker notion of equality between two persons.
*/
public boolean isSamePerson(Person otherPerson) {
@@ -71,7 +95,7 @@ public boolean isSamePerson(Person otherPerson) {
}
return otherPerson != null
- && otherPerson.getName().equals(getName());
+ && (otherPerson.getPhone().equals(getPhone()) || otherPerson.getEmail().equals(getEmail()));
}
/**
@@ -84,34 +108,33 @@ public boolean equals(Object other) {
return true;
}
- // instanceof handles nulls
if (!(other instanceof Person)) {
return false;
}
Person otherPerson = (Person) other;
- return name.equals(otherPerson.name)
- && phone.equals(otherPerson.phone)
- && email.equals(otherPerson.email)
- && address.equals(otherPerson.address)
- && tags.equals(otherPerson.tags);
+ return otherPerson.getName().equals(getName())
+ && otherPerson.getPhone().equals(getPhone())
+ && otherPerson.getEmail().equals(getEmail())
+ && 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);
+ return Objects.hash(name, phone, email, tags);
}
@Override
public String toString() {
- return new ToStringBuilder(this)
- .add("name", name)
- .add("phone", phone)
- .add("email", email)
- .add("address", address)
- .add("tags", tags)
- .toString();
+ final StringBuilder builder = new StringBuilder();
+ builder.append(getName())
+ .append(" Phone: ")
+ .append(getPhone())
+ .append(" Email: ")
+ .append(getEmail())
+ .append(" Remark: ")
+ .append(getRemark());
+ return builder.toString();
}
-
}
diff --git a/src/main/java/seedu/address/model/person/PhoneContainsKeywordsPredicate.java b/src/main/java/seedu/address/model/person/PhoneContainsKeywordsPredicate.java
new file mode 100644
index 00000000000..c771ce30a37
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/PhoneContainsKeywordsPredicate.java
@@ -0,0 +1,44 @@
+package seedu.address.model.person;
+
+import java.util.List;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.StringUtil;
+import seedu.address.commons.util.ToStringBuilder;
+
+/**
+ * Tests that a {@code Person}'s {@code Phone} matches any of the keywords given.
+ */
+public class PhoneContainsKeywordsPredicate implements Predicate {
+ private final List keywords;
+
+ public PhoneContainsKeywordsPredicate(List keywords) {
+ this.keywords = keywords;
+ }
+
+ @Override
+ public boolean test(Person person) {
+ return keywords.stream()
+ .anyMatch(keyword -> StringUtil.containsWordIgnoreCase(person.getPhone().value, keyword));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof PhoneContainsKeywordsPredicate)) {
+ return false;
+ }
+
+ PhoneContainsKeywordsPredicate otherPhoneContainsKeywordsPredicate = (PhoneContainsKeywordsPredicate) other;
+ return keywords.equals(otherPhoneContainsKeywordsPredicate.keywords);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("keywords", keywords).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Remark.java b/src/main/java/seedu/address/model/person/Remark.java
new file mode 100644
index 00000000000..d3dd151d1c0
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Remark.java
@@ -0,0 +1,38 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Represents a Person's remark in the address book.
+ * Guarantees: immutable; is always valid
+ */
+public class Remark {
+ public final String value;
+
+ /**
+ * Constructs a {@code Remark}.
+ *
+ * @param remark A remark.
+ */
+ public Remark(String remark) {
+ requireNonNull(remark);
+ value = remark;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other == this // short circuit if same object
+ || (other instanceof Remark // instanceof handles nulls
+ && value.equals(((Remark) other).value)); // state check
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Status.java b/src/main/java/seedu/address/model/person/Status.java
new file mode 100644
index 00000000000..7169f5d178e
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Status.java
@@ -0,0 +1,19 @@
+package seedu.address.model.person;
+
+import seedu.address.model.person.enums.ApplicantState;
+import seedu.address.model.person.enums.InterviewerState;
+
+/**
+ * Represents a Person's status in the Tether.
+ */
+public abstract class Status {
+ public static final String MESSAGE_USAGE = "For applicants, status can only be one of "
+ + ApplicantState.STAGE_ONE + ", "
+ + ApplicantState.STAGE_TWO + ", "
+ + ApplicantState.STAGE_THREE + ", "
+ + ApplicantState.OUTCOME_ONE + ", "
+ + ApplicantState.OUTCOME_TWO + ", or "
+ + ApplicantState.OUTCOME_THREE + ".\nFor interviewers, status can only be one of "
+ + InterviewerState.FREE + ", or "
+ + InterviewerState.OCCUPIED + " [applicant name].";
+}
diff --git a/src/main/java/seedu/address/model/person/UniquePersonList.java b/src/main/java/seedu/address/model/person/UniquePersonList.java
index cc0a68d79f9..a535e95b9df 100644
--- a/src/main/java/seedu/address/model/person/UniquePersonList.java
+++ b/src/main/java/seedu/address/model/person/UniquePersonList.java
@@ -36,6 +36,7 @@ public boolean contains(Person toCheck) {
return internalList.stream().anyMatch(toCheck::isSamePerson);
}
+
/**
* Adds a person to the list.
* The person must not already exist in the list.
diff --git a/src/main/java/seedu/address/model/person/enums/ApplicantState.java b/src/main/java/seedu/address/model/person/enums/ApplicantState.java
new file mode 100644
index 00000000000..660a618ccd3
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/enums/ApplicantState.java
@@ -0,0 +1,37 @@
+package seedu.address.model.person.enums;
+
+/**
+ * Enumerates an applicant's statuses at any given time
+ */
+public enum ApplicantState {
+ STAGE_ONE {
+ public String toString() {
+ return "resume review";
+ }
+ },
+ STAGE_TWO {
+ public String toString() {
+ return "pending interview";
+ }
+ },
+ STAGE_THREE {
+ public String toString() {
+ return "completed interview";
+ }
+ },
+ OUTCOME_ONE {
+ public String toString() {
+ return "waiting list";
+ }
+ },
+ OUTCOME_TWO {
+ public String toString() {
+ return "accepted";
+ }
+ },
+ OUTCOME_THREE {
+ public String toString() {
+ return "rejected";
+ }
+ },
+}
diff --git a/src/main/java/seedu/address/model/person/enums/InterviewerState.java b/src/main/java/seedu/address/model/person/enums/InterviewerState.java
new file mode 100644
index 00000000000..04e3872bb27
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/enums/InterviewerState.java
@@ -0,0 +1,17 @@
+package seedu.address.model.person.enums;
+
+/**
+ * Enumerates an Interviewer's status at any given time.
+ */
+public enum InterviewerState {
+ FREE {
+ public String toString() {
+ return "free";
+ }
+ },
+ OCCUPIED {
+ public String toString() {
+ return "interview with";
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/enums/Type.java b/src/main/java/seedu/address/model/person/enums/Type.java
new file mode 100644
index 00000000000..8b460375c6a
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/enums/Type.java
@@ -0,0 +1,8 @@
+package seedu.address.model.person.enums;
+
+/**
+ * Enumerates possible types for persons added to Tether.
+ */
+public enum Type {
+ PERSON, APPLICANT, INTERVIEWER;
+}
diff --git a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
index fa764426ca7..26ca466245c 100644
--- a/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
+++ b/src/main/java/seedu/address/model/person/exceptions/PersonNotFoundException.java
@@ -3,4 +3,8 @@
/**
* Signals that the operation is unable to find the specified person.
*/
-public class PersonNotFoundException extends RuntimeException {}
+public class PersonNotFoundException extends RuntimeException {
+ public PersonNotFoundException() {
+ super("Person not found!");
+ }
+}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..8c2530d9e14 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -6,37 +6,52 @@
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.ApplicantStatus;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Interviewer;
+import seedu.address.model.person.InterviewerStatus;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.Remark;
+import seedu.address.model.person.enums.ApplicantState;
+import seedu.address.model.person.enums.InterviewerState;
import seedu.address.model.tag.Tag;
/**
* Contains utility methods for populating {@code AddressBook} with sample data.
*/
public class SampleDataUtil {
+
+ public static final Remark EMPTY_REMARK = new Remark("");
+
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"))
+ new Applicant(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
+ EMPTY_REMARK,
+ new ApplicantStatus(ApplicantState.STAGE_ONE.toString()),
+ getTagSet()),
+ new Applicant(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
+ EMPTY_REMARK,
+ new ApplicantStatus(ApplicantState.STAGE_ONE.toString()),
+ getTagSet()),
+ new Interviewer(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
+ EMPTY_REMARK,
+ new InterviewerStatus(InterviewerState.FREE.toString()),
+ getTagSet()),
+ new Applicant(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
+ EMPTY_REMARK,
+ new ApplicantStatus(ApplicantState.STAGE_ONE.toString()),
+ getTagSet()),
+ new Interviewer(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
+ EMPTY_REMARK,
+ new InterviewerStatus(InterviewerState.FREE.toString()),
+ getTagSet()),
+ new Interviewer(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
+ EMPTY_REMARK,
+ new InterviewerStatus(InterviewerState.FREE.toString()),
+ getTagSet())
};
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedInterview.java b/src/main/java/seedu/address/storage/JsonAdaptedInterview.java
new file mode 100644
index 00000000000..c0fa8f7a01f
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedInterview.java
@@ -0,0 +1,91 @@
+package seedu.address.storage;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.interview.Interview;
+import seedu.address.model.person.Person;
+
+
+
+
+
+/**
+ * Jackson-friendly version of {@link Interview}.
+ */
+public class JsonAdaptedInterview {
+ public static final String MISSING_FIELD_MESSAGE_FORMAT = "Interview's %s field is missing!";
+
+ private final String description;
+ private final String date;
+ private final String startTime;
+ private final String endTime;
+ private final JsonAdaptedPerson applicant;
+ private final JsonAdaptedPerson interviewer;
+
+ /**
+ * Constructs a {@code JsonAdaptedInterview} with the given interview details.
+ */
+ @JsonCreator
+ public JsonAdaptedInterview(@JsonProperty("description") String description,
+ @JsonProperty("date") String date,
+ @JsonProperty("startTime") String startTime,
+ @JsonProperty("endTime") String endTime,
+ @JsonProperty("applicant") JsonAdaptedPerson applicant,
+ @JsonProperty("interviewer") JsonAdaptedPerson interviewer) {
+ this.description = description;
+ this.date = date;
+ this.startTime = startTime;
+ this.endTime = endTime;
+ this.applicant = applicant;
+ this.interviewer = interviewer;
+ }
+
+ /**
+ * Converts a given {@code Interview} into this class for Jackson use.
+ */
+ public JsonAdaptedInterview(Interview source) {
+ description = source.getDescription();
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ date = source.getDate().format(formatter);
+ startTime = source.getStartTime().toString();
+ endTime = source.getEndTime().toString();
+ applicant = new JsonAdaptedPerson(source.getApplicant());
+ interviewer = new JsonAdaptedPerson(source.getInterviewer());
+
+
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted interview object into the model's {@code Interview} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted interview.
+ */
+ public Interview toModelType() throws IllegalValueException {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ if (this.date == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ LocalDate.class.getSimpleName()));
+ }
+ final LocalDate date = LocalDate.parse(this.date, formatter);
+ if (this.startTime == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ LocalTime.class.getSimpleName()));
+ }
+ final LocalTime start = LocalTime.parse(startTime);
+ if (this.endTime == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT,
+ LocalTime.class.getSimpleName()));
+ }
+ final LocalTime end = LocalTime.parse(endTime);
+ final Person app = applicant.toModelType();
+ final Person inter = interviewer.toModelType();
+ return new Interview(app, inter, date, start, end, this.description);
+
+ }
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
index bd1ca0f56c8..23819ca2e74 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
@@ -10,11 +10,17 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.ApplicantStatus;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Interviewer;
+import seedu.address.model.person.InterviewerStatus;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.Remark;
+import seedu.address.model.person.Status;
+import seedu.address.model.person.enums.Type;
import seedu.address.model.tag.Tag;
/**
@@ -27,23 +33,30 @@ class JsonAdaptedPerson {
private final String name;
private final String phone;
private final String email;
- private final String address;
- private final List tags = new ArrayList<>();
+ private final String remark;
+ private final List tagged = new ArrayList<>();
+ private final String type;
+ private final String status;
/**
* Constructs a {@code JsonAdaptedPerson} with the given person details.
*/
@JsonCreator
public JsonAdaptedPerson(@JsonProperty("name") String name, @JsonProperty("phone") String phone,
- @JsonProperty("email") String email, @JsonProperty("address") String address,
- @JsonProperty("tags") List tags) {
+ @JsonProperty("email") String email,
+ @JsonProperty("remark") String remark,
+ @JsonProperty("tagged") List tagged,
+ @JsonProperty("type") String type,
+ @JsonProperty("status") String status) {
this.name = name;
this.phone = phone;
this.email = email;
- this.address = address;
- if (tags != null) {
- this.tags.addAll(tags);
+ this.remark = remark;
+ if (tagged != null) {
+ this.tagged.addAll(tagged);
}
+ this.type = type;
+ this.status = status;
}
/**
@@ -53,10 +66,12 @@ public JsonAdaptedPerson(Person source) {
name = source.getName().fullName;
phone = source.getPhone().value;
email = source.getEmail().value;
- address = source.getAddress().value;
- tags.addAll(source.getTags().stream()
+ remark = source.getRemark().value;
+ tagged.addAll(source.getTags().stream()
.map(JsonAdaptedTag::new)
.collect(Collectors.toList()));
+ type = source.getPersonType();
+ status = source.getCurrentStatus();
}
/**
@@ -66,7 +81,7 @@ public JsonAdaptedPerson(Person source) {
*/
public Person toModelType() throws IllegalValueException {
final List personTags = new ArrayList<>();
- for (JsonAdaptedTag tag : tags) {
+ for (JsonAdaptedTag tag : tagged) {
personTags.add(tag.toModelType());
}
@@ -94,16 +109,32 @@ public Person toModelType() throws IllegalValueException {
}
final Email modelEmail = new Email(email);
- if (address == null) {
- throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName()));
+ if (remark == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Remark.class.getSimpleName()));
}
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
- }
- final Address modelAddress = new Address(address);
+ final Remark modelRemark = new Remark(remark);
final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
+
+ if (status == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.class.getSimpleName()));
+ }
+
+ if (type.equals(Type.APPLICANT.toString())) {
+ if (!ApplicantStatus.isValidStatus(status)) {
+ throw new IllegalValueException(ApplicantStatus.MESSAGE_CONSTRAINTS);
+ }
+ ApplicantStatus applicantStatus = new ApplicantStatus(status);
+ return new Applicant(modelName, modelPhone, modelEmail, modelRemark, applicantStatus, modelTags);
+ } else if (type.equals(Type.INTERVIEWER.toString())) {
+ if (!InterviewerStatus.isValidStatus(status)) {
+ throw new IllegalValueException(InterviewerStatus.MESSAGE_CONSTRAINTS);
+ }
+ InterviewerStatus interviewerStatus = new InterviewerStatus(status);
+ return new Interviewer(modelName, modelPhone, modelEmail, modelRemark, interviewerStatus, modelTags);
+ }
+
+ return new Person(modelName, modelPhone, modelEmail, modelRemark, modelTags);
}
}
diff --git a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
index 41e06f264e1..cd2ce828d3f 100644
--- a/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
+++ b/src/main/java/seedu/address/storage/JsonAddressBookStorage.java
@@ -15,7 +15,7 @@
import seedu.address.model.ReadOnlyAddressBook;
/**
- * A class to access AddressBook data stored as a json file on the hard disk.
+ * A class to access Tether data stored as a json file on the hard disk.
*/
public class JsonAddressBookStorage implements AddressBookStorage {
diff --git a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
index 5efd834091d..54a749321f4 100644
--- a/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
+++ b/src/main/java/seedu/address/storage/JsonSerializableAddressBook.java
@@ -1,5 +1,7 @@
package seedu.address.storage;
+import static seedu.address.logic.commands.AddInterviewCommand.MESSAGE_DUPLICATE_INTERVIEW;
+
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -11,8 +13,10 @@
import seedu.address.commons.exceptions.IllegalValueException;
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
+
/**
* An Immutable AddressBook that is serializable to JSON format.
*/
@@ -22,13 +26,19 @@ class JsonSerializableAddressBook {
public static final String MESSAGE_DUPLICATE_PERSON = "Persons list contains duplicate person(s).";
private final List persons = new ArrayList<>();
+ private final List interviews = new ArrayList<>();
/**
* Constructs a {@code JsonSerializableAddressBook} with the given persons.
*/
@JsonCreator
- public JsonSerializableAddressBook(@JsonProperty("persons") List persons) {
+ public JsonSerializableAddressBook(@JsonProperty("persons") List persons,
+ @JsonProperty("interviews") List interviews) {
this.persons.addAll(persons);
+ if (interviews != null) {
+ this.interviews.addAll(interviews);
+ }
+
}
/**
@@ -38,6 +48,9 @@ public JsonSerializableAddressBook(@JsonProperty("persons") List {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
- public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
+ public static final String USERGUIDE_URL = "https://ay2324s2-cs2103t-f11-3.github.io/tp/UserGuide.html";
+ public static final String COMMON_COMMANDS = "\n\nFor quick help, here are some common commands "
+ + "(omit the [] when using!): "
+ + "\n1. Quick add an applicant/interviewer: add_[applicant/interviewer] n/[name] p/[phone] e/[email] "
+ + "\n2. Add an interview: add_interview desc/[description] date/[date] st/[starting time] et/[ending time] "
+ + "a/[applicant phone] i/[interviewer phone]"
+ + "\n3. Delete an applicant/interviewer: delete_person [phone]"
+ + "\n4. Delete an interview: delete_interview [interview index]"
+ + "\n5. List applicants/interviewers: list_persons"
+ + "\n6. List interviews: list_interviews"
+ + "\n7. Add and edit statuses for applicants/interviews: [applicant/interviewer]_status [phone] [status]"
+ + "\n8. Find person(s) by email/name/phone: find_[email/name/phone] [keyword1] [keyword2]..."
+ + "\n9. Filter interview(s) by date: filter_interviews_by_date [date in YYYY-MM-DD]"
+ + "\n10. Filter persons(s) by status: filter_by_status [status]"
+ + "\n11. View overall statistics: view_overall_statistics"
+ + "\n12. Add remarks to applicant/interviewer: remark [INDEX] r/[REMARK]"
+ + "\n13. Clear all existing records in Tether: clear";
+ public static final String HELP_MESSAGE = "Refer to our user guide at " + USERGUIDE_URL + " for detailed info "
+ + "on how to use Tether." + COMMON_COMMANDS;
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
private static final String FXML = "HelpWindow.fxml";
diff --git a/src/main/java/seedu/address/ui/InterviewCard.java b/src/main/java/seedu/address/ui/InterviewCard.java
new file mode 100644
index 00000000000..ee92a8554d8
--- /dev/null
+++ b/src/main/java/seedu/address/ui/InterviewCard.java
@@ -0,0 +1,73 @@
+package seedu.address.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.address.model.interview.Interview;
+
+
+/**
+ * An UI component that displays information of a {@code Person}.
+ */
+public class InterviewCard extends UiPart {
+
+ private static final String FXML = "InterviewListCard.fxml";
+
+ /**
+ * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
+ * As a consequence, UI elements' variable names cannot be set to such keywords
+ * or an exception will be thrown by JavaFX during runtime.
+ *
+ * @see The issue on AddressBook level 4
+ */
+
+ public final Interview interview;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label id;
+ @FXML
+ private Label description;
+ @FXML
+ private Label applicantNamePhone;
+ @FXML
+ private Label interviewerNamePhone;
+ @FXML
+ private Label date;
+ @FXML
+ private Label startEndTime;
+
+ /**
+ * Creates a {@code InterviewCard} with the given {@code Interview} and {@code int}.
+ */
+ public InterviewCard(Interview interview, int displayedIndex) {
+ super(FXML);
+ this.interview = interview;
+ id.setText(displayedIndex + ". ");
+ applicantNamePhone.setText("Interview with " + interview.getApplicant().getName().toString());
+ interviewerNamePhone.setText("Interviewer: " + interview.getInterviewer().getName().toString());
+ date.setText("Date: " + interview.getDate().toString());
+ startEndTime.setText(interview.getStartTime().toString() + "-" + interview.getEndTime().toString());
+ description.setText("Description: " + interview.getDescription());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof InterviewCard)) {
+ return false;
+ }
+
+ // state check
+ InterviewCard card = (InterviewCard) other;
+ return id.getText().equals(card.id.getText())
+ && interview.equals(card.interview);
+ }
+}
diff --git a/src/main/java/seedu/address/ui/InterviewListPanel.java b/src/main/java/seedu/address/ui/InterviewListPanel.java
new file mode 100644
index 00000000000..d26efbbd028
--- /dev/null
+++ b/src/main/java/seedu/address/ui/InterviewListPanel.java
@@ -0,0 +1,48 @@
+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.interview.Interview;
+
+/**
+ * Panel containing the list of persons.
+ */
+public class InterviewListPanel extends UiPart {
+ private static final String FXML = "InterviewListPanel.fxml";
+ private final Logger logger = LogsCenter.getLogger(InterviewListPanel.class);
+
+ @FXML
+ private ListView interviewListView;
+
+ /**
+ * Creates a {@code InterviewListPanel} with the given {@code ObservableList}.
+ */
+ public InterviewListPanel(ObservableList interviewList) {
+ super(FXML);
+ interviewListView.setItems(interviewList);
+ interviewListView.setCellFactory(listView -> new InterviewListViewCell());
+ }
+
+ /**
+ * Custom {@code ListCell} that displays the graphics of a {@code Interview} using a {@code InterviewCard}.
+ */
+ class InterviewListViewCell extends ListCell {
+ @Override
+ protected void updateItem(Interview interview, boolean empty) {
+ super.updateItem(interview, empty);
+ if (empty || interview == null) {
+ setGraphic(null);
+ setText(null);
+ } else {
+ setGraphic(new InterviewCard(interview, getIndex() + 1).getRoot());
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
index 79e74ef37c0..77f8fe7ee06 100644
--- a/src/main/java/seedu/address/ui/MainWindow.java
+++ b/src/main/java/seedu/address/ui/MainWindow.java
@@ -32,6 +32,7 @@ public class MainWindow extends UiPart {
// Independent Ui parts residing in this Ui container
private PersonListPanel personListPanel;
+ private InterviewListPanel interviewListPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
@@ -43,6 +44,8 @@ public class MainWindow extends UiPart {
@FXML
private StackPane personListPanelPlaceholder;
+ @FXML
+ private StackPane interviewListPanelPlaceholder;
@FXML
private StackPane resultDisplayPlaceholder;
@@ -113,6 +116,9 @@ void fillInnerParts() {
personListPanel = new PersonListPanel(logic.getFilteredPersonList());
personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
+ interviewListPanel = new InterviewListPanel(logic.getFilteredInterviewList());
+ interviewListPanelPlaceholder.getChildren().add(interviewListPanel.getRoot());
+
resultDisplay = new ResultDisplay();
resultDisplayPlaceholder.getChildren().add(resultDisplay.getRoot());
diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java
index 094c42cda82..14e67aff416 100644
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ b/src/main/java/seedu/address/ui/PersonCard.java
@@ -8,6 +8,7 @@
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import seedu.address.model.person.Person;
+import seedu.address.model.person.enums.ApplicantState;
/**
* An UI component that displays information of a {@code Person}.
@@ -35,25 +36,60 @@ public class PersonCard extends UiPart {
@FXML
private Label phone;
@FXML
- private Label address;
- @FXML
private Label email;
@FXML
+ private Label remark;
+ @FXML
+ private FlowPane status;
+ @FXML
private FlowPane tags;
/**
- * Creates a {@code PersonCode} with the given {@code Person} and index to display.
+ * Creates a {@code PersonCard} with the given {@code Person} and {@code int}.
*/
public PersonCard(Person person, int displayedIndex) {
super(FXML);
this.person = person;
id.setText(displayedIndex + ". ");
name.setText(person.getName().fullName);
- phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
- email.setText(person.getEmail().value);
+ phone.setText("Phone: " + person.getPhone().value);
+ email.setText("Email: " + person.getEmail().value);
+ remark.setText("Remark: " + person.getRemark().value);
+ status.getChildren().add(new Label(person.getCurrentStatus()));
+ status.getChildren().get(0).getStyleClass().add(determineStatusColor(person));
person.getTags().stream()
.sorted(Comparator.comparing(tag -> tag.tagName))
.forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
}
+
+ private String determineStatusColor(Person person) {
+ String currentStatus = person.getCurrentStatus();
+ if (currentStatus.equals(ApplicantState.OUTCOME_TWO.toString())) {
+ return "bg-green";
+ } else if (currentStatus.equals(ApplicantState.OUTCOME_THREE.toString())) {
+ return "bg-red";
+ } else if (currentStatus.equals(ApplicantState.OUTCOME_ONE.toString())) {
+ return "bg-orange";
+ } else {
+ return "bg-black";
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ // short circuit if same object
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof PersonCard)) {
+ return false;
+ }
+
+ // state check
+ PersonCard card = (PersonCard) other;
+ return id.getText().equals(card.id.getText())
+ && person.equals(card.person);
+ }
}
diff --git a/src/main/java/seedu/address/ui/UiManager.java b/src/main/java/seedu/address/ui/UiManager.java
index fdf024138bc..550431d7f41 100644
--- a/src/main/java/seedu/address/ui/UiManager.java
+++ b/src/main/java/seedu/address/ui/UiManager.java
@@ -20,7 +20,7 @@ public class UiManager implements Ui {
public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane";
private static final Logger logger = LogsCenter.getLogger(UiManager.class);
- private static final String ICON_APPLICATION = "/images/address_book_32.png";
+ private static final String ICON_APPLICATION = "/images/tether_logo.png";
private Logic logic;
private MainWindow mainWindow;
@@ -35,10 +35,7 @@ public UiManager(Logic logic) {
@Override
public void start(Stage primaryStage) {
logger.info("Starting UI...");
-
- //Set the application icon.
primaryStage.getIcons().add(getImage(ICON_APPLICATION));
-
try {
mainWindow = new MainWindow(primaryStage, logic);
mainWindow.show(); //This should be called before creating other UI parts
diff --git a/src/main/resources/images/tether_logo.png b/src/main/resources/images/tether_logo.png
new file mode 100644
index 00000000000..e334ec06c5d
Binary files /dev/null and b/src/main/resources/images/tether_logo.png differ
diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml
index 124283a392e..16961bd39fe 100644
--- a/src/main/resources/view/CommandBox.fxml
+++ b/src/main/resources/view/CommandBox.fxml
@@ -3,7 +3,6 @@
-
-
+
+
-
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css
index 36e6b001cd8..03c9a78f370 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/DarkTheme.css
@@ -1,10 +1,33 @@
.background {
- -fx-background-color: derive(#1d1d1d, 20%);
- background-color: #383838; /* Used in the default.html file */
+ -fx-background-color: derive(#012A4A, 20%);
+ background-color: #012A4A; /* Used in the default.html file */
+}
+
+.bg-green {
+ -fx-background-color: green;
+}
+
+.bg-red {
+ -fx-background-color: #800000;
+}
+
+.bg-black {
+ -fx-background-color: black;
+}
+
+.bg-orange {
+ -fx-background-color: #EB4F0E;
+}
+
+.list-header {
+ -fx-font-size: 20pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
}
.label {
- -fx-font-size: 11pt;
+ -fx-font-size: 18pt;
-fx-font-family: "Segoe UI Semibold";
-fx-text-fill: #555555;
-fx-opacity: 0.9;
@@ -41,8 +64,8 @@
.table-view {
-fx-base: #1d1d1d;
- -fx-control-inner-background: #1d1d1d;
- -fx-background-color: #1d1d1d;
+ -fx-control-inner-background: #012A4A;
+ -fx-background-color: #012A4A;
-fx-table-cell-border-color: transparent;
-fx-table-header-border-color: transparent;
-fx-padding: 5;
@@ -90,7 +113,7 @@
.list-view {
-fx-background-insets: 0;
-fx-padding: 0;
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#012A4A, 20%);
}
.list-cell {
@@ -100,11 +123,11 @@
}
.list-cell:filled:even {
- -fx-background-color: #3c3e3f;
+ -fx-background-color: #013A63;
}
.list-cell:filled:odd {
- -fx-background-color: #515658;
+ -fx-background-color: #01497C;
}
.list-cell:filled:selected {
@@ -185,7 +208,7 @@
}
.context-menu {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: derive(#013A63, 50%);
}
.context-menu .label {
@@ -217,7 +240,7 @@
-fx-border-color: #e2e2e2;
-fx-border-width: 2;
-fx-background-radius: 0;
- -fx-background-color: #1d1d1d;
+ -fx-background-color: #013A63;
-fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
-fx-font-size: 11pt;
-fx-text-fill: #d8d8d8;
@@ -282,11 +305,11 @@
}
.scroll-bar {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#2A6F97, 20%);
}
.scroll-bar .thumb {
- -fx-background-color: derive(#1d1d1d, 50%);
+ -fx-background-color: derive(white, 50%);
-fx-background-insets: 3;
}
@@ -318,7 +341,7 @@
}
#commandTextField {
- -fx-background-color: transparent #383838 transparent #383838;
+ -fx-background-color: transparent #012A4A transparent #012A4A;
-fx-background-insets: 0;
-fx-border-color: #383838 #383838 #ffffff #383838;
-fx-border-insets: 0;
@@ -333,7 +356,7 @@
}
#resultDisplay .content {
- -fx-background-color: transparent, #383838, transparent, #383838;
+ -fx-background-color: transparent, #012A4A, transparent, #012A4A;
-fx-background-radius: 0;
}
@@ -348,5 +371,18 @@
-fx-padding: 1 3 1 3;
-fx-border-radius: 2;
-fx-background-radius: 2;
- -fx-font-size: 11;
+ -fx-font-size: 18;
+}
+
+#status {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#status .label {
+ -fx-text-fill: white;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 18;
}
diff --git a/src/main/resources/view/HelpWindow.fxml b/src/main/resources/view/HelpWindow.fxml
index e01f330de33..eb6de66edc2 100644
--- a/src/main/resources/view/HelpWindow.fxml
+++ b/src/main/resources/view/HelpWindow.fxml
@@ -3,7 +3,6 @@
-
@@ -26,11 +25,6 @@
-
diff --git a/src/main/resources/view/InterviewListCard.fxml b/src/main/resources/view/InterviewListCard.fxml
new file mode 100644
index 00000000000..8de805b13b3
--- /dev/null
+++ b/src/main/resources/view/InterviewListCard.fxml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/InterviewListPanel.fxml b/src/main/resources/view/InterviewListPanel.fxml
new file mode 100644
index 00000000000..575ab4e362a
--- /dev/null
+++ b/src/main/resources/view/InterviewListPanel.fxml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..d41cb1e3943 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -6,55 +6,62 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml
index f5e812e25e6..8740f965d90 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/src/main/resources/view/PersonListCard.fxml
@@ -7,30 +7,35 @@
+
-
-
+
+
-
+
-
+
-
-
+
+
+
diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml
index a1bb6bbace8..bf473e4da8c 100644
--- a/src/main/resources/view/PersonListPanel.fxml
+++ b/src/main/resources/view/PersonListPanel.fxml
@@ -3,6 +3,14 @@
+
+
+
+
+
+
+ Persons
+
diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml
index 01b691792a9..22f3400f9eb 100644
--- a/src/main/resources/view/ResultDisplay.fxml
+++ b/src/main/resources/view/ResultDisplay.fxml
@@ -3,7 +3,6 @@
-
-
+
+
diff --git a/src/main/resources/view/StatusBarFooter.fxml b/src/main/resources/view/StatusBarFooter.fxml
index 7b430f9c6a2..322ef0599d9 100644
--- a/src/main/resources/view/StatusBarFooter.fxml
+++ b/src/main/resources/view/StatusBarFooter.fxml
@@ -4,7 +4,7 @@
-
+
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
index 6a4d2b7181c..15047decade 100644
--- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
+++ b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
@@ -3,11 +3,9 @@
"name": "Valid Person",
"phone": "9482424",
"email": "hans@example.com",
- "address": "4th street"
}, {
"name": "Person With Invalid Phone Field",
"phone": "948asdf2424",
"email": "hans@example.com",
- "address": "4th street"
} ]
}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
index ccd21f7d1a9..b6156e4bc23 100644
--- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
@@ -2,7 +2,6 @@
"persons": [ {
"name": "Person with invalid name field: Ha!ns Mu@ster",
"phone": "9482424",
- "email": "hans@example.com",
- "address": "4th street"
+ "email": "hans@example.com"
} ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
index a7427fe7aa2..8702d915ca7 100644
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
@@ -3,12 +3,17 @@
"name": "Alice Pauline",
"phone": "94351253",
"email": "alice@example.com",
- "address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
+ "remark" : "",
+ "type": "APPLICANT",
+ "status": "resume review",
+ "tagged": [ "friends" ]
}, {
"name": "Alice Pauline",
"phone": "94351253",
"email": "pauline@example.com",
- "address": "4th street"
+ "remark" : "",
+ "status": "resume review",
+ "type": "APPLICANT",
+ "tagged" : [ ]
} ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
index ad3f135ae42..671f3da654c 100644
--- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
@@ -2,7 +2,6 @@
"persons": [ {
"name": "Hans Muster",
"phone": "9482424",
- "email": "invalid@email!3e",
- "address": "4th street"
+ "email": "invalid@email!3e"
} ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
index 72262099d35..379fbf438f6 100644
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
@@ -1,46 +1,77 @@
{
- "_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
+ "_comment": "Tether save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
"persons" : [ {
"name" : "Alice Pauline",
"phone" : "94351253",
"email" : "alice@example.com",
- "address" : "123, Jurong West Ave 6, #08-111",
- "tags" : [ "friends" ]
+ "remark" : "",
+ "status": "resume review",
+ "type": "APPLICANT",
+ "tagged" : [ "Applicant","friends" ]
}, {
"name" : "Benson Meier",
"phone" : "98765432",
"email" : "johnd@example.com",
- "address" : "311, Clementi Ave 2, #02-25",
- "tags" : [ "owesMoney", "friends" ]
+ "remark" : "",
+ "status": "resume review",
+ "type": "APPLICANT",
+ "tagged" : [ "Applicant","owesMoney", "friends" ]
}, {
"name" : "Carl Kurz",
"phone" : "95352563",
"email" : "heinz@example.com",
- "address" : "wall street",
- "tags" : [ ]
+ "remark" : "",
+ "status": "resume review",
+ "type": "APPLICANT",
+ "tagged" : [ "Applicant" ]
}, {
"name" : "Daniel Meier",
"phone" : "87652533",
"email" : "cornelia@example.com",
- "address" : "10th street",
- "tags" : [ "friends" ]
+ "remark" : "",
+ "status": "resume review",
+ "type": "APPLICANT",
+ "tagged" : [ "Applicant","friends" ]
}, {
"name" : "Elle Meyer",
"phone" : "9482224",
"email" : "werner@example.com",
- "address" : "michegan ave",
- "tags" : [ ]
+ "remark" : "",
+ "status": "resume review",
+ "type": "APPLICANT",
+ "tagged" : [ "Applicant" ]
}, {
"name" : "Fiona Kunz",
"phone" : "9482427",
"email" : "lydia@example.com",
- "address" : "little tokyo",
- "tags" : [ ]
+ "remark" : "",
+ "status": "resume review",
+ "type": "APPLICANT",
+ "tagged" : [ "Applicant" ]
}, {
"name" : "George Best",
"phone" : "9482442",
"email" : "anna@example.com",
- "address" : "4th street",
- "tags" : [ ]
- } ]
+ "remark" : "",
+ "status": "resume review",
+ "type": "APPLICANT",
+ "tagged" : [ "Applicant" ]
+ }, {
+ "name" : "head",
+ "phone" : "12345678",
+ "email" : "head@cube.com",
+ "remark" : "",
+ "status": "resume review",
+ "type" : "APPLICANT",
+ "tagged" : [ "Applicant" ]
+ }, {
+ "name" : "cube",
+ "phone" : "87654321",
+ "email" : "cube@head.com",
+ "status": "free",
+ "type" : "INTERVIEWER",
+ "remark" : "",
+ "tagged" : [ "Interviewer" ]
+ }
+ ]
}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index baf8ce336a2..f46bee64093 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -1,14 +1,17 @@
package seedu.address.logic;
import static org.junit.jupiter.api.Assertions.assertEquals;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX;
+import static seedu.address.logic.Messages.MESSAGE_PERSON_NOT_IN_LIST;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_CUBE;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_CUBE;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_CUBE;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.AMY;
+import static seedu.address.testutil.TypicalPersons.CUBE;
import java.io.IOException;
import java.nio.file.AccessDeniedException;
@@ -18,7 +21,8 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
-import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AddApplicantPersonCommand;
+import seedu.address.logic.commands.AddInterviewerPersonCommand;
import seedu.address.logic.commands.CommandResult;
import seedu.address.logic.commands.ListCommand;
import seedu.address.logic.commands.exceptions.CommandException;
@@ -27,7 +31,8 @@
import seedu.address.model.ModelManager;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.UserPrefs;
-import seedu.address.model.person.Person;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.Interviewer;
import seedu.address.storage.JsonAddressBookStorage;
import seedu.address.storage.JsonUserPrefsStorage;
import seedu.address.storage.StorageManager;
@@ -60,8 +65,8 @@ public void execute_invalidCommandFormat_throwsParseException() {
@Test
public void execute_commandExecutionError_throwsCommandException() {
- String deleteCommand = "delete 9";
- assertCommandException(deleteCommand, MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ String deleteCommand = "delete_person 999";
+ assertCommandException(deleteCommand, MESSAGE_PERSON_NOT_IN_LIST);
}
@Test
@@ -71,14 +76,26 @@ public void execute_validCommand_success() throws Exception {
}
@Test
- public void execute_storageThrowsIoException_throwsCommandException() {
- assertCommandFailureForExceptionFromStorage(DUMMY_IO_EXCEPTION, String.format(
+ public void execute_applicantStorageThrowsIoException_throwsCommandException() {
+ assertApplicantCommandFailureForExceptionFromStorage(DUMMY_IO_EXCEPTION, String.format(
LogicManager.FILE_OPS_ERROR_FORMAT, DUMMY_IO_EXCEPTION.getMessage()));
}
@Test
- public void execute_storageThrowsAdException_throwsCommandException() {
- assertCommandFailureForExceptionFromStorage(DUMMY_AD_EXCEPTION, String.format(
+ public void execute_applicantStorageThrowsAdException_throwsCommandException() {
+ assertApplicantCommandFailureForExceptionFromStorage(DUMMY_AD_EXCEPTION, String.format(
+ LogicManager.FILE_OPS_PERMISSION_ERROR_FORMAT, DUMMY_AD_EXCEPTION.getMessage()));
+ }
+
+ @Test
+ public void execute_interviewerStorageThrowsIoException_throwsCommandException() {
+ assertInterviewerCommandFailureForExceptionFromStorage(DUMMY_IO_EXCEPTION, String.format(
+ LogicManager.FILE_OPS_ERROR_FORMAT, DUMMY_IO_EXCEPTION.getMessage()));
+ }
+
+ @Test
+ public void execute_interviewerStorageThrowsAdException_throwsCommandException() {
+ assertInterviewerCommandFailureForExceptionFromStorage(DUMMY_AD_EXCEPTION, String.format(
LogicManager.FILE_OPS_PERMISSION_ERROR_FORMAT, DUMMY_AD_EXCEPTION.getMessage()));
}
@@ -95,7 +112,7 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
* @see #assertCommandFailure(String, Class, String, Model)
*/
private void assertCommandSuccess(String inputCommand, String expectedMessage,
- Model expectedModel) throws CommandException, ParseException {
+ Model expectedModel) throws CommandException, ParseException {
CommandResult result = logic.execute(inputCommand);
assertEquals(expectedMessage, result.getFeedbackToUser());
assertEquals(expectedModel, model);
@@ -122,7 +139,7 @@ private void assertCommandException(String inputCommand, String expectedMessage)
* @see #assertCommandFailure(String, Class, String, Model)
*/
private void assertCommandFailure(String inputCommand, Class extends Throwable> expectedException,
- String expectedMessage) {
+ String expectedMessage) {
Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
assertCommandFailure(inputCommand, expectedException, expectedMessage, expectedModel);
}
@@ -135,7 +152,7 @@ private void assertCommandFailure(String inputCommand, Class extends Throwable
* @see #assertCommandSuccess(String, String, Model)
*/
private void assertCommandFailure(String inputCommand, Class extends Throwable> expectedException,
- String expectedMessage, Model expectedModel) {
+ String expectedMessage, Model expectedModel) {
assertThrows(expectedException, expectedMessage, () -> logic.execute(inputCommand));
assertEquals(expectedModel, model);
}
@@ -146,7 +163,34 @@ private void assertCommandFailure(String inputCommand, Class extends Throwable
* @param e the exception to be thrown by the Storage component
* @param expectedMessage the message expected inside exception thrown by the Logic component
*/
- private void assertCommandFailureForExceptionFromStorage(IOException e, String expectedMessage) {
+ private void assertApplicantCommandFailureForExceptionFromStorage(IOException e, String expectedMessage) {
+ Path prefPath = temporaryFolder.resolve("ExceptionUserPrefs.json");
+
+ // Inject LogicManager with an AddressBookStorage that throws the IOException e when saving
+ JsonAddressBookStorage addressBookStorage = new JsonAddressBookStorage(prefPath) {
+ @Override
+ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
+ throws IOException {
+ throw e;
+ }
+ };
+
+ JsonUserPrefsStorage userPrefsStorage =
+ new JsonUserPrefsStorage(temporaryFolder.resolve("ExceptionUserPrefs.json"));
+ StorageManager storage = new StorageManager(addressBookStorage, userPrefsStorage);
+
+ logic = new LogicManager(model, storage);
+
+ // Triggers the saveAddressBook method by executing an add command
+ String addApplicantCommand = AddApplicantPersonCommand.COMMAND_WORD + NAME_DESC_AMY + PHONE_DESC_AMY
+ + EMAIL_DESC_AMY;
+ Applicant expectedPerson = new PersonBuilder(AMY).withTags().build_applicant();
+ ModelManager expectedModel = new ModelManager();
+ expectedModel.addPerson(expectedPerson);
+ assertCommandFailure(addApplicantCommand, CommandException.class, expectedMessage, expectedModel);
+ }
+
+ private void assertInterviewerCommandFailureForExceptionFromStorage(IOException e, String expectedMessage) {
Path prefPath = temporaryFolder.resolve("ExceptionUserPrefs.json");
// Inject LogicManager with an AddressBookStorage that throws the IOException e when saving
@@ -165,11 +209,11 @@ public void saveAddressBook(ReadOnlyAddressBook addressBook, Path filePath)
logic = new LogicManager(model, storage);
// Triggers the saveAddressBook method by executing an 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 addInterviewerCommand = AddInterviewerPersonCommand.COMMAND_WORD + NAME_DESC_CUBE + PHONE_DESC_CUBE
+ + EMAIL_DESC_CUBE;
+ Interviewer expectedPerson = new PersonBuilder(CUBE).withTags().build_interviewer();
ModelManager expectedModel = new ModelManager();
expectedModel.addPerson(expectedPerson);
- assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel);
+ assertCommandFailure(addInterviewerCommand, CommandException.class, expectedMessage, expectedModel);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/AddApplicantCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddApplicantCommandIntegrationTest.java
new file mode 100644
index 00000000000..f261329b9a0
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddApplicantCommandIntegrationTest.java
@@ -0,0 +1,48 @@
+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.logic.Messages;
+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 AddApplicantCommand}.
+ */
+public class AddApplicantCommandIntegrationTest {
+
+ 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 AddApplicantPersonCommand(validPerson), model,
+ String.format(AddApplicantPersonCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
+ expectedModel);
+ }
+
+ @Test
+ public void execute_duplicatePerson_throwsCommandException() {
+ Person personInList = model.getAddressBook().getPersonList().get(0);
+ assertCommandFailure(new AddApplicantPersonCommand(personInList), model,
+ AddApplicantPersonCommand.MESSAGE_DUPLICATE_PERSON);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/AddApplicantCommandTest.java b/src/test/java/seedu/address/logic/commands/AddApplicantCommandTest.java
new file mode 100644
index 00000000000..506ca23c32e
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddApplicantCommandTest.java
@@ -0,0 +1,236 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalPersons.ALICE;
+
+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.Messages;
+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.interview.Interview;
+import seedu.address.model.person.Person;
+import seedu.address.testutil.PersonBuilder;
+
+public class AddApplicantCommandTest {
+
+ @Test
+ public void constructor_nullPerson_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new AddApplicantPersonCommand(null));
+ }
+
+ @Test
+ public void execute_personAcceptedByModel_addSuccessful() throws Exception {
+ ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded();
+ Person validPerson = new PersonBuilder().build();
+
+ CommandResult commandResult = new AddApplicantPersonCommand(validPerson).execute(modelStub);
+
+ assertEquals(String.format(AddApplicantPersonCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
+ commandResult.getFeedbackToUser());
+ assertEquals(Arrays.asList(validPerson), modelStub.personsAdded);
+ }
+
+ @Test
+ public void execute_duplicatePerson_throwsCommandException() {
+ Person validPerson = new PersonBuilder().build();
+ AddPersonCommand addPersonCommand = new AddApplicantPersonCommand(validPerson);
+ ModelStub modelStub = new ModelStubWithPerson(validPerson);
+
+ assertThrows(CommandException.class,
+ AddApplicantPersonCommand.MESSAGE_DUPLICATE_PERSON, () -> addPersonCommand.execute(modelStub)
+ );
+ }
+
+ @Test
+ public void equals() {
+ Person alice = new PersonBuilder().withName("Alice").build();
+ Person bob = new PersonBuilder().withName("Bob").build();
+ AddPersonCommand addAliceCommand = new AddApplicantPersonCommand(alice);
+ AddPersonCommand addBobCommand = new AddApplicantPersonCommand(bob);
+
+ // same object -> returns true
+ assertTrue(addAliceCommand.equals(addAliceCommand));
+
+ // same values -> returns true
+ AddPersonCommand addAliceCommandCopy = new AddApplicantPersonCommand(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));
+ }
+
+ @Test
+ public void toStringMethod() {
+ AddPersonCommand addPersonCommand = new AddApplicantPersonCommand(ALICE);
+ String expected = AddApplicantPersonCommand.class.getCanonicalName() + "{toAdd=" + ALICE + "}";
+ assertEquals(expected, addPersonCommand.toString());
+ }
+
+ /**
+ * A default model stub that have all 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 void sortInterview() {
+ 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 addInterview(Interview interview) {
+ 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 boolean hasInterview(Interview interview) {
+ 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 deleteInterview(Interview 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.");
+ }
+
+ @Override
+ public void updateFilteredInterviewList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredInterviewList() {
+ 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/AddApplicantStatusCommandTest.java b/src/test/java/seedu/address/logic/commands/AddApplicantStatusCommandTest.java
new file mode 100644
index 00000000000..5fbbaa2ec27
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddApplicantStatusCommandTest.java
@@ -0,0 +1,99 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_REMARK_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_REMARK_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+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.ApplicantStatus;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.Remark;
+import seedu.address.testutil.PersonBuilder;
+
+public class AddApplicantStatusCommandTest {
+ private static final String APPLICANT_STATUS_STUB = "resume review";
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_addStatusUnfilteredList_success() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person editedPerson = new PersonBuilder(firstPerson).withStatus(APPLICANT_STATUS_STUB).build_applicant();
+ AddApplicantStatusCommand addApplicantStatusCommand = new AddApplicantStatusCommand(firstPerson.getPhone(),
+ new ApplicantStatus(editedPerson.getCurrentStatus()));
+ String expectedMessage = String.format(AddApplicantStatusCommand.MESSAGE_ADD_STATUS_SUCCESS, editedPerson);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(firstPerson, editedPerson);
+
+ assertCommandSuccess(addApplicantStatusCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_filteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person editedPerson = new PersonBuilder(model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()))
+ .withStatus(APPLICANT_STATUS_STUB).build_applicant();
+
+ AddApplicantStatusCommand addApplicantStatusCommand = new AddApplicantStatusCommand(firstPerson.getPhone(),
+ new ApplicantStatus(editedPerson.getCurrentStatus()));
+ String expectedMessage = String.format(AddApplicantStatusCommand.MESSAGE_ADD_STATUS_SUCCESS, editedPerson);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(firstPerson, editedPerson);
+
+ assertCommandSuccess(addApplicantStatusCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidPersonPhoneUnfilteredList_failure() {
+ AddApplicantStatusCommand addApplicantStatusCommand = new AddApplicantStatusCommand(new Phone("111"),
+ new ApplicantStatus(APPLICANT_STATUS_STUB));
+
+ assertCommandFailure(addApplicantStatusCommand, model, Messages.MESSAGE_INCORRECT_APPLICANT_PHONE_NUMBER);
+ }
+
+ @Test
+ public void equals() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ final AddApplicantStatusCommand standardCommand = new AddApplicantStatusCommand(firstPerson.getPhone(),
+ new ApplicantStatus(APPLICANT_STATUS_STUB));
+
+ // same values -> returns true
+ AddApplicantStatusCommand commandWithSameValues = new AddApplicantStatusCommand(firstPerson.getPhone(),
+ new ApplicantStatus(APPLICANT_STATUS_STUB));
+ assertEquals(standardCommand, commandWithSameValues);
+
+ // same object -> returns true
+ assertEquals(standardCommand, standardCommand);
+
+ // null -> returns false
+ assertNotEquals(null, standardCommand);
+
+ // different types -> returns false
+ assertNotEquals(standardCommand, new ClearCommand());
+
+ // different index -> returns false
+ assertNotEquals(standardCommand, new RemarkCommand(INDEX_SECOND_PERSON,
+ new Remark(VALID_REMARK_AMY)));
+
+ // different remark -> returns false
+ assertNotEquals(standardCommand, new RemarkCommand(INDEX_FIRST_PERSON,
+ new Remark(VALID_REMARK_BOB)));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/AddInterviewCommandIntergrationTest.java b/src/test/java/seedu/address/logic/commands/AddInterviewCommandIntergrationTest.java
new file mode 100644
index 00000000000..564b4b9d864
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddInterviewCommandIntergrationTest.java
@@ -0,0 +1,50 @@
+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.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.interview.Interview;
+import seedu.address.testutil.InterviewBuilder;
+
+
+public class AddInterviewCommandIntergrationTest {
+ private Model model;
+
+ @BeforeEach
+ public void setUp() {
+ model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ }
+
+ @Test
+ public void execute_newInterview_success() {
+ Interview validInterview = new InterviewBuilder().buildInterview();
+
+ Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.addInterview(validInterview);
+
+ assertCommandSuccess(new AddInterviewCommand(validInterview.getDescription(),
+ validInterview.getApplicant().getPhone(), validInterview.getInterviewer().getPhone(),
+ validInterview.getDate(), validInterview.getStartTime(), validInterview.getEndTime()), model,
+ String.format(AddInterviewCommand.MESSAGE_SUCCESS, "\n" + Messages.formatInterview(validInterview)),
+ expectedModel);
+ }
+
+ @Test
+ public void execute_duplicateInterview_throwsCommandException() {
+ Interview validInterview = new InterviewBuilder().buildInterview();
+ model.addInterview(validInterview);
+ Interview interviewInList = model.getAddressBook().getInterviewList().get(0);
+ assertCommandFailure(new AddInterviewCommand(interviewInList.getDescription(),
+ interviewInList.getApplicant().getPhone(), interviewInList.getInterviewer().getPhone(),
+ interviewInList.getDate(), interviewInList.getStartTime(), interviewInList.getEndTime()), model,
+ AddInterviewCommand.MESSAGE_DUPLICATE_INTERVIEW);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/AddInterviewCommandTest.java b/src/test/java/seedu/address/logic/commands/AddInterviewCommandTest.java
new file mode 100644
index 00000000000..8299133698e
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AddInterviewCommandTest.java
@@ -0,0 +1,57 @@
+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 java.time.LocalDate;
+import java.time.LocalTime;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.Interviewer;
+import seedu.address.testutil.PersonBuilder;
+
+public class AddInterviewCommandTest {
+ @Test
+ public void equals() {
+ Applicant head = new PersonBuilder().withName("head").withPhone("12345678")
+ .withEmail("head@cube.com").withTags("Applicant").build_applicant();
+ Interviewer cube = new PersonBuilder().withName("cube").withPhone("87654321")
+ .withEmail("cube@head.com").withTags("Interviewer").withStatus("free").build_interviewer();
+ AddInterviewCommand addInterviewCommand = new AddInterviewCommand("technical", head.getPhone(),
+ cube.getPhone(), LocalDate.of(2022, 11, 11),
+ LocalTime.of(10, 00), LocalTime.of(11, 00));
+
+ // same object -> returns true
+ assertTrue(addInterviewCommand.equals(addInterviewCommand));
+
+ // same values -> returns true
+ AddInterviewCommand addInterviewCommandCopy = new AddInterviewCommand("technical", head.getPhone(),
+ cube.getPhone(), LocalDate.of(2022, 11, 11),
+ LocalTime.of(10, 00), LocalTime.of(11, 00));
+ assertTrue(addInterviewCommand.equals(addInterviewCommandCopy));
+
+ // different types -> returns false
+ assertFalse(addInterviewCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(addInterviewCommand.equals(null));
+
+ }
+
+ @Test
+ public void toStringMethod() {
+ Applicant head = new PersonBuilder().withName("head").withPhone("12345678")
+ .withEmail("head@cube.com").withTags("Applicant").build_applicant();
+ Interviewer cube = new PersonBuilder().withName("cube").withPhone("87654321")
+ .withEmail("cube@head.com").withTags("Interviewer").withStatus("free").build_interviewer();
+ AddInterviewCommand addInterviewCommand = new AddInterviewCommand("tech", head.getPhone(),
+ cube.getPhone(), LocalDate.of(2022, 11, 11),
+ LocalTime.of(10, 00), LocalTime.of(11, 00));
+ String expected = "Description: tech" + " applicant: 12345678" + " interviewer: 87654321"
+ + " date: 2022-11-11" + " start: 10:00" + " end: 11:00";
+ assertEquals(expected, addInterviewCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java b/src/test/java/seedu/address/logic/commands/AddInterviewerCommandIntegrationTest.java
similarity index 76%
rename from src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
rename to src/test/java/seedu/address/logic/commands/AddInterviewerCommandIntegrationTest.java
index 162a0c86031..6db9209afa5 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandIntegrationTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddInterviewerCommandIntegrationTest.java
@@ -17,7 +17,7 @@
/**
* Contains integration tests (interaction with the Model) for {@code AddCommand}.
*/
-public class AddCommandIntegrationTest {
+public class AddInterviewerCommandIntegrationTest {
private Model model;
@@ -33,16 +33,16 @@ public void execute_newPerson_success() {
Model expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
expectedModel.addPerson(validPerson);
- assertCommandSuccess(new AddCommand(validPerson), model,
- String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
+ assertCommandSuccess(new AddInterviewerPersonCommand(validPerson), model,
+ String.format(AddInterviewerPersonCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
expectedModel);
}
@Test
public void execute_duplicatePerson_throwsCommandException() {
Person personInList = model.getAddressBook().getPersonList().get(0);
- assertCommandFailure(new AddCommand(personInList), model,
- AddCommand.MESSAGE_DUPLICATE_PERSON);
+ assertCommandFailure(new AddInterviewerPersonCommand(personInList), model,
+ AddInterviewerPersonCommand.MESSAGE_DUPLICATE_PERSON);
}
}
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddInterviewerCommandTest.java
similarity index 73%
rename from src/test/java/seedu/address/logic/commands/AddCommandTest.java
rename to src/test/java/seedu/address/logic/commands/AddInterviewerCommandTest.java
index 90e8253f48e..5982b22923a 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddInterviewerCommandTest.java
@@ -22,14 +22,15 @@
import seedu.address.model.Model;
import seedu.address.model.ReadOnlyAddressBook;
import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.Person;
import seedu.address.testutil.PersonBuilder;
-public class AddCommandTest {
+public class AddInterviewerCommandTest {
@Test
public void constructor_nullPerson_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> new AddCommand(null));
+ assertThrows(NullPointerException.class, () -> new AddInterviewerPersonCommand(null));
}
@Test
@@ -37,9 +38,9 @@ public void execute_personAcceptedByModel_addSuccessful() throws Exception {
ModelStubAcceptingPersonAdded modelStub = new ModelStubAcceptingPersonAdded();
Person validPerson = new PersonBuilder().build();
- CommandResult commandResult = new AddCommand(validPerson).execute(modelStub);
+ CommandResult commandResult = new AddInterviewerPersonCommand(validPerson).execute(modelStub);
- assertEquals(String.format(AddCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
+ assertEquals(String.format(AddInterviewerPersonCommand.MESSAGE_SUCCESS, Messages.format(validPerson)),
commandResult.getFeedbackToUser());
assertEquals(Arrays.asList(validPerson), modelStub.personsAdded);
}
@@ -47,24 +48,25 @@ public void execute_personAcceptedByModel_addSuccessful() throws Exception {
@Test
public void execute_duplicatePerson_throwsCommandException() {
Person validPerson = new PersonBuilder().build();
- AddCommand addCommand = new AddCommand(validPerson);
+ AddInterviewerPersonCommand addCommand = new AddInterviewerPersonCommand(validPerson);
ModelStub modelStub = new ModelStubWithPerson(validPerson);
- assertThrows(CommandException.class, AddCommand.MESSAGE_DUPLICATE_PERSON, () -> addCommand.execute(modelStub));
+ assertThrows(CommandException.class, AddInterviewerPersonCommand.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);
+ AddInterviewerPersonCommand addAliceCommand = new AddInterviewerPersonCommand(alice);
+ AddPersonCommand addBobCommand = new AddInterviewerPersonCommand(bob);
// same object -> returns true
assertTrue(addAliceCommand.equals(addAliceCommand));
// same values -> returns true
- AddCommand addAliceCommandCopy = new AddCommand(alice);
+ AddPersonCommand addAliceCommandCopy = new AddInterviewerPersonCommand(alice);
assertTrue(addAliceCommand.equals(addAliceCommandCopy));
// different types -> returns false
@@ -79,9 +81,9 @@ public void equals() {
@Test
public void toStringMethod() {
- AddCommand addCommand = new AddCommand(ALICE);
- String expected = AddCommand.class.getCanonicalName() + "{toAdd=" + ALICE + "}";
- assertEquals(expected, addCommand.toString());
+ AddPersonCommand addPersonCommand = new AddInterviewerPersonCommand(ALICE);
+ String expected = AddInterviewerPersonCommand.class.getCanonicalName() + "{toAdd=" + ALICE + "}";
+ assertEquals(expected, addPersonCommand.toString());
}
/**
@@ -93,6 +95,10 @@ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
throw new AssertionError("This method should not be called.");
}
+ @Override
+ public void sortInterview() {
+ throw new AssertionError("This method should not be called.");
+ }
@Override
public ReadOnlyUserPrefs getUserPrefs() {
throw new AssertionError("This method should not be called.");
@@ -123,6 +129,11 @@ public void addPerson(Person person) {
throw new AssertionError("This method should not be called.");
}
+ @Override
+ public void addInterview(Interview interview) {
+ throw new AssertionError("This method should not be called.");
+ }
+
@Override
public void setAddressBook(ReadOnlyAddressBook newData) {
throw new AssertionError("This method should not be called.");
@@ -138,11 +149,21 @@ public boolean hasPerson(Person person) {
throw new AssertionError("This method should not be called.");
}
+ @Override
+ public boolean hasInterview(Interview interview) {
+ 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 deleteInterview(Interview 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.");
@@ -157,6 +178,17 @@ public ObservableList getFilteredPersonList() {
public void updateFilteredPersonList(Predicate predicate) {
throw new AssertionError("This method should not be called.");
}
+
+ @Override
+ public ObservableList getFilteredInterviewList() {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void updateFilteredInterviewList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
}
/**
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 643a1d08069..3b92af9eaea 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -2,10 +2,15 @@
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_APPLICANT;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_DESCRIPTION;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_END_TIME;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEWER;
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_START_TIME;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.testutil.Assert.assertThrows;
@@ -17,9 +22,9 @@
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
import seedu.address.model.Model;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.model.person.Person;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
/**
* Contains helper methods for testing commands.
@@ -28,46 +33,62 @@ 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_NAME_CUBE = "cube";
+ public static final String VALID_NAME_HEAD = "head";
public static final String VALID_PHONE_AMY = "11111111";
public static final String VALID_PHONE_BOB = "22222222";
+ public static final String VALID_PHONE_HEAD = "12345678";
+ public static final String VALID_PHONE_CUBE = "87654321";
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_EMAIL_CUBE = "cube@head.com";
+ public static final String VALID_REMARK_AMY = "Like skiing.";
+ public static final String VALID_REMARK_BOB = "Favourite pastime: Eating";
+ public static final String VALID_TAG_APPLIANT = "applicant";
+ public static final String VALID_TAG_INTERVIEWER = "interviewer";
public static final String VALID_TAG_HUSBAND = "husband";
public static final String VALID_TAG_FRIEND = "friend";
+ public static final String VALID_INTERVIEW_DESCRIPTION = "technical";
+ public static final String INTERVIEW_DESCRIPTION = " " + PREFIX_DESCRIPTION + VALID_INTERVIEW_DESCRIPTION;
+ public static final String VALID_INTERVIEW_DATE = "2022-11-11";
+ public static final String INVALID_INTERVIEW_DATE = "2022-11-11222";
+ public static final String INTERVIEW_DATE_INVALID = " " + PREFIX_DATE + INVALID_INTERVIEW_DATE;
+ public static final String INTERVIEW_DATE = " " + PREFIX_DATE + VALID_INTERVIEW_DATE;
+ public static final String VALID_INTERVIEW_START_TIME = "10:00";
+ public static final String INVALID_INTERVIEW_START_TIME = "10:00000";
+ public static final String INTERVIEW_START_TIME_INVALID = " " + PREFIX_START_TIME + INVALID_INTERVIEW_START_TIME;
+ public static final String START_TIME = " " + PREFIX_START_TIME + VALID_INTERVIEW_START_TIME;
+ public static final String VALID_INTERVIEW_END_TIME = "11:00";
+ public static final String INVALID_INTERVIEW_END_TIME = "11:000000";
+ public static final String INTERVIEW_END_TIME_INVALID = " " + PREFIX_END_TIME + INVALID_INTERVIEW_END_TIME;
+ public static final String END_TIME = " " + PREFIX_END_TIME + VALID_INTERVIEW_END_TIME;
+ public static final String APPLICANT_PHONE = " " + PREFIX_APPLICANT + VALID_PHONE_HEAD;
+ public static final String INVALID_APPLICANT_PHONE = " " + PREFIX_APPLICANT + "99999999aaa";
+ public static final String INVALID_INTERVIEWER_PHONE = " " + PREFIX_INTERVIEWER + "99999999aaa";
+ public static final String INTERVIEWER_PHONE = " " + PREFIX_INTERVIEWER + VALID_PHONE_CUBE;
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 NAME_DESC_CUBE = " " + PREFIX_NAME + VALID_NAME_CUBE;
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 PHONE_DESC_CUBE = " " + PREFIX_PHONE + VALID_PHONE_CUBE;
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 EMAIL_DESC_CUBE = " " + PREFIX_EMAIL + VALID_EMAIL_CUBE;
+ public static final String TAG_DESC_APPLICANT = " " + PREFIX_TAG + VALID_TAG_APPLIANT;
+ public static final String TAG_DESC_INTERVIEWER = " " + PREFIX_TAG + VALID_TAG_INTERVIEWER;
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
@@ -106,10 +127,12 @@ public static void assertCommandFailure(Command command, Model actualModel, Stri
// only do so by copying its components.
AddressBook expectedAddressBook = new AddressBook(actualModel.getAddressBook());
List expectedFilteredList = new ArrayList<>(actualModel.getFilteredPersonList());
+ List expectedInterview = new ArrayList<>(actualModel.getAddressBook().getInterviewList());
assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel));
assertEquals(expectedAddressBook, actualModel.getAddressBook());
assertEquals(expectedFilteredList, actualModel.getFilteredPersonList());
+ assertEquals(expectedInterview, actualModel.getAddressBook().getInterviewList());
}
/**
* Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the
diff --git a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
index b6f332eabca..7944e052a6b 100644
--- a/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/DeleteCommandTest.java
@@ -10,14 +10,29 @@
import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
+
import org.junit.jupiter.api.Test;
-import seedu.address.commons.core.index.Index;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.commons.core.GuiSettings;
import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+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.interview.Interview;
import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.testutil.InterviewBuilder;
+import seedu.address.testutil.PersonBuilder;
/**
* Contains integration tests (interaction with the Model) and unit tests for
@@ -28,9 +43,9 @@ public class DeleteCommandTest {
private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
@Test
- public void execute_validIndexUnfilteredList_success() {
+ public void execute_validPhoneUnfilteredList_success() {
Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON);
+ DeleteCommand deleteCommand = new DeleteCommand(personToDelete.getPhone());
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
Messages.format(personToDelete));
@@ -42,19 +57,19 @@ public void execute_validIndexUnfilteredList_success() {
}
@Test
- public void execute_invalidIndexUnfilteredList_throwsCommandException() {
- Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
- DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
+ public void execute_invalidPhoneUnfilteredList_throwsCommandException() {
+ Phone invalidPhone = new Phone("999");
+ DeleteCommand deleteCommand = new DeleteCommand(invalidPhone);
- assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ assertCommandFailure(deleteCommand, model, Messages.MESSAGE_PERSON_NOT_IN_LIST);
}
@Test
- public void execute_validIndexFilteredList_success() {
+ public void execute_validPhoneFilteredList_success() {
showPersonAtIndex(model, INDEX_FIRST_PERSON);
Person personToDelete = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
- DeleteCommand deleteCommand = new DeleteCommand(INDEX_FIRST_PERSON);
+ DeleteCommand deleteCommand = new DeleteCommand(personToDelete.getPhone());
String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS,
Messages.format(personToDelete));
@@ -67,28 +82,227 @@ public void execute_validIndexFilteredList_success() {
}
@Test
- public void execute_invalidIndexFilteredList_throwsCommandException() {
+ public void execute_invalidPhoneFilteredList_throwsCommandException() {
+ Person targetSecondPerson = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+ Phone invalidPhone = targetSecondPerson.getPhone();
+
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(invalidPhone);
+
+ assertCommandFailure(deleteCommand, model, Messages.MESSAGE_PERSON_NOT_IN_LIST);
+ }
+
+ @Test
+ public void execute_personHasNoScheduledInterviews_deletesSuccessfully() throws CommandException {
+ Person personToDelete = new PersonBuilder().build();
+ ModelStub modelStub = new ModelStubWithPersonAndNoInterviews(personToDelete);
+
+ Phone targetPhone = personToDelete.getPhone();
+ DeleteCommand deleteCommand = new DeleteCommand(targetPhone);
+
+ String expectedMessage = String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, Messages
+ .format(personToDelete));
+
+ CommandResult commandResult = deleteCommand.execute(modelStub);
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_personHasScheduledInterviews_throwsCommandException() {
+ Person personWithScheduledInterviews = new PersonBuilder().build_applicant();
+ Interview scheduledInterview = new InterviewBuilder().withApplicant(personWithScheduledInterviews).build();
+ ModelStub modelStub = new ModelStubWithPersonAndScheduledInterview(personWithScheduledInterviews,
+ scheduledInterview);
+
+ Phone targetPhone = personWithScheduledInterviews.getPhone();
+ DeleteCommand deleteCommand = new DeleteCommand(targetPhone);
- DeleteCommand deleteCommand = new DeleteCommand(outOfBoundIndex);
+ assertCommandFailure(deleteCommand, modelStub, Messages.MESSAGE_PERSON_HAS_INTERVIEW);
+ }
+
+
+ private class ModelStubWithPersonAndNoInterviews extends ModelStub {
+ private List persons = new ArrayList<>();
+
+ ModelStubWithPersonAndNoInterviews(Person person) {
+ this.persons.add(person);
+ }
+
+ @Override
+ public boolean hasPerson(Person person) {
+ return this.persons.contains(person);
+ }
+
+ @Override
+ public void deletePerson(Person target) {
+ // This method can remain empty as its execution is what's being tested
+ }
+
+ @Override
+ public ObservableList getFilteredInterviewList() {
+ // Simulate no interviews in the model
+ return FXCollections.observableArrayList();
+ }
- assertCommandFailure(deleteCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ @Override
+ public ObservableList getFilteredPersonList() {
+ // Return the list containing the scheduled interview
+ return FXCollections.observableArrayList(persons);
+ }
}
+ private class ModelStubWithPersonAndScheduledInterview extends ModelStub {
+
+ private List interviews = new ArrayList<>();
+ private List persons = new ArrayList<>();
+
+
+ ModelStubWithPersonAndScheduledInterview(Person person, Interview interview) {
+ this.persons.add(person);
+ this.interviews.add(interview);
+ }
+
+ @Override
+ public boolean hasPerson(Person person) {
+ return this.persons.contains(person);
+ }
+
+ @Override
+ public ObservableList getFilteredInterviewList() {
+ // Return the list containing the scheduled interview
+ return FXCollections.observableArrayList(interviews);
+ }
+
+ @Override
+ public ObservableList getFilteredPersonList() {
+ // Return the list containing the scheduled interview
+ return FXCollections.observableArrayList(persons);
+ }
+
+ @Override
+ public ReadOnlyAddressBook getAddressBook() {
+ return new AddressBook();
+ }
+ }
+
+
+
+
+
+ private class ModelStub implements Model {
+ @Override
+ public void setUserPrefs(ReadOnlyUserPrefs userPrefs) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void sortInterview() {
+ 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 addInterview(Interview interview) {
+ 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 boolean hasInterview(Interview interview) {
+ 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 deleteInterview(Interview 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.");
+ }
+
+ @Override
+ public void updateFilteredInterviewList(Predicate predicate) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public ObservableList getFilteredInterviewList() {
+ throw new AssertionError("This method should not be called.");
+ }
+ }
+
+
@Test
public void equals() {
- DeleteCommand deleteFirstCommand = new DeleteCommand(INDEX_FIRST_PERSON);
- DeleteCommand deleteSecondCommand = new DeleteCommand(INDEX_SECOND_PERSON);
+ DeleteCommand deleteFirstCommand = new DeleteCommand(new Phone("94351253"));
+ DeleteCommand deleteSecondCommand = new DeleteCommand(new Phone("88888888"));
// same object -> returns true
assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
// same values -> returns true
- DeleteCommand deleteFirstCommandCopy = new DeleteCommand(INDEX_FIRST_PERSON);
+ DeleteCommand deleteFirstCommandCopy = new DeleteCommand(new Phone("94351253"));
assertTrue(deleteFirstCommand.equals(deleteFirstCommandCopy));
// different types -> returns false
@@ -103,9 +317,9 @@ public void equals() {
@Test
public void toStringMethod() {
- Index targetIndex = Index.fromOneBased(1);
- DeleteCommand deleteCommand = new DeleteCommand(targetIndex);
- String expected = DeleteCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ Phone targetPhone = new Phone("94351253");
+ DeleteCommand deleteCommand = new DeleteCommand(targetPhone);
+ String expected = DeleteCommand.class.getCanonicalName() + "{targetPhone=" + targetPhone + "}";
assertEquals(expected, deleteCommand.toString());
}
diff --git a/src/test/java/seedu/address/logic/commands/DeleteInterviewCommandTest.java b/src/test/java/seedu/address/logic/commands/DeleteInterviewCommandTest.java
new file mode 100644
index 00000000000..fe184ef6154
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/DeleteInterviewCommandTest.java
@@ -0,0 +1,78 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.DeleteInterviewCommand.MESSAGE_DELETE_INTERVIEW_SUCCESS;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_INTERVIEW;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_INTERVIEW;
+import static seedu.address.testutil.TypicalInterviews.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.interview.Interview;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * {@code DeleteCommand}.
+ */
+public class DeleteInterviewCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validIndexUnfilteredList_success() {
+ Interview interviewToDelete = model.getFilteredInterviewList().get(INDEX_FIRST_INTERVIEW.getZeroBased());
+ DeleteInterviewCommand deleteInterviewCommand = new DeleteInterviewCommand(
+ INDEX_FIRST_INTERVIEW.getZeroBased());
+ String expectedMessage = String.format(MESSAGE_DELETE_INTERVIEW_SUCCESS,
+ Messages.formatInterview(interviewToDelete));
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.deleteInterview(interviewToDelete);
+ assertCommandSuccess(deleteInterviewCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ DeleteInterviewCommand deleteInterviewCommand = new DeleteInterviewCommand(-2);
+ assertCommandFailure(deleteInterviewCommand, model, Messages.MESSAGE_INTERVIEW_NOT_IN_LIST);
+ }
+
+
+ @Test
+ public void equals() {
+ DeleteInterviewCommand deleteFirstCommand = new DeleteInterviewCommand(INDEX_FIRST_INTERVIEW.getZeroBased());
+ DeleteInterviewCommand deleteSecondCommand = new DeleteInterviewCommand(INDEX_SECOND_INTERVIEW.getZeroBased());
+
+ // same object -> returns true
+ assertTrue(deleteFirstCommand.equals(deleteFirstCommand));
+
+ // same values -> returns true
+ DeleteInterviewCommand deleteFirstCommandCopy = new DeleteInterviewCommand(
+ INDEX_FIRST_INTERVIEW.getZeroBased());
+ 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));
+ }
+
+ @Test
+ public void toStringMethod() {
+ int targetInt = INDEX_FIRST_INTERVIEW.getZeroBased();
+ DeleteInterviewCommand deleteCommand = new DeleteInterviewCommand(targetInt);
+ String expected = DeleteInterviewCommand.class.getCanonicalName() + "{targetInt=" + targetInt + "}";
+ assertEquals(expected, deleteCommand.toString());
+ }
+}
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 469dd97daa7..00000000000
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ /dev/null
@@ -1,184 +0,0 @@
-package seedu.address.logic.commands;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_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.index.Index;
-import seedu.address.logic.Messages;
-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, Messages.format(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, Messages.format(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, Messages.format(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, Messages.format(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)));
- }
-
- @Test
- public void toStringMethod() {
- Index index = Index.fromOneBased(1);
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
- EditCommand editCommand = new EditCommand(index, editPersonDescriptor);
- String expected = EditCommand.class.getCanonicalName() + "{index=" + index + ", editPersonDescriptor="
- + editPersonDescriptor + "}";
- assertEquals(expected, editCommand.toString());
- }
-
-}
diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
deleted file mode 100644
index b17c1f3d5c2..00000000000
--- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package seedu.address.logic.commands;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-
-import org.junit.jupiter.api.Test;
-
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
-
-public class EditPersonDescriptorTest {
-
- @Test
- public void equals() {
- // same values -> returns true
- EditPersonDescriptor descriptorWithSameValues = new EditPersonDescriptor(DESC_AMY);
- assertTrue(DESC_AMY.equals(descriptorWithSameValues));
-
- // same object -> returns true
- assertTrue(DESC_AMY.equals(DESC_AMY));
-
- // null -> returns false
- assertFalse(DESC_AMY.equals(null));
-
- // different types -> returns false
- assertFalse(DESC_AMY.equals(5));
-
- // different values -> returns false
- assertFalse(DESC_AMY.equals(DESC_BOB));
-
- // different name -> returns false
- EditPersonDescriptor editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withName(VALID_NAME_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different phone -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withPhone(VALID_PHONE_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different email -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withEmail(VALID_EMAIL_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different address -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build();
- assertFalse(DESC_AMY.equals(editedAmy));
-
- // different tags -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withTags(VALID_TAG_HUSBAND).build();
- assertFalse(DESC_AMY.equals(editedAmy));
- }
-
- @Test
- public void toStringMethod() {
- EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
- String expected = EditPersonDescriptor.class.getCanonicalName() + "{name="
- + editPersonDescriptor.getName().orElse(null) + ", phone="
- + editPersonDescriptor.getPhone().orElse(null) + ", email="
- + editPersonDescriptor.getEmail().orElse(null) + ", address="
- + editPersonDescriptor.getAddress().orElse(null) + ", tags="
- + editPersonDescriptor.getTags().orElse(null) + "}";
- assertEquals(expected, editPersonDescriptor.toString());
- }
-}
diff --git a/src/test/java/seedu/address/logic/commands/FilterInterviewsByDateCommandTest.java b/src/test/java/seedu/address/logic/commands/FilterInterviewsByDateCommandTest.java
new file mode 100644
index 00000000000..3eabf8271c2
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FilterInterviewsByDateCommandTest.java
@@ -0,0 +1,63 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.time.LocalDate;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+
+public class FilterInterviewsByDateCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private LocalDate firstTargetDate = LocalDate.parse("2001-03-22");
+ private LocalDate secondTargetDate = LocalDate.parse("2001-04-17");
+
+ @Test
+ public void equals() {
+ FilterInterviewsByDateCommand filterInterviewsByDateFirstCommand =
+ new FilterInterviewsByDateCommand(firstTargetDate);
+ FilterInterviewsByDateCommand filterInterviewsByDateSecondCommand =
+ new FilterInterviewsByDateCommand(secondTargetDate);
+
+ // same object -> returns true
+ assertEquals(filterInterviewsByDateFirstCommand, filterInterviewsByDateFirstCommand);
+
+ // same values -> returns true
+ FilterInterviewsByDateCommand filterInterviewsByDateFirstCommandCopy =
+ new FilterInterviewsByDateCommand(firstTargetDate);
+ assertEquals(filterInterviewsByDateFirstCommand, filterInterviewsByDateFirstCommandCopy);
+
+ // different types -> returns false
+ assertNotEquals(1, filterInterviewsByDateFirstCommand);
+
+ // null -> returns false
+ assertNotEquals(null, filterInterviewsByDateFirstCommand);
+
+ // different person -> returns false
+ assertNotEquals(filterInterviewsByDateFirstCommand, filterInterviewsByDateSecondCommand);
+ }
+
+ @Test
+ public void execute_nonExistentDate_noInterviewFound() {
+ String expectedMessage = "No interviews found on " + firstTargetDate;
+ FilterInterviewsByDateCommand command = new FilterInterviewsByDateCommand(firstTargetDate);
+ command.execute(model);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void toStringMethod() {
+ FilterInterviewsByDateCommand filterInterviewsByDateCommand =
+ new FilterInterviewsByDateCommand(firstTargetDate);
+ String expected = FilterInterviewsByDateCommand.class.getCanonicalName() + "{targetDate=" + firstTargetDate
+ + "}";
+ assertEquals(expected, filterInterviewsByDateCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/FilterPersonsByStatusCommandTest.java b/src/test/java/seedu/address/logic/commands/FilterPersonsByStatusCommandTest.java
new file mode 100644
index 00000000000..cec413521ba
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FilterPersonsByStatusCommandTest.java
@@ -0,0 +1,69 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+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.ApplicantStatus;
+import seedu.address.model.person.enums.ApplicantState;
+
+public class FilterPersonsByStatusCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private ApplicantStatus firstTargetStatus = new ApplicantStatus(ApplicantState.STAGE_ONE.toString());
+ private ApplicantStatus secondTargetStatus = new ApplicantStatus(ApplicantState.OUTCOME_THREE.toString());
+
+ @Test
+ public void equals() {
+ FilterPersonsByStatusCommand filterPersonsByStatusFirstCommand =
+ new FilterPersonsByStatusCommand(firstTargetStatus);
+ FilterPersonsByStatusCommand filterInterviewsByDateSecondCommand =
+ new FilterPersonsByStatusCommand(secondTargetStatus);
+
+ // same object -> returns true
+ assertEquals(filterPersonsByStatusFirstCommand, filterPersonsByStatusFirstCommand);
+
+ // same values -> returns true
+ FilterPersonsByStatusCommand filterPersonsByStatusCommandCopy =
+ new FilterPersonsByStatusCommand(firstTargetStatus);
+ assertEquals(filterPersonsByStatusFirstCommand, filterPersonsByStatusCommandCopy);
+
+ // different types -> returns false
+ assertNotEquals(1, filterPersonsByStatusFirstCommand);
+
+ // null -> returns false
+ assertNotEquals(null, filterPersonsByStatusFirstCommand);
+
+ // different person -> returns false
+ assertNotEquals(filterPersonsByStatusFirstCommand, filterInterviewsByDateSecondCommand);
+ }
+
+ @Test
+ public void execute_existingStatus_success() {
+ String expectedMessage = CommandResult.class.getCanonicalName() + "{feedbackToUser="
+ + "Listed all persons with status: " + firstTargetStatus + ", showHelp=false, exit=false}";
+ FilterPersonsByStatusCommand command = new FilterPersonsByStatusCommand(firstTargetStatus);
+ assertEquals(expectedMessage, command.execute(model).toString());
+ }
+
+ @Test
+ public void execute_nonExistentStatus_noPersonFound() {
+ String expectedMessage = CommandResult.class.getCanonicalName() + "{feedbackToUser="
+ + "No persons found with status: " + secondTargetStatus.toString() + ", showHelp=false, exit=false}";
+ FilterPersonsByStatusCommand command = new FilterPersonsByStatusCommand(secondTargetStatus);
+ assertEquals(expectedMessage, command.execute(model).toString());
+ }
+
+ @Test
+ public void toStringMethod() {
+ FilterPersonsByStatusCommand filterPersonsByStatusCommand =
+ new FilterPersonsByStatusCommand(firstTargetStatus);
+ String expected = FilterPersonsByStatusCommand.class.getCanonicalName() + "{targetStatus=" + firstTargetStatus
+ + "}";
+ assertEquals(expected, filterPersonsByStatusCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/FindEmailCommandTest.java b/src/test/java/seedu/address/logic/commands/FindEmailCommandTest.java
new file mode 100644
index 00000000000..b6b638de21c
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FindEmailCommandTest.java
@@ -0,0 +1,92 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.Messages.MESSAGE_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.EmailContainsKeywordsPredicate;
+
+/**
+ * Contains integration tests (interaction with the Model) for {@code FindCommand}.
+ */
+public class FindEmailCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void equals() {
+ EmailContainsKeywordsPredicate firstPredicate =
+ new EmailContainsKeywordsPredicate(Collections.singletonList("first"));
+ EmailContainsKeywordsPredicate secondPredicate =
+ new EmailContainsKeywordsPredicate(Collections.singletonList("second"));
+
+ FindEmailCommand findFirstCommand = new FindEmailCommand(firstPredicate);
+ FindEmailCommand findSecondCommand = new FindEmailCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(findFirstCommand.equals(findFirstCommand));
+
+ // same values -> returns true
+ FindCommand findFirstCommandCopy = new FindEmailCommand(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);
+ EmailContainsKeywordsPredicate predicate = preparePredicate(" ");
+ FindEmailCommand command = new FindEmailCommand(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);
+ EmailContainsKeywordsPredicate predicate = preparePredicate("lydia@example.com "
+ + "werner@example.com heinz@example.com");
+ FindEmailCommand command = new FindEmailCommand(predicate);
+ expectedModel.updateFilteredPersonList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel);
+ assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
+ }
+
+ @Test
+ public void toStringMethod() {
+ EmailContainsKeywordsPredicate predicate = new EmailContainsKeywordsPredicate(Arrays.asList("keyword"));
+ FindEmailCommand findCommand = new FindEmailCommand(predicate);
+ String expected = FindEmailCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, findCommand.toString());
+ }
+
+ /**
+ * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}.
+ */
+ private EmailContainsKeywordsPredicate preparePredicate(String userInput) {
+ return new EmailContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+")));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/FindCommandTest.java b/src/test/java/seedu/address/logic/commands/FindNameCommandTest.java
similarity index 85%
rename from src/test/java/seedu/address/logic/commands/FindCommandTest.java
rename to src/test/java/seedu/address/logic/commands/FindNameCommandTest.java
index b8b7dbba91a..20c29734e42 100644
--- a/src/test/java/seedu/address/logic/commands/FindCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/FindNameCommandTest.java
@@ -23,7 +23,7 @@
/**
* Contains integration tests (interaction with the Model) for {@code FindCommand}.
*/
-public class FindCommandTest {
+public class FindNameCommandTest {
private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
@@ -34,14 +34,14 @@ public void equals() {
NameContainsKeywordsPredicate secondPredicate =
new NameContainsKeywordsPredicate(Collections.singletonList("second"));
- FindCommand findFirstCommand = new FindCommand(firstPredicate);
- FindCommand findSecondCommand = new FindCommand(secondPredicate);
+ FindNameCommand findFirstCommand = new FindNameCommand(firstPredicate);
+ FindNameCommand findSecondCommand = new FindNameCommand(secondPredicate);
// same object -> returns true
assertTrue(findFirstCommand.equals(findFirstCommand));
// same values -> returns true
- FindCommand findFirstCommandCopy = new FindCommand(firstPredicate);
+ FindCommand findFirstCommandCopy = new FindNameCommand(firstPredicate);
assertTrue(findFirstCommand.equals(findFirstCommandCopy));
// different types -> returns false
@@ -58,7 +58,7 @@ public void equals() {
public void execute_zeroKeywords_noPersonFound() {
String expectedMessage = String.format(MESSAGE_PERSONS_LISTED_OVERVIEW, 0);
NameContainsKeywordsPredicate predicate = preparePredicate(" ");
- FindCommand command = new FindCommand(predicate);
+ FindCommand command = new FindNameCommand(predicate);
expectedModel.updateFilteredPersonList(predicate);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
assertEquals(Collections.emptyList(), model.getFilteredPersonList());
@@ -68,7 +68,7 @@ public void execute_zeroKeywords_noPersonFound() {
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);
+ FindCommand command = new FindNameCommand(predicate);
expectedModel.updateFilteredPersonList(predicate);
assertCommandSuccess(command, model, expectedMessage, expectedModel);
assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
@@ -77,8 +77,8 @@ public void execute_multipleKeywords_multiplePersonsFound() {
@Test
public void toStringMethod() {
NameContainsKeywordsPredicate predicate = new NameContainsKeywordsPredicate(Arrays.asList("keyword"));
- FindCommand findCommand = new FindCommand(predicate);
- String expected = FindCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ FindCommand findCommand = new FindNameCommand(predicate);
+ String expected = FindNameCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
assertEquals(expected, findCommand.toString());
}
diff --git a/src/test/java/seedu/address/logic/commands/FindPhoneCommandTest.java b/src/test/java/seedu/address/logic/commands/FindPhoneCommandTest.java
new file mode 100644
index 00000000000..e2807ec6066
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/FindPhoneCommandTest.java
@@ -0,0 +1,91 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.Messages.MESSAGE_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.PhoneContainsKeywordsPredicate;
+
+/**
+ * Contains integration tests (interaction with the Model) for {@code FindCommand}.
+ */
+public class FindPhoneCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private Model expectedModel = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void equals() {
+ PhoneContainsKeywordsPredicate firstPredicate =
+ new PhoneContainsKeywordsPredicate(Collections.singletonList("123"));
+ PhoneContainsKeywordsPredicate secondPredicate =
+ new PhoneContainsKeywordsPredicate(Collections.singletonList("456"));
+
+ FindPhoneCommand findFirstCommand = new FindPhoneCommand(firstPredicate);
+ FindPhoneCommand findSecondCommand = new FindPhoneCommand(secondPredicate);
+
+ // same object -> returns true
+ assertTrue(findFirstCommand.equals(findFirstCommand));
+
+ // same values -> returns true
+ FindPhoneCommand findFirstCommandCopy = new FindPhoneCommand(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);
+ PhoneContainsKeywordsPredicate predicate = preparePredicate(" ");
+ FindCommand command = new FindPhoneCommand(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);
+ PhoneContainsKeywordsPredicate predicate = preparePredicate("9482427 9482224 95352563");
+ FindCommand command = new FindPhoneCommand(predicate);
+ expectedModel.updateFilteredPersonList(predicate);
+ assertCommandSuccess(command, model, expectedMessage, expectedModel);
+ assertEquals(Arrays.asList(CARL, ELLE, FIONA), model.getFilteredPersonList());
+ }
+
+ @Test
+ public void toStringMethod() {
+ PhoneContainsKeywordsPredicate predicate = new PhoneContainsKeywordsPredicate(Arrays.asList("123"));
+ FindCommand findCommand = new FindPhoneCommand(predicate);
+ String expected = FindPhoneCommand.class.getCanonicalName() + "{predicate=" + predicate + "}";
+ assertEquals(expected, findCommand.toString());
+ }
+
+ /**
+ * Parses {@code userInput} into a {@code NameContainsKeywordsPredicate}.
+ */
+ private PhoneContainsKeywordsPredicate preparePredicate(String userInput) {
+ return new PhoneContainsKeywordsPredicate(Arrays.asList(userInput.split("\\s+")));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ListInterviewsCommandTest.java b/src/test/java/seedu/address/logic/commands/ListInterviewsCommandTest.java
new file mode 100644
index 00000000000..9c29132eab9
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ListInterviewsCommandTest.java
@@ -0,0 +1,31 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.testutil.TypicalInterviews.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 ListInterviewsCommand.
+ */
+public class ListInterviewsCommandTest {
+
+ 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 ListInterviewsCommand(), model, ListInterviewsCommand.MESSAGE_SUCCESS, expectedModel);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/RemarkCommandTest.java b/src/test/java/seedu/address/logic/commands/RemarkCommandTest.java
new file mode 100644
index 00000000000..88ef8b1b8c4
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/RemarkCommandTest.java
@@ -0,0 +1,135 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_REMARK_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_REMARK_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandSuccess;
+import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import static seedu.address.testutil.TypicalIndexes.INDEX_SECOND_PERSON;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.Messages;
+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.model.person.Remark;
+import seedu.address.testutil.PersonBuilder;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for RemarkCommand.
+ */
+public class RemarkCommandTest {
+
+ private static final String REMARK_STUB = "Some remark";
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_addRemarkUnfilteredList_success() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person editedPerson = new PersonBuilder(firstPerson).withRemark(REMARK_STUB).build_applicant();
+
+ RemarkCommand remarkCommand = new RemarkCommand(INDEX_FIRST_PERSON, new Remark(editedPerson.getRemark().value));
+
+ String expectedMessage = String.format(RemarkCommand.MESSAGE_ADD_REMARK_SUCCESS, editedPerson);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(firstPerson, editedPerson);
+
+ assertCommandSuccess(remarkCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_deleteRemarkUnfilteredList_success() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person editedPerson = new PersonBuilder(firstPerson).withRemark("").build_applicant();
+
+ RemarkCommand remarkCommand = new RemarkCommand(INDEX_FIRST_PERSON,
+ new Remark(editedPerson.getRemark().toString()));
+
+ String expectedMessage = String.format(RemarkCommand.MESSAGE_DELETE_REMARK_SUCCESS, editedPerson);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(firstPerson, editedPerson);
+
+ assertCommandSuccess(remarkCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_filteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person editedPerson = new PersonBuilder(model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()))
+ .withRemark(REMARK_STUB).build_applicant();
+
+ RemarkCommand remarkCommand = new RemarkCommand(INDEX_FIRST_PERSON, new Remark(editedPerson.getRemark().value));
+
+ String expectedMessage = String.format(RemarkCommand.MESSAGE_ADD_REMARK_SUCCESS, editedPerson);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(firstPerson, editedPerson);
+
+ assertCommandSuccess(remarkCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidPersonIndexUnfilteredList_failure() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ RemarkCommand remarkCommand = new RemarkCommand(outOfBoundIndex, new Remark(VALID_REMARK_BOB));
+
+ assertCommandFailure(remarkCommand, 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());
+
+ RemarkCommand remarkCommand = new RemarkCommand(outOfBoundIndex, new Remark(VALID_REMARK_BOB));
+
+ assertCommandFailure(remarkCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ final RemarkCommand standardCommand = new RemarkCommand(INDEX_FIRST_PERSON,
+ new Remark(VALID_REMARK_AMY));
+
+ // same values -> returns true
+ RemarkCommand commandWithSameValues = new RemarkCommand(INDEX_FIRST_PERSON,
+ new Remark(VALID_REMARK_AMY));
+ assertTrue(standardCommand.equals(commandWithSameValues));
+
+ // same object -> returns true
+ assertTrue(standardCommand.equals(standardCommand));
+
+ // null -> returns false
+ assertFalse(standardCommand.equals(null));
+
+ // different types -> returns false
+ assertFalse(standardCommand.equals(new ClearCommand()));
+
+ // different index -> returns false
+ assertFalse(standardCommand.equals(new RemarkCommand(INDEX_SECOND_PERSON,
+ new Remark(VALID_REMARK_AMY))));
+
+ // different remark -> returns false
+ assertFalse(standardCommand.equals(new RemarkCommand(INDEX_FIRST_PERSON,
+ new Remark(VALID_REMARK_BOB))));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ViewOverallStatisticsCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewOverallStatisticsCommandTest.java
new file mode 100644
index 00000000000..db010660147
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ViewOverallStatisticsCommandTest.java
@@ -0,0 +1,62 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+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.Test;
+
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+
+public class ViewOverallStatisticsCommandTest {
+ private final Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_viewOverallStatisticsUnfilteredList_success() {
+ ViewOverallStatisticsCommand viewOverallStatisticsCommand = new ViewOverallStatisticsCommand();
+
+ String expectedMessage = "Number of applicants: 8"
+ + " (Resume review: 7, Pending interview: 1, Completed interview: 0, Waiting list: 0, Accepted: 0"
+ + ", Rejected: 0)" + "\nNumber of interviewers: "
+ + "1" + " (Free: 0, Busy: 1)" + "\nNumber of interviews: 0";
+
+ assertCommandSuccess(viewOverallStatisticsCommand, model, expectedMessage, model);
+ }
+
+ @Test
+ public void execute_viewOverallStatisticsFilteredList_success() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+ ViewOverallStatisticsCommand viewOverallStatisticsCommand = new ViewOverallStatisticsCommand();
+
+ String expectedMessage = "Number of applicants: 8"
+ + " (Resume review: 7, Pending interview: 1, Completed interview: 0, Waiting list: 0, Accepted: 0"
+ + ", Rejected: 0)" + "\nNumber of interviewers: "
+ + "1" + " (Free: 0, Busy: 1)" + "\nNumber of interviews: 0";
+
+ assertCommandSuccess(viewOverallStatisticsCommand, model, expectedMessage, model);
+ }
+
+ @Test
+ public void equals() {
+ final ViewOverallStatisticsCommand standardCommand = new ViewOverallStatisticsCommand();
+ final ViewOverallStatisticsCommand standardCommandCopy = new ViewOverallStatisticsCommand();
+
+ // same object -> return true
+ assertEquals(standardCommand, standardCommand);
+
+ // same type -> return true
+ assertEquals(standardCommand, standardCommandCopy);
+
+ // null -> return false
+ assertNotEquals(standardCommand, null);
+
+ // different types -> return false
+ assertNotEquals(standardCommand, new ClearCommand());
+
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddApplicantCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddApplicantCommandParserTest.java
new file mode 100644
index 00000000000..106921eeaad
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddApplicantCommandParserTest.java
@@ -0,0 +1,108 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+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_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.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_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.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.AddApplicantPersonCommand;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Phone;
+import seedu.address.testutil.PersonBuilder;
+
+public class AddApplicantCommandParserTest {
+ private AddApplicantCommandParser parser = new AddApplicantCommandParser();
+
+ @Test
+ public void parse_applicantAllFieldsPresent_success() {
+ Applicant expectedPerson = new PersonBuilder(BOB).build_applicant();
+
+ // whitespace only preamble
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB,
+ new AddApplicantPersonCommand(expectedPerson));
+
+ // all fields present
+ assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB,
+ new AddApplicantPersonCommand(expectedPerson));
+
+ // adding tags when creating person does nothing
+ Applicant expectedPersonMultipleTags = new PersonBuilder(BOB).build_applicant();
+ assertParseSuccess(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddApplicantPersonCommand(expectedPersonMultipleTags));
+ }
+
+ @Test
+ public void parse_applicantOptionalFieldsMissing_success() {
+ // zero tags
+ Applicant expectedPerson = new PersonBuilder(AMY).withTags().build_applicant();
+ assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY,
+ new AddApplicantPersonCommand(expectedPerson));
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND, AddApplicantPersonCommand.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() {
+ // invalid name
+ assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS);
+
+ // invalid phone
+ assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS);
+
+ // invalid email
+ assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS);
+
+
+ // two invalid values, only first invalid value reported
+ assertParseFailure(parser, INVALID_NAME_DESC + INVALID_PHONE_DESC + EMAIL_DESC_BOB,
+ Name.MESSAGE_CONSTRAINTS);
+
+ // non-empty preamble
+ assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ String.format(MESSAGE_INVALID_COMMAND, AddApplicantPersonCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddApplicantStatusCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddApplicantStatusCommandParserTest.java
new file mode 100644
index 00000000000..05a685d4d8d
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddApplicantStatusCommandParserTest.java
@@ -0,0 +1,57 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS;
+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.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.AddApplicantStatusCommand;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.ApplicantStatus;
+import seedu.address.model.person.Person;
+
+public class AddApplicantStatusCommandParserTest {
+ private AddApplicantStatusCommandParser parser = new AddApplicantStatusCommandParser();
+ private final ApplicantStatus nonEmptyStatus = new ApplicantStatus("resume review");
+
+ @Test
+ public void parse_phoneSpecified_success() {
+ Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ String userInput = firstPerson.getPhone().value + " " + PREFIX_STATUS + nonEmptyStatus;
+ AddApplicantStatusCommand expectedCommand = new AddApplicantStatusCommand(firstPerson.getPhone(),
+ nonEmptyStatus);
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_incompleteFields_failure() {
+ String expectedMessage = String.format(Messages.MESSAGE_INVALID_COMMAND,
+ AddApplicantStatusCommand.MESSAGE_USAGE);
+
+ // no parameters
+ assertParseFailure(parser, AddApplicantStatusCommand.COMMAND_WORD, expectedMessage);
+
+ // no phone
+ assertParseFailure(parser, AddApplicantStatusCommand.COMMAND_WORD + " " + nonEmptyStatus,
+ expectedMessage);
+ }
+
+ @Test
+ public void parse_invalidValues_failure() {
+ // invalid status
+ assertParseFailure(parser, VALID_PHONE_AMY + " " + PREFIX_STATUS + "nonsense",
+ ApplicantStatus.MESSAGE_CONSTRAINTS);
+
+ // invalid phone
+ assertParseFailure(parser, "1" + " " + PREFIX_STATUS + "resume review",
+ String.format(Messages.MESSAGE_INVALID_COMMAND, AddApplicantStatusCommand.MESSAGE_USAGE));
+ }
+}
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 5bc11d3cdaa..00000000000
--- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
+++ /dev/null
@@ -1,196 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.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.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.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.Messages;
-import seedu.address.logic.commands.AddCommand;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Person;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-import seedu.address.testutil.PersonBuilder;
-
-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 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_repeatedNonTagValue_failure() {
- String validExpectedPersonString = NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_FRIEND;
-
- // multiple names
- assertParseFailure(parser, NAME_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // multiple phones
- assertParseFailure(parser, PHONE_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // multiple emails
- assertParseFailure(parser, EMAIL_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // multiple addresses
- assertParseFailure(parser, ADDRESS_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
-
- // multiple fields repeated
- assertParseFailure(parser,
- validExpectedPersonString + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY + ADDRESS_DESC_AMY
- + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE));
-
- // invalid value followed by valid value
-
- // invalid name
- assertParseFailure(parser, INVALID_NAME_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // invalid email
- assertParseFailure(parser, INVALID_EMAIL_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // invalid phone
- assertParseFailure(parser, INVALID_PHONE_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid address
- assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
-
- // valid value followed by invalid value
-
- // invalid name
- assertParseFailure(parser, validExpectedPersonString + INVALID_NAME_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME));
-
- // invalid email
- assertParseFailure(parser, validExpectedPersonString + INVALID_EMAIL_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
-
- // invalid phone
- assertParseFailure(parser, validExpectedPersonString + INVALID_PHONE_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid address
- assertParseFailure(parser, validExpectedPersonString + INVALID_ADDRESS_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
- }
-
- @Test
- public void parse_optionalFieldsMissing_success() {
- // zero tags
- Person expectedPerson = new PersonBuilder(AMY).withTags().build();
- assertParseSuccess(parser, NAME_DESC_AMY + PHONE_DESC_AMY + EMAIL_DESC_AMY + ADDRESS_DESC_AMY,
- new AddCommand(expectedPerson));
- }
-
- @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/AddInterviewCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddInterviewCommandParserTest.java
new file mode 100644
index 00000000000..eb0be1c5f72
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddInterviewCommandParserTest.java
@@ -0,0 +1,105 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+import static seedu.address.logic.Messages.MESSAGE_NOT_DATE;
+import static seedu.address.logic.Messages.MESSAGE_NOT_TIME;
+import static seedu.address.logic.commands.CommandTestUtil.APPLICANT_PHONE;
+import static seedu.address.logic.commands.CommandTestUtil.END_TIME;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEWER_PHONE;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEW_DATE;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEW_DATE_INVALID;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEW_DESCRIPTION;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEW_END_TIME_INVALID;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEW_START_TIME_INVALID;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_APPLICANT_PHONE;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_INTERVIEWER_PHONE;
+import static seedu.address.logic.commands.CommandTestUtil.PREAMBLE_WHITESPACE;
+import static seedu.address.logic.commands.CommandTestUtil.START_TIME;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.AddInterviewCommand;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.Interviewer;
+import seedu.address.testutil.PersonBuilder;
+
+public class AddInterviewCommandParserTest {
+ private AddInterviewCommandParser parser = new AddInterviewCommandParser();
+ @Test
+ public void parse_interviewAllFieldsPresent_success() {
+ Applicant head = new PersonBuilder().withName("head").withPhone("12345678")
+ .withEmail("head@cube.com").withTags("Applicant").build_applicant();
+ Interviewer cube = new PersonBuilder().withName("cube").withPhone("87654321")
+ .withEmail("cube@head.com").withTags("Interviewer").withStatus("free").build_interviewer();
+ AddInterviewCommand addInterviewCommand = new AddInterviewCommand("technical", head.getPhone(),
+ cube.getPhone(), LocalDate.of(2022, 11, 11),
+ LocalTime.of(10, 00), LocalTime.of(11, 00));
+
+ // whitespace only preamble
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + INTERVIEW_DESCRIPTION + INTERVIEW_DATE + START_TIME
+ + END_TIME + APPLICANT_PHONE + INTERVIEWER_PHONE, addInterviewCommand);
+
+ // all fields present
+ assertParseSuccess(parser, INTERVIEW_DESCRIPTION + INTERVIEW_DATE + START_TIME + END_TIME
+ + APPLICANT_PHONE + INTERVIEWER_PHONE, addInterviewCommand);
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND, AddInterviewCommand.MESSAGE_USAGE);
+
+ // missing description prefix
+ assertParseFailure(parser, INTERVIEW_DATE + START_TIME
+ + END_TIME + APPLICANT_PHONE + INTERVIEWER_PHONE, expectedMessage);
+
+ // missing date prefix
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION + START_TIME
+ + END_TIME + APPLICANT_PHONE + INTERVIEWER_PHONE, expectedMessage);
+
+ // missing start time prefix
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION + INTERVIEW_DATE
+ + END_TIME + APPLICANT_PHONE + INTERVIEWER_PHONE, expectedMessage);
+
+ // missing end time prefix
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION + INTERVIEW_DATE
+ + START_TIME + APPLICANT_PHONE + INTERVIEWER_PHONE, expectedMessage);
+
+ // missing applicant phone number prefix
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION
+ + INTERVIEW_DATE + START_TIME + END_TIME + INTERVIEWER_PHONE, expectedMessage);
+
+ // missing interviewer phone number prefix
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION
+ + INTERVIEW_DATE + START_TIME + END_TIME + APPLICANT_PHONE, expectedMessage);
+ }
+
+ @Test
+ public void parse_invalidValue_failure() {
+ // invalid date
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION + INTERVIEW_DATE_INVALID + START_TIME + END_TIME
+ + APPLICANT_PHONE + INTERVIEWER_PHONE, MESSAGE_NOT_DATE);
+
+ // invalid start time
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION + INTERVIEW_DATE
+ + INTERVIEW_START_TIME_INVALID + END_TIME + APPLICANT_PHONE + INTERVIEWER_PHONE, MESSAGE_NOT_TIME);
+
+ // invalid start time
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION + INTERVIEW_DATE
+ + START_TIME + INTERVIEW_END_TIME_INVALID + APPLICANT_PHONE + INTERVIEWER_PHONE, MESSAGE_NOT_TIME);
+
+ // invalid applicant phone number
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION + INTERVIEW_DATE + START_TIME + END_TIME
+ + INVALID_APPLICANT_PHONE + INTERVIEWER_PHONE,
+ "Phone numbers should only contain numbers, and it should be at least 3 digits long");
+
+ // invalid interviewer phone number
+ assertParseFailure(parser, INTERVIEW_DESCRIPTION + INTERVIEW_DATE + START_TIME
+ + END_TIME + APPLICANT_PHONE + INVALID_INTERVIEWER_PHONE,
+ "Phone numbers should only contain numbers, and it should be at least 3 digits long");
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddInterviewerCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddInterviewerCommandParserTest.java
new file mode 100644
index 00000000000..24222cdcc4f
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/AddInterviewerCommandParserTest.java
@@ -0,0 +1,107 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_CUBE;
+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.NAME_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_CUBE;
+import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_CUBE;
+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_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.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalPersons.CUBE;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.AddInterviewerPersonCommand;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Interviewer;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Phone;
+import seedu.address.testutil.PersonBuilder;
+
+public class AddInterviewerCommandParserTest {
+ private AddInterviewerCommandParser parser = new AddInterviewerCommandParser();
+
+ @Test
+ public void parse_interviewerAllFieldsPresent_success() {
+ Interviewer expectedPerson = new PersonBuilder(CUBE).build_interviewer();
+
+ // whitespace only preamble
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + NAME_DESC_CUBE + PHONE_DESC_CUBE + EMAIL_DESC_CUBE
+ + TAG_DESC_FRIEND, new AddInterviewerPersonCommand(expectedPerson));
+
+ // all fields present
+ assertParseSuccess(parser, NAME_DESC_CUBE + PHONE_DESC_CUBE + EMAIL_DESC_CUBE
+ + TAG_DESC_FRIEND, new AddInterviewerPersonCommand(expectedPerson));
+
+ // adding tags when creating person does nothing
+ Interviewer expectedPersonMultipleTags = new PersonBuilder(CUBE).build_interviewer();
+ assertParseSuccess(parser, NAME_DESC_CUBE + PHONE_DESC_CUBE + EMAIL_DESC_CUBE
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, new AddInterviewerPersonCommand(expectedPersonMultipleTags));
+ }
+
+ @Test
+ public void parse_interviewerOptionalFieldsMissing_success() {
+ // zero tags
+ Interviewer expectedPerson = new PersonBuilder(CUBE).withTags().build_interviewer();
+ assertParseSuccess(parser, NAME_DESC_CUBE + PHONE_DESC_CUBE + EMAIL_DESC_CUBE,
+ new AddInterviewerPersonCommand(expectedPerson));
+ }
+
+ @Test
+ public void parse_compulsoryFieldMissing_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND,
+ AddInterviewerPersonCommand.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() {
+ // invalid name
+ assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS);
+
+ // invalid phone
+ assertParseFailure(parser, NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Phone.MESSAGE_CONSTRAINTS);
+
+ // invalid email
+ assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS);
+
+ // two invalid values, only first invalid value reported
+ assertParseFailure(parser, INVALID_NAME_DESC + INVALID_PHONE_DESC + EMAIL_DESC_BOB,
+ Name.MESSAGE_CONSTRAINTS);
+
+ // non-empty preamble
+ assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ String.format(MESSAGE_INVALID_COMMAND, AddInterviewerPersonCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 5a1ab3dbc0c..3bbea2ea344 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -2,30 +2,45 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
import static seedu.address.logic.Messages.MESSAGE_UNKNOWN_COMMAND;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+import java.time.LocalDate;
+import java.time.LocalTime;
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.AddApplicantPersonCommand;
+import seedu.address.logic.commands.AddApplicantStatusCommand;
+import seedu.address.logic.commands.AddInterviewCommand;
+import seedu.address.logic.commands.AddInterviewerPersonCommand;
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.FilterPersonsByStatusCommand;
+import seedu.address.logic.commands.FindEmailCommand;
+import seedu.address.logic.commands.FindNameCommand;
+import seedu.address.logic.commands.FindPhoneCommand;
import seedu.address.logic.commands.HelpCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.RemarkCommand;
+import seedu.address.logic.commands.ViewOverallStatisticsCommand;
import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.ApplicantStatus;
+import seedu.address.model.person.EmailContainsKeywordsPredicate;
+import seedu.address.model.person.Interviewer;
import seedu.address.model.person.NameContainsKeywordsPredicate;
-import seedu.address.model.person.Person;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.PhoneContainsKeywordsPredicate;
+import seedu.address.model.person.Remark;
+import seedu.address.model.person.enums.ApplicantState;
import seedu.address.testutil.PersonBuilder;
import seedu.address.testutil.PersonUtil;
@@ -34,10 +49,33 @@ 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);
+ public void parseCommand_add_applicant() throws Exception {
+ Applicant person = new PersonBuilder().build_applicant();
+ AddApplicantPersonCommand command = (AddApplicantPersonCommand) parser.parseCommand(
+ PersonUtil.getAddApplicantCommand(person));
+ assertEquals(new AddApplicantPersonCommand(person), command);
+ }
+
+ @Test
+ public void parseCommand_add_interview() throws Exception {
+ Applicant head = new PersonBuilder().withName("head").withPhone("12345678")
+ .withEmail("head@cube.com").withTags("Applicant").build_applicant();
+ Interviewer cube = new PersonBuilder().withName("cube").withPhone("87654321")
+ .withEmail("cube@head.com").withTags("Interviewer").withStatus("free").build_interviewer();
+ AddInterviewCommand addInterviewCommand = new AddInterviewCommand("technical", head.getPhone(),
+ cube.getPhone(), LocalDate.of(2022, 11, 11),
+ LocalTime.of(10, 00), LocalTime.of(11, 00));
+ AddInterviewCommand command = (AddInterviewCommand) parser.parseCommand(
+ "add_interview desc/technical date/2022-11-11 st/10:00 et/11:00 a/12345678 i/87654321");
+ assertEquals(addInterviewCommand, command);
+ }
+
+ @Test
+ public void parseCommand_add_interviewer() throws Exception {
+ Interviewer person = new PersonBuilder().withStatus("free").build_interviewer();
+ AddInterviewerPersonCommand command = (AddInterviewerPersonCommand) parser.parseCommand(
+ PersonUtil.getAddInterviewerCommand(person));
+ assertEquals(new AddInterviewerPersonCommand(person), command);
}
@Test
@@ -49,18 +87,10 @@ public void parseCommand_clear() throws Exception {
@Test
public void parseCommand_delete() throws Exception {
DeleteCommand command = (DeleteCommand) parser.parseCommand(
- DeleteCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
- assertEquals(new DeleteCommand(INDEX_FIRST_PERSON), command);
+ DeleteCommand.COMMAND_WORD + " 87652533");
+ assertEquals(new DeleteCommand(new Phone("87652533")), 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 {
@@ -69,11 +99,54 @@ public void parseCommand_exit() throws Exception {
}
@Test
- public void parseCommand_find() throws Exception {
+ public void parseCommand_addApplicantStatus() throws Exception {
+ String phone = "98362254";
+ String status = ApplicantState.STAGE_ONE.toString();
+ AddApplicantStatusCommand command = (AddApplicantStatusCommand) parser.parseCommand(
+ AddApplicantStatusCommand.COMMAND_WORD + " " + phone + " s/" + status);
+ assertEquals(command, new AddApplicantStatusCommand(new Phone(phone), new ApplicantStatus(status)));
+ }
+
+ @Test
+ public void parseCommand_find_email() throws Exception {
+ List keywords = Arrays.asList("foo@email.com", "bar@email.com", "baz@email.com");
+ FindEmailCommand command = (FindEmailCommand) parser.parseCommand(
+ FindEmailCommand.COMMAND_WORD + " "
+ + keywords.stream().collect(Collectors.joining(" ")));
+ assertEquals(new FindEmailCommand(new EmailContainsKeywordsPredicate(keywords)), command);
+ }
+
+ @Test
+ public void parseCommand_find_name() 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);
+ FindNameCommand command = (FindNameCommand) parser.parseCommand(
+ FindNameCommand.COMMAND_WORD + " "
+ + keywords.stream().collect(Collectors.joining(" ")));
+ assertEquals(new FindNameCommand(new NameContainsKeywordsPredicate(keywords)), command);
+ }
+
+ @Test
+ public void parseCommand_find_phone() throws Exception {
+ List keywords = Arrays.asList("123", "456", "789");
+ FindPhoneCommand command = (FindPhoneCommand) parser.parseCommand(
+ FindPhoneCommand.COMMAND_WORD + " "
+ + keywords.stream().collect(Collectors.joining(" ")));
+ assertEquals(new FindPhoneCommand(new PhoneContainsKeywordsPredicate(keywords)), command);
+ }
+
+ @Test
+ public void parseCommand_filterPersonsByStatus() throws Exception {
+ FilterPersonsByStatusCommand command = (FilterPersonsByStatusCommand) parser.parseCommand(
+ FilterPersonsByStatusCommand.COMMAND_WORD + " resume review");
+ assertEquals(command, new FilterPersonsByStatusCommand(
+ new ApplicantStatus(ApplicantState.STAGE_ONE.toString())));
+ }
+
+ @Test
+ public void parseCommand_viewOverallStatistics() throws Exception {
+ ViewOverallStatisticsCommand command = (ViewOverallStatisticsCommand) parser.parseCommand(
+ ViewOverallStatisticsCommand.COMMAND_WORD);
+ assertEquals(new ViewOverallStatisticsCommand(), command);
}
@Test
@@ -88,10 +161,18 @@ public void parseCommand_list() throws Exception {
assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand);
}
+ @Test
+ public void parseCommand_remark() throws Exception {
+ final Remark remark = new Remark("Some remark.");
+ RemarkCommand command = (RemarkCommand) parser.parseCommand(RemarkCommand.COMMAND_WORD + " "
+ + INDEX_FIRST_PERSON.getOneBased() + " " + PREFIX_REMARK + remark.value);
+ assertEquals(new RemarkCommand(INDEX_FIRST_PERSON, remark), command);
+ }
+
@Test
public void parseCommand_unrecognisedInput_throwsParseException() {
- assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND_FORMAT, HelpCommand.MESSAGE_USAGE), ()
- -> parser.parseCommand(""));
+ assertThrows(ParseException.class, String.format(MESSAGE_INVALID_COMMAND, HelpCommand.MESSAGE_USAGE), ()
+ -> parser.parseCommand(""));
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java b/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java
index 9bf1ccf1cef..23921bcd71f 100644
--- a/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java
+++ b/src/test/java/seedu/address/logic/parser/CommandParserTestUtil.java
@@ -20,6 +20,7 @@ public static void assertParseSuccess(Parser extends Command> parser, String u
Command command = parser.parse(userInput);
assertEquals(expectedCommand, command);
} catch (ParseException pe) {
+ System.out.println(pe.getMessage());
throw new IllegalArgumentException("Invalid userInput.", pe);
}
}
diff --git a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
index 6a40e14a649..3f6acbce143 100644
--- a/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/DeleteCommandParserTest.java
@@ -1,13 +1,13 @@
package seedu.address.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
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;
+import seedu.address.model.person.Phone;
/**
* As we are only doing white-box testing, our test cases do not cover path variations
@@ -22,11 +22,11 @@ public class DeleteCommandParserTest {
@Test
public void parse_validArgs_returnsDeleteCommand() {
- assertParseSuccess(parser, "1", new DeleteCommand(INDEX_FIRST_PERSON));
+ assertParseSuccess(parser, "87652533", new DeleteCommand(new Phone("87652533")));
}
@Test
public void parse_invalidArgs_throwsParseException() {
- assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, DeleteCommand.MESSAGE_USAGE));
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND, DeleteCommand.MESSAGE_USAGE));
}
}
diff --git a/src/test/java/seedu/address/logic/parser/DeleteInterviewCommandParserTest.java b/src/test/java/seedu/address/logic/parser/DeleteInterviewCommandParserTest.java
new file mode 100644
index 00000000000..3346696c536
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/DeleteInterviewCommandParserTest.java
@@ -0,0 +1,32 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.DeleteInterviewCommand;
+
+/**
+ * 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 DeleteInterviewCommandParserTest {
+
+ private DeleteInterviewCommandParser parser = new DeleteInterviewCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsDeleteCommand() {
+ assertParseSuccess(parser, "1", new DeleteInterviewCommand(0));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND,
+ DeleteInterviewCommand.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 cc7175172d4..00000000000
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package seedu.address.logic.parser;
-
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_ADDRESS_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_NAME_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_PHONE_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.INVALID_TAG_DESC;
-import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.TAG_DESC_HUSBAND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_AMY;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
-import static seedu.address.logic.parser.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.Messages;
-import seedu.address.logic.commands.EditCommand;
-import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.model.person.Address;
-import seedu.address.model.person.Email;
-import seedu.address.model.person.Name;
-import seedu.address.model.person.Phone;
-import seedu.address.model.tag.Tag;
-import seedu.address.testutil.EditPersonDescriptorBuilder;
-
-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);
-
- // 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_failure() {
- // More extensive testing of duplicate parameter detections is done in
- // AddCommandParserTest#parse_repeatedNonTagValue_failure()
-
- // valid followed by invalid
- Index targetIndex = INDEX_FIRST_PERSON;
- String userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + PHONE_DESC_BOB;
-
- assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // invalid followed by valid
- userInput = targetIndex.getOneBased() + PHONE_DESC_BOB + INVALID_PHONE_DESC;
-
- assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
-
- // mulltiple valid fields repeated
- userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY
- + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND
- + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND;
-
- assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
-
- // multiple invalid values
- userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC
- + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC;
-
- assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
- }
-
- @Test
- public void parse_resetTags_success() {
- Index targetIndex = INDEX_THIRD_PERSON;
- String userInput = targetIndex.getOneBased() + TAG_EMPTY;
-
- EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder().withTags().build();
- EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
-
- assertParseSuccess(parser, userInput, expectedCommand);
- }
-}
diff --git a/src/test/java/seedu/address/logic/parser/FilterPersonsByStatusCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FilterPersonsByStatusCommandParserTest.java
new file mode 100644
index 00000000000..c02f0e736b4
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FilterPersonsByStatusCommandParserTest.java
@@ -0,0 +1,39 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.FilterPersonsByStatusCommand;
+import seedu.address.model.person.ApplicantStatus;
+import seedu.address.model.person.Status;
+import seedu.address.model.person.enums.ApplicantState;
+
+public class FilterPersonsByStatusCommandParserTest {
+ private FilterPersonsByStatusCommandParser parser = new FilterPersonsByStatusCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND,
+ Status.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_invalidArg_throwsParseException() {
+ assertParseFailure(parser, "nonsense status", String.format(MESSAGE_INVALID_COMMAND,
+ Status.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_validArgs_returnsFilterPersonsByStatusCommand() {
+ // no leading and trailing whitespaces
+ FilterPersonsByStatusCommand expectedFilterPersonsByStatusCommand =
+ new FilterPersonsByStatusCommand(new ApplicantStatus(ApplicantState.STAGE_ONE.toString()));
+ assertParseSuccess(parser, ApplicantState.STAGE_ONE.toString(), expectedFilterPersonsByStatusCommand);
+
+ // varying leading and trailing whitespaces
+ assertParseSuccess(parser, " resume review ", expectedFilterPersonsByStatusCommand);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/FindEmailCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindEmailCommandParserTest.java
new file mode 100644
index 00000000000..d52f5a4a30e
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FindEmailCommandParserTest.java
@@ -0,0 +1,36 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+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.FindEmailCommand;
+import seedu.address.model.person.EmailContainsKeywordsPredicate;
+
+public class FindEmailCommandParserTest {
+
+ private FindEmailCommandParser parser = new FindEmailCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND,
+ FindEmailCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_validArgs_returnsFindEmailCommand() {
+ // no leading and trailing whitespaces
+ FindEmailCommand expectedFindEmailCommand =
+ new FindEmailCommand(new EmailContainsKeywordsPredicate(Arrays.asList(
+ "Alice@email.com", "Bob@email.com")));
+ assertParseSuccess(parser, "Alice@email.com Bob@email.com", expectedFindEmailCommand);
+
+ // multiple whitespaces between keywords
+ assertParseSuccess(parser, " \n Alice@email.com \n \t Bob@email.com \t", expectedFindEmailCommand);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindNameCommandParserTest.java
similarity index 56%
rename from src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
rename to src/test/java/seedu/address/logic/parser/FindNameCommandParserTest.java
index d92e64d12f9..03c07811910 100644
--- a/src/test/java/seedu/address/logic/parser/FindCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/FindNameCommandParserTest.java
@@ -1,6 +1,6 @@
package seedu.address.logic.parser;
-import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
@@ -8,27 +8,28 @@
import org.junit.jupiter.api.Test;
-import seedu.address.logic.commands.FindCommand;
+import seedu.address.logic.commands.FindNameCommand;
import seedu.address.model.person.NameContainsKeywordsPredicate;
-public class FindCommandParserTest {
+public class FindNameCommandParserTest {
- private FindCommandParser parser = new FindCommandParser();
+ private FindNameCommandParser parser = new FindNameCommandParser();
@Test
public void parse_emptyArg_throwsParseException() {
- assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND,
+ FindNameCommand.MESSAGE_USAGE));
}
@Test
- public void parse_validArgs_returnsFindCommand() {
+ public void parse_validArgs_returnsFindNameCommand() {
// no leading and trailing whitespaces
- FindCommand expectedFindCommand =
- new FindCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")));
- assertParseSuccess(parser, "Alice Bob", expectedFindCommand);
+ FindNameCommand expectedFindNameCommand =
+ new FindNameCommand(new NameContainsKeywordsPredicate(Arrays.asList("Alice", "Bob")));
+ assertParseSuccess(parser, "Alice Bob", expectedFindNameCommand);
// multiple whitespaces between keywords
- assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindCommand);
+ assertParseSuccess(parser, " \n Alice \n \t Bob \t", expectedFindNameCommand);
}
}
diff --git a/src/test/java/seedu/address/logic/parser/FindPhoneCommandParserTest.java b/src/test/java/seedu/address/logic/parser/FindPhoneCommandParserTest.java
new file mode 100644
index 00000000000..0f8e6e2d21b
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/FindPhoneCommandParserTest.java
@@ -0,0 +1,35 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+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.FindPhoneCommand;
+import seedu.address.model.person.PhoneContainsKeywordsPredicate;
+
+public class FindPhoneCommandParserTest {
+
+ private FindPhoneCommandParser parser = new FindPhoneCommandParser();
+
+ @Test
+ public void parse_emptyArg_throwsParseException() {
+ assertParseFailure(parser, " ", String.format(MESSAGE_INVALID_COMMAND,
+ FindPhoneCommand.MESSAGE_USAGE));
+ }
+
+ @Test
+ public void parse_validArgs_returnsFindPhoneCommand() {
+ // no leading and trailing whitespaces
+ FindPhoneCommand expectedFindPhoneCommand =
+ new FindPhoneCommand(new PhoneContainsKeywordsPredicate(Arrays.asList("123", "456")));
+ assertParseSuccess(parser, "123 456", expectedFindPhoneCommand);
+
+ // multiple whitespaces between keywords
+ assertParseSuccess(parser, " \n 123 \n \t 456 \t", expectedFindPhoneCommand);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
index 4256788b1a7..e798b069d83 100644
--- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
+++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
@@ -14,7 +14,6 @@
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;
@@ -23,13 +22,11 @@
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";
@@ -102,29 +99,6 @@ public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exc
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));
diff --git a/src/test/java/seedu/address/logic/parser/RemarkCommandParserTest.java b/src/test/java/seedu/address/logic/parser/RemarkCommandParserTest.java
new file mode 100644
index 00000000000..a7bbdd65cbf
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/RemarkCommandParserTest.java
@@ -0,0 +1,43 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_REMARK;
+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.commons.core.index.Index;
+import seedu.address.logic.commands.RemarkCommand;
+import seedu.address.model.person.Remark;
+
+public class RemarkCommandParserTest {
+ private RemarkCommandParser parser = new RemarkCommandParser();
+ private final Remark nonEmptyRemark = new Remark("Some remark.");
+
+ @Test
+ public void parse_indexSpecified_success() {
+ // have remark
+ Index targetIndex = INDEX_FIRST_PERSON;
+ String userInput = targetIndex.getOneBased() + " " + PREFIX_REMARK + nonEmptyRemark;
+ RemarkCommand expectedCommand = new RemarkCommand(INDEX_FIRST_PERSON, nonEmptyRemark);
+ assertParseSuccess(parser, userInput, expectedCommand);
+
+ // no remark
+ userInput = targetIndex.getOneBased() + " " + PREFIX_REMARK;
+ expectedCommand = new RemarkCommand(INDEX_FIRST_PERSON, new Remark(""));
+ assertParseSuccess(parser, userInput, expectedCommand);
+ }
+
+ @Test
+ public void parse_missingCompulsoryField_failure() {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND, RemarkCommand.MESSAGE_USAGE);
+
+ // no parameters
+ assertParseFailure(parser, RemarkCommand.COMMAND_WORD, expectedMessage);
+
+ // no index
+ assertParseFailure(parser, RemarkCommand.COMMAND_WORD + " " + nonEmptyRemark, expectedMessage);
+ }
+}
diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java
index 68c8c5ba4d5..59156a7d686 100644
--- a/src/test/java/seedu/address/model/AddressBookTest.java
+++ b/src/test/java/seedu/address/model/AddressBookTest.java
@@ -3,12 +3,13 @@
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.time.LocalDate;
+import java.time.LocalTime;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -18,8 +19,11 @@
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
+import seedu.address.model.interview.Interview;
+import seedu.address.model.interview.exceptions.InterviewNotFoundException;
import seedu.address.model.person.Person;
import seedu.address.model.person.exceptions.DuplicatePersonException;
+import seedu.address.testutil.InterviewBuilder;
import seedu.address.testutil.PersonBuilder;
public class AddressBookTest {
@@ -46,7 +50,7 @@ public void resetData_withValidReadOnlyAddressBook_replacesData() {
@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)
+ Person editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND)
.build();
List newPersons = Arrays.asList(ALICE, editedAlice);
AddressBookStub newData = new AddressBookStub(newPersons);
@@ -73,16 +77,86 @@ public void hasPerson_personInAddressBook_returnsTrue() {
@Test
public void hasPerson_personWithSameIdentityFieldsInAddressBook_returnsTrue() {
addressBook.addPerson(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ Person editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND)
.build();
assertTrue(addressBook.hasPerson(editedAlice));
}
+ @Test
+ public void hasPerson_personWithSamePhoneInAddressBook_returnsTrue() {
+ addressBook.addPerson(ALICE);
+ Person editedAlice = new PersonBuilder(ALICE).withEmail("alice@example.org").build();
+ assertTrue(addressBook.hasPerson(editedAlice));
+ }
+
+ @Test
+ public void hasPerson_personWithDifferentPhoneInAddressBook_returnsFalse() {
+ addressBook.addPerson(ALICE);
+ Person bob = new PersonBuilder().withPhone("11111111").build(); // Use a phone number not used by ALICE
+ assertFalse(addressBook.hasPerson(bob));
+ }
+
+
@Test
public void getPersonList_modifyList_throwsUnsupportedOperationException() {
assertThrows(UnsupportedOperationException.class, () -> addressBook.getPersonList().remove(0));
}
+ @Test
+ public void addInterview_nullInterview_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> addressBook.addInterview(null));
+ }
+
+ @Test
+ public void addInterview_uniqueInterview_success() {
+ Interview interview = new InterviewBuilder().buildInterview();
+ addressBook.addInterview(interview);
+ assertTrue(addressBook.hasInterview(interview));
+ }
+
+ @Test
+ public void removeInterview_interviewDoesNotExist_throwsInterviewNotFoundException() {
+ Interview interview = new InterviewBuilder().buildInterview();
+ assertThrows(InterviewNotFoundException.class, () -> addressBook.removeInterview(interview));
+ }
+
+ @Test
+ public void removeInterview_existingInterview_removesInterview() {
+ Interview interview = new InterviewBuilder().buildInterview();
+ addressBook.addInterview(interview);
+ addressBook.removeInterview(interview);
+ AddressBook expectedAddressBook = new AddressBook();
+ assertEquals(expectedAddressBook, addressBook);
+ }
+
+ @Test
+ public void setInterviews_nullList_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> addressBook.setInterviews(null));
+ }
+
+ @Test
+ public void setInterviews_list_replacesOwnListWithProvidedList() {
+ Interview interview = new InterviewBuilder().buildInterview();
+ List interviewList = Collections.singletonList(interview);
+ addressBook.setInterviews(interviewList);
+ AddressBook expectedAddressBook = new AddressBook();
+ expectedAddressBook.setInterviews(interviewList);
+ assertEquals(expectedAddressBook, addressBook);
+ }
+
+ @Test
+ public void sortInterviews_sortsInterviewsByDateAndTime() {
+ Interview earlierInterview = new InterviewBuilder().withDate(LocalDate.now())
+ .withStartTime(LocalTime.of(9, 0)).buildInterview();
+ Interview laterInterview = new InterviewBuilder().withDate(LocalDate.now())
+ .withStartTime(LocalTime.of(10, 0)).buildInterview();
+ addressBook.addInterview(laterInterview);
+ addressBook.addInterview(earlierInterview);
+ addressBook.sortInterviews();
+ assertEquals(Arrays.asList(earlierInterview, laterInterview), addressBook.getInterviewList());
+ }
+
+
@Test
public void toStringMethod() {
String expected = AddressBook.class.getCanonicalName() + "{persons=" + addressBook.getPersonList() + "}";
@@ -103,6 +177,11 @@ private static class AddressBookStub implements ReadOnlyAddressBook {
public ObservableList getPersonList() {
return persons;
}
+
+ @Override
+ public ObservableList getInterviewList() {
+ throw new AssertionError("This method should not be called.");
+ }
}
}
diff --git a/src/test/java/seedu/address/model/ModelManagerTest.java b/src/test/java/seedu/address/model/ModelManagerTest.java
index 2cf1418d116..ef7a001d541 100644
--- a/src/test/java/seedu/address/model/ModelManagerTest.java
+++ b/src/test/java/seedu/address/model/ModelManagerTest.java
@@ -10,13 +10,17 @@
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.LocalTime;
import java.util.Arrays;
import org.junit.jupiter.api.Test;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.model.interview.Interview;
import seedu.address.model.person.NameContainsKeywordsPredicate;
import seedu.address.testutil.AddressBookBuilder;
+import seedu.address.testutil.InterviewBuilder;
public class ModelManagerTest {
@@ -93,6 +97,48 @@ public void getFilteredPersonList_modifyList_throwsUnsupportedOperationException
assertThrows(UnsupportedOperationException.class, () -> modelManager.getFilteredPersonList().remove(0));
}
+ @Test
+ public void deletePerson_personIsInModel_deletesPerson() {
+ modelManager.addPerson(ALICE);
+ modelManager.deletePerson(ALICE);
+ ModelManager expectedModel = new ModelManager();
+ assertEquals(expectedModel, modelManager);
+ }
+
+ @Test
+ public void addInterview_nullInterview_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> modelManager.addInterview(null));
+ }
+
+ @Test
+ public void addInterview_validInterview_addsInterview() {
+ Interview interview = new InterviewBuilder().buildInterview();
+ modelManager.addInterview(interview);
+ assertTrue(modelManager.hasInterview(interview));
+ }
+
+ @Test
+ public void deleteInterview_interviewIsInModel_deletesInterview() {
+ Interview interview = new InterviewBuilder().buildInterview();
+ modelManager.addInterview(interview);
+ modelManager.deleteInterview(interview);
+ assertFalse(modelManager.hasInterview(interview));
+ }
+
+ @Test
+ public void sortInterview_interviewsAreInModel_sortsInterviews() {
+ Interview earlierInterview = new InterviewBuilder().withDate(LocalDate.now())
+ .withStartTime(LocalTime.of(9, 0)).buildInterview();
+ Interview laterInterview = new InterviewBuilder().withDate(LocalDate.now())
+ .withStartTime(LocalTime.of(10, 0)).buildInterview();
+ modelManager.addInterview(laterInterview);
+ modelManager.addInterview(earlierInterview);
+ modelManager.sortInterview();
+ assertEquals(Arrays.asList(earlierInterview, laterInterview), modelManager.getFilteredInterviewList());
+ }
+
+
+
@Test
public void equals() {
AddressBook addressBook = new AddressBookBuilder().withPerson(ALICE).withPerson(BENSON).build();
diff --git a/src/test/java/seedu/address/model/interview/InterviewTest.java b/src/test/java/seedu/address/model/interview/InterviewTest.java
new file mode 100644
index 00000000000..7d05dd5c38d
--- /dev/null
+++ b/src/test/java/seedu/address/model/interview/InterviewTest.java
@@ -0,0 +1,118 @@
+package seedu.address.model.interview;
+
+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.time.LocalDate;
+import java.time.LocalTime;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.InterviewBuilder;
+import seedu.address.testutil.PersonBuilder;
+
+
+public class InterviewTest {
+
+ private final InterviewBuilder interviewBuilder = new InterviewBuilder();
+ private final PersonBuilder personBuilder = new PersonBuilder();
+
+ @Test
+ public void equals() {
+ Interview baseInterview = interviewBuilder.buildInterview();
+
+ // Create variations of the interview using the builder for testing
+ Interview interviewWithDifferentApplicant = interviewBuilder
+ .withApplicant(personBuilder.withPhone("11111111").build_applicant())
+ .buildInterview();
+ Interview interviewWithDifferentInterviewer = interviewBuilder
+ .withInterviewer(personBuilder.withPhone("22222222").withStatus("free").build_interviewer())
+ .buildInterview();
+ Interview interviewWithDifferentDate = interviewBuilder
+ .withDate(LocalDate.of(2024, 2, 2))
+ .buildInterview();
+ Interview interviewWithDifferentStartTime = interviewBuilder
+ .withStartTime(LocalTime.of(11, 0))
+ .buildInterview();
+ Interview interviewWithDifferentEndTime = interviewBuilder
+ .withEndTime(LocalTime.of(13, 0))
+ .buildInterview();
+
+ // Standard equality checks
+ assertTrue(baseInterview.equals(baseInterview));
+
+ // Different attributes checks
+ assertFalse(baseInterview.equals(null));
+ assertFalse(baseInterview.equals(new Object()));
+
+ // Different applicant checks
+ assertFalse(baseInterview.equals(interviewWithDifferentApplicant));
+
+ // Different interviewer checks
+ assertFalse(baseInterview.equals(interviewWithDifferentInterviewer));
+
+ // Different date checks
+ assertFalse(baseInterview.equals(interviewWithDifferentDate));
+
+ // Different start time checks
+ assertFalse(baseInterview.equals(interviewWithDifferentStartTime));
+
+ // Different end time checks
+ assertFalse(baseInterview.equals(interviewWithDifferentEndTime));
+ }
+
+ @Test
+ public void isSameInterview_sameAttributes_returnsTrue() {
+ Interview baseInterview = interviewBuilder.buildInterview();
+ Interview sameAttributesInterview = new InterviewBuilder()
+ .withApplicant(baseInterview.getApplicant())
+ .withInterviewer(baseInterview.getInterviewer())
+ .withDate(baseInterview.getDate())
+ .withStartTime(baseInterview.getStartTime())
+ .withEndTime(baseInterview.getEndTime())
+ .withDescription(baseInterview.getDescription())
+ .buildInterview();
+
+ assertTrue(baseInterview.isSameInterview(sameAttributesInterview));
+ }
+
+ @Test
+ public void isSameInterview_differentDescription_returnsTrue() {
+ Interview baseInterview = interviewBuilder.buildInterview();
+ Interview differentDescriptionInterview = new InterviewBuilder()
+ .withDescription("A different description")
+ .buildInterview();
+
+ // Assuming that a different description does not make two interviews "different"
+ assertTrue(baseInterview.isSameInterview(differentDescriptionInterview));
+ }
+
+ @Test
+ public void isSameInterview_differentDate_returnsFalse() {
+ Interview baseInterview = interviewBuilder.buildInterview();
+ Interview differentDateInterview = new InterviewBuilder()
+ .withDate(LocalDate.of(2025, 1, 1))
+ .buildInterview();
+
+ assertFalse(baseInterview.isSameInterview(differentDateInterview));
+ }
+
+ @Test
+ public void toString_correctFormat() {
+ Interview baseInterview = interviewBuilder.buildInterview();
+ String expectedString = " ------Interview------\n"
+ + "Applicant: head\n"
+ + "Interviewer: cube\n"
+ + "Date: "
+ + LocalDate.now().toString()
+ + "\n"
+ + "Start: 10:00 "
+ + "End: 12:59\n"
+ + "Description: technical interview";
+
+ assertEquals(expectedString, baseInterview.toString());
+ }
+
+
+}
diff --git a/src/test/java/seedu/address/model/interview/UniqueInterviewListTest.java b/src/test/java/seedu/address/model/interview/UniqueInterviewListTest.java
new file mode 100644
index 00000000000..6a6c95b2299
--- /dev/null
+++ b/src/test/java/seedu/address/model/interview/UniqueInterviewListTest.java
@@ -0,0 +1,143 @@
+package seedu.address.model.interview;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import seedu.address.model.interview.exceptions.DuplicateInterviewException;
+import seedu.address.model.interview.exceptions.InterviewNotFoundException;
+import seedu.address.testutil.InterviewBuilder;
+
+
+public class UniqueInterviewListTest {
+
+ @Test
+ public void contains_nullInterview_throwsNullPointerException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ assertThrows(NullPointerException.class, () -> uniqueInterviewList.contains(null));
+ }
+
+ @Test
+ public void contains_interviewNotInList_returnsFalse() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ Interview interview = new InterviewBuilder().buildInterview();
+ assertFalse(uniqueInterviewList.contains(interview));
+ }
+
+ @Test
+ public void add_nullInterview_throwsNullPointerException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ assertThrows(NullPointerException.class, () -> uniqueInterviewList.add(null));
+ }
+
+ @Test
+ public void add_duplicateInterview_throwsDuplicateInterviewException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ Interview interview = new InterviewBuilder().buildInterview();
+ uniqueInterviewList.add(interview);
+ assertThrows(DuplicateInterviewException.class, () -> uniqueInterviewList.add(interview));
+ }
+
+ @Test
+ public void remove_nullInterview_throwsNullPointerException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ assertThrows(NullPointerException.class, () -> uniqueInterviewList.remove(null));
+ }
+
+ @Test
+ public void remove_interviewDoesNotExist_throwsInterviewNotFoundException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ Interview interview = new InterviewBuilder().buildInterview();
+ assertThrows(InterviewNotFoundException.class, () -> uniqueInterviewList.remove(interview));
+ }
+
+ @Test
+ public void remove_existingInterview_removesInterview() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ Interview interview = new InterviewBuilder().buildInterview();
+ uniqueInterviewList.add(interview);
+ uniqueInterviewList.remove(interview);
+ UniqueInterviewList expectedUniqueInterviewList = new UniqueInterviewList();
+ assertEquals(expectedUniqueInterviewList, uniqueInterviewList);
+ }
+
+ @Test
+ public void setInterviews_nullList_throwsNullPointerException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ assertThrows(NullPointerException.class, () -> uniqueInterviewList.setInterviews((List) null));
+ }
+
+ @Test
+ public void setInterviews_nullUniqueInterviewList_throwsNullPointerException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ assertThrows(NullPointerException.class, () -> uniqueInterviewList.setInterviews((UniqueInterviewList) null));
+ }
+
+ @Test
+ public void setInterviews_uniqueInterviewList_replacesOwnListWithProvidedUniqueInterviewList() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ uniqueInterviewList.add(new InterviewBuilder().buildInterview());
+ UniqueInterviewList expectedUniqueInterviewList = new UniqueInterviewList();
+ Interview interview = new InterviewBuilder().withDescription("Another interview").buildInterview();
+ expectedUniqueInterviewList.add(interview);
+ uniqueInterviewList.setInterviews(expectedUniqueInterviewList);
+ assertEquals(expectedUniqueInterviewList, uniqueInterviewList);
+ }
+
+ @Test
+ public void setInterviews_listWithDuplicateInterviews_throwsDuplicateInterviewException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ List listWithDuplicateInterviews = Arrays.asList(new InterviewBuilder()
+ .buildInterview(), new InterviewBuilder().buildInterview());
+ assertThrows(DuplicateInterviewException.class, () -> uniqueInterviewList
+ .setInterviews(listWithDuplicateInterviews));
+ }
+
+ @Test
+ public void setInterview_targetInterviewNotInList_throwsInterviewNotFoundException() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ Interview targetInterview = new InterviewBuilder().buildInterview();
+ Interview editedInterview = new InterviewBuilder().withDescription("Edited interview").buildInterview();
+ assertThrows(InterviewNotFoundException.class, () -> uniqueInterviewList
+ .setInterview(targetInterview, editedInterview));
+ }
+
+ @Test
+ public void setInterview_editedInterviewHasDifferentIdentity_success() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ Interview originalInterview = new InterviewBuilder().buildInterview();
+ uniqueInterviewList.add(originalInterview);
+ Interview editedInterview = new InterviewBuilder().withDescription("Edited interview").buildInterview();
+ uniqueInterviewList.setInterview(originalInterview, editedInterview);
+ UniqueInterviewList expectedUniqueInterviewList = new UniqueInterviewList();
+ expectedUniqueInterviewList.add(editedInterview);
+ assertEquals(expectedUniqueInterviewList, uniqueInterviewList);
+ }
+
+ @Test
+ public void sortInterviewsByDate() {
+ UniqueInterviewList uniqueInterviewList = new UniqueInterviewList();
+ Interview interview1 = new InterviewBuilder().withDate(LocalDate.of(2024, 12, 25))
+ .withStartTime(LocalTime.of(10, 0)).buildInterview();
+ Interview interview2 = new InterviewBuilder().withDate(LocalDate.of(2024, 12, 24))
+ .withStartTime(LocalTime.of(9, 0)).buildInterview();
+ uniqueInterviewList.add(interview1);
+ uniqueInterviewList.add(interview2);
+ uniqueInterviewList.sortInterviewsByDate();
+ ObservableList expectedSortedInterviews = FXCollections.observableArrayList();
+ expectedSortedInterviews.add(interview2); // Earlier date should come first
+ expectedSortedInterviews.add(interview1);
+ assertEquals(expectedSortedInterviews, uniqueInterviewList.asUnmodifiableObservableList());
+ }
+
+}
+
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 314885eca26..00000000000
--- a/src/test/java/seedu/address/model/person/AddressTest.java
+++ /dev/null
@@ -1,56 +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
- }
-
- @Test
- public void equals() {
- Address address = new Address("Valid Address");
-
- // same values -> returns true
- assertTrue(address.equals(new Address("Valid Address")));
-
- // same object -> returns true
- assertTrue(address.equals(address));
-
- // null -> returns false
- assertFalse(address.equals(null));
-
- // different types -> returns false
- assertFalse(address.equals(5.0f));
-
- // different values -> returns false
- assertFalse(address.equals(new Address("Other Valid Address")));
- }
-}
diff --git a/src/test/java/seedu/address/model/person/EmailContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/EmailContainsKeywordsPredicateTest.java
new file mode 100644
index 00000000000..fdd8fb4e4dd
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/EmailContainsKeywordsPredicateTest.java
@@ -0,0 +1,83 @@
+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 java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+public class EmailContainsKeywordsPredicateTest {
+
+ @Test
+ public void equals() {
+ List firstPredicateKeywordList = Collections.singletonList("first@email.com");
+ List secondPredicateKeywordList = Arrays.asList("first@email.com", "second@email.com");
+
+ EmailContainsKeywordsPredicate firstPredicate = new EmailContainsKeywordsPredicate(firstPredicateKeywordList);
+ EmailContainsKeywordsPredicate secondPredicate = new EmailContainsKeywordsPredicate(secondPredicateKeywordList);
+
+ // same object -> returns true
+ assertTrue(firstPredicate.equals(firstPredicate));
+
+ // same values -> returns true
+ EmailContainsKeywordsPredicate firstPredicateCopy = new EmailContainsKeywordsPredicate(
+ 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_emailContainsKeywords_returnsTrue() {
+ // One keyword
+ EmailContainsKeywordsPredicate predicate = new EmailContainsKeywordsPredicate(
+ Collections.singletonList("Alice@email.com"));
+ assertTrue(predicate.test(new PersonBuilder().withEmail("Alice@email.com").build()));
+
+ // Multiple keywords and only one matching keyword
+ predicate = new EmailContainsKeywordsPredicate(Arrays.asList("Alice@email.com", "Bob@email.com"));
+ assertTrue(predicate.test(new PersonBuilder().withEmail("Alice@email.com").build()));
+
+ // Mixed-case keywords
+ predicate = new EmailContainsKeywordsPredicate(Arrays.asList("aLIce@email.com", "bOB@email.com"));
+ assertTrue(predicate.test(new PersonBuilder().withEmail("Alice@email.com").build()));
+ }
+
+ @Test
+ public void test_emailDoesNotContainKeywords_returnsFalse() {
+ // Zero keywords
+ EmailContainsKeywordsPredicate predicate = new EmailContainsKeywordsPredicate(Collections.emptyList());
+ assertFalse(predicate.test(new PersonBuilder().withEmail("Alice@email.com").build()));
+
+ // Non-matching keyword
+ predicate = new EmailContainsKeywordsPredicate(Arrays.asList("Carol@email.com"));
+ assertFalse(predicate.test(new PersonBuilder().withEmail("Alice@email.com").build()));
+
+ // Keywords match phone and name, but does not match email
+ predicate = new EmailContainsKeywordsPredicate(Arrays.asList("12345", "bob@email.com", "Alice"));
+ assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345")
+ .withEmail("alice@email.com").build()));
+ }
+
+ @Test
+ public void toStringMethod() {
+ List keywords = List.of("keyword1@email.com", "keyword2@email.com");
+ EmailContainsKeywordsPredicate predicate = new EmailContainsKeywordsPredicate(keywords);
+
+ String expected = EmailContainsKeywordsPredicate.class.getCanonicalName() + "{keywords=" + keywords + "}";
+ assertEquals(expected, predicate.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
index 6b3fd90ade7..5d02c60f9fb 100644
--- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
+++ b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
@@ -68,10 +68,10 @@ public void test_nameDoesNotContainKeywords_returnsFalse() {
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
+ // Keywords match phone and email, 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()));
+ .withEmail("alice@email.com").build()));
}
@Test
diff --git a/src/test/java/seedu/address/model/person/PersonTest.java b/src/test/java/seedu/address/model/person/PersonTest.java
index 31a10d156c9..4d4bebe3317 100644
--- a/src/test/java/seedu/address/model/person/PersonTest.java
+++ b/src/test/java/seedu/address/model/person/PersonTest.java
@@ -1,9 +1,7 @@
package seedu.address.model.person;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import static seedu.address.logic.commands.CommandTestUtil.VALID_ADDRESS_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_NAME_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_PHONE_BOB;
@@ -32,23 +30,27 @@ public void isSamePerson() {
// 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 phone and email -> returns false
+ Person editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).build();
+ assertFalse(ALICE.isSamePerson(editedAlice));
- // different name, all other attributes same -> returns false
+ // different name, same phone, same email -> returns true
editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
- assertFalse(ALICE.isSamePerson(editedAlice));
+ assertTrue(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));
+ // same name, same phone, different attributes -> returns true
+ editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB)
+ .withTags(VALID_TAG_HUSBAND).build();
+ assertTrue(ALICE.isSamePerson(editedAlice));
+
+ // same name, same email, different attributes -> returns true
+ editedAlice = new PersonBuilder(ALICE).withPhone(VALID_PHONE_BOB)
+ .withTags(VALID_TAG_HUSBAND).build();
+ assertTrue(ALICE.isSamePerson(editedAlice));
- // 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));
+ // same name, same phone, same email, different attributes -> returns true
+ editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build();
+ assertTrue(ALICE.isSamePerson(editedAlice));
}
@Test
@@ -81,19 +83,8 @@ public void equals() {
editedAlice = new PersonBuilder(ALICE).withEmail(VALID_EMAIL_BOB).build();
assertFalse(ALICE.equals(editedAlice));
- // different address -> returns false
- editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).build();
- assertFalse(ALICE.equals(editedAlice));
-
// different tags -> returns false
editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND).build();
assertFalse(ALICE.equals(editedAlice));
}
-
- @Test
- public void toStringMethod() {
- String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
- + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", tags=" + ALICE.getTags() + "}";
- assertEquals(expected, ALICE.toString());
- }
}
diff --git a/src/test/java/seedu/address/model/person/PhoneContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/PhoneContainsKeywordsPredicateTest.java
new file mode 100644
index 00000000000..52acf72819e
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/PhoneContainsKeywordsPredicateTest.java
@@ -0,0 +1,79 @@
+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 java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+public class PhoneContainsKeywordsPredicateTest {
+
+ @Test
+ public void equals() {
+ List firstPredicateKeywordList = Collections.singletonList("first");
+ List secondPredicateKeywordList = Arrays.asList("first", "second");
+
+ PhoneContainsKeywordsPredicate firstPredicate = new PhoneContainsKeywordsPredicate(firstPredicateKeywordList);
+ PhoneContainsKeywordsPredicate secondPredicate = new PhoneContainsKeywordsPredicate(secondPredicateKeywordList);
+
+ // same object -> returns true
+ assertTrue(firstPredicate.equals(firstPredicate));
+
+ // same values -> returns true
+ PhoneContainsKeywordsPredicate firstPredicateCopy = new PhoneContainsKeywordsPredicate(
+ 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_phoneContainsKeywords_returnsTrue() {
+ // One keyword
+ PhoneContainsKeywordsPredicate predicate = new PhoneContainsKeywordsPredicate(Collections.singletonList("123"));
+ assertTrue(predicate.test(new PersonBuilder().withPhone("123").build()));
+
+ // Multiple keywords and only one matching keyword
+ predicate = new PhoneContainsKeywordsPredicate(Arrays.asList("123", "456"));
+ assertTrue(predicate.test(new PersonBuilder().withPhone("123").build()));
+
+ }
+
+ @Test
+ public void test_phoneDoesNotContainKeywords_returnsFalse() {
+ // Zero keywords
+ PhoneContainsKeywordsPredicate predicate = new PhoneContainsKeywordsPredicate(Collections.emptyList());
+ assertFalse(predicate.test(new PersonBuilder().withPhone("123").build()));
+
+ // Non-matching keyword
+ predicate = new PhoneContainsKeywordsPredicate(Arrays.asList("456"));
+ assertFalse(predicate.test(new PersonBuilder().withPhone("123").build()));
+
+ // Keywords match name and email, but does not match phone
+ predicate = new PhoneContainsKeywordsPredicate(Arrays.asList("54321", "alice@email.com", "Alice"));
+ assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345")
+ .withEmail("alice@email.com").build()));
+ }
+
+ @Test
+ public void toStringMethod() {
+ List keywords = List.of("123", "456");
+ PhoneContainsKeywordsPredicate predicate = new PhoneContainsKeywordsPredicate(keywords);
+
+ String expected = PhoneContainsKeywordsPredicate.class.getCanonicalName() + "{keywords=" + keywords + "}";
+ assertEquals(expected, predicate.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/RemarkTest.java b/src/test/java/seedu/address/model/person/RemarkTest.java
new file mode 100644
index 00000000000..34117c68ddf
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/RemarkTest.java
@@ -0,0 +1,31 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+public class RemarkTest {
+
+ @Test
+ public void equals() {
+ Remark remark = new Remark("Hello");
+
+ // same object -> returns true
+ assertTrue(remark.equals(remark));
+
+ // same values -> returns true
+ Remark remarkCopy = new Remark(remark.value);
+ assertTrue(remark.equals(remarkCopy));
+
+ // different types -> returns false
+ assertFalse(remark.equals(1));
+
+ // null -> returns false
+ assertFalse(remark.equals(null));
+
+ // different remark -> returns false
+ Remark differentRemark = new Remark("Bye");
+ assertFalse(remark.equals(differentRemark));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
index 17ae501df08..6a866223e8d 100644
--- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java
+++ b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
@@ -3,7 +3,6 @@
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;
@@ -42,7 +41,7 @@ public void contains_personInList_returnsTrue() {
@Test
public void contains_personWithSameIdentityFieldsInList_returnsTrue() {
uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ Person editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND)
.build();
assertTrue(uniquePersonList.contains(editedAlice));
}
@@ -85,7 +84,7 @@ public void setPerson_editedPersonIsSamePerson_success() {
@Test
public void setPerson_editedPersonHasSameIdentity_success() {
uniquePersonList.add(ALICE);
- Person editedAlice = new PersonBuilder(ALICE).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND)
+ Person editedAlice = new PersonBuilder(ALICE).withTags(VALID_TAG_HUSBAND)
.build();
uniquePersonList.setPerson(ALICE, editedAlice);
UniquePersonList expectedUniquePersonList = new UniquePersonList();
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedInterviewTest.java b/src/test/java/seedu/address/storage/JsonAdaptedInterviewTest.java
new file mode 100644
index 00000000000..70248be3e64
--- /dev/null
+++ b/src/test/java/seedu/address/storage/JsonAdaptedInterviewTest.java
@@ -0,0 +1,62 @@
+package seedu.address.storage;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static seedu.address.storage.JsonAdaptedInterview.MISSING_FIELD_MESSAGE_FORMAT;
+import static seedu.address.testutil.Assert.assertThrows;
+import static seedu.address.testutil.TypicalInterviews.TECH_INTERVIEW;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+
+public class JsonAdaptedInterviewTest {
+ private static final String INVALID_DATE = "2024-02-30"; // Invalid date
+ private static final String INVALID_START_TIME = "25:00"; // Invalid time
+ private static final String INVALID_END_TIME = "26:00"; // Invalid time
+
+ private static final String VALID_DESCRIPTION = TECH_INTERVIEW.getDescription();
+ private static final String VALID_DATE = TECH_INTERVIEW.getDate().toString();
+ private static final String VALID_START_TIME = TECH_INTERVIEW.getStartTime().toString();
+ private static final String VALID_END_TIME = TECH_INTERVIEW.getEndTime().toString();
+ private static final JsonAdaptedPerson VALID_APPLICANT = new JsonAdaptedPerson(TECH_INTERVIEW.getApplicant());
+ private static final JsonAdaptedPerson VALID_INTERVIEWER = new JsonAdaptedPerson(TECH_INTERVIEW.getInterviewer());
+
+ @Test
+ public void toModelType_validInterviewDetails_returnsInterview() throws Exception {
+ JsonAdaptedInterview interview = new JsonAdaptedInterview(TECH_INTERVIEW);
+ assertEquals(TECH_INTERVIEW, interview.toModelType());
+ }
+
+ @Test
+ public void toModelType_nullDate_throwsIllegalValueException() {
+ JsonAdaptedInterview interview =
+ new JsonAdaptedInterview(VALID_DESCRIPTION, null, VALID_START_TIME,
+ VALID_END_TIME, VALID_APPLICANT, VALID_INTERVIEWER);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, LocalDate.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, interview::toModelType);
+
+ }
+
+ @Test
+ public void toModelType_nullStartTime_throwsIllegalValueException() {
+ JsonAdaptedInterview interview =
+ new JsonAdaptedInterview(VALID_DESCRIPTION, VALID_DATE, null,
+ VALID_END_TIME, VALID_APPLICANT, VALID_INTERVIEWER);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, LocalTime.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, interview::toModelType);
+
+ }
+
+ @Test
+ public void toModelType_nullEndTime_throwsIllegalValueException() {
+ JsonAdaptedInterview interview =
+ new JsonAdaptedInterview(VALID_DESCRIPTION, VALID_DATE, VALID_START_TIME,
+ null, VALID_APPLICANT, VALID_INTERVIEWER);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, LocalTime.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, interview::toModelType);
+
+ }
+}
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
index 83b11331cdb..3df626b0c14 100644
--- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
+++ b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
@@ -12,22 +12,25 @@
import org.junit.jupiter.api.Test;
import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.ApplicantStatus;
import seedu.address.model.person.Email;
+import seedu.address.model.person.InterviewerStatus;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.Status;
+import seedu.address.model.person.enums.ApplicantState;
+import seedu.address.model.person.enums.Type;
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 String VALID_REMARK = BENSON.getRemark().toString();
private static final List VALID_TAGS = BENSON.getTags().stream()
.map(JsonAdaptedTag::new)
.collect(Collectors.toList());
@@ -41,14 +44,17 @@ public void toModelType_validPersonDetails_returnsPerson() throws Exception {
@Test
public void toModelType_invalidName_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.APPLICANT.toString(), ApplicantState.OUTCOME_ONE.toString());
String expectedMessage = Name.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullName_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(null, VALID_PHONE, VALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.APPLICANT.toString(), ApplicantState.OUTCOME_ONE.toString());
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@@ -56,14 +62,17 @@ public void toModelType_nullName_throwsIllegalValueException() {
@Test
public void toModelType_invalidPhone_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.APPLICANT.toString(), ApplicantState.OUTCOME_ONE.toString());
String expectedMessage = Phone.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullPhone_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, null, VALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.APPLICANT.toString(), ApplicantState.OUTCOME_ONE.toString());
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@@ -71,40 +80,65 @@ public void toModelType_nullPhone_throwsIllegalValueException() {
@Test
public void toModelType_invalidEmail_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.APPLICANT.toString(), ApplicantState.OUTCOME_ONE.toString());
String expectedMessage = Email.MESSAGE_CONSTRAINTS;
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_nullEmail_throwsIllegalValueException() {
- JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, null, VALID_REMARK, VALID_TAGS,
+ Type.APPLICANT.toString(), ApplicantState.OUTCOME_ONE.toString());
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_invalidAddress_throwsIllegalValueException() {
+ 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_REMARK, invalidTags,
+ Type.APPLICANT.toString(), ApplicantState.OUTCOME_ONE.toString());
+ assertThrows(IllegalValueException.class, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidApplicantStatus_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.APPLICANT.toString(), "blah");
+ String expectedMessage = ApplicantStatus.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_invalidInterviewerStatus_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS);
- String expectedMessage = Address.MESSAGE_CONSTRAINTS;
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.INTERVIEWER.toString(), "🇺🇸");
+ String expectedMessage = InterviewerStatus.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());
+ public void toModelType_nullApplicantStatus_throwsIllegalValueException() {
+ JsonAdaptedPerson person =
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.APPLICANT.toString(), null);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.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));
+ public void toModelType_nullInterviewerStatus_throwsIllegalValueException() {
JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, invalidTags);
- assertThrows(IllegalValueException.class, person::toModelType);
+ new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_REMARK, VALID_TAGS,
+ Type.INTERVIEWER.toString(), null);
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Status.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
}
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/InterviewBuilder.java b/src/test/java/seedu/address/testutil/InterviewBuilder.java
new file mode 100644
index 00000000000..e4568fce18c
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/InterviewBuilder.java
@@ -0,0 +1,139 @@
+package seedu.address.testutil;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.HashSet;
+import java.util.Set;
+
+import seedu.address.model.interview.Interview;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.ApplicantStatus;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Interviewer;
+import seedu.address.model.person.InterviewerStatus;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.Remark;
+import seedu.address.model.person.enums.ApplicantState;
+import seedu.address.model.person.enums.InterviewerState;
+import seedu.address.model.tag.Tag;
+
+/**
+ * A utility class to help with building Interview objects.
+ */
+public class InterviewBuilder {
+ public static final String DEFAULT_DESCRIPTION = "technical interview";
+ public static final LocalDate DEFAULT_DATE = LocalDate.now();
+ public static final LocalTime DEFAULT_START_TIME = LocalTime.of(10, 00);
+ public static final LocalTime DEFAULT_END_TIME = LocalTime.of(12, 59);
+
+ public static final Name DEFAULT_APPLICANT_NAME = new Name("head");
+ public static final Name DEFAULT_INTERVIEWER_NAME = new Name("cube");
+ public static final Phone DEFAULT_APPLICANT_PHONE_NUMBER = new Phone("12345678");
+ public static final Phone DEFAULT_INTERVIEWER_PHONE_NUMBER = new Phone("87654321");
+ public static final Email DEFAULT_APPLICANT_EMAIL = new Email("head@cube.com");
+ public static final Email DEFAULT_INTERVIWER_EMAIL = new Email("cube@head.com");
+ public static final Remark DEFAULT_REMARK = new Remark("");
+ public static final Set DEFAULT_TAGS = new HashSet<>();
+ public static final Applicant DEFAULT_APPLICANT = new Applicant(DEFAULT_APPLICANT_NAME,
+ DEFAULT_APPLICANT_PHONE_NUMBER, DEFAULT_APPLICANT_EMAIL, DEFAULT_REMARK,
+ new ApplicantStatus(ApplicantState.STAGE_ONE.toString()), DEFAULT_TAGS);
+ public static final Interviewer DEFAULT_INTERVIEWER = new Interviewer(DEFAULT_INTERVIEWER_NAME,
+ DEFAULT_INTERVIEWER_PHONE_NUMBER, DEFAULT_INTERVIWER_EMAIL, DEFAULT_REMARK,
+ new InterviewerStatus(InterviewerState.FREE.toString()),
+ DEFAULT_TAGS);
+ private String description;
+ private LocalDate date;
+ private LocalTime startTime;
+ private LocalTime endTime;
+ private Applicant applicant;
+ private Interviewer interviewer;
+
+ /**
+ * Initializes the InterviewBuilder.
+ */
+ public InterviewBuilder() {
+ applicant = DEFAULT_APPLICANT;
+ interviewer = DEFAULT_INTERVIEWER;
+ date = DEFAULT_DATE;
+ startTime = DEFAULT_START_TIME;
+ endTime = DEFAULT_END_TIME;
+ description = DEFAULT_DESCRIPTION;
+ }
+
+ /**
+ * Initializes the InterviewBuilder with the data of {@code interviewToCopy}.
+ */
+ public InterviewBuilder(Interview interviewToCopy) {
+ applicant = (Applicant) interviewToCopy.getApplicant();
+ interviewer = (Interviewer) interviewToCopy.getInterviewer();
+ date = interviewToCopy.getDate();
+ startTime = interviewToCopy.getStartTime();
+ endTime = interviewToCopy.getEndTime();
+ description = interviewToCopy.getDescription();
+ }
+
+ /**
+ * Sets the {@code Applicant} of the {@code Interview} that we are building.
+ */
+ public InterviewBuilder withApplicant(Person applicant) {
+ this.applicant = (Applicant) applicant;
+ return this;
+ }
+
+ /**
+ * Sets the {@code Interviewer} of the {@code Interview} that we are building.
+ */
+ public InterviewBuilder withInterviewer(Person interviewer) {
+ this.interviewer = (Interviewer) interviewer;
+ return this;
+ }
+
+ /**
+ * Sets the {@code Description} of the {@code Interview} that we are building.
+ */
+ public InterviewBuilder withDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * Sets the {@code Date} of the {@code Interview} that we are building.
+ */
+ public InterviewBuilder withDate(LocalDate date) {
+ this.date = date;
+ return this;
+ }
+
+ /**
+ * Sets the {@code StartTime} of the {@code Interview} that we are building.
+ */
+ public InterviewBuilder withStartTime(LocalTime startTime) {
+ this.startTime = startTime;
+ return this;
+ }
+
+ /**
+ * Sets the {@code EndTime} of the {@code Interview} that we are building.
+ */
+ public InterviewBuilder withEndTime(LocalTime endTime) {
+ this.endTime = endTime;
+ return this;
+ }
+
+ /**
+ * Builds the {@code Interview} with the given {@code Applicant}, {@code Interviewer}, {@code Date},
+ * {@code StartTime}, {@code EndTime}, {@code Description}
+ */
+ public Interview build() {
+ return new Interview(applicant, interviewer, date, startTime, endTime, description);
+ }
+
+ /**
+ * Builds an {@code Interview} with the current properties.
+ */
+ public Interview buildInterview() {
+ return new Interview(applicant, interviewer, date, startTime, endTime, description);
+ }
+}
diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java
index 6be381d39ba..56ec60080d0 100644
--- a/src/test/java/seedu/address/testutil/PersonBuilder.java
+++ b/src/test/java/seedu/address/testutil/PersonBuilder.java
@@ -3,11 +3,16 @@
import java.util.HashSet;
import java.util.Set;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.ApplicantStatus;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Interviewer;
+import seedu.address.model.person.InterviewerStatus;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.Remark;
+import seedu.address.model.person.enums.Type;
import seedu.address.model.tag.Tag;
import seedu.address.model.util.SampleDataUtil;
@@ -16,26 +21,31 @@
*/
public class PersonBuilder {
- public static final String DEFAULT_NAME = "Amy Bee";
+ public static final String DEFAULT_NAME = "Alice Pauline";
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";
+ public static final String DEFAULT_EMAIL = "alice@gmail.com";
+ public static final String DEFAULT_REMARK = "She likes aardvarks.";
+ public static final String DEFAULT_STATUS = "resume review";
private Name name;
private Phone phone;
private Email email;
- private Address address;
+ private Remark remark;
private Set tags;
+ private String type;
+ private String status;
/**
- * Creates a {@code PersonBuilder} with the default details.
+ * Initializes the PersonBuilder.
*/
public PersonBuilder() {
name = new Name(DEFAULT_NAME);
phone = new Phone(DEFAULT_PHONE);
email = new Email(DEFAULT_EMAIL);
- address = new Address(DEFAULT_ADDRESS);
+ remark = new Remark(DEFAULT_REMARK);
tags = new HashSet<>();
+ type = Type.PERSON.toString();
+ status = DEFAULT_STATUS;
}
/**
@@ -45,8 +55,15 @@ public PersonBuilder(Person personToCopy) {
name = personToCopy.getName();
phone = personToCopy.getPhone();
email = personToCopy.getEmail();
- address = personToCopy.getAddress();
+ remark = personToCopy.getRemark();
+ type = personToCopy.getPersonType();
tags = new HashSet<>(personToCopy.getTags());
+ if (type.equals("APPLICANT")) {
+ tags.add(new Tag("Applicant"));
+ } else if (type.equals("INTERVIEWER")) {
+ tags.add(new Tag("Interviewer"));
+ }
+ status = personToCopy.getCurrentStatus();
}
/**
@@ -65,14 +82,6 @@ public PersonBuilder withTags(String ... 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.
*/
@@ -89,8 +98,43 @@ public PersonBuilder withEmail(String email) {
return this;
}
+ /**
+ * Sets the {@code Remark} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withRemark(String remark) {
+ this.remark = new Remark(remark);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Status} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ /**
+ * Builds the {@code Person} with the given {@code Name}, {@code Phone},
+ * {@code Email},{@code Remark} and {@code Tags}.
+ */
public Person build() {
- return new Person(name, phone, email, address, tags);
+ return new Person(name, phone, email, remark, tags);
+ }
+
+ /**
+ * Builds the {@code Applicant} with the given {@code Name}, {@code Phone},
+ * {@code Email},{@code Remark}, {@code Status} and {@code Tags}.
+ */
+ public Applicant build_applicant() {
+ return new Applicant(name, phone, email, remark, new ApplicantStatus(status), tags);
}
+ /**
+ * Builds the {@code Interviewer} with the given {@code Name}, {@code Phone},
+ * {@code Email},{@code Remark}, {@code Status} and {@code Tags}.
+ */
+ public Interviewer build_interviewer() {
+ return new Interviewer(name, phone, email, remark, new InterviewerStatus(status), tags);
+ }
}
diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java
index 90849945183..690e45ab5fb 100644
--- a/src/test/java/seedu/address/testutil/PersonUtil.java
+++ b/src/test/java/seedu/address/testutil/PersonUtil.java
@@ -1,17 +1,13 @@
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.logic.commands.AddApplicantPersonCommand;
+import seedu.address.logic.commands.AddInterviewerPersonCommand;
import seedu.address.model.person.Person;
-import seedu.address.model.tag.Tag;
/**
* A utility class for Person.
@@ -21,8 +17,13 @@ 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);
+
+ public static String getAddInterviewerCommand(Person person) {
+ return AddInterviewerPersonCommand.COMMAND_WORD + " " + getPersonDetails(person);
+ }
+
+ public static String getAddApplicantCommand(Person person) {
+ return AddApplicantPersonCommand.COMMAND_WORD + " " + getPersonDetails(person);
}
/**
@@ -33,30 +34,11 @@ public static String getPersonDetails(Person person) {
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 + " ")
+ 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
index 1e613937657..f5db6f343b1 100644
--- a/src/test/java/seedu/address/testutil/TypicalIndexes.java
+++ b/src/test/java/seedu/address/testutil/TypicalIndexes.java
@@ -9,4 +9,7 @@ public class TypicalIndexes {
public static final Index INDEX_FIRST_PERSON = Index.fromOneBased(1);
public static final Index INDEX_SECOND_PERSON = Index.fromOneBased(2);
public static final Index INDEX_THIRD_PERSON = Index.fromOneBased(3);
+ public static final Index INDEX_LAST_PERSON = Index.fromOneBased(9);
+ public static final Index INDEX_FIRST_INTERVIEW = Index.fromOneBased(1);
+ public static final Index INDEX_SECOND_INTERVIEW = Index.fromOneBased(2);
}
diff --git a/src/test/java/seedu/address/testutil/TypicalInterviews.java b/src/test/java/seedu/address/testutil/TypicalInterviews.java
new file mode 100644
index 00000000000..f95d7dfa4f7
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/TypicalInterviews.java
@@ -0,0 +1,55 @@
+package seedu.address.testutil;
+
+import static seedu.address.testutil.TypicalPersons.getTypicalPersons;
+
+import java.time.LocalDate;
+import java.time.LocalTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import seedu.address.model.AddressBook;
+import seedu.address.model.interview.Interview;
+import seedu.address.model.person.Applicant;
+import seedu.address.model.person.Interviewer;
+import seedu.address.model.person.Person;
+
+/**
+ * A utility class containing a list of {@code Interview} objects to be used in tests.
+ */
+public class TypicalInterviews {
+
+ public static final Interview TECH_INTERVIEW = new InterviewBuilder().withApplicant(
+ (Applicant) TypicalPersons.ALICE)
+ .withInterviewer((Interviewer) TypicalPersons.CUBE)
+ .withDate(LocalDate.of(2024, 01, 01))
+ .withStartTime(LocalTime.of(10, 00)).withEndTime(LocalTime.of(12, 00))
+ .withDescription("Technical interview for software engineer position").buildInterview();
+
+ public static final Interview HR_INTERVIEW = new InterviewBuilder().withApplicant(
+ (Applicant) TypicalPersons.BENSON)
+ .withInterviewer((Interviewer) TypicalPersons.CUBE)
+ .withDate(LocalDate.of(2024, 01, 02))
+ .withStartTime(LocalTime.of(13, 00)).withEndTime(LocalTime.of(14, 00))
+ .withDescription("HR interview for culture fit assessment").buildInterview();
+
+ private TypicalInterviews() {} // prevents instantiation
+
+ /**
+ * Returns an {@code AddressBook} with all the typical interviews.
+ */
+ public static AddressBook getTypicalAddressBook() {
+ AddressBook ab = new AddressBook();
+ for (Person p: getTypicalPersons()) {
+ ab.addPerson(p);
+ }
+ for (Interview interview : getTypicalInterviews()) {
+ ab.addInterview(interview);
+ }
+ return ab;
+ }
+
+ public static List getTypicalInterviews() {
+ return new ArrayList<>(Arrays.asList(TECH_INTERVIEW, HR_INTERVIEW));
+ }
+}
diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java
index fec76fb7129..6db3fe95779 100644
--- a/src/test/java/seedu/address/testutil/TypicalPersons.java
+++ b/src/test/java/seedu/address/testutil/TypicalPersons.java
@@ -1,21 +1,19 @@
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.Applicant;
+import seedu.address.model.person.Interviewer;
import seedu.address.model.person.Person;
/**
@@ -24,37 +22,42 @@
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();
+ .withEmail("alice@example.com")
+ .withPhone("94351253").withRemark("She likes aardvarks.")
+ .withStatus("resume review").withTags("Applicant", "friends").build_applicant();
public static final Person BENSON = new PersonBuilder().withName("Benson Meier")
- .withAddress("311, Clementi Ave 2, #02-25")
+ .withRemark("He can't take beer!")
.withEmail("johnd@example.com").withPhone("98765432")
- .withTags("owesMoney", "friends").build();
+ .withTags("Applicant", "owesMoney", "friends").build_applicant();
public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563")
- .withEmail("heinz@example.com").withAddress("wall street").build();
+ .withEmail("heinz@example.com").withTags("Applicant").build_applicant();
public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533")
- .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build();
+ .withEmail("cornelia@example.com").withTags("Applicant", "friends").build_applicant();
public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224")
- .withEmail("werner@example.com").withAddress("michegan ave").build();
+ .withEmail("werner@example.com").withTags("Applicant").build_applicant();
public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427")
- .withEmail("lydia@example.com").withAddress("little tokyo").build();
+ .withEmail("lydia@example.com").withTags("Applicant").build_applicant();
public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442")
- .withEmail("anna@example.com").withAddress("4th street").build();
+ .withEmail("anna@example.com").withTags("Applicant").build_applicant();
// Manually added
public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424")
- .withEmail("stefan@example.com").withAddress("little india").build();
+ .withEmail("stefan@example.com").withTags("Applicant").build_applicant();
public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131")
- .withEmail("hans@example.com").withAddress("chicago ave").build();
+ .withEmail("hans@example.com").withTags("Applicant").build_applicant();
// 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();
+ .withEmail(VALID_EMAIL_AMY).withTags("Applicant").build_applicant();
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();
+ .withEmail(VALID_EMAIL_BOB).withTags("Applicant")
+ .build_applicant();
+ public static final Applicant HEAD = new PersonBuilder().withName("head").withPhone("12345678")
+ .withEmail("head@cube.com").withTags("Applicant").build_applicant();
+
+ public static final Interviewer CUBE = new PersonBuilder().withName("cube").withPhone("87654321")
+ .withEmail("cube@head.com").withTags("Interviewer").withStatus("free").build_interviewer();
public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER
private TypicalPersons() {} // prevents instantiation
@@ -71,6 +74,6 @@ public static AddressBook getTypicalAddressBook() {
}
public static List getTypicalPersons() {
- return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE));
+ return new ArrayList<>(Arrays.asList(ALICE, BENSON, CARL, DANIEL, ELLE, FIONA, GEORGE, HEAD, CUBE));
}
}