diff --git a/.gitignore b/.gitignore
index 284c4ca7cd9..384e9a410e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,3 +21,180 @@ src/test/data/sandbox/
# MacOS custom attributes files created by Finder
.DS_Store
docs/_site/
+
+/bin/
+
+##################################
+### Generated by ignore v0.1.0 ###
+##################################
+
+#############
+### macOS ###
+#############
+
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+
+############
+### Node ###
+############
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+.pnpm-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Snowpack dependency directory (https://snowpack.dev/)
+web_modules/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional stylelint cache
+.stylelintcache
+
+# Microbundle cache
+.rpt2_cache/
+.rts2_cache_cjs/
+.rts2_cache_es/
+.rts2_cache_umd/
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+.parcel-cache
+
+# Next.js build output
+.next
+out
+
+# Nuxt.js build / generate output
+.nuxt
+dist
+
+# Gatsby files
+.cache/
+# Comment in the public line in if your project uses Gatsby and not Next.js
+# https://nextjs.org/blog/next-9-1#public-directory-support
+# public
+
+# vuepress build output
+.vuepress/dist
+
+# vuepress v2.x temp and cache directory
+.temp
+.cache
+
+# Docusaurus cache and generated files
+.docusaurus
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# TernJS port file
+.tern-port
+
+# Stores VSCode versions used for testing VSCode extensions
+.vscode-test
+
+# yarn v2
+.yarn/cache
+.yarn/unplugged
+.yarn/build-state.yml
+.yarn/install-state.gz
+.pnp.*
+
+###############
+### The end ###
+###############
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000000..ca832cc9861
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "editor.formatOnSave": false,
+ "editor.detectIndentation": false
+}
diff --git a/README.md b/README.md
index 13f5c77403f..16178e51fff 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,30 @@
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
+# TAfinder
+
+[![codecov](https://codecov.io/gh/AY2324S1-CS2103T-W10-1/tp/graph/badge.svg?token=4DBT7T1IUV)](https://codecov.io/gh/AY2324S1-CS2103T-W10-1/tp)
+
+This is a CS2103T project by [AY2324S1-CS2103T-W10-1](https://ay2324s1-cs2103t-w10-1.github.io/tp/AboutUs.html). It is based on the AddressBook-Level3 project created by the [SE-EDU initiative](https://se-education.org/).
+
+TAfinder is a desktop app for NUS professors to find and track Teaching Assistant (TA) information. It is optimised for use via a Command Line Interface (CLI) while still having the benefits of a Graphical User Interface (GUI). If you are an NUS professor that can type fast, and who is in need of an app to find and manage TA information, TAfinder is the app for you!
![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.
+## Features
+
+- **Basic Applicant management**
+ - Listing all applicants: `list`
+ - Hiding/unhiding an applicant from the list: `hide`/`unhide`
+ - Editing an applicant's information: `edit`
+ - Adding an applicant: `add`
+ - Viewing an applicant's details: `view`
+- **Applicant evaluation & comparison**
+ - Sorting applicants by grades: `sort`
+ - Comparing 2 applicants: `compare`
+ - Bookmarking/Unbookmarking an applicant: `bookmark`/`unbookmark`
+ - Adding comments for an applicant: `comment`
+- **Data management and export**
+ - Import applicants from a spreadsheet: `import`
+ - Attaching file to an applicant's profile: `attach`
+
+## Issues and Bugs
+
+Spot a bug? Let us know by launching a Bug Report at the [Issues tab](https://github.com/AY2324S1-CS2103T-W10-1/tp/issues)!
diff --git a/build.gradle b/build.gradle
index a2951cc709e..b91261d3e79 100644
--- a/build.gradle
+++ b/build.gradle
@@ -65,8 +65,12 @@ dependencies {
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: jUnitVersion
}
+run {
+ enableAssertions = true
+}
+
shadowJar {
- archiveFileName = 'addressbook.jar'
+ archiveFileName = 'tafinder.jar'
}
defaultTasks 'clean', 'test'
diff --git a/docs/AboutUs.md b/docs/AboutUs.md
index 1c9514e966a..fb42cddc649 100644
--- a/docs/AboutUs.md
+++ b/docs/AboutUs.md
@@ -1,59 +1,57 @@
---
-layout: page
title: About Us
---
We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg).
-You can reach us at the email `seer[at]comp.nus.edu.sg`
+## Project Team
-## Project team
+### Ravern Koh
-### John Doe
+
-
+[[github](https://github.com/ravern)]
+[[portfolio](team/ravern.md)]
-[[homepage](http://www.comp.nus.edu.sg/~damithch)]
-[[github](https://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
-
-* Role: Project Advisor
+* Role: Team Lead
+* Responsibilities: UI
-### Jane Doe
+### Amos Ting
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/amosting)]
+[[portfolio](team/amosting.md)]
-* Role: Team Lead
+* Role: Testing
* Responsibilities: UI
-### Johnny Doe
+### Heng Yi
-
+
-[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)]
+[[github](http://github.com/lheng1)]
+[[portfolio](team/lheng1.md)]
-* Role: Developer
-* Responsibilities: Data
+* Role: Code Quality
+* Responsibilities: Model
-### Jean Doe
+### Nabonita Sen
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/nabonitasen)]
+[[portfolio](team/nabonitasen.md)]
-* Role: Developer
-* Responsibilities: Dev Ops + Threading
+* Role: Deliverables and Deadlines
+* Responsibilities: Model
-### James Doe
+### Amy Ling
-
+
-[[github](http://github.com/johndoe)]
-[[portfolio](team/johndoe.md)]
+[[github](http://github.com/ylyma)]
+[[portfolio](team/ylyma.md)]
-* Role: Developer
-* Responsibilities: UI
+* Role: Documentation
+* Responsibilities: Storage
diff --git a/docs/Configuration.md b/docs/Configuration.md
index 13cf0faea16..deb49761c98 100644
--- a/docs/Configuration.md
+++ b/docs/Configuration.md
@@ -1,5 +1,4 @@
---
-layout: page
title: Configuration guide
---
diff --git a/docs/DevOps.md b/docs/DevOps.md
index d2fd91a6001..f1718c8e1a7 100644
--- a/docs/DevOps.md
+++ b/docs/DevOps.md
@@ -1,13 +1,7 @@
---
-layout: page
title: DevOps guide
---
-* Table of Contents
-{:toc}
-
---------------------------------------------------------------------------------------------------------------------
-
## Build automation
This project uses Gradle for **build automation and dependency management**. **You are recommended to read [this Gradle Tutorial from the se-edu/guides](https://se-education.org/guides/tutorials/gradle.html)**.
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 8a861859bfd..8c7b52eab0b 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,30 +1,25 @@
---
-layout: page
title: Developer Guide
+show-sticky-toc: true
---
-* Table of Contents
-{:toc}
+
+{% include toc.md header=true show-in-toc=true ordered=true %}
--------------------------------------------------------------------------------------------------------------------
-## **Acknowledgements**
+## Acknowledgements
-* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well}
+* Reused code from [ToothTracker](https://github.com/AY2324S1-CS2103T-W10-3/tp) for table of contents, sticky navigation and auto-numbering of headers for website
--------------------------------------------------------------------------------------------------------------------
-## **Setting up, getting started**
+## Setting up, getting started
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.
-
+## Design
### Architecture
@@ -47,7 +42,7 @@ The bulk of the app's work is done by the following four components:
* [**`Model`**](#model-component): Holds the data of the App in memory.
* [**`Storage`**](#storage-component): Reads data from, and writes data to, the hard disk.
-[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components.
+[**`commons`**](#common-classes) represents a collection of classes used by multiple other components.
**How the architecture components interact with each other**
@@ -95,7 +90,8 @@ The sequence diagram below illustrates the interactions within the `Logic` compo
![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png)
-
:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
+
+:information_source: **Note:** The lifeline for `DeleteCommandParser` should end at the destroy marker (X) but due to a limitation of PlantUML, the lifeline reaches the end of diagram.
How the `Logic` component works:
@@ -132,7 +128,6 @@ The `Model` component,
-
### Storage component
**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java)
@@ -144,104 +139,317 @@ The `Storage` component,
* inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed).
* depends on some classes in the `Model` component (because the `Storage` component's job is to save/retrieve objects that belong to the `Model`)
+--------------------------------------------------------------------------------------------------------------------
+
### Common classes
Classes used by multiple components are in the `seedu.addressbook.commons` package.
--------------------------------------------------------------------------------------------------------------------
-## **Implementation**
+## Implementation
This section describes some noteworthy details on how certain features are implemented.
-### \[Proposed\] Undo/redo feature
+### Hide/unhide feature
-#### Proposed Implementation
+The hide/unhide mechanism introduces the capability to selectively hide certain applicants from view, improving user experience and providing greater control over the displayed information. This also includes a way to view all hidden applicants in a list.
-The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations:
+#### Implementation
-* `VersionedAddressBook#commit()` — Saves the current address book state in its history.
-* `VersionedAddressBook#undo()` — Restores the previous address book state from its history.
-* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history.
+This feature implements the following operations:
-These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively.
+* `HideCommand#execute()` — Hides the specified applicant from the list.
+* `UnhideCommand#execute()` — Unhides the specified applicant from the list.
+* `UnhideAllCommand#execute()` — Unhides all hidden applicants.
+* `ListHiddenCommand#execute()` — Displays a list of all hidden applicants.
-Given below is an example usage scenario and how the undo/redo mechanism behaves at each step.
+Given below is an example usage scenario and how the hide/unhide mechanism behaves at each step.
-Step 1. The user launches the application for the first time. The `VersionedAddressBook` will be initialized with the initial address book state, and the `currentStatePointer` pointing to that single address book state.
+Step 1. The user launches the application for the first time. The applicant list displays all applicants without any hidden applicants.
-![UndoRedoState0](images/UndoRedoState0.png)
+Step 2. The user decides to hide the 3rd applicant in the applicant list by executing hide 3. The `hide` command calls `Model#setPerson()` to replace the applicant with a hidden version. `Model#updateFilteredPersonList()` to update the list of applicants displayed in the UI to exclude the hidden 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.
+Step 3. The user executes `list hidden` to view all hidden applicants. The `list hidden` command calls `Model#updateFilteredPersonList()` to update the list of applicants displayed in the UI to include only hidden applicants.
-![UndoRedoState1](images/UndoRedoState1.png)
+Step 4. The user decides to unhide the 1st applicant in the hidden applicant list by executing unhide 1. The `hide` command calls `Model#setPerson()` to replace the hidden applicant with a non-hidden version. The `unhide` command calls `Model#updateFilteredPersonList()` to update the list of applicants displayed in the UI to include the unhidden applicant.
-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`.
+Step 5. Alternatively, the user can choose to unhide all hidden applicants by executing unhide all. The `unhide all` command creates a copy of the model and calls `Model#updateFilteredPersonList()` and `Model#getFilteredPersonList()` on that model to receive a list of hidden applicants. It then replaces every hidden person in the original model with a non-hidden version by calling `Model#setPerson()`. The `unhide all` command calls `Model#updateFilteredPersonList()` to update the list of applicants displayed in the UI to include all unhidden applicants.
-![UndoRedoState2](images/UndoRedoState2.png)
+The following sequence diagram shows how the undo operation works:
-
: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`.
+![HideSequenceDiagram](images/HideSequenceDiagram.png)
-
+The following activity diagram summarizes what happens when a user executes a new command:
-Step 4. The user now decides that adding the person was a mistake, and decides to undo that action by executing the `undo` command. The `undo` command will call `Model#undoAddressBook()`, which will shift the `currentStatePointer` once to the left, pointing it to the previous address book state, and restores the address book to that state.
+
-![UndoRedoState3](images/UndoRedoState3.png)
+### Bookmark/Unbookmark feature
-
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather
-than attempting to perform the undo.
+The bookmark/unbookmark mechanism gives users the ability to bookmark or unbookmark certain applicants they want to take note of, as well as list these bookmarked applicants. This allows users to better differentiate between a long list of applicants, improving the ease of usage of this application and user experience.
-
+#### Implementation
-The following sequence diagram shows how the undo operation works:
+The bookmark/unbookmark mechanism is mainly facilitated by the `BookmarkCommand/UnbookmarkCommand`, `BookmarkCommandParser/UnbookmarkCommandParser`, and `isBookmarked` classes. It extends the abstract class `Command` with an `execute` functionality, to facilitate the execution of the command. Specifically, the bookmark/unbookmark feature is implemented through the following components and operations:
+- `BookmarkCommand/UnbookmarkCommand` — Core component responsible for executing the bookmarking/unbookmarking of a specific applicant.
+- `BookmarkCommandParser/UnbookmarkCommandParser` — Contains the functionalities for user input parsing. It ensures that user input is valid as a bookmark/unbookmark command by meeting specific requirements.
+- `Person` — Represents the TA applicants with their respective fields, such as `isBookmarked`.
+- `isBookmarked` — Represents whether an applicant is bookmarked or not through the use of a Boolean value.
-![UndoSequenceDiagram](images/UndoSequenceDiagram.png)
+Given below is an example usage scenario and how the bookmark/unbookmark 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 0. Assume that there is an existing list of applicants in the application after launch.
-
+Step 1. The user launches the application. Applicant list is displayed.
-The `redo` command does the opposite — it calls `Model#redoAddressBook()`, which shifts the `currentStatePointer` once to the right, pointing to the previously undone state, and restores the address book to that state.
+Step 2. The user executes `bookmark 5` command to bookmark the 5th applicant in TAfinder. The `BookmarkCommmandParser` is invoked to parse the user's input.
-
: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 3. `BookmarkCommandParser` will then invoke ParserUtil for parsing of the index and check for index errors. If the index is invalid, the system will generate an error message. The error message will be displayed to the user, providing clear feedback about the issue and the specific constraints that are not met.
-
+Step 4. If the index is valid, `BookmarkCommand#execute()` fetches the intended applicant from the currently visible list and bookmarks the applicant with the corresponding index.
-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.
+Step 5. Then, `BookmarkCommand#execute()` updates `Model#setModel` with the updated Person. Displaying the updated applicant list with a bookmark indicator.
-![UndoRedoState4](images/UndoRedoState4.png)
+Step 6. A success message is displayed to the user to confirm that the applicant has been bookmarked successfully.
-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.
+Step 7. The user then decides to view all bookmarked applicants by executing the `list-bookmarked` command. The `list-bookmarked` command calls `Model#updateFilteredPersonList()`, which updates the list of applicants presented in the UI to only include bookmarked applicants.
-![UndoRedoState5](images/UndoRedoState5.png)
+The following sequence diagram shows how the bookmark operation works:
-The following activity diagram summarizes what happens when a user executes a new command:
+![BookmarkSequenceDiagram](images/BookmarkSequenceDiagram.png)
+
+The following activity diagram summarizes what happens when a user executes a `bookmark` command:
+
+![BookmarkActivityDiagram](images/BookmarkActivityDiagram.png)
+
+### View feature
+
+The view mechanism gives users the ability to view a specific applicant's details. This allows users to better focus on and evaluate a singular applicant when they need to, improving the ease of usage of this application and user experience.
+
+#### Implementation
+
+The view mechanism is mainly facilitated by the `ViewCommand` and `ViewCommandParser` classes. It extends the abstract class `Command` with an `execute` functionality, to facilitate the execution of the command. Specifically, the view feature is implemented through the following components and operations:
+- `ViewCommand` — Core component responsible for executing the the viewing of a singular applicant's details from the list.
+- `ViewCommandParser` — Contains the functionalities for user input parsing. It ensures that user input is valid as a view command by meeting specific requirements.
+- `Person` — Represents the TA applicants with their respective fields.
+
+Given below is an example usage scenario and how the view mechanism behaves at each step.
+
+Step 0. Assume that there is an existing list of applicants in the application after launch.
+
+Step 1. The user launches the application. Applicant list is displayed.
+
+Step 2. The user executes `view 5` command to view the details of the 5th applicant in TAfinder. The `ViewCommmandParser` is invoked to parse the user's input.
+
+Step 3. `ViewCommandParser` will then invoke ParserUtil for parsing of the index and check for index errors. If the index is invalid, the system will generate an error message. The error message will be displayed to the user, providing clear feedback about the issue and the specific constraints that are not met.
+
+Step 4. If the index is valid, `ViewCommand#execute()` fetches the intended applicant.
+
+Step 5. Then, `CommentCommand#execute()` calls `Model#showPersonAtIndex` to set `Model#currentPerson` with the intended applicant (in the form of a `Person`). `PersonDetailPanel` displays details of the intended applicant corresponding to the given index.
+
+Step 6. A success message is displayed to the user to confirm that specified applicant's details has been displayed successfully.
+
+The following sequence diagram shows how the `view` operation works:
+
+![ViewSequenceDiagram](images/ViewSequenceDiagram.png)
+
+The following activity diagram summarizes what happens when a user executes a `view` command:
+
+![ViewActivityDiagram](images/ViewActivityDiagram.png)
+
+
+### Compare feature
+
+The compare mechanism allows users to compare two distinct TA applicants in the TAfinder app.
+
+#### Implementation
+
+The compare mechanism is mainly facilitated by the `CompareCommand`, `CompareCommandParser`, and `CompareWindow` classes. It extends the abstract class `Command` with an `execute` functionality, to facilitate the execution of the command. Specifically, the compare feature is implemented through the following components and operations:
+
+- `CompareCommand` — Core component responsible for executing the comparison of two TA applicants in the list.
+- `Person` — Represents the TA applicants with their respective fields, such as `Gpa`, to be used for comparison.
+- `CompareCommandParser` — Contains the functionalities for user input parsing. It ensures that user input is valid as a compare command by meeting specific requirements.
+- `CompareWindow` — Main User Interface (UI) for after a compare command is successfully executed. It will display the content of the two TA applicants side by side.
+
+
+Given below is an example usage scenario and how the compare mechanism behaves at each step.
+
+Step 0. Assume that there is an existing list of applicants in the application after launch.
+
+Step 1. The user enters the compare command `compare 1 2` to compare the first and second applicants in the existing list. The `CompareCommandParser` is invoked to parse the user's input.
+
+Step 2. `CompareCommandParser` will then invoke `ParserUtil` for parsing of the indices and check for index errors. If indices are invalid, the system will generate an error message. The error message will be displayed to the user, providing clear feedback about the issue and the specific constraints that are not met.
+
+Step 3. If indices are valid, `CompareCommand#execute()` fetches the two intended applicants from the currently visible list and ensures that both indexes do not point to the same applicant.
+
+Step 4. Then, `CompareCommand#execute()` creates a new `CompareWindow` instance which is immediately shown, with the two applicants' information passed to `CompareWindow`, in the form of the `Person` model.
+
+The following sequence diagram displays how the compare function works until Step 4:
+
+![CompareSequenceDiagram](images/CompareSequenceDiagram.png)
+
+Step 5. `CompareWindow` handles the GUI presentation aspects, in the form of a pop-up window. It uses the JavaFX `GridPane` layout to display the respective `Person` attributes side-by-side.
+
+Step 6. A success message is displayed to the user to confirm that the comparison of applicants is successful.
+
+The following activity diagram summarizes what happens when a user executes a `compare` command:
+
+![CompareActivityDiagram](images/CompareActivityDiagram.png)
+
+### Comment feature
+
+The comment command allows users to insert a comment on TA applicants in TAfinder app.
+
+#### Implementation
+
+The comment mechanism is mainly facilitated by the `CommentCommand` and `CommentCommandParser` classes. It extends the abstract class `Command` with an `execute` functionality, to facilitate the execution of the command. Specifically, the comment feature is implemented through the following components and operations:
+
+- `CommentCommand` — Core component responsible for executing the adding of comments to a TA applicant in the list.
+- `Person` — Represents the TA applicants with their respective fields, which includes the `comment` field.
+- `CommentCommandParser` — Contains the functionalities for user input parsing. It ensures that user input is valid as a compare command by meeting specific requirements.
+- `MainWindow` — Main User Interface (UI) for after a comment command is successfully executed. It will display the content of the TA applicant with the comment added.'
+
+Given below is an example usage scenario and how the comment mechanism behaves at each step.
+
+Step 0. Assume that there is an existing list of applicants in the application after launch.
+
+Step 1. The user enters the comment command `comment 3 c/Hardworking` to comment on the 3rd applicant in the applicant
+list with `Hardworking` as the comment by executing. The `CommentCommmandParser` is invoked to parse the user's input.
+
+Step 2. If the index is valid, `CommentCommand#execute()` fetches the intended applicant from the currently visible list
+and adds the comment `Hardworking` to the applicant with the corresponding index. The adding of comment is destructive,
+meaning if the specified applicant has an existing comment, it will be overwritten.
+
+Step 3. `CommmentCommandParser` will then invoke ParserUtil for parsing of the index and check for index errors.
+If the index is invalid, the system will generate an error message. The error message will be displayed to the user,
+providing clear feedback about the issue and the specific constraints that are not met.
+
+Step 4. Then, `CommentCommand#execute()` updates `Model#setModel` with the updated Person.
+Displaying the updated applicant list in `MainWindow` with the comment added.
+
+Step 5. A success message is displayed to the user to confirm that the comment has been added to the applicant successfully.
+
+![CommentSequenceDiagram](images/CommentSequenceDiagram.png)
-
+The following activity diagram summarizes what happens when a user executes a `comment` command:
-#### Design considerations:
+![CommentActivityDiagram](images/CommentActivityDiagram.png)
-**Aspect: How undo & redo executes:**
+#### Design considerations
-* **Alternative 1 (current choice):** Saves the entire address book.
- * Pros: Easy to implement.
- * Cons: May have performance issues in terms of memory usage.
+**Aspect: Comparison GUI:**
-* **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.
+* **Alternative 1 (current choice):** Compare two applicants in one pop-up window.
+ * Pros:
+ 1. Easier to implement the pop-up window
+ 2. Able to avoid over-dependencies within the UI component.
+ * Cons: Design may not be uniform with main window.
-_{more aspects and alternatives to be added}_
+* **Alternative 2:** Compare two applicants in the main window.
+ * Pros:
+ 1. Easy view of information.
+ * Cons:
+ 1. Difficult to implement the UI change when a compare command is inputted, as whole window needs to be modified.
+ 2. Inconvenient to refer back to list of applicants if needed.
-### \[Proposed\] Data archiving
+* **Alternative 3:** Compare multiple (two or more) applicants.
+ * Pros: More convenient to choose between applicants.
+ * Cons: Harder to implement the UI.
-_{Explain here how the data archiving feature will be implemented}_
+### Import feature
+
+The import feature allows users to mass-create applicants within the application, from a list of applicants in a CSV file. This file could be exported from mySoC or elsewhere, as long as it is in the correct format.
+
+#### Implementation
+
+The format of the CSV file is as follows:
+
+1. The file must only contain text separated by commas and newlines — a limited subset of the true CSV file format. No escaping or quoting is allowed.
+2. The first line must be the header row, containing column names. The columns can be in any order, but must contain the following (case-sensitive):
+ - studentNo
+ - name
+ - phone
+ - email
+ - gpa
+ - previousGrade
+ - tags
+3. The following lines contain one applicant per row. The data of the applicant should be in the order of which the header row specified the columns i.e. if column 1 is `studentNo`, the first cell of each row should contain the applicant's student number.
+
+Here is an example CSV:
+
+```csv
+studentNo,name,phone,email,gpa,previousGrade,tags
+A0123486A,Jasmine David,98472983,jasmine_david@u.nus.edu,4.3,B+,deansList;pastTA
+A0456123A,Sandeep Kopparthi,86753746,sandeep@u.nus.edu,5.0,B+,pastTA
+A0775848D,Lim Boon Kong,97777777,boonkong@u.nus.edu,3.5,C,deansList
+A0483910A,Mohammed Taufiq bin Rozaini,85535252,taufiq@u.nus.edu,4.2,A+,
+```
+
+The import mechanism is mainly facilitated by the `ImportCommand`, `ImportCommandParser`, and `Attachment` classes. It extends the abstract class `Command` with an `execute` functionality, to facilitate the execution of the command. Specifically, the import feature is implemented through the following components and operations:
+
+- `ImportCommand` — Core component responsible for executing the import of TA applicants from the CSV file.
+- `ImportCommandParser` — Contains the functionalities for user input parsing. It ensures that user input is valid as an import command by meeting specific requirements.
+- `Person` — Represents the TA applicants with their respective fields, such as `Attachment`, which are created and added to the main model.
+- `Attachment` — Represents a reference to a file. This is merely used as a representation and handle to a file rather than to attach to any `Person` in particular.
+
+Given below is an example usage scenario and how the attach mechanism behaves at each step.
+
+Step 0. Assume that there is a valid CSV file to import named `applicants.csv`, such as the one above, and that it is in the same directory as the `tafinder.jar` file that was executed.
+
+Step 1. The user enters the import command `import applicants.csv` to import applicants from the CSV file specified in the previous step.
+
+Step 2. `ImportCommandParser` will then invoke `ParserUtil` for parsing of the file path to check for any invalid path characters. If the file path contains invalid path characters, the system will generate an error message. The error message will be displayed to the user, providing clear feedback about the issue and the specific constraints that are not met.
+
+Step 3. If indices are valid, `ImportCommand#execute()` opens the file and creates a `Scanner` over the file handle.
+
+Step 4. Then, the header row of the CSV is read and the order of the headers is saved into a map for later reference.
+
+Step 5. Next, a loop is executed to read through the rows of the CSV one-by-one. Each row is split via the comma, then parsed in the order that was recorded in the previous step. Any validation errors found cause a new error line number to be reported as an error, but no exception is thrown as we want to parse as many applicants as possible. A `Person` instance is created with the relevant data for each successful line i.e. no validation error.
+
+Step 6. Finally, a message is displayed to the user indicating the number of applicants successfully imported, the number of applicants that failed to be imported, and the line numbers containing those failed applicants.
+
+
+### Attach feature
+
+The attach feature allows users to attach files to TA applicants in the app. Attaching files *copies* these files into the `data` directory and adds a reference to those files to that `Person` model. This means that even if the original files are deleted, TAfinder would still have access to the copies of those files.
+
+#### Implementation
+
+The attachment mechanism is mainly facilitated by the `AttachCommand`, `AttachCommandParser`, and `Attachment` classes. It extends the abstract class `Command` with an `execute` functionality, to facilitate the execution of the command. Specifically, the attach feature is implemented through the following components and operations:
+
+- `AttachCommand` — Core component responsible for executing the attachment of a file to an applicant.
+- `AttachCommandParser` — Contains the functionalities for user input parsing. It ensures that user input is valid as an attach command by meeting specific requirements.
+- `Person` — Represents the TA applicants with their respective fields, such as `Attachment`, to attach files to.
+- `Attachment` — Represents a reference to a file. This can be a file that has been "attached" to a `Person`, or just a file within the file system of the computer.
+
+Given below is an example usage scenario and how the attach mechanism behaves at each step.
+
+Step 0. Assume that there is an existing list of applicants in the application after launch.
+
+Step 1. The user enters the attach command `attach 1 f/Downloads/resume.pdf f/Downloads/cv.txt` to attach the files `resume.pdf` and `cv.txt` in the `Downloads` directory to the first user in the visible list.
+
+Step 2. `AttachCommandParser` will then invoke `ParserUtil` for parsing of the index and check for index errors, and then parses the file paths to check for any invalid path characters. If index is invalid or the file path contains invalid path characters, the system will generate an error message. The error message will be displayed to the user, providing clear feedback about the issue and the specific constraints that are not met.
+
+Step 3. If indices are valid, `AttachCommand#execute()` fetches the intended applicant from the currently visible list.
+
+Step 4. Then, `AttachCommand#execute()` copies each attachment into the `data` directory, to the path `data/attachments/{student number}/{filename}`.
+
+Step 5. Finally, a success message is displayed to the user indicating the number of attachments that have been copied.
+
+The following sequence diagram shows how the `attach` operation works:
+
+![AttachSequenceDiagram](images/AttachSequenceDiagram.png)
+
+The following activity diagram summarizes what happens when a user executes a `attach` command:
+
+![AttachActivityDiagram](images/AttachActivityDiagram.png)
--------------------------------------------------------------------------------------------------------------------
-## **Documentation, logging, testing, configuration, dev-ops**
+## Documentation, logging, testing, configuration, dev-ops
+
+We've broken these out into separate guides, do check them out.
* [Documentation guide](Documentation.md)
* [Testing guide](Testing.md)
@@ -251,62 +459,135 @@ _{Explain here how the data archiving feature will be implemented}_
--------------------------------------------------------------------------------------------------------------------
-## **Appendix: Requirements**
+## Appendix: Requirements
### Product scope
**Target user profile**:
-* has a need to manage a significant number of contacts
-* prefer desktop apps over other types
-* can type fast
-* prefers typing to mouse interactions
-* is reasonably comfortable using CLI apps
+* **University Professors**, specifically:
+ * Tech-savvy
+ * Within NUS SoC
+ * Responsible for selecting student TAs
+ * Managing a significant number of TA applicants
+ * Can type fast
+ * Prefers typing over all other means of input
+
+**Value proposition**:
+
+* **Efficient TA Selection:**
+ * Simplify the process of identifying and selecting qualified TAs within NUS SoC. Find the most suitable TAs from a diverse pool of applicants efficiently.
+
+* **Enhanced Convenience:**
+ * Conveniently connect with students and potential TAs, streamlining communication and making the TA selection process smoother.
+
+* **Effective Evaluation:**
+ * Assess TA applicants based on a range of holistic rubrics, including grades, student life involvement, past TA experiences, etc.
+
+
+### User Stories
+
+#### High Priority (Must-Have)
+
+| As a... | I want to... | So that I can... |
+| ------------------------------ | -------------------------------------------------- | ---------------------------------------------------- |
+| NUS SOC professor | add a TA applicant and details to the list | see the TA applicant in the list |
+| NUS SOC professor | hide a TA applicant from the list | narrow down the list of applicants for review |
+| NUS SOC professor | edit the details of a TA applicant | keep their information up-to-date or correct errors |
+| NUS SOC professor | click to view the full details of a TA applicant | determine if they’re a fit for the position accordingly |
+| NUS SOC professor | search for a specific TA applicant by name or details | easily find their information without scrolling |
+| NUS SOC professor | delete a TA applicant | remove entries that are no longer needed |
+
+#### Medium Priority (Nice-to-Have)
+
+| As a... | I want to... | So that I can... |
+| ------------------------------ | -------------------------------------------------------- | --------------------------------------------------------- |
+| NUS SOC professor who wants the best for my students | sort TA applicants by grades or other factors | find the best TA applicant for my module |
+| NUS SOC professor | add comments/notes for myself on a TA applicant | refer to them when comparing applicants |
+| NUS SOC professor | export the list of TA applicants to a spreadsheet | easily share the list with colleagues or refer to it offline |
+| NUS SOC professor | compare between two TA applicants on the same screen | make a final decision on selecting the TA for my module |
+| NUS SOC professor | rate and provide feedback on TA applicants after interviews or assessments | collaborate with colleagues on the hiring decision. |
+| NUS SOC professor | attach files (e.g., resumes) to TA applicant profiles | keep all pertinent information in one place. |
+
+#### Low Priority (Unlikely to Have)
+
+| As a... | I want to... | So that I can... |
+| ------------------------------ | ---------------------------------------------------------- | ----------------------------------------------------------- |
+| NUS SOC professor | set up automated notifications for when new TA applicants apply | stay informed without constantly checking the list. |
+| NUS SOC professor | send TA applicants invitations and reminders | schedule interviews with TA applicants directly through the system. |
+| NUS SOC professor | track the progress and performance of hired TAs throughout the semester | provide feedback and assess their contributions. |
+| NUS SOC professor | filter applicants by availability and schedule | match them with suitable time slots for my module |
+| NUS SOC professor | set up automatic reminders for TA performance evaluations | stay organized and proactive in managing my teaching assistants. |
+| NUS SOC professor | track the number of applicants for each TA position | gauge the level of interest and competition for specific roles. |
+| NUS SOC professor | archive or store past TA applicant data | reference them in future semesters or academic years. |
+| NUS SOC professor | sort the list of TA applicants by their application status | streamline the selection process. |
+| NUS SOC professor | archive previous TA applicant profiles | maintain a historical record of applicants. |
+| NUS SOC professor | create and manage multiple TA applicant lists | keep information organized for different modules or terms. |
+| NUS SOC professor | batch process approvals or rejections of TA applications | save time during the selection process. |
+| NUS SOC professor | access a help feature within the platform | understand how to use different functionalities efficiently. |
+| NUS SOC professor | customize the appearance and layout of the TA applicant list | enhance the user experience. |
+| NUS SOC professor | flag TA applicants for further review | remember to revisit certain profiles as the process continues. |
+| NUS SOC professor | generate reports summarizing TA applicant data | easier overview and presentation to department heads or committees. |
+| NUS SOC professor | retrieve deleted or archived TA applicant data | recover information if needed. |
+| NUS SOC professor | access a log of all actions performed on the system | enhance security and accountability. |
+| NUS SOC professor | receive recommendations on TA applicants based on AI analysis | facilitate a smarter selection process. |
+| NUS SOC professor | import a spreadsheet of TA applicants to my module | save time by not adding them individually |
+| NUS SOC professor | search for a TA applicant using an identifier | contact the TA directly if needed |
-**Value proposition**: manage contacts faster than a typical mouse/GUI driven app
+### Use cases
+(For all use cases below, the **System** is `TAfinder` and the **Actor** is `NUS SOC professor`, unless specified otherwise)
-### User stories
+**UC01 - Edit an applicant**
-Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*`
+**Preconditions:**
-| 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 |
+* User is logged into the system.
+* There is at least one applicant in the list.
-*{More to be added}*
+**MSS**
-### Use cases
+1. User requests to edit an applicant's information.
+2. System requests the index of the applicant to edit.
+3. User specifies the index and information.
+4. System validates the input.
+5. System updates the applicant's information.
+6. System shows the updated list of applicants.
+
+ Use case ends.
-(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise)
+**Extensions**
-**Use case: Delete a person**
+* 4a. The given index or information is invalid.
-**MSS**
+ * 4a1. System shows an error message.
-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
+ Use case resumes at step 2.
- Use case ends.
-**Extensions**
+**UC02 - Compare Two Applicants**
+
+**Preconditions:**
-* 2a. The list is empty.
+* User is logged into the system.
+* There are at least two applicants in the list.
- Use case ends.
+**MSS**
-* 3a. The given index is invalid.
+1. User requests to compare two applicants.
+2. System requests the indices of the first and second applicants to compare.
+3. User specifies the indices.
+4. System validates the input.
+5. System retrieves the information of both applicants.
- * 3a1. AddressBook shows an error message.
+ Use case ends.
- Use case resumes at step 2.
+**Extensions:**
+
+* 4a. The given indices are missing or invalid.
+ * 4a1. System shows an error message
+
+ Use case resumes at step 2.
*{More to be added}*
@@ -315,63 +596,133 @@ Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unli
1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed.
2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage.
3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse.
-
-*{More to be added}*
+4. Should be able to process user inputs in at most 100 milliseconds.
+4. Should have intuitive and easy-to-use commands that corresponds closely to their use cases (i.e. 'add' to add an item).
+5. Non-trivial methods and classes should be well-documented.
+6. Should provide a comprehensive help section accessible to professors for all the different features.
### Glossary
* **Mainstream OS**: Windows, Linux, Unix, OS-X
-* **Private contact detail**: A contact detail that is not meant to be shared with others
+* **TA (Teaching Assistant)**: A graduate or undergraduate student who assists a professor in teaching activities, such as grading, leading discussions or conducting tutorials.
+* **NUS professor**: A faculty member at the National University of Singapore (NUS) responsible for teaching and academic activities.
--------------------------------------------------------------------------------------------------------------------
-## **Appendix: Instructions for manual testing**
+## Appendix: Instructions for Manual Testing
-Given below are instructions to test the app manually.
+### Launch and shutdown
-
:information_source: **Note:** These instructions only provide a starting point for testers to work on;
-testers are expected to do more *exploratory* testing.
+1. Initial launch
+ 1. Download the jar file and copy into an empty folder
+ 2. Open a command-line and navigate to the directory the jar file is in
+ 3. Run the command `java -jar tafinder.jar`. Expected: Shows the GUI with a set of sample TA applicants. The window size may not be optimum.
+2. Shutdown
+ 1. Click “File”
+ 2. Click “Exit”. Expected: The app closes.
-
+### Hide and unhide
-### Launch and shutdown
+1. Hide the first applicant
+ 1. Run `hide 1`. Expected: The first applicant disappears.
+ 2. Run `list hidden`. Expected: Only the first applicant is shown.
+2. Unhide the hidden applicant
+ 1. Run the commands above beforehand to have a hidden applicant
+ 2. Run `list hidden`
+ 3. Run `unhide 1`. Expected: All the applicants are shown including the unhidden first applicant.
+3. Unhide all applicants
+ 1. Ensure there are at least 2 applicants in the current view.
+ 2. Run `list`
+ 3. Run `hide 1` then `hide 1` again. Expected: The first 2 applicants disappear.
+ 4. Run `unhide-all`. Expected: The first 2 applicants appear in the list again.
-1. Initial launch
+### Bookmark and unbookmark
+
+1. Bookmark the first applicant
+ 1. Run `bookmark 1`. Expected: The bookmark icon next to the first applicant becomes filled.
+ 2. Run `list bookmarked`. Expected: All the applicants that are bookmarked are shown.
+2. Unbookmark the first applicant
+ 1. Run the commands above beforehand to have a bookmarked applicant
+ 2. Run `list bookmarked`
+ 3. Run `unbookmark 1`. Expected: The unbookmarked applicant disappears from the list, so only the remaining bookmarked applicants are shown.
+
+### Sort
+
+1. Run `list`.
+2. Run `sort gpa`. Expected: The applicants should be sorted by GPA in descending order.
- 1. Download the jar file and copy into an empty folder
+### View
- 1. Double-click the jar file Expected: Shows the GUI with a set of sample contacts. The window size may not be optimum.
+1. Run `view 1`. Expected: All the details of the applicant with the index 1 are shown in the detail view.
-1. Saving window preferences
+### Compare
- 1. Resize the window to an optimum size. Move the window to a different location. Close the window.
+1. Ensure that there are at least 2 applicants in the current view
+2. Run `compare 1 2`. Expected: A new window is created with details from both applicants.
- 1. Re-launch the app by double-clicking the jar file.
- Expected: The most recent window size and location is retained.
+### Import
-1. _{ more test cases … }_
+1. Copy and paste the file below into a file called `sample.csv` in the **same directory** as the jar file
+2. Run `import sample.csv`. Expected: 4 applicants are successfully imported into TAfinder.
-### Deleting a person
+```
+studentNo,name,phone,email,gpa,previousGrade,tags
+A0123486A,Jasmine David,98472983,jasmine_david@u.nus.edu,4.3,B+,deansList;pastTA
+A0456123A,Sandeep Kopparthi,86753746,sandeep@u.nus.edu,5.0,B+,pastTA
+A0775848D,Lim Boon Kong,97777777,boonkong@u.nus.edu,3.5,C,deansList
+A0483910A,Mohammed Taufiq bin Rozaini,85535252,taufiq@u.nus.edu,4.2,A+,
+```
-1. Deleting a person while all persons are being shown
+### Attach
- 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list.
+1. Download or create a sample PDF file (you can use the exported user guide as a sample) and place it in the **same directory** as the jar file
+2. Ensure there is at least one applicant in the list
+3. Run `attach 1 f/name-of-file.pdf`. Expected: The file is attached to the first applicant by indication of filename in applicant details. The details can be viewed by running the command `view 1`.
- 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.
+--------------------------------------------------------------------------------------------------------------------
+
+## Appendix: Effort
- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same.
+### Difficulty Level
- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous.
+- Numerous challenges were encountered during the project's execution.
+- One of the challenges we faced was working together as a team and coordinating efforts to minimize merge conflicts. We held regular team meetings to discuss progress and identify conflicts. We used version control tools like Git to track changes and resolve conflicts early. We divided tasks among team members and established coding conventions for consistency. When conflicts occurred, we had a designated merge manager to resolve them. By prioritizing communication, planning, and conflict resolution, we minimized the impact of merge conflicts on our project.
+- Another major challenge we faced was managing and meeting tough deadlines as the project became more complex. To address this, we prioritized tasks based on importance and urgency, adjusted our timeline as needed, communicated openly within the team, and optimized productivity by leveraging our strengths. With this proactive and strategic approach, we successfully met the deadlines and delivered high-quality results.
+- Overall, we found this project to be challenging but rewarding. It provided us with opportunities to enhance our technical skills, collaborate effectively as a team, and overcome various obstacles. Despite the difficulties, we are proud of the accomplishments and growth we achieved throughout the project.
-1. _{ more test cases … }_
+### Effort Required
-### Saving data
+- Reused `model` classes that were already implemented (e.g. `AddressBookParser`, `Person`)
+- Reused `ui` classes and FXML files for certain parts of the overall GUI (eg. input box, message box)
+- Reused `Storage` classes that saves and loads data from files
-1. Dealing with missing/corrupted data files
+### Achievements
- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_
+- For most of us, this was our first experience contributing pull requests (PRs) to a project that was already established and had some existing code (known as a brownfield project).
+- This opportunity allowed us to expand our skills and knowledge beyond just working on Orbital, and gave us valuable experience in working with a larger and more complex codebase.
+- By contributing to a brownfield project, we were able to gain insights into the challenges of maintaining and improving existing code, and learn from the expertise of other developers who had worked on the project before us.
+
+--------------------------------------------------------------------------------------------------------------------
-1. _{ more test cases … }_
+## Appendix: Planned Enhancements
+
+1. **Limiting GPA Field to 2 Decimal Places**
+ - The current `GPA` field can accept more than 2 decimal places. We plan to limit the field to strictly 2 decimal places, and anything more than that will generate an error message.
+2. **Limiting Phone Number Field to At Most 8 Numbers**
+ - The current `phone number` field accepts unlimited numbers. We plan to limit the field to at most 8 numbers (length of phone number in Singapore), as our target audience is NUS professors and Teaching Assistants (TAs).
+3. **Requiring Alphabetical Characters in the Name Field**
+ - The current `name` field accepts a string of only numbers as a name, which should not be the case. We plan to limit the field to require alphabetical characters, and anything else would result in an error message.
+4. **Improving Display of Added Applicant Message**
+ - When a person is successfully added using the `add` command but there is no `comment` field for the person, the current main UI window will display a message `New applicant added: erferferferf Doe; Phone: 98765432; Email: [johnd@example.com](mailto:johnd@example.com); GPA: 4.9; Comment: Optional.empty; Tags: [pastTA]`, with a default `Optional.empty` string for the Comment field. We plan to enhance the message by removing the display of the `Comment` field, when none is added.
+5. **Improving Error Messages for Commands Requiring an Index**
+ - Currently, our application only accepts positive numbers (1, 2, 3…) as acceptable inputs that conform to the command format for commands that require indexes (it then validates these numbers to check the range etc.). Therefore, non-positive numbers (0, -1…) are considered as invalid formatting of the errors. Thus, the current error message is ‘`Invalid command format!'`. We plan to change the error message to better reflect the issue with non-positive numbers rather than have a generic invalid command format message.
+6. **Making Applicants in List Clickable**
+ - Although there is visual feedback on clicking each of the applicants, the details shown on the right side of the application does not change. We plan to allow the clicking of applicants in addition to the command `view INDEX` to show the details of each applicant.
+7. **Enhancing Compare Command Popup Window**
+ - Upon successfully invoking the `compare` command, a popup window will be displayed. However, if any of the applicants’ details (fields) are too long, the details will not be able to be displayed fully. We plan to enhance the popup window by allowing the details to be displayed fully by implementing scroll panels for each field instead.
+8. **Adjusting Color Schemes of Help and Compare Windows**
+ - The current UI color scheme for the help and compare windows, currently grey, differ quite a bit from the main window, where the color scheme is purple-centric. We plan to adjust the color schemes of these 2 pop-up windows to match that of the main window.
+9. **Limiting Interview Score Field to 2 Decimal Places**
+ - The current `Interview Score` field can accept more than 2 decimal places. We plan to limit the field to strictly 2 decimal places, and anything more than that will generate an error message.
+10. **Preventing Integer Overflow When Adding Applicants**
+ - If the user were to add more than 2147483647 (max integer for Java), an integer overflow error may result on the input of any command. Our enhancement would be to prevent users from adding more applicants when 2147483647 applicants are already in the list.
diff --git a/docs/Documentation.md b/docs/Documentation.md
index 3e68ea364e7..63e98128cd7 100644
--- a/docs/Documentation.md
+++ b/docs/Documentation.md
@@ -1,5 +1,4 @@
---
-layout: page
title: Documentation guide
---
diff --git a/docs/Logging.md b/docs/Logging.md
index 5e4fb9bc217..36a50c87dbf 100644
--- a/docs/Logging.md
+++ b/docs/Logging.md
@@ -1,5 +1,4 @@
---
-layout: page
title: Logging guide
---
diff --git a/docs/SettingUp.md b/docs/SettingUp.md
index 275445bd551..1bb632d2a1d 100644
--- a/docs/SettingUp.md
+++ b/docs/SettingUp.md
@@ -1,14 +1,7 @@
---
-layout: page
title: Setting up and getting started
---
-* Table of Contents
-{:toc}
-
-
---------------------------------------------------------------------------------------------------------------------
-
## Setting up the project in your computer
:exclamation: **Caution:**
diff --git a/docs/Testing.md b/docs/Testing.md
index 8a99e82438a..ea3013ebaf6 100644
--- a/docs/Testing.md
+++ b/docs/Testing.md
@@ -1,13 +1,7 @@
---
-layout: page
-title: Testing guide
+title: Testing
---
-* Table of Contents
-{:toc}
-
---------------------------------------------------------------------------------------------------------------------
-
## Running tests
There are two ways to run tests.
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 57437026c7b..cd579988048 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -1,41 +1,157 @@
---
layout: page
title: User Guide
+show-sticky-toc: true
---
-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.
+## Overview
-* Table of Contents
-{:toc}
+Introducing **TAfinder - the one-stop solution for all your Teaching Assistant (TA) selection needs**.
---------------------------------------------------------------------------------------------------------------------
+As an NUS School Of Computing (SOC) professor, with TAfinder, you can easily:
+1. **Manage** troves of TA applicants.
+2. Make more **informed decisions** with the help of our **compare** and **sort** functions.
+3. **Import** data to the TAfinder application.
+
+TAfinder utilises a Command Line Interface (CLI), while still enjoying the benefits of a Graphical User Interface (GUI),
+for a more efficient user experience.
+
+So say goodbye to the days of manually sifting through hundreds of TA applications. Enrich your TA selection process with
+the power of TAfinder today!
+
+## About This Guide
+
+This guide shows you the relevant information for setting up and using TAfinder to manage your TA applications.
+
+You can click on any of the links below to navigate to the respective sections for more information.
+
+{% include toc.md header=true show-in-toc=true ordered=true %}
## Quick start
-1. Ensure you have Java `11` or above installed in your Computer.
+### Prerequisites
+
+#### Java
+Ensure you have [Java `11`](https://www.oracle.com/sg/java/technologies/javase/jdk11-archive-downloads.html)
+or above installed. Java is the language that your computer uses to understand TAfinder.
+
+
+
+**:bulb: Not sure how to check your Java version?**
+
+**Step 1.** Open up **Command Prompt** (Windows) or **Terminal** (Mac and Linux).
+
+**Step 2.** Type and run the command `java -version`.
+
+**Step 3.** Check the version number provided (`xxx`) is at least `11`.
+
+An example is shown below:
+ ```
+ > java -version
+ java version "xxx"
+ ```
+
+
-1. Download the latest `addressbook.jar` from [here](https://github.com/se-edu/addressbook-level3/releases).
+
-1. Copy the file to the folder you want to use as the _home folder_ for your AddressBook.
+#### Glossary
-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)
+| Words/Abbreviations | Explanation |
+|---------------------|----------------------------------------------------------------------------------------------------------------------------------|
+| **TA** | Teaching Assistant. |
+| **Applicant** | Applicant refers to a student who has applied as a TA. |
+| **Mainstream OS** | Windows, Linux or Mac. |
+| **CLI** | Command-Line Interface. |
+| **GUI** | Graphical User Interface. |
+| **Toast** | A popup alert to inform users about certain information. |
+| **Tag** | Tags are associated with applicants, users can tag applicants with any keyword they want, the number of tags are not restricted. |
+
+### Installation
+
+**Step 1.** Download the latest `tafinder.jar` file from [here](https://github.com/AY2324S1-CS2103T-W10-1/tp/releases/latest).
+
+**Step 2.** Copy the file to the folder you want to use as the _home folder_ for your TAfinder.
+
+**Step 3.** Double-click on the `tafinder.jar` file to start the TAfinder app.
+
+
+
+**:bulb: TAfinder does not open?**
+
+**Step 1.** Open a command terminal.
+
+**Step 2.** Navigate to the location of the `tafinder.jar` file.
+
+**Step 3.** Type in `java -jar ` (Keep in mind of the space at the end).
+
+**Step 4.** Drag and drop `tafinder.jar` into the command terminal.
+
+**Step 5.** Press enter and execute the command.
+
+An example of the final command is displayed below:
+
+ ```
+ > java -jar xxxx/xxxx/tafinder.jar
+ ```
+
+
-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:
+The GUI similar to the below should appear in a few seconds:
+![Ui](images/startupUI.png)
- * `list` : Lists all contacts.
+### Utilisation
- * `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.
+**Step 1:** Type the command in the Command Input 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:
- * `delete 3` : Deletes the 3rd contact shown in the current list.
+- `list` : Lists all applicants.
- * `clear` : Deletes all contacts.
+- `add s/A0251647W n/Harry Lee p/89064678 e/harry@example.com g/4.3 pg/A` : Adds an applicant named "Harry Lee" to the list.
- * `exit` : Exits the app.
+- `delete 3` : Deletes the 3rd applicant shown in the current list.
-1. Refer to the [Features](#features) below for details of each command.
+- `clear` : Deletes all applicants.
+
+- `exit` : Exits the app.
+
+**Step 2:** Observe the changes to the application.
+
+
+
+**:bulb: Not sure what the commands above do?**
+
+* Find all about the usable commands [here](#features).
+* Look at a summary of all the usable commands [here](#command-summary).
+* Return to the [Table of Contents](#toc-heading) to find your desired command.
+
+
+
+### Navigating the User Interface
+
+![UI with shaded areas](images/navigationUI.png)
+
+The UI has the following areas:
+- **Navigation Bar**
+ - This is where you can navigate to the `File` and `Help` menus.
+- **Command Input Box**
+ - This is where commands are typed.
+ - Press `Enter` to execute it.
+- **Command Result Screen**
+ - This is where the result of entering a command is displayed.
+- **Applicant List**
+ - This is where the list of applicants is displayed.
+- **Applicant Viewing Box**
+ - This is where the details of the selected applicant is displayed after the command `view` is executed.
+
+
+
+**:bulb: Not sure what `view` does?**
+
+* Find out more about `view` [here](#viewing-the-details-of-a-single-applicant-view).
+
+
--------------------------------------------------------------------------------------------------------------------
@@ -46,128 +162,937 @@ AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized fo
**:information_source: Notes about the command format:**
* Words in `UPPER_CASE` are the parameters to be supplied by the user.
- e.g. in `add n/NAME`, `NAME` is a parameter which can be used as `add n/John Doe`.
+ **e.g.** `add n/NAME`,
+ `NAME` is a parameter which can be used as `add n/John Doe`.
+
* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`.
+ **e.g.** `n/NAME [t/TAG]` ,
+ can be used as `n/John Doe t/friend` or as `n/John Doe`.
+
+
+* Items with `…` after them can be used zero or more times.
+ **e.g.** `[t/TAG]…`,
+ can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
-* Items with `…` after them can be used multiple times including zero times.
- e.g. `[t/TAG]…` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc.
* Parameters can be in any order.
- e.g. if the command specifies `n/NAME p/PHONE_NUMBER`, `p/PHONE_NUMBER n/NAME` is also acceptable.
+ **e.g.** if the command specifies `n/NAME p/PHONE_NUMBER`,
+ `p/PHONE_NUMBER n/NAME` is also acceptable.
+
-* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
+* Extraneous parameters will be ignored.
e.g. if the command specifies `help 123`, it will be interpreted as `help`.
-* If you are using a PDF version of this document, be careful when copying and pasting commands that span multiple lines as space characters surrounding line-breaks may be omitted when copied over to the application.
+
+* 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.
+
+
+
+**`add s/STUDENT_NUMBER n/NAME p/PHONE e/EMAIL g/GPA [pg/PREVIOUS_GRADE] [is/INTERVIEW_SCORE] [c/COMMENT] [t/TAG]…`**
+
+- **`s/STUDENT_NUMBER`**: Student number of the applicant.
+- **`n/NAME`**: Name of the applicant.
+- **`p/PHONE`**: Phone number of the applicant.
+- **`e/EMAIL`**: Email address of the applicant.
+- **`g/GPA`**: GPA of the applicant.
+- **`pg/PREVIOUS_GRADE`**: Previous grade of the applicant.
+- **`[is/INTERVIEW_SCORE]`**: Interview score of the applicant.
+- **`[c/COMMENT]`**: Comments for the applicant.
+- **`[t/TAG]`**: Tags of the applicant.
+
-### Viewing help : `help`
+
+
+:bulb: You can add more than 1 tag to an applicant by adding more `t/TAG` parameters.
-Shows a message explaning how to access the help page.
+
:bulb: **Tip:**
-A person can have any number of tags (including 0)
-Examples:
-* `add n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01`
-* `add n/Betsy Crowe t/friend e/betsycrowe@example.com a/Newgate Prison p/1234567 t/criminal`
+Following the example above, if you entered everything correctly, you should see the following:
+
+![add.jpg](images/add_afterUI.png)
+
-### Listing all persons : `list`
+**Expected Outputs:**
-Shows a list of all persons in the address book.
+
+
+**Erroneous Outputs:**
-Edits an existing person in the address book.
+
-Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…`
+Invalid command format:
+**`Invalid command format!`
+`add: Adds an applicant to the list. Parameters: s/STUDENT NUMBER n/NAME p/PHONE e/EMAIL g/GPA pg/PREV GRADE
+[is/INTERVIEW SCORE] [c/COMMENT] [t/TAG]...`
+`Example: add s/A0343434C n/John Doe p/98765432 e/johnd@example.com g/4.9 pg/A is/9.1 c/Hardworking and diligent t/pastTA`**
+
+
-* Edits the person at the specified `INDEX`. The index refers to the index number shown in the displayed person list. The index **must be a positive integer** 1, 2, 3, …
-* At least one of the optional fields must be provided.
-* Existing values will be updated to the input values.
-* When editing tags, the existing tags of the person will be removed i.e adding of tags is not cumulative.
-* You can remove all the person’s tags by typing `t/` without
- specifying any tags after it.
+
-Examples:
-* `edit 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.
+Repeated applicant:
+**`This applicant already exists in the applicant list.`**
-### Locating persons by name: `find`
+
-Finds persons whose names contain any of the given keywords.
+---
-Format: `find KEYWORD [MORE_KEYWORDS]`
+#### Editing an applicant: `edit`
-* The search is case-insensitive. e.g `hans` will match `Hans`
-* The order of the keywords does not matter. e.g. `Hans Bo` will match `Bo Hans`
-* Only the name is searched.
-* Only full words will be matched e.g. `Han` will not match `Hans`
-* Persons matching at least one keyword will be returned (i.e. `OR` search).
- e.g. `Hans Bo` will return `Hans Gruber`, `Bo Yang`
+**Format:**
-Examples:
-* `find John` returns `john` and `John Doe`
-* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png)
+
-### Deleting a person : `delete`
+**`edit INDEX [s/STUDENT NUMBER] [n/NAME] [p/PHONE] [e/EMAIL] [g/GPA] [pg/PREVIOUS_GRADE] [is/INTERVIEW_SCORE] [c/COMMENT] [t/TAG]…`**
-Deletes the specified person from the address book.
+- **`INDEX`**: The index of the applicant to edit. The index must be a positive integer (e.g., 1, 2, 3…).
-Format: `delete INDEX`
+- `[optional fields]`: **At least one** of the following optional fields must be provided for editing:
+ - **`s/STUDENT NUMBER`**: Student number of the applicant.
+ - **`n/NAME`**: Name of the applicant.
+ - **`p/PHONE`**: Phone number of the applicant.
+ - **`e/EMAIL`**: Email address of the applicant.
+ - **`g/GPA`**: GPA of the applicant.
+ - **`pg/PREVIOUS_GRADE`**: Previous grade of the applicant.
+ - **`c/comment`**: Comment of the applicant.
+ - **`is/INTERVIEW_SCORE`**: Interview score of the applicant.
+ - **`t/TAG`**: Tags of the applicant. Note that editing tags will replace existing tags; it is not cumulative.
+ - To remove all existing tags, use **`t/`** without specifying any tags after it.
-* Deletes the person at the specified `INDEX`.
-* The index refers to the index number shown in the displayed person list.
-* The index **must be a positive integer** 1, 2, 3, …
+
-Examples:
-* `list` followed by `delete 2` deletes the 2nd person in the address book.
-* `find Betsy` followed by `delete 1` deletes the 1st person in the results of the `find` command.
+**Examples:**
+
+
+
+**`edit 1 p/91234567 e/johndoe@example.com`**
+- Edits the following fields of the first person in the list:
+ - **Phone number**: **`91234567`**
+ - **Email address**: **`johndoe@example.com`**
+
+
+
+
+
+**`edit 2 n/Alex Yeoh t/`**
+- Edits the following fields of the second person in the list:
+ - **Name**: **`Betsy Crower`**
+ - Clears all existing tags
+
+
+
+Following the example above, if you entered everything correctly, you should see the following:
+
+![edit function UI](images/edit_afterUI.png)
+
+**Expected Outputs:**
+
+
-### Clearing all entries : `clear`
+**Erroneous Outputs:**
+
+
+
+Invalid command format:
+`Invalid command format!`
+`edit: Edits the details of the applicant identified by the index number used in the displayed applicant list. Existing values will be overwritten by the input values.`
+`Parameters: INDEX (must be a positive integer) [n/NAME] [p/PHONE] [e/EMAIL] [g/GPA] [c/COMMENT] [t/TAG]...`
+`Example: edit 1 p/91234567 e/johndoe@example.com`
+
+
+
+
+
+Index out of range:
+**`Error: Invalid index. Please enter an index within range.`**
+
+
+
+---
+
+#### Deleting an applicant: `delete`
+
+
+![delete function UI](images/delete_afterUI.png)
+
+**Format:**
+
+
+
+**`delete INDEX`**
+
+**`INDEX`**: The index corresponding to the applicant to be deleted. The index must be a positive integer (e.g., 1, 2, 3…).
+
+
+
+**Examples:**
+
+
+
+**`delete 3`**
+- Deletes the third applicant in the list.
+
+
-Clears all entries from the address book.
+**Erroneous Outputs:**
-Format: `clear`
+
-### Exiting the program : `exit`
+Missing index:
+**`Invalid command format!`
+`delete: Displays the applicant identified by the index number used in the displayed applicant list.`
+`Parameters: INDEX (must be a positive integer)`
+`Example: delete 1`**
+
+
+
+
+
+Index out of range:
+**`Error: Invalid index. Please enter an index within range.`**
+
+
+
+**`list`**
+- Shows a list of all applicants.
+
+
+
+![listUI](images/listUI.png)
+
+
+
+**`list hidden`**
+- Shows a list of all hidden applicants.
+
+
+
+![listhiddenUI](images/listhiddenUI.png)
+
+
+
+**`list bookmarked`**
+- Shows a list of all bookmarked applicants.
+
+
+
+![listbookmarkedUI](images/listbookmarkedUI.png)
+
+---
+
+#### Viewing the details of a single applicant: `view`
+
+Displays the details of a specified applicant in the details panel.
+
+**Format:**
+
+
+
+**`view INDEX`**
+
+**`INDEX`**: The index corresponding to the applicant you want to be displayed. The index must be a positive integer (e.g., 1, 2, 3…).
+
+
+
+**Examples:**
+
+
+
+**`view 3`**
+- Displays the following details about the third applicant.
+ - Name
+ - Tags
+ - Phone number
+ - Email Address
+ - GPA
+ - Previous Grade
+ - Interview Score
+ - Comments
+ - Attachments
+
+
+Following the example above, if you entered everything correctly, you should see the following:
+
+![view function UI](images/viewUI.png)
+
+**Expected Outputs:**
+
+
+
+Sample display of the details of an applicant:
+- **`Name: John Doe`**
+- **`Tags: pastTA, deansList`**
+- **`Phone Number: 91234567`**
+- **`Email Address: johndoe@example.come`**
+- **`GPA: 5.0`**
+- **`Previous Grade: A`**
+- **`Interview Scorw (optional): 8`**
+- **`Comment (optional): Good fit, has teaching experience`**
+- **`Attachments: Resume.pdf`**
+
+Confirmation message:
+**`Displaying: APPLICANT_NAME.`**
+
+
+
+**Erroneous Outputs:**
+
+
+
+Missing index:
+**`Invalid command format!`
+`view: Displays the applicant identified by the index number used in the displayed applicant list.`
+`Parameters: INDEX (must be a positive integer)`
+`Example: view 1`**
+
+
+
+
+
+Index out of range:
+**`"Error: Invalid index. Please enter an index within range."`**
+
+
+
+---
+
+#### Hiding an applicant from list: `hide`/`unhide`
+
+Hides/unhides an applicant from the list of applicants.
+
+**Format:**
+
+
+
+`hide INDEX`
+- Hides the applicant at the specified INDEX from all future lists. The index refers to the number shown in the displayed person list.
+ Index must be a positive integer (e.g. 1, 2, 3...).
+
+
+
+![hideUI](images/hide_afterUI.png)
+
+
+
+`unhide INDEX`
+- Unhides the applicant at the specified INDEX from all future lists. The index refers to the number shown in the displayed person list.
+ Index must be a positive integer (e.g. 1, 2, 3...).
+
+
+
+![unhideUI](images/unhide_afterUI.png)
+
+
+
+`unhide-all`
+- Unhides all applicants that were previously hidden.
+
+
+
+**`hide 2`**
+- Hides the applicant at index 2.
+
+
+
+
+
+**`list hidden` followed by `unhide 2`**
+- Lists all hidden applicants, then unhides the applicant at index 2 in the list of hidden applicants.
+
+
+
+
+
+**`unhide-all`**
+- Unhides all applicants.
+
+
+
+Missing index:
+**`Invalid command format!`
+`hide: Hides an applicant, identified by the index number used in the last list, from all future lists of applicants.`
+`Parameter: INDEX (must be a positive integer)`
+`Example: hide 1`**
+
+
+
+
+
+Index out of range:
+**`Error: Invalid index. Please enter an index within range.`**
+
+
+
+---
-Exits the program.
+#### Clearing all applicants: `clear`
-Format: `exit`
+Clears your entire list of applicants.
+**Format:**
+
+
+
+**`clear`**
+
+
+
+Following the example above, if you entered everything correctly, you should see the following:
+
+
+| Before | After |
+|:-----------------------------------------------:|:----------------------------------------------:|
+| ![clear function UI](images/clear_beforeUI.png) | ![clear function UI](images/clear_afterUI.png) |
+
+**Expected Outputs:**
+
+
+
+- Successfully cleared all applicants.
+- Confirmation message:
+**`Applicant list has been cleared!`**
+
+
+
+
+---
+
+#### Exiting the application: `exit`
+
+Exits the application, while ensuring all your changes are saved.
+
+**Format:**
+
+
+
+**`exit`**
+
+
+
+**Expected Outputs:**
+
+
+
+- Application closes.
+
+
+
+---
+
+### Applicant evaluation & comparison
+
+#### Sorting applicants by GPA: `sort`
+
+Sorts applicants by a designated field.
+
+| Before | After |
+|:----------------------------------:|:----------------------------------:|
+| ![sortUI](images/sort_afterUI.png) | ![sortUI](images/sort_afterUI.png) |
+
+**Format:**
+
+
+
+**`sort FIELD`**
+
+**`FIELD`**: The field that applicants are sorted by.
+* Valid fields: `name`, `studentNo`, `gpa`, `previousGrade`, `interviewScore`, `comment`, `phone`, `email`, `tags`
+
+
+
+**Expected Output:**
+
+
+
+- A sorted list of applicants.
+
+
+
+**Erroneous Outputs:**
+
+
+
+Empty list:
+**`No applicants to sort.`**
+
+
+
+---
+
+#### Comparing 2 applicants: `compare`
+
+Compares two applicants side by side to make informed decisions.
+
+
+| TAfinder window | Popup window |
+|:----------------------------------:|:----------------------------------------------:|
+| ![compareUI](images/compareUI.png) | ![compare_popupUI](images/compare_popupUI.png) |
+
+
+**Format:**
+
+
+
+**`compare INDEX1 INDEX2`**
+
+**`INDEX1`**: The index of the first applicant to compare.
+
+**`INDEX2`**: The index of the second applicant to compare.
+
+Both indices must be a positive integer (e.g., 1, 2, 3…), and should not be the same.
+
+
+
+**Examples:**
+
+
+
+**`compare 1 2`**
+- Compares the first and second applicants.
+
+
+
+**Expected Outputs:**
+
+
+
+- Successfully compare two applicants.
+- Confirmation message:
+ **`Comparison successful!`**
+- A side-by-side comparison of the two applicants is displayed in a user-friendly format.
+- This comparison window will include:
+ - Student number
+ - Name
+ - Various TA selection criteria such as:
+ - GPA
+ - Interview Score
+ - Module Grade
+ - Comments
+- The system also highlights the differences between the two applicants, making it easy to see variations in their profiles.
+
+
+
+**Erroneous Outputs:**
+
+
+
+Applicant not found:
+**`Error: One or both of the specified applicants were not found in the list.`**
+
+
+
+
+
+Comparing the same applicant:
+**`Error: Please provide distinct indices. You cannot compare the same applicant.`**
+
+
+
+
+
+Comparing more than 2 applicants:
+**`Invalid command format!`
+`Please follow the format: compare INDEX1 INDEX2.`
+`Parameters: INDEX (must be positive integers)`**
+
+
+
+---
+
+#### Bookmarking applicants: `bookmark`/`unbookmark`
+
+Bookmarks/Unbookmarks a specific applicant.
+
+**Format:**
+
+
+
+**`bookmark INDEX` / `unbookmark INDEX`**
+
+**`INDEX`**: The index corresponding to the applicant you want to bookmark/unbookmark. The index must be a positive integer (e.g., 1, 2, 3…).
+
+
+
+**Examples:**
+
+
+
+**`bookmark 2`**
+- Bookmarks the second applicant.
+
+
+
+Following the example above, if you entered everything correctly, you should see the following:
+
+| Before | After |
+|:-----------------------------------------------------:|:----------------------------------:|
+| ![bookmark function UI](images/bookmark_beforeUI.png) | ![bookmark function UI](images/bookmark_afterUI.png) |
+
+
+
+**`unbookmark 2`**
+- Unbookmarks the second applicant.
+
+
+
+Following the example above, if you entered everything correctly, you should see the following:
+
+| Before | After |
+|:---------------------------------------------------------:|:--------------------------------------------------------:|
+| ![unbookmark function UI](images/unbookmark_beforeUI.png) | ![unbookmark function UI](images/unbookmark_afterUI.png) |
+
+
+**Expected Outputs:**
+
+
+
+- Successfully bookmarked/unbookmarked applicant at the given index.
+- Confirmation message:
+ **`Applicant at index INDEX has been successfully bookmarked/unbookmarked.`**
+
+
+
+**Erroneous Outputs:**
+
+
+
+Missing index:
+**`Invalid command format!`
+`bookmark: Bookmarks an applicant, identified by the index number used in the last list, from all future lists of applicants.`
+`Parameter: INDEX (must be a positive integer)`
+`Example: bookmark 1`**
+
+
+
+
+
+Index out of range:
+**`Error: Invalid index. Please enter an index within range.`**
+
+
+
+---
+
+#### Commenting on applicant: `comment`
+
+One of yours TAs is unable to make it for the interview.
+You want to make a comment on the applicant to remind yourself to follow up with him/her.
+Lets find out how to do use the comment feature to do so.
+
+**Format:**
+
+
+
+**`comment INDEX c/COMMENT`**
+
+**`INDEX`**: The index corresponding to the applicant to be commented. The index must be a positive integer (e.g., 1, 2, 3…).
+
+The index of the applicant is the number beside the applicant's name in the list of applicants.
+
+
+
+**Example:**
+
+
+
+**`comment 3 c/Unable to make it for interview`**
+- Comments on the third applicant with the comment: "Unable to make it for interview"
+
+
+
+Following the example above, if you entered everything correctly, you should see the following:
+
+![commentUI.jpg](images/comment_afterUI.png)
+
+You have now successfully commented on the applicant. As you comment on more applicants in the list,
+watch out for the expected and erroneous messages below that could be displayed in the command result screen!
+
+**Expected Outputs:**
+
+
+
+- Successfully commented on the applicant at the given index.
+- Confirmation message:
+**`Applicant has been successfully commented on.`**
+
+
+
+**Erroneous Outputs:**
+
+
+
+Invalid command format:
+**`Invalid command format!"`
+`"comment: Edits the comment of the person identified by the index number used in the last person listing. Existing comment will be overwritten by the input."`
+`"Parameters: INDEX (must be a positive integer) c/ [COMMENT]"`
+`"Example: comment 1 c/ Hardworking student`**
+
+
+
+
+
+Index out of range:
+**`Error: Invalid index. Please enter an index within range.`**
+
+
+
+
+---
+
+### Files and data management
+
+#### Importing applicants from spreadsheet: `import`
+
+Imports an entire list of applicants along with their details from a CSV file. If applicants with the same student number already exist, they will be skipped.
+
+| Sample .csv | After import |
+|:----------------------------------:|:------------------------------------------:|
+| ![sampleCSV](images/samplecsv.png) | ![importUI](images/import_afterUI.png) |
+
+**Format:**
+
+
+
+**`import FILENAME`**
+
+**`FILENAME`**: The desired filename of the CSV file to import from (including the file extension).
+
+
+
+
+
+The first line of the CSV file should contain the column names e.g. `studentNo`, `name` in any order. It must contain all the column names as specified in the example below. The following rows should contain the data for each applicant in the order specified by the header row.
+
+```csv
+studentNo,name,phone,email,gpa,previousGrade,tags
+A0123486A,Jasmine David,98472983,jasmine_david@u.nus.edu,4.3,B+,deansList;pastTA
+A0456123A,Sandeep Kopparthi,86753746,sandeep@u.nus.edu,5.0,B+,pastTA
+A0775848D,Lim Boon Kong,97777777,boonkong@u.nus.edu,3.5,C,deansList
+A0483910A,Mohammed Taufiq bin Rozaini,85535252,taufiq@u.nus.edu,4.2,A+,
+```
+
+*Note: The CSV format supported is only a **subset** of the [RFC 4180 CSV standard](https://www.loc.gov/preservation/digital/formats/fdd/fdd000323.shtml). Only text, commas and newlines are respected. No quoting or escaping is recognised by the parser.*
+
+
+
+**Examples:**
+
+
+
+**`import ta-applicants.csv`**
+- Imports a entire list of applicants, from a file in the CSV format called `ta-applicants.csv` in the same directory as the JAR file, into TAfinder.
+
+
+
+**Expected Output:**
+
+
+
+- Successfully attached a file to the applicant at the corresponding index.
+- Confirmation message:
+ **`Imported `i` applicants successfully!`**
+
+
+
+**Erroneous Outputs:**
+
+
+
+Missing file permissions or invalid file path:
+**`Failed to open and load applicant file.`**
+
+
+
+---
+
+#### Attaching file to applicant profiles: `attach`
+
+Attaches local files to the profiles of applicants to provide even more richness and insight into their applications.
+
+![attach function UI](images/attach_afterUI.png)
+
+**Format:**
+
+
+
+**`attach INDEX f/FILEPATH`**
+
+**`INDEX`**: The index of the applicant to edit. The index must be a positive integer (e.g., 1, 2, 3…).
+
+**`FILEPATH`**: The desired path of the file to attach to the applicant’s profile. This is relative to the path of the JAR file unless either `/` or `C:\` is at the start of the path, then the path will be treated as an absolute path.
+
+
+
+**Examples:**
+
+
+
+**`attach 2 f/john-resume.pdf`**
+- Attaches the file called `john-resume.pdf` in the same directory as the `tafinder.jar` file to the second applicant in the applicant list.
+
+
+
+
+
+**`attach 78 f//home/jennifer/resumes/benson-resume.pdf`**
+- Attaches the file called `benson-resume.pdf` in the directory `/home/jennifer/resumes` to the 78th applicant in the applicant list.
+
+
+
+**Expected Outputs:**
+
+
+
+- Successfully attached a file to the applicant at the corresponding index.
+- Confirmation message:
+ **`Attached `i` attachments to `name`!`**
+
+
+
+**Erroneous Outputs:**
+
+
+
+Invalid file path or corrupted data:
+**`Failed to copy attachment.`**
+
+
+
+
+
+Any other unexpected error:
+**`Error: Unknown error. Please contact the app developer at contact@email.com`**
+
+
+
+---
+## Data Management
### Saving the data
-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
+TAfinder data is saved in the hard disk automatically after any command that changes the data. There is no need to save manually.
### Editing the data file
-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.
+TAfinder data is saved automatically as a JSON file `[JAR file location]/data/tafinder.json`. Advanced users are welcome to update data directly by editing that data file.
-
:exclamation: **Caution:**
-If your changes to the data file makes its format invalid, AddressBook will discard all data and start with an empty data file at the next run. Hence, it is recommended to take a backup of the file before editing it.
-
-### Archiving data files `[coming in v2.0]`
+
+
+**Warning!:**
-_Details coming soon ..._
+If your changes to the data file makes its format invalid, TAfinder 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.
+
+
--------------------------------------------------------------------------------------------------------------------
@@ -178,20 +1103,51 @@ _Details coming soon ..._
--------------------------------------------------------------------------------------------------------------------
-## Known issues
+## Known Issues
1. **When using multiple screens**, if you move the application to a secondary screen, and later switch to using only the primary screen, the GUI will open off-screen. The remedy is to delete the `preferences.json` file created by the application before running the application again.
--------------------------------------------------------------------------------------------------------------------
-
-## Command summary
-
-Action | Format, Examples
---------|------------------
-**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…` e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague`
-**Clear** | `clear`
-**Delete** | `delete INDEX` e.g., `delete 3`
-**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…` e.g.,`edit 2 n/James Lee e/jameslee@example.com`
-**Find** | `find KEYWORD [MORE_KEYWORDS]` e.g., `find James Jake`
-**List** | `list`
-**Help** | `help`
+## Summary
+### Prefix Summary
+
+| Parameter | Prefix | Rules |
+|-----------------|--------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Student Number | s/ | - Should be in the format `CdddddddC`, where `d` represents digit and `C` represents capital letters. |
+| Name | n/ | - Should only contain alphanumeric characters and spaces. |
+| Phone | p/ | - Should only contain digits. - Should have at least 3 digits. |
+| Email | e/ | - Should only be of the form `local@domain` and only accept alphanumeric characters. - `local` allows for special characters `+`, `_`, `.` and `-` as well. - `domain` must be at least 2 letters long. |
+| GPA | g/ | - Should be in the range of 0.00 to 5.00 inclusive. - Can be given in 0, 1 or 2 decimal places. |
+| Previous Grade | pg/ | - Should be one of the following: A+, A, A-, B+, B, B-, C+, C, D+, D, F. |
+| Interview Score | is/ | - Should be in the range of 0.00 to 10.00 inclusive. - Can be given in 0, 1 or 2 decimal places. |
+| Comment | c/ | - No restrictions. |
+| Tag | t/ | - Should only contain alphanumeric characters. - Should not contain spaces. |
+
+
+### Command Summary
+#### Basic applicant management
+
+| Action | Format, Examples |
+|-----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| **Add** | - `add s/STUDENT_NUMBER n/NAME p/PHONE e/EMAIL g/GPA pg/PREVIOUS_GRADE [is/INTERVIEW_SCORE] [c/COMMENT] [t/TAG]…` - e.g., `add s/A0269357C n/john doe p/91234567 e/johndoe@example.com g/5.0 pg/A+ t/pastTA t/deanslist` |
+| **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` |
+| **Delete** | - `delete INDEX` - e.g., `delete 3` |
+| **List** | - `list [FIELD]` |
+| **View** | - `view INDEX` - e.g., `view 3` |
+| **Hide/Unhide** | - `hide INDEX` / `unhide INDEX` - e.g., `hide 3` / `unhide 3` |
+
+#### Applicant comparison and evaluation
+
+| Action | Format, Examples |
+|-------------------------|-----------------------------------------------------------------------------------|
+| **Sort** | - `sort FIELD` - e.g., `sort gpa`, `sort name` |
+| **Compare** | - `compare INDEX1 INDEX2` - e.g.,`compare 1 2` |
+| **Bookmark/Unbookmark** | - `bookmark INDEX` / `unbookmark INDEX` - e.g., `bookmark 3` / `unbookmark 3` |
+| **Comment** | - `comment INDEX c/COMMENT` - e.g., `comment 3 c/Hardworking` |
+
+#### Data import and management
+
+| Action | Format, Examples |
+|-------------------------|-----------------------------------------------------------------|
+| **Import** | - `import FILENAME` - e.g., `import ta-applicants.csv` |
+| **Attach** | - `attach INDEX FILEPATH` - e.g.,`attach 2 john-resume.pdf` |
diff --git a/docs/_config.yml b/docs/_config.yml
index 6bd245d8f4e..c4114e407fc 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -1,4 +1,4 @@
-title: "AB-3"
+title: "TAfinder"
theme: minima
header_pages:
@@ -8,7 +8,7 @@ header_pages:
markdown: kramdown
-repository: "se-edu/addressbook-level3"
+repository: "AY2324S1-CS2103T-W10-1/tp"
github_icon: "images/github-icon.png"
plugins:
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
index 83ac5326933..2ab41953830 100644
--- a/docs/_includes/head.html
+++ b/docs/_includes/head.html
@@ -5,8 +5,6 @@
- {%- include custom-head.html -%}
-
- {{page.title}}
+ {{ page.title }}
diff --git a/docs/_includes/toc.html b/docs/_includes/toc.html
new file mode 100644
index 00000000000..81437a69869
--- /dev/null
+++ b/docs/_includes/toc.html
@@ -0,0 +1,183 @@
+
+{% capture tocWorkspace %}
+ {% comment %}
+ Copyright (c) 2017 Vladimir "allejo" Jimenez
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ {% endcomment %}
+ {% comment %}
+ Version 1.2.0
+ https://github.com/allejo/jekyll-toc
+
+ "...like all things liquid - where there's a will, and ~36 hours to spare, there's usually a/some way" ~jaybe
+
+ Usage:
+ {% include toc.html html=content sanitize=true class="inline_toc" id="my_toc" h_min=2 h_max=3 %}
+
+ Parameters:
+ * html (string) - the HTML of compiled markdown generated by kramdown in Jekyll
+
+ Optional Parameters:
+ * sanitize (bool) : false - when set to true, the headers will be stripped of any HTML in the TOC
+ * class (string) : '' - a CSS class assigned to the TOC
+ * id (string) : '' - an ID to assigned to the TOC
+ * h_min (int) : 1 - the minimum TOC header level to use; any header lower than this value will be ignored
+ * h_max (int) : 6 - the maximum TOC header level to use; any header greater than this value will be ignored
+ * ordered (bool) : false - when set to true, an ordered list will be outputted instead of an unordered list
+ * item_class (string) : '' - add custom class(es) for each list item; has support for '%level%' placeholder, which is the current heading level
+ * submenu_class (string) : '' - add custom class(es) for each child group of headings; has support for '%level%' placeholder which is the current "submenu" heading level
+ * base_url (string) : '' - add a base url to the TOC links for when your TOC is on another page than the actual content
+ * anchor_class (string) : '' - add custom class(es) for each anchor element
+ * skip_no_ids (bool) : false - skip headers that do not have an `id` attribute
+
+ Output:
+ An ordered or unordered list representing the table of contents of a markdown block. This snippet will only
+ generate the table of contents and will NOT output the markdown given to it
+ {% endcomment %}
+
+ {% capture newline %}
+ {% endcapture %}
+ {% assign newline = newline | rstrip %}
+
+ {% capture deprecation_warnings %}{% endcapture %}
+
+ {% if include.baseurl %}
+ {% capture deprecation_warnings %}{{ deprecation_warnings }}{{ newline }}{% endcapture %}
+ {% endif %}
+
+ {% if include.skipNoIDs %}
+ {% capture deprecation_warnings %}{{ deprecation_warnings }}{{ newline }}{% endcapture %}
+ {% endif %}
+
+ {% capture jekyll_toc %}{% endcapture %}
+ {% assign orderedList = include.ordered | default: false %}
+ {% assign baseURL = include.base_url | default: include.baseurl | default: '' %}
+ {% assign skipNoIDs = include.skip_no_ids | default: include.skipNoIDs | default: false %}
+ {% assign minHeader = include.h_min | default: 1 %}
+ {% assign maxHeader = include.h_max | default: 6 %}
+ {% assign nodes = include.html | strip | split: ' maxHeader %}
+ {% continue %}
+ {% endif %}
+
+ {% assign _workspace = node | split: '' | first }}>{% endcapture %}
+ {% assign header = _workspace[0] | replace: _hAttrToStrip, '' %}
+
+ {% if include.item_class and include.item_class != blank %}
+ {% capture listItemClass %} class="{{ include.item_class | replace: '%level%', currLevel | split: '.' | join: ' ' }}"{% endcapture %}
+ {% endif %}
+
+ {% if include.submenu_class and include.submenu_class != blank %}
+ {% assign subMenuLevel = currLevel | minus: 1 %}
+ {% capture subMenuClass %} class="{{ include.submenu_class | replace: '%level%', subMenuLevel | split: '.' | join: ' ' }}"{% endcapture %}
+ {% endif %}
+
+ {% capture anchorBody %}{% if include.sanitize %}{{ header | strip_html }}{% else %}{{ header }}{% endif %}{% endcapture %}
+
+ {% if htmlID %}
+ {% capture anchorAttributes %} href="{% if baseURL %}{{ baseURL }}{% endif %}#{{ htmlID }}"{% endcapture %}
+
+ {% if include.anchor_class %}
+ {% capture anchorAttributes %}{{ anchorAttributes }} class="{{ include.anchor_class | split: '.' | join: ' ' }}"{% endcapture %}
+ {% endif %}
+
+ {% capture listItem %}{{ anchorBody }}{% endcapture %}
+ {% elsif skipNoIDs == true %}
+ {% continue %}
+ {% else %}
+ {% capture listItem %}{{ anchorBody }}{% endcapture %}
+ {% endif %}
+
+ {% if currLevel > lastLevel %}
+ {% capture jekyll_toc %}{{ jekyll_toc }}<{{ listModifier }}{{ subMenuClass }}>{% endcapture %}
+ {% elsif currLevel < lastLevel %}
+ {% assign repeatCount = lastLevel | minus: currLevel %}
+
+ {% for i in (1..repeatCount) %}
+ {% capture jekyll_toc %}{{ jekyll_toc }}{{ listModifier }}>{% endcapture %}
+ {% endfor %}
+
+ {% capture jekyll_toc %}{{ jekyll_toc }}{% endcapture %}
+ {% else %}
+ {% capture jekyll_toc %}{{ jekyll_toc }}{% endcapture %}
+ {% endif %}
+
+ {% capture jekyll_toc %}{{ jekyll_toc }}
+
diff --git a/docs/_layouts/page.html b/docs/_layouts/page.html
index 01e4b2a93b8..d26fc7ce87e 100644
--- a/docs/_layouts/page.html
+++ b/docs/_layouts/page.html
@@ -1,6 +1,7 @@
---
layout: default
---
+
diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss
index 0d3f6e80ced..b56670d5e6b 100644
--- a/docs/_sass/minima/_base.scss
+++ b/docs/_sass/minima/_base.scss
@@ -1,15 +1,28 @@
html {
- font-size: $base-font-size;
+ font-size: $base-font-size;
}
/**
* Reset some basic elements
*/
-body, h1, h2, h3, h4, h5, h6,
-p, blockquote, pre, hr,
-dl, dd, ol, ul, figure {
- margin: 0;
- padding: 0;
+body,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+hr,
+dl,
+dd,
+ol,
+ul,
+figure {
+ margin: 0;
+ padding: 0;
}
@@ -19,19 +32,19 @@ dl, dd, ol, ul, figure {
* Basic styling
*/
body {
- font: $base-font-weight #{$base-font-size}/#{$base-line-height} $base-font-family;
- color: $text-color;
- background-color: $background-color;
- -webkit-text-size-adjust: 100%;
- -webkit-font-feature-settings: "kern" 1;
- -moz-font-feature-settings: "kern" 1;
- -o-font-feature-settings: "kern" 1;
- font-feature-settings: "kern" 1;
- font-kerning: normal;
- display: flex;
- min-height: 100vh;
- flex-direction: column;
- overflow-wrap: break-word;
+ font: $base-font-weight #{$base-font-size}/#{$base-line-height} $base-font-family;
+ color: $text-color;
+ background-color: $background-color;
+ -webkit-text-size-adjust: 100%;
+ -webkit-font-feature-settings: "kern" 1;
+ -moz-font-feature-settings: "kern" 1;
+ -o-font-feature-settings: "kern" 1;
+ font-feature-settings: "kern" 1;
+ font-kerning: normal;
+ display: flex;
+ min-height: 100vh;
+ flex-direction: column;
+ overflow-wrap: break-word;
}
@@ -39,23 +52,34 @@ body {
/**
* Set `margin-bottom` to maintain vertical rhythm
*/
-h1, h2, h3, h4, h5, h6,
-p, blockquote, pre,
-ul, ol, dl, figure,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+p,
+blockquote,
+pre,
+ul,
+ol,
+dl,
+figure,
%vertical-rhythm {
- margin-bottom: $spacing-unit / 2;
+ margin-bottom: $spacing-unit / 2;
}
hr {
- margin-top: $spacing-unit;
- margin-bottom: $spacing-unit;
+ margin-top: $spacing-unit;
+ margin-bottom: $spacing-unit;
}
/**
* `main` element
*/
main {
- display: block; /* Default value of `display` of `main` element is 'inline' in IE 11. */
+ display: block;
+ /* Default value of `display` of `main` element is 'inline' in IE 11. */
}
@@ -64,8 +88,8 @@ main {
* Images
*/
img {
- max-width: 100%;
- vertical-align: middle;
+ max-width: 100%;
+ vertical-align: middle;
}
@@ -73,12 +97,12 @@ img {
/**
* Figures
*/
-figure > img {
- display: block;
+figure>img {
+ display: block;
}
figcaption {
- font-size: $small-font-size;
+ font-size: $small-font-size;
}
@@ -86,15 +110,17 @@ figcaption {
/**
* Lists
*/
-ul, ol {
- margin-left: $spacing-unit;
+ul,
+ol {
+ margin-left: $spacing-unit;
}
li {
- > ul,
- > ol {
- margin-bottom: 0;
- }
+
+ >ul,
+ >ol {
+ margin-bottom: 0;
+ }
}
@@ -102,8 +128,13 @@ li {
/**
* Headings
*/
-h1, h2, h3, h4, h5, h6 {
- font-weight: $base-font-weight;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-weight: $base-font-weight;
}
@@ -112,25 +143,25 @@ h1, h2, h3, h4, h5, h6 {
* Links
*/
a {
- color: $link-base-color;
- text-decoration: none;
+ color: $link-base-color;
+ text-decoration: none;
- &:visited {
- color: $link-visited-color;
- }
+ &:visited {
+ color: $link-visited-color;
+ }
- &:hover {
- color: $text-color;
- text-decoration: underline;
- }
+ &:hover {
+ color: $text-color;
+ text-decoration: underline;
+ }
- .social-media-list &:hover {
- text-decoration: none;
+ .social-media-list &:hover {
+ text-decoration: none;
- .username {
- text-decoration: underline;
+ .username {
+ text-decoration: underline;
+ }
}
- }
}
@@ -138,19 +169,20 @@ a {
* Blockquotes
*/
blockquote {
- color: $brand-color;
- border-left: 4px solid $brand-color-light;
- padding-left: $spacing-unit / 2;
- @include relative-font-size(1.125);
- font-style: italic;
-
- > :last-child {
- margin-bottom: 0;
- }
-
- i, em {
- font-style: normal;
- }
+ color: $brand-color;
+ border-left: 4px solid $brand-color-light;
+ padding-left: $spacing-unit / 2;
+ @include relative-font-size(1.125);
+ font-style: italic;
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+
+ i,
+ em {
+ font-style: normal;
+ }
}
@@ -160,36 +192,36 @@ blockquote {
*/
pre,
code {
- font-family: $code-font-family;
- font-size: 0.9375em;
- border: 1px solid $brand-color-light;
- border-radius: 3px;
- background-color: $code-background-color;
+ font-family: $code-font-family;
+ font-size: 0.9375em;
+ border: 1px solid $brand-color-light;
+ border-radius: 3px;
+ background-color: $code-background-color;
}
code {
- padding: 1px 5px;
+ padding: 1px 5px;
}
pre {
- padding: 8px 12px;
- overflow-x: auto;
-
- > code {
- border: 0;
- padding-right: 0;
- padding-left: 0;
- }
+ padding: 8px 12px;
+ overflow-x: auto;
+
+ >code {
+ border: 0;
+ padding-right: 0;
+ padding-left: 0;
+ }
}
.highlight {
- border-radius: 3px;
- background: $code-background-color;
- @extend %vertical-rhythm;
-
- .highlighter-rouge & {
+ border-radius: 3px;
background: $code-background-color;
- }
+ @extend %vertical-rhythm;
+
+ .highlighter-rouge & {
+ background: $code-background-color;
+ }
}
@@ -198,18 +230,18 @@ pre {
* Wrapper
*/
.wrapper {
- max-width: calc(#{$content-width} - (#{$spacing-unit}));
- margin-right: auto;
- margin-left: auto;
- padding-right: $spacing-unit / 2;
- padding-left: $spacing-unit / 2;
- @extend %clearfix;
-
- @media screen and (min-width: $on-large) {
- max-width: calc(#{$content-width} - (#{$spacing-unit} * 2));
- padding-right: $spacing-unit;
- padding-left: $spacing-unit;
- }
+ max-width: calc(#{$content-width} - (#{$spacing-unit}));
+ margin-right: auto;
+ margin-left: auto;
+ padding-right: $spacing-unit / 2;
+ padding-left: $spacing-unit / 2;
+ @extend %clearfix;
+
+ @media screen and (min-width: $on-large) {
+ max-width: calc(#{$content-width} - (#{$spacing-unit} * 2));
+ padding-right: $spacing-unit;
+ padding-left: $spacing-unit;
+ }
}
@@ -218,9 +250,9 @@ pre {
* Clearfix
*/
%clearfix:after {
- content: "";
- display: table;
- clear: both;
+ content: "";
+ display: table;
+ clear: both;
}
@@ -230,66 +262,74 @@ pre {
*/
.orange {
- color: #f66a0a;
+ color: #f66a0a;
}
.grey {
- color: #828282;
+ color: #828282;
}
/**
* Tables
*/
table {
- margin-bottom: $spacing-unit;
- width: 100%;
- text-align: $table-text-align;
- color: $table-text-color;
- border-collapse: collapse;
- border: 1px solid $table-border-color;
- tr {
- &:nth-child(even) {
- background-color: $table-zebra-color;
- }
- }
- th, td {
- padding: ($spacing-unit / 3) ($spacing-unit / 2);
- }
- th {
- background-color: $table-header-bg-color;
- border: 1px solid $table-header-border;
- }
- td {
+ margin-bottom: $spacing-unit;
+ width: 100%;
+ text-align: $table-text-align;
+ color: $table-text-color;
+ border-collapse: collapse;
border: 1px solid $table-border-color;
- }
- @include media-query($on-laptop) {
- display: block;
- overflow-x: auto;
- -webkit-overflow-scrolling: touch;
- -ms-overflow-style: -ms-autohiding-scrollbar;
- }
+ tr {
+ &:nth-child(even) {
+ background-color: $table-zebra-color;
+ }
+ }
+
+ th,
+ td {
+ padding: ($spacing-unit / 3) ($spacing-unit / 2);
+ }
+
+ th {
+ background-color: $table-header-bg-color;
+ border: 1px solid $table-header-border;
+ }
+
+ td {
+ border: 1px solid $table-border-color;
+ }
+
+ @include media-query($on-laptop) {
+ display: block;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ -ms-overflow-style: -ms-autohiding-scrollbar;
+ }
}
@media print {
- /**
+
+ /**
* Prevents page break from cutting through content when printing
*/
- body {
- display: block;
- }
- /**
+ body {
+ display: block;
+ }
+
+ /**
* Replaces the top navigation menu with the project name when printing
*/
- .site-header .wrapper {
- display: none;
- }
- .site-header {
- text-align: center;
- }
- .site-header:before {
- content: "AB-3";
- font-size: 32px;
- }
-}
+ .site-header .wrapper {
+ display: none;
+ }
+
+ .site-header {
+ text-align: center;
+ }
+ .site-header:before {
+ content: "TAfinder";
+ font-size: 32px;
+ }
+}
diff --git a/docs/_sass/tafinder.scss b/docs/_sass/tafinder.scss
new file mode 100644
index 00000000000..318a573bb28
--- /dev/null
+++ b/docs/_sass/tafinder.scss
@@ -0,0 +1,106 @@
+//@@author ravern-reused
+//Reused from https://github.com/AY2324S1-CS2103T-W10-3/tp/blob/master/docs/_sass/toothtracker.scss
+
+.page-content {
+ display: flex;
+ justify-content: space-between;
+ flex-direction: row;
+ overflow-x: clip;
+
+ .sticky-toc {
+ width: 25%;
+ height: 100vh;
+ align-self: flex-start;
+ border-right: 1px solid #e0e0e0;
+
+ @media screen and (max-width: $on-medium) {
+ display: none;
+ }
+ }
+
+ .wrapper {
+ box-sizing: border-box;
+ width: 100%;
+ max-width: $on-large;
+
+ @media screen and (min-width: $on-medium) {
+ width: 75%;
+ }
+ }
+}
+
+.sticky-toc {
+ $margin-block: 20px;
+ position: -webkit-sticky;
+ position: sticky;
+ top: $margin-block;
+ bottom: $margin-block;
+ max-height: calc(100vh - 2 * #{$margin-block});
+ line-height: 200%;
+ padding-inline: 6px;
+ overflow-y: auto;
+ font-size: small;
+
+ @media print {
+ display: none;
+ }
+
+ ul,
+ ol {
+ margin-left: 10px;
+ margin-top: 0;
+ margin-bottom: 0;
+
+ li {
+ list-style-type: none;
+ padding-left: 5px;
+ margin-bottom: 1px;
+
+ a {
+ color: gray;
+ text-decoration: none;
+ transition: color 0.3s ease;
+
+ &:hover {
+ color: black;
+ }
+ }
+ }
+ }
+}
+
+// Back to Top Button
+.back-to-top {
+ position: fixed;
+ bottom: 20px;
+ right: 20px;
+ background: rgba(73, 122, 255, .8);
+ width: 25px;
+ height: 25px;
+ display: block;
+ text-decoration: none;
+ -webkit-border-radius: 30px;
+ border-radius: 30px;
+ display: none;
+ color: white;
+ z-index: 999;
+ cursor: pointer;
+ font-size: 16px;
+ transition: all 0.3s ease;
+ text-align: center;
+ line-height: 25px;
+
+ &:hover {
+ background: rgb(73, 122, 255);
+ text-decoration: none;
+ color: white;
+ }
+}
+
+.centered-image {
+ display: block;
+ margin-top: 10px;
+ margin-left: auto;
+ margin-right: auto;
+ width: 720px;
+}
diff --git a/docs/_sass/toc.scss b/docs/_sass/toc.scss
new file mode 100644
index 00000000000..3932f1e7dc2
--- /dev/null
+++ b/docs/_sass/toc.scss
@@ -0,0 +1,69 @@
+#markdown-toc {
+ //@@author ravern-reused
+ //Reused from https://github.com/AY2324S1-CS2103T-W10-3/tp/blob/master/docs/_sass/toc.scss
+ //with minor modifications
+
+ //Adapted from https://stackoverflow.com/a/51007932/9311854
+ ol {
+ counter-reset: item;
+ }
+
+ > li,
+ ol > li {
+ counter-increment: item;
+ }
+
+ ol > li {
+ display: block;
+ position: relative;
+ }
+
+ ol > li:before {
+ content: counters(item, ".") ". ";
+ margin-left: -1em;
+ }
+}
+
+
+article.post {
+ counter-reset: section;
+
+ h2:not(.no_toc) {
+ counter-reset: subsection;
+ }
+
+ h3:not(.no_toc) {
+ counter-reset: subsubsection;
+ }
+
+ h4:not(.no_toc) {
+ counter-reset: subsubsubsection;
+ }
+
+ h2:not(.no_toc)::before {
+ color: inherit !important;
+ counter-increment: section;
+ content: counter(section) ". ";
+ }
+
+ h3:not(.no_toc)::before {
+ color: inherit !important;
+ counter-increment: subsection;
+ content: counter(section) "." counter(subsection) ". ";
+ }
+
+ h4:not(.no_toc)::before {
+ color: inherit !important;
+ counter-increment: subsubsection;
+ content: counter(section) "." counter(subsection) "." counter(subsubsection) ". ";
+ }
+}
+
+
+article.post {
+ h2:not(.no_toc)::before,
+ h3:not(.no_toc)::before,
+ h4:not(.no_toc)::before {
+ color: black;
+ }
+}
diff --git a/docs/assets/css/style.scss b/docs/assets/css/style.scss
index b5ec6976efa..1aa2cfdf586 100644
--- a/docs/assets/css/style.scss
+++ b/docs/assets/css/style.scss
@@ -4,7 +4,9 @@
@import
"minima/skins/{{ site.minima.skin | default: 'classic' }}",
- "minima/initialize";
+ "minima/initialize",
+ "toc",
+ "tafinder";
.icon {
height: 21px;
diff --git a/docs/diagrams/AttachActivityDiagram.puml b/docs/diagrams/AttachActivityDiagram.puml
new file mode 100644
index 00000000000..48631ded8e0
--- /dev/null
+++ b/docs/diagrams/AttachActivityDiagram.puml
@@ -0,0 +1,30 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User enters "attach 1 f/resume.pdf" command;
+:AttachCommandParser parses the command;
+if () then ([successful parse])
+ :Valid argument format;
+else ([else])
+ :Display error message;
+ stop
+endif
+:Begin AttachCommand execution;
+if () then ([index within visible range])
+ :Fetch applicant to attach to;
+else ([else])
+ :Display error message;
+ stop
+endif
+:Copy attachments into data directory;
+if () then ([found errors while copying])
+ :Display error message;
+ stop
+else ([else])
+ :Update person with new attachments;
+endif
+:Display success message;
+stop
+@enduml
diff --git a/docs/diagrams/AttachSequenceDiagram.puml b/docs/diagrams/AttachSequenceDiagram.puml
new file mode 100644
index 00000000000..1981ef67ac7
--- /dev/null
+++ b/docs/diagrams/AttachSequenceDiagram.puml
@@ -0,0 +1,84 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant ":AttachCommandParser" as AttachCommandParser LOGIC_COLOR
+participant ":AttachCommand" as AttachCommand LOGIC_COLOR
+participant ":CommandResult" as CommandResult LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+participant "attachedPerson:Person" as Person MODEL_COLOR
+end box
+
+[-> LogicManager : execute("attach 1 f/resume.pdf f/transcript.pdf")
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand("attach 1 f/resume.pdf f/transcript.pdf")
+activate AddressBookParser
+
+create AttachCommandParser
+AddressBookParser -> AttachCommandParser
+activate AttachCommandParser
+
+AttachCommandParser --> AddressBookParser
+deactivate AttachCommandParser
+
+AddressBookParser -> AttachCommandParser : parse("1 f/resume.pdf f/transcript.pdf")
+activate AttachCommandParser
+
+create AttachCommand
+AttachCommandParser -> AttachCommand
+activate AttachCommand
+
+AttachCommand --> AttachCommandParser
+deactivate AttachCommand
+
+AttachCommandParser --> AddressBookParser
+deactivate AttachCommandParser
+'Hidden arrow to position the destroy marker below the end of the activation bar.
+AttachCommandParser -[hidden]-> AddressBookParser
+destroy AttachCommandParser
+
+AddressBookParser --> LogicManager
+deactivate AddressBookParser
+
+LogicManager -> AttachCommand : execute()
+activate AttachCommand
+
+loop 2 times
+ AttachCommand --> AttachCommand : copyAttachment()
+ activate AttachCommand
+ deactivate AttachCommand
+end
+
+create Person
+AttachCommand --> Person
+activate Person
+
+Person --> AttachCommand
+deactivate Person
+
+AttachCommand -> Model : setPerson(person)
+activate Model
+
+Model --> AttachCommand
+deactivate Model
+
+create CommandResult
+AttachCommand -> CommandResult
+activate CommandResult
+
+CommandResult --> AttachCommand
+deactivate CommandResult
+
+AttachCommand --> LogicManager : result
+deactivate AttachCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/BookmarkActivityDiagram.puml b/docs/diagrams/BookmarkActivityDiagram.puml
new file mode 100644
index 00000000000..05811b8cc15
--- /dev/null
+++ b/docs/diagrams/BookmarkActivityDiagram.puml
@@ -0,0 +1,19 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User enters "bookmark" command with APPLICANT_INDEX;
+:BookmarkCommandParser parses the command and APPLICANT_INDEX;
+if () then ([valid command])
+ :Valid APPLICANT_INDEX obtained;
+else ([else])
+ :Display error message;
+ stop
+endif
+:Execute BookmarkCommand;
+:Update applicant at APPLICANT_INDEX as bookmarked in Model;
+:Display updated list with applicant at APPLICANT_INDEX bookmarked;
+:Display success message;
+stop
+@enduml
diff --git a/docs/diagrams/BookmarkSequenceDIagram.puml b/docs/diagrams/BookmarkSequenceDIagram.puml
new file mode 100644
index 00000000000..b44ba59323e
--- /dev/null
+++ b/docs/diagrams/BookmarkSequenceDIagram.puml
@@ -0,0 +1,73 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+
+actor User
+
+box Ui UI_COLOR_T1
+participant ":UI" as UI UI_COLOR
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager BLUE_COLOR
+participant ":AddressBookParser" as AddressBookParser BLUE_COLOR
+participant ":BookmarkCommandParser" as BookmarkCommandParser BLUE_COLOR
+participant ":ParserUtil" as ParserUtil BLUE_COLOR
+participant "b:BookmarkCommand" as BookmarkCommand BLUE_COLOR
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+
+User -> UI: "Bookmark 1 2"
+activate UI
+
+UI -> LogicManager : execute(bookmark)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(bookmark)
+activate AddressBookParser
+
+create BookmarkCommandParser
+AddressBookParser -> BookmarkCommandParser : parseCommand(bookmark)
+activate BookmarkCommandParser
+
+BookmarkCommandParser -> ParserUtil: Input
+activate ParserUtil
+
+ParserUtil --> BookmarkCommandParser: Valid index
+deactivate ParserUtil
+
+create BookmarkCommand
+BookmarkCommandParser -> BookmarkCommand: Valid index
+activate BookmarkCommand
+
+BookmarkCommand --> BookmarkCommandParser: b
+deactivate BookmarkCommand
+
+BookmarkCommandParser --> AddressBookParser: b
+deactivate BookmarkCommandParser
+
+AddressBookParser --> LogicManager: b
+destroy BookmarkCommandParser
+deactivate AddressBookParser
+
+LogicManager -> BookmarkCommand: execute()
+activate BookmarkCommand
+
+BookmarkCommand -> Model: setPerson()
+activate Model
+
+Model --> BookmarkCommand
+deactivate Model
+
+BookmarkCommand --> LogicManager: result
+deactivate BookmarkCommand
+
+LogicManager --> UI: Success Message
+destroy BookmarkCommand
+deactivate LogicManager
+
+UI --> User: Success Message
+deactivate
+
+@enduml
diff --git a/docs/diagrams/CommentActivityDiagram.puml b/docs/diagrams/CommentActivityDiagram.puml
new file mode 100644
index 00000000000..6d174799b14
--- /dev/null
+++ b/docs/diagrams/CommentActivityDiagram.puml
@@ -0,0 +1,19 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User enters "comment 3 c/Hardworking" command;
+:CommentCommandParser parses the command;
+if () then ([successful parse])
+ :Valid index;
+else ([else])
+ :Display error message;
+ stop
+endif
+
+:Execute CommentCommand;
+:Update applicant;
+:Display success message;
+stop
+@enduml
diff --git a/docs/diagrams/CommentSequenceDiagram.puml b/docs/diagrams/CommentSequenceDiagram.puml
new file mode 100644
index 00000000000..d8b9bbde611
--- /dev/null
+++ b/docs/diagrams/CommentSequenceDiagram.puml
@@ -0,0 +1,41 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+
+actor User
+
+box Ui MODEL_COLOR_T1
+participant ":UI" as UI RED_COLOR
+
+box Logic LOGIC_COLOR_T1
+participant ":CommentCommand" as CommentCommand BLUE_COLOR
+participant ":CommentCommandParser" as CommentCommandParser GREEN_COLOR
+participant ":ParserUtil" as ParserUtil GREEN_COLOR
+
+
+User -> UI: "comment 3 c/Hardworking"
+activate UI
+
+UI -> CommentCommand: Execute command
+activate CommentCommand
+
+CommentCommand -> CommentCommandParser: Parse command
+activate CommentCommandParser
+
+CommentCommandParser -> ParserUtil: Parse index
+activate ParserUtil
+ParserUtil -> ParserUtil: Validate index
+activate ParserUtil
+deactivate ParserUtil
+
+ParserUtil --> CommentCommandParser: Valid index
+deactivate ParserUtil
+CommentCommandParser --> CommentCommand: Valid index
+deactivate CommentCommandParser
+CommentCommand --> UI: Valid applicant
+deactivate CommentCommand
+UI --> User: MainWindow
+deactivate
+
+@enduml
diff --git a/docs/diagrams/CompareActivityDiagram.puml b/docs/diagrams/CompareActivityDiagram.puml
new file mode 100644
index 00000000000..1be56eb5fd0
--- /dev/null
+++ b/docs/diagrams/CompareActivityDiagram.puml
@@ -0,0 +1,26 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User enters "compare 1 2" command;
+:CompareCommandParser parses the command;
+if () then ([successful parse])
+ :Valid indices;
+else ([else])
+ :Display error message;
+ stop
+endif
+:Execute CompareCommand;
+if () then ([distinct indices])
+ :Fetch applicants from list;
+else ([else])
+ :Display error message;
+ stop
+endif
+
+:Create CompareWindow;
+:Display comparison results;
+:Display success message;
+stop
+@enduml
diff --git a/docs/diagrams/CompareSequenceDiagram.puml b/docs/diagrams/CompareSequenceDiagram.puml
new file mode 100644
index 00000000000..000f6ac9879
--- /dev/null
+++ b/docs/diagrams/CompareSequenceDiagram.puml
@@ -0,0 +1,41 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+
+actor User
+
+box Ui MODEL_COLOR_T1
+participant ":UI" as UI RED_COLOR
+
+box Logic LOGIC_COLOR_T1
+participant ":CompareCommand" as CompareCommand BLUE_COLOR
+participant ":CompareCommandParser" as CompareCommandParser GREEN_COLOR
+participant ":ParserUtil" as ParserUtil GREEN_COLOR
+
+
+User -> UI: "compare 1 2"
+activate UI
+
+UI -> CompareCommand: Execute command
+activate CompareCommand
+
+CompareCommand -> CompareCommandParser: Parse command
+activate CompareCommandParser
+
+CompareCommandParser -> ParserUtil: Parse index
+activate ParserUtil
+ParserUtil -> ParserUtil: Validate indices
+activate ParserUtil
+deactivate ParserUtil
+
+ParserUtil --> CompareCommandParser: Valid indices
+deactivate ParserUtil
+CompareCommandParser --> CompareCommand: Valid indices
+deactivate CompareCommandParser
+CompareCommand --> UI: Valid applicants
+deactivate CompareCommand
+UI --> User: CompareWindow
+deactivate
+
+@enduml
diff --git a/docs/diagrams/HideActivityDiagram.puml b/docs/diagrams/HideActivityDiagram.puml
new file mode 100644
index 00000000000..8cff53fbd1c
--- /dev/null
+++ b/docs/diagrams/HideActivityDiagram.puml
@@ -0,0 +1,13 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User executes command;
+:Command is parsed;
+:Command is executed;
+:Replace Person in Model with hidden Person;
+:Update Model to display only non-hidden Persons;
+
+stop
+@enduml
diff --git a/docs/diagrams/HideSequenceDiagram.puml b/docs/diagrams/HideSequenceDiagram.puml
new file mode 100644
index 00000000000..0fdf4372589
--- /dev/null
+++ b/docs/diagrams/HideSequenceDiagram.puml
@@ -0,0 +1,52 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager LOGIC_COLOR
+participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR
+participant "u:HideCommand" as HideCommand LOGIC_COLOR
+end box
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+end box
+[-> LogicManager : execute(hide)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(hide)
+activate AddressBookParser
+
+create HideCommand
+AddressBookParser -> HideCommand
+activate HideCommand
+
+HideCommand --> AddressBookParser
+deactivate HideCommand
+
+AddressBookParser --> LogicManager : u
+deactivate AddressBookParser
+
+LogicManager -> HideCommand : execute()
+activate HideCommand
+
+HideCommand -> Model : setPerson()
+activate Model
+
+Model --> HideCommand
+deactivate Model
+
+HideCommand -> Model : updateFilteredPersonList()
+activate Model
+
+Model --> HideCommand
+deactivate Model
+
+HideCommand --> LogicManager : result
+deactivate HideCommand
+HideCommand -[hidden]-> LogicManager : result
+destroy HideCommand
+
+[<--LogicManager
+deactivate LogicManager
+@enduml
diff --git a/docs/diagrams/ViewActivityDiagram.puml b/docs/diagrams/ViewActivityDiagram.puml
new file mode 100644
index 00000000000..8aa7f940211
--- /dev/null
+++ b/docs/diagrams/ViewActivityDiagram.puml
@@ -0,0 +1,19 @@
+@startuml
+skin rose
+skinparam ActivityFontSize 15
+skinparam ArrowFontSize 12
+start
+:User enters "view" command with APPLICANT_INDEX;
+:ViewCommandParser parses the command and APPLICANT_INDEX;
+if () then ([valid command])
+ :Valid APPLICANT_INDEX obtained;
+else ([else])
+ :Display error message;
+ stop
+endif
+:Execute ViewCommand;
+:Update currentPerson in Model as applicant at APPLICANT_INDEX;
+:Display details of applicant at APPLICANT_INDEX;
+:Display success message;
+stop
+@enduml
diff --git a/docs/diagrams/ViewSequenceDiagram.puml b/docs/diagrams/ViewSequenceDiagram.puml
new file mode 100644
index 00000000000..54afe5d4856
--- /dev/null
+++ b/docs/diagrams/ViewSequenceDiagram.puml
@@ -0,0 +1,73 @@
+@startuml
+!include style.puml
+skinparam ArrowFontStyle plain
+
+
+actor User
+
+box Ui UI_COLOR_T1
+participant ":UI" as UI UI_COLOR
+
+box Logic LOGIC_COLOR_T1
+participant ":LogicManager" as LogicManager BLUE_COLOR
+participant ":AddressBookParser" as AddressBookParser BLUE_COLOR
+participant ":ViewCommandParser" as ViewCommandParser BLUE_COLOR
+participant ":ParserUtil" as ParserUtil BLUE_COLOR
+participant "b:ViewCommand" as ViewCommand BLUE_COLOR
+
+box Model MODEL_COLOR_T1
+participant ":Model" as Model MODEL_COLOR
+
+User -> UI: "view 1 2"
+activate UI
+
+UI -> LogicManager : execute(view)
+activate LogicManager
+
+LogicManager -> AddressBookParser : parseCommand(view)
+activate AddressBookParser
+
+create ViewCommandParser
+AddressBookParser -> ViewCommandParser : parseCommand(view)
+activate ViewCommandParser
+
+ViewCommandParser -> ParserUtil: Input
+activate ParserUtil
+
+ParserUtil --> ViewCommandParser: Valid index
+deactivate ParserUtil
+
+create ViewCommand
+ViewCommandParser -> ViewCommand: Valid index
+activate ViewCommand
+
+ViewCommand --> ViewCommandParser: b
+deactivate ViewCommand
+
+ViewCommandParser --> AddressBookParser: b
+deactivate ViewCommandParser
+
+AddressBookParser --> LogicManager: b
+destroy ViewCommandParser
+deactivate AddressBookParser
+
+LogicManager -> ViewCommand: execute()
+activate ViewCommand
+
+ViewCommand -> Model: showPersonAtIndex()
+activate Model
+
+Model --> ViewCommand
+deactivate Model
+
+ViewCommand --> LogicManager: result
+deactivate ViewCommand
+
+LogicManager --> UI: Updated detail view
+destroy ViewCommand
+deactivate LogicManager
+
+UI --> User: Success Message
+deactivate
+
+@enduml
diff --git a/docs/diagrams/style.puml b/docs/diagrams/style.puml
index f7d7347ae84..ecb7117fe3f 100644
--- a/docs/diagrams/style.puml
+++ b/docs/diagrams/style.puml
@@ -33,6 +33,14 @@
!define USER_COLOR #000000
+!define RED_COLOR #FF0000
+!define ORANGE_COLOR #FF7F00
+!define YELLOW_COLOR #FFFF00
+!define GREEN_COLOR #00DE00
+!define BLUE_COLOR #0000FF
+!define INDIGO_COLOR #4B0082
+!define VIOLET_COLOR #9400D3
+
skinparam Package {
BackgroundColor #FFFFFF
BorderThickness 1
diff --git a/docs/images/AttachActivityDiagram.png b/docs/images/AttachActivityDiagram.png
new file mode 100644
index 00000000000..cdde30232ed
Binary files /dev/null and b/docs/images/AttachActivityDiagram.png differ
diff --git a/docs/images/AttachSequenceDiagram.png b/docs/images/AttachSequenceDiagram.png
new file mode 100644
index 00000000000..a23de9816e7
Binary files /dev/null and b/docs/images/AttachSequenceDiagram.png differ
diff --git a/docs/images/BookmarkActivityDiagram.png b/docs/images/BookmarkActivityDiagram.png
new file mode 100644
index 00000000000..31d33ad06d0
Binary files /dev/null and b/docs/images/BookmarkActivityDiagram.png differ
diff --git a/docs/images/BookmarkSequenceDIagram.png b/docs/images/BookmarkSequenceDIagram.png
new file mode 100644
index 00000000000..963f806d994
Binary files /dev/null and b/docs/images/BookmarkSequenceDIagram.png differ
diff --git a/docs/images/CommentActivityDiagram.png b/docs/images/CommentActivityDiagram.png
new file mode 100644
index 00000000000..34623973a0f
Binary files /dev/null and b/docs/images/CommentActivityDiagram.png differ
diff --git a/docs/images/CommentSequenceDiagram.png b/docs/images/CommentSequenceDiagram.png
new file mode 100644
index 00000000000..f995fc9b47b
Binary files /dev/null and b/docs/images/CommentSequenceDiagram.png differ
diff --git a/docs/images/CompareActivityDiagram.png b/docs/images/CompareActivityDiagram.png
new file mode 100644
index 00000000000..8728dc5438a
Binary files /dev/null and b/docs/images/CompareActivityDiagram.png differ
diff --git a/docs/images/CompareSequenceDiagram.png b/docs/images/CompareSequenceDiagram.png
new file mode 100644
index 00000000000..daefbb4e02d
Binary files /dev/null and b/docs/images/CompareSequenceDiagram.png differ
diff --git a/docs/images/HideActivityDiagram.png b/docs/images/HideActivityDiagram.png
new file mode 100644
index 00000000000..cfd22a1d912
Binary files /dev/null and b/docs/images/HideActivityDiagram.png differ
diff --git a/docs/images/HideSequenceDiagram.png b/docs/images/HideSequenceDiagram.png
new file mode 100644
index 00000000000..8e6fb7d479b
Binary files /dev/null and b/docs/images/HideSequenceDiagram.png differ
diff --git a/docs/images/Ui.png b/docs/images/Ui.png
index 5bd77847aa2..057ddb08815 100644
Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ
diff --git a/docs/images/ViewActivityDiagram.png b/docs/images/ViewActivityDiagram.png
new file mode 100644
index 00000000000..78c33b4af04
Binary files /dev/null and b/docs/images/ViewActivityDiagram.png differ
diff --git a/docs/images/ViewSequenceDiagram.png b/docs/images/ViewSequenceDiagram.png
new file mode 100644
index 00000000000..ae1c23d1cce
Binary files /dev/null and b/docs/images/ViewSequenceDiagram.png differ
diff --git a/docs/images/add.jpg b/docs/images/add.jpg
new file mode 100644
index 00000000000..0485d3f98e5
Binary files /dev/null and b/docs/images/add.jpg differ
diff --git a/docs/images/add_afterUI.png b/docs/images/add_afterUI.png
new file mode 100644
index 00000000000..434ada6ed66
Binary files /dev/null and b/docs/images/add_afterUI.png differ
diff --git a/docs/images/add_command_image_UG.png b/docs/images/add_command_image_UG.png
new file mode 100644
index 00000000000..f8d0760df34
Binary files /dev/null and b/docs/images/add_command_image_UG.png differ
diff --git a/docs/images/amosting.png b/docs/images/amosting.png
new file mode 100644
index 00000000000..deb41290c45
Binary files /dev/null and b/docs/images/amosting.png differ
diff --git a/docs/images/attach_afterUI.png b/docs/images/attach_afterUI.png
new file mode 100644
index 00000000000..1c793cffe96
Binary files /dev/null and b/docs/images/attach_afterUI.png differ
diff --git a/docs/images/attach_beforeUI.png b/docs/images/attach_beforeUI.png
new file mode 100644
index 00000000000..3b95d0870a0
Binary files /dev/null and b/docs/images/attach_beforeUI.png differ
diff --git a/docs/images/bookmark_afterUI.png b/docs/images/bookmark_afterUI.png
new file mode 100644
index 00000000000..10b465c30bb
Binary files /dev/null and b/docs/images/bookmark_afterUI.png differ
diff --git a/docs/images/bookmark_beforeUI.png b/docs/images/bookmark_beforeUI.png
new file mode 100644
index 00000000000..49c11187077
Binary files /dev/null and b/docs/images/bookmark_beforeUI.png differ
diff --git a/docs/images/clear_afterUI.png b/docs/images/clear_afterUI.png
new file mode 100644
index 00000000000..1df23d4df8d
Binary files /dev/null and b/docs/images/clear_afterUI.png differ
diff --git a/docs/images/clear_beforeUI.png b/docs/images/clear_beforeUI.png
new file mode 100644
index 00000000000..ce79e615a29
Binary files /dev/null and b/docs/images/clear_beforeUI.png differ
diff --git a/docs/images/comment.jpg b/docs/images/comment.jpg
new file mode 100644
index 00000000000..1d60acde7b1
Binary files /dev/null and b/docs/images/comment.jpg differ
diff --git a/docs/images/comment_afterUI.png b/docs/images/comment_afterUI.png
new file mode 100644
index 00000000000..6954c194ebe
Binary files /dev/null and b/docs/images/comment_afterUI.png differ
diff --git a/docs/images/comment_beforeUI.png b/docs/images/comment_beforeUI.png
new file mode 100644
index 00000000000..3f3354980d1
Binary files /dev/null and b/docs/images/comment_beforeUI.png differ
diff --git a/docs/images/compareUI.png b/docs/images/compareUI.png
new file mode 100644
index 00000000000..17f200f0ba5
Binary files /dev/null and b/docs/images/compareUI.png differ
diff --git a/docs/images/compare_popupUI.png b/docs/images/compare_popupUI.png
new file mode 100644
index 00000000000..2faa17548b3
Binary files /dev/null and b/docs/images/compare_popupUI.png differ
diff --git a/docs/images/delete_afterUI.png b/docs/images/delete_afterUI.png
new file mode 100644
index 00000000000..fd1b429f92a
Binary files /dev/null and b/docs/images/delete_afterUI.png differ
diff --git a/docs/images/delete_beforeUI.png b/docs/images/delete_beforeUI.png
new file mode 100644
index 00000000000..05bf8fca769
Binary files /dev/null and b/docs/images/delete_beforeUI.png differ
diff --git a/docs/images/edit_afterUI.png b/docs/images/edit_afterUI.png
new file mode 100644
index 00000000000..baadf8ea1b3
Binary files /dev/null and b/docs/images/edit_afterUI.png differ
diff --git a/docs/images/edit_beforeUI.png b/docs/images/edit_beforeUI.png
new file mode 100644
index 00000000000..78c3339e01b
Binary files /dev/null and b/docs/images/edit_beforeUI.png differ
diff --git a/docs/images/hide_afterUI.png b/docs/images/hide_afterUI.png
new file mode 100644
index 00000000000..298ac3e5a9f
Binary files /dev/null and b/docs/images/hide_afterUI.png differ
diff --git a/docs/images/hide_beforeUI.png b/docs/images/hide_beforeUI.png
new file mode 100644
index 00000000000..ff08973b442
Binary files /dev/null and b/docs/images/hide_beforeUI.png differ
diff --git a/docs/images/import_afterUI.png b/docs/images/import_afterUI.png
new file mode 100644
index 00000000000..f4b7037feaa
Binary files /dev/null and b/docs/images/import_afterUI.png differ
diff --git a/docs/images/import_beforeUI.png b/docs/images/import_beforeUI.png
new file mode 100644
index 00000000000..36c7ad3ada5
Binary files /dev/null and b/docs/images/import_beforeUI.png differ
diff --git a/docs/images/lheng1.png b/docs/images/lheng1.png
new file mode 100644
index 00000000000..8e70db01894
Binary files /dev/null and b/docs/images/lheng1.png differ
diff --git a/docs/images/listUI.png b/docs/images/listUI.png
new file mode 100644
index 00000000000..a1a31406998
Binary files /dev/null and b/docs/images/listUI.png differ
diff --git a/docs/images/listbookmarkedUI.png b/docs/images/listbookmarkedUI.png
new file mode 100644
index 00000000000..d6e267e7972
Binary files /dev/null and b/docs/images/listbookmarkedUI.png differ
diff --git a/docs/images/listhiddenUI.png b/docs/images/listhiddenUI.png
new file mode 100644
index 00000000000..192b8c47e3e
Binary files /dev/null and b/docs/images/listhiddenUI.png differ
diff --git a/docs/images/nabonitasen.png b/docs/images/nabonitasen.png
new file mode 100644
index 00000000000..85d2e480306
Binary files /dev/null and b/docs/images/nabonitasen.png differ
diff --git a/docs/images/navigationUI.png b/docs/images/navigationUI.png
new file mode 100644
index 00000000000..b06c787493c
Binary files /dev/null and b/docs/images/navigationUI.png differ
diff --git a/docs/images/navigation_coloredUI.png b/docs/images/navigation_coloredUI.png
new file mode 100644
index 00000000000..9598d5789d1
Binary files /dev/null and b/docs/images/navigation_coloredUI.png differ
diff --git a/docs/images/prof.png b/docs/images/prof.png
new file mode 100644
index 00000000000..e3938b09d71
Binary files /dev/null and b/docs/images/prof.png differ
diff --git a/docs/images/ravern.png b/docs/images/ravern.png
new file mode 100644
index 00000000000..6ac9c7d7654
Binary files /dev/null and b/docs/images/ravern.png differ
diff --git a/docs/images/samplecsv.png b/docs/images/samplecsv.png
new file mode 100644
index 00000000000..1652f424990
Binary files /dev/null and b/docs/images/samplecsv.png differ
diff --git a/docs/images/sort_afterUI.png b/docs/images/sort_afterUI.png
new file mode 100644
index 00000000000..e36e9aadbf7
Binary files /dev/null and b/docs/images/sort_afterUI.png differ
diff --git a/docs/images/sort_beforeUI.png b/docs/images/sort_beforeUI.png
new file mode 100644
index 00000000000..5799f28a123
Binary files /dev/null and b/docs/images/sort_beforeUI.png differ
diff --git a/docs/images/startup-UI.png b/docs/images/startup-UI.png
new file mode 100644
index 00000000000..4fac9bf8ba1
Binary files /dev/null and b/docs/images/startup-UI.png differ
diff --git a/docs/images/startupUI.png b/docs/images/startupUI.png
new file mode 100644
index 00000000000..e72d01883a0
Binary files /dev/null and b/docs/images/startupUI.png differ
diff --git a/docs/images/tafinder-UI.png b/docs/images/tafinder-UI.png
new file mode 100644
index 00000000000..3b7c341f3cc
Binary files /dev/null and b/docs/images/tafinder-UI.png differ
diff --git a/docs/images/unbookmark_afterUI.png b/docs/images/unbookmark_afterUI.png
new file mode 100644
index 00000000000..97779e8c3d2
Binary files /dev/null and b/docs/images/unbookmark_afterUI.png differ
diff --git a/docs/images/unbookmark_beforeUI.png b/docs/images/unbookmark_beforeUI.png
new file mode 100644
index 00000000000..a75100a8ea6
Binary files /dev/null and b/docs/images/unbookmark_beforeUI.png differ
diff --git a/docs/images/unhide_afterUI.png b/docs/images/unhide_afterUI.png
new file mode 100644
index 00000000000..7ae8304f9be
Binary files /dev/null and b/docs/images/unhide_afterUI.png differ
diff --git a/docs/images/unhide_beforeUI.png b/docs/images/unhide_beforeUI.png
new file mode 100644
index 00000000000..ba6f4978c0f
Binary files /dev/null and b/docs/images/unhide_beforeUI.png differ
diff --git a/docs/images/unhideall_afterUI.png b/docs/images/unhideall_afterUI.png
new file mode 100644
index 00000000000..27ebd06b4eb
Binary files /dev/null and b/docs/images/unhideall_afterUI.png differ
diff --git a/docs/images/unhideall_beforeUI.png b/docs/images/unhideall_beforeUI.png
new file mode 100644
index 00000000000..cab3b5abfb3
Binary files /dev/null and b/docs/images/unhideall_beforeUI.png differ
diff --git a/docs/images/viewUI.png b/docs/images/viewUI.png
new file mode 100644
index 00000000000..d311b4dc86c
Binary files /dev/null and b/docs/images/viewUI.png differ
diff --git a/docs/images/ylyma.png b/docs/images/ylyma.png
new file mode 100644
index 00000000000..9215330e694
Binary files /dev/null and b/docs/images/ylyma.png differ
diff --git a/docs/index.md b/docs/index.md
index 7601dbaad0d..e345c3c4511 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,17 +1,17 @@
---
-layout: page
-title: AddressBook Level-3
+layout: default
+title: Home
---
-[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions)
-[![codecov](https://codecov.io/gh/se-edu/addressbook-level3/branch/master/graph/badge.svg)](https://codecov.io/gh/se-edu/addressbook-level3)
+[![CI Status](https://github.com/AY2324S1-CS2103T-W10-1/tp/workflows/Java%20CI/badge.svg)](https://github.com/AY2324S1-CS2103T-W10-1/tp/actions)
+[![codecov](https://codecov.io/gh/AY2324S1-CS2103T-W10-1/tp/graph/badge.svg?token=4DBT7T1IUV)](https://codecov.io/gh/AY2324S1-CS2103T-W10-1/tp)
![Ui](images/Ui.png)
-**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
+**TAfinder is a desktop resource management application used by NUS SoC professors to choose TAs from a large pool of applicants.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface).
-* If you are interested in using AddressBook, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start).
-* If you are interested about developing AddressBook, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
+* If you are interested in using TAfinder, head over to the [**User Guide**](UserGuide.html).
+* If you are interested about developing TAfinder, the [**Developer Guide**](DeveloperGuide.html) is a good place to start.
**Acknowledgements**
diff --git a/docs/stylesheets/main.css b/docs/stylesheets/main.css
new file mode 100644
index 00000000000..1074ade42dd
--- /dev/null
+++ b/docs/stylesheets/main.css
@@ -0,0 +1,144 @@
+mark {
+ background-color: #ff0;
+ border-radius: 5px;
+ padding-top: 0;
+ padding-bottom: 0;
+}
+
+.indented {
+ padding-left: 20px;
+}
+
+.theme-card img {
+ width: 100%;
+}
+
+/* Scrollbar */
+
+.slim-scroll::-webkit-scrollbar {
+ width: 5px;
+}
+
+.slim-scroll::-webkit-scrollbar-thumb {
+ background: #808080;
+ border-radius: 20px;
+}
+
+.slim-scroll::-webkit-scrollbar-track {
+ background: transparent;
+ border-radius: 20px;
+}
+
+.slim-scroll-blue::-webkit-scrollbar {
+ width: 5px;
+}
+
+.slim-scroll-blue::-webkit-scrollbar-thumb {
+ background: #00b0ef;
+ border-radius: 20px;
+}
+
+.slim-scroll-blue::-webkit-scrollbar-track {
+ background: transparent;
+ border-radius: 20px;
+}
+
+/* Layout containers */
+
+#flex-body {
+ display: flex;
+ flex: 1;
+ align-items: start;
+}
+
+#content-wrapper {
+ flex: 1;
+ margin: 0 auto;
+ min-width: 0;
+ max-width: 1000px;
+ overflow-x: auto;
+ padding: 0.8rem 20px 0 20px;
+ transition: 0.4s;
+ -webkit-transition: 0.4s;
+}
+
+#site-nav,
+#page-nav {
+ display: flex;
+ flex-direction: column;
+ position: sticky;
+ top: var(--sticky-header-height);
+ flex: 0 0 auto;
+ max-width: 300px;
+ max-height: calc(100vh - var(--sticky-header-height));
+ width: 300px;
+}
+
+#site-nav {
+ border-right: 1px solid lightgrey;
+ padding-bottom: 20px;
+ z-index: 999;
+}
+
+.site-nav-top {
+ margin: 0.8rem 0;
+ padding: 0 12px 12px 12px;
+}
+
+.nav-component {
+ overflow-y: auto;
+}
+
+#page-nav {
+ border-left: 1px solid lightgrey;
+}
+
+@media screen and (max-width: 1299.98px) {
+ #page-nav {
+ display: none;
+ }
+}
+
+/* Bootstrap medium(md) responsive breakpoint */
+@media screen and (max-width: 991.98px) {
+ #site-nav {
+ display: none;
+ }
+}
+
+/* Bootstrap small(sm) responsive breakpoint */
+@media (max-width: 767.98px) {
+ .indented {
+ padding-left: 10px;
+ }
+
+ #content-wrapper {
+ padding: 0 10px;
+ }
+}
+
+/* Bootstrap extra small(xs) responsive breakpoint */
+@media screen and (max-width: 575.98px) {
+ #site-nav {
+ display: none;
+ }
+}
+
+/* Hide site navigation when printing */
+@media print {
+ #site-nav {
+ display: none;
+ }
+
+ #page-nav {
+ display: none;
+ }
+}
+
+h2,
+h3,
+h4,
+h5,
+h6 {
+ color: #e46c0a;
+}
diff --git a/docs/stylesheets/userguide.css b/docs/stylesheets/userguide.css
new file mode 100644
index 00000000000..015b8227b7d
--- /dev/null
+++ b/docs/stylesheets/userguide.css
@@ -0,0 +1,17 @@
+.bordered-table {
+ border-collapse: collapse;
+}
+
+.bordered-table th {
+ border: 1px solid black;
+ border-bottom: 2px solid black;
+}
+
+.bordered-table td {
+ border: 1px solid black;
+ padding: 6px;
+}
+
+.bordered-table tr:nth-child(even) {
+ background-color: rgba(0, 0, 0, 0.05);
+}
diff --git a/docs/team/amosting.md b/docs/team/amosting.md
new file mode 100644
index 00000000000..d20cfe75736
--- /dev/null
+++ b/docs/team/amosting.md
@@ -0,0 +1,41 @@
+---
+title: Amos' Project Portfolio Page
+---
+
+## Project: TAfinder
+
+TAfinder is a desktop address book application used by SOC professors to choose TAs from a large pool of applicants.
+The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added the comment command.
+ * What it does: Allows the user to add comments to applicants.
+ * Justification: This allows flexibility for the user to add any additional information about the applicant.
+ * Highlights: This feature allows the user to add comments to applicants.
+ * Credits: *AB3*
+
+* **Code contributed**: [RepoSense link]()
+
+* **Project management**:
+ * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
+
+* **Enhancements to existing features**:
+* Refactored the ability to add applicants.
+ * What it does: Allows the user to add applicants and their information to the list of applicants.
+ * Justification: This feature is necessary as it allows the user to add applicants to the list of applicants.
+ * Highlights: This feature allows the user to add applicants with their information to the list of applicants.
+ * Credits: *AB3*
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `add` and `comment`: [\#57](), [\#89]()
+ * Reformatted UG to be more reader friendly: [\#107](), [\#175]()
+ * Developer Guide:
+ * Added implementation details of the `comment` feature.
+
+* **Community**:
+ * PRs reviewed (with non-trivial review comments): [\#83](), [\#66](), [\#184](), [\#178](), [\#193]()
+
+* **Tools**:
+ * Integrated a third party library (MarkBind) to the project ([\#107]())
diff --git a/docs/team/johndoe.md b/docs/team/johndoe.md
deleted file mode 100644
index 773a07794e2..00000000000
--- a/docs/team/johndoe.md
+++ /dev/null
@@ -1,46 +0,0 @@
----
-layout: page
-title: John Doe's Project Portfolio Page
----
-
-### Project: AddressBook Level 3
-
-AddressBook - Level 3 is a desktop address book application used for teaching Software Engineering principles. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
-
-Given below are my contributions to the project.
-
-* **New Feature**: Added the ability to undo/redo previous commands.
- * What it does: allows the user to undo all previous commands one at a time. Preceding undo commands can be reversed by using the redo command.
- * Justification: This feature improves the product significantly because a user can make mistakes in commands and the app should provide a convenient way to rectify them.
- * Highlights: This enhancement affects existing commands and commands to be added in future. It required an in-depth analysis of design alternatives. The implementation too was challenging as it required changes to existing commands.
- * Credits: *{mention here if you reused any code/ideas from elsewhere or if a third-party library is heavily used in the feature so that a reader can make a more accurate judgement of how much effort went into the feature}*
-
-* **New Feature**: Added a history command that allows the user to navigate to previous commands using up/down keys.
-
-* **Code contributed**: [RepoSense link]()
-
-* **Project management**:
- * Managed releases `v1.3` - `v1.5rc` (3 releases) on GitHub
-
-* **Enhancements to existing features**:
- * Updated the GUI color scheme (Pull requests [\#33](), [\#34]())
- * Wrote additional tests for existing features to increase coverage from 88% to 92% (Pull requests [\#36](), [\#38]())
-
-* **Documentation**:
- * User Guide:
- * Added documentation for the features `delete` and `find` [\#72]()
- * Did cosmetic tweaks to existing documentation of features `clear`, `exit`: [\#74]()
- * Developer Guide:
- * Added implementation details of the `delete` feature.
-
-* **Community**:
- * PRs reviewed (with non-trivial review comments): [\#12](), [\#32](), [\#19](), [\#42]()
- * Contributed to forum discussions (examples: [1](), [2](), [3](), [4]())
- * Reported bugs and suggestions for other teams in the class (examples: [1](), [2](), [3]())
- * Some parts of the history feature I added was adopted by several other class mates ([1](), [2]())
-
-* **Tools**:
- * Integrated a third party library (Natty) to the project ([\#42]())
- * Integrated a new Github plugin (CircleCI) to the team repo
-
-* _{you can add/remove categories in the list above}_
diff --git a/docs/team/lheng1.md b/docs/team/lheng1.md
new file mode 100644
index 00000000000..5b7d44043e6
--- /dev/null
+++ b/docs/team/lheng1.md
@@ -0,0 +1,47 @@
+---
+title: Heng Yi's Project Portfolio Page
+---
+
+## Project: TAfinder
+
+TAfinder is a desktop resource management application used by NUS SOC professors to choose TAs from a large pool of applicants. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+### Summary of Contributions
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=lheng1&breakdown=true)
+
+
+* **Enhancements implemented**:
+ * Enhancement 1: Compare between two applicants
+ * What it does: The enhancement facilitates a side-by-side comparison of two applicants through a user interface (UI).
+ * Description: The UI displays comprehensive details of both applicants, including Student Number, Name, GPA, Interview Score, Previous Module Grade, and Comments. The window intelligently compares values (e.g., GPA, Interview Score, Previous Module Grade) and highlights the higher score for quick reference. This feature streamlines the decision-making process for selecting a Teaching Assistant (TA).
+ * Justification: The implemented feature offers a holistic view of applicants' credentials and simplifies the decision-making process by highlighting superior scores. This ensures that the professor can easily discern which applicant excels in specific criteria.
+ * Highlights: Creating the UI posed challenges, particularly in integrating it with the main window. Ultimately, a separate window was developed to pop up upon the execution of the compare command. Besides, having to constantly update and integrate all the different parameters, including new ones created by my teammates ate up most of my time on doing the tP, as there were a lot of dependencies between each parameter.
+ * Credits: Special thanks to Ravern for inspiring the idea of utilizing a separate window.
+
+
+* **Contributions to the UG**:
+ * Created the skeletal version of the UG for the team: [PR #52](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/52)
+ * In charge of updating the `edit` and `compare` function of UG: [PR #52](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/52), [PR #85](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/85)
+ * Helped check overall quality of UG: [PR #211](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/211)
+
+
+* **Contributions to the DG**:
+ * Created the compare command implementation details: [PR #95](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/95)
+ * Created sample use cases for the team: [PR #48](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/48)
+ * Update target user profile, value proposition, and user stories: [PR #47](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/47)
+ * In charge of Planned Enhancements appendix: [PR #206](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/206)
+
+
+* **Contributions to team-based tasks**:
+ * Created deadlines for the milestones.
+ * Created labels and milestones in team GitHub page.
+ * Enabled assertions for the project: [PR #87](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/34)
+
+
+* **Review/mentoring contributions**:
+ * Reviewed teammates' PRs: [PR #34](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/87), [PR #42](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/42), [PR #57](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/57), [PR #61](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/61), [PR #75](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/75), [PR #77](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/77), [PR #79](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/79), [PR #89](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/89), [PR #90](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/90), [PR #96](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/96), [PR #98](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/98), [PR #110](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/110), [PR #204](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/204), [PR #209](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/209)
+
+
+* **Contributions beyond the project team**:
+ * Reported bugs and suggestions for other teams in the class: [PED repo](https://github.com/LHeng1/ped)
diff --git a/docs/team/nabonitasen.md b/docs/team/nabonitasen.md
new file mode 100644
index 00000000000..77b6f2df5f6
--- /dev/null
+++ b/docs/team/nabonitasen.md
@@ -0,0 +1,40 @@
+---
+title: Nabonita's Project Portfolio Page
+---
+
+## Project: TAfinder
+
+TAfinder is a desktop resource management application used by NUS SOC professors to choose TAs from a large pool of applicants. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added the ability to bookmark/unbookmark users.
+ * What it does: allows the user to bookmark/unbookmark specific applicants of their choice, and list all bookmarked applicants. (List function later enhanced by Amy)
+ * Justification: This feature improves the product significantly because a user may want to take special note of certain applicants or may require a way to sigal which applicants have already been chosen to be a TA.
+ * Highlights: This feature's development involved creating new data structures for bookmarking, extending the user interface, and ensuring seamless integration with existing application features.
+
+
+* **New Feature**: Added an initial view command to display a singular applicant.
+ * What it does: allows the user to view a singular applicant.
+ * Justification: This feature improves the product significantly because a user may want to view and evaluate just one applicant's details instead of the whole list.
+ * Credits: Later updated by Ravern to display applicant on a separate panel.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=nabonitasen&breakdown=true)
+
+* **Enhancements to existing features**:
+ * Updated the GUI design of applicant list and colour scheme.
+ * Initial refactoring of address field to GPA field.
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `bookmark/unbookmark` and `view`.
+ * Did tweaks to existing documentation of features `clear` and `exit`.
+ * Developer Guide:
+ * Added implementation details of the `bookmark/unbookmark` and `view` feature.
+ * About Us Page:
+ * Updated the About Us page with the information of the team.
+
+* **Community**:
+ * Reviewed PRs for team members.
+ * Reported bugs and suggestions for other teams in the class during PE-D.
+
diff --git a/docs/team/ravern.md b/docs/team/ravern.md
new file mode 100644
index 00000000000..b5284b6b157
--- /dev/null
+++ b/docs/team/ravern.md
@@ -0,0 +1,39 @@
+---
+title: Ravern's Project Portfolio Page
+---
+
+## Project: TAfinder
+
+TAfinder is a desktop resource management application used by NUS SoC professors to choose TAs from a large pool of applicants. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **New Feature**: Added the `attach` command. (PR [\#64](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/64), [\#66](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/66), [\#72](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/72), [\#75](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/75))
+ * What it does: allows the user to attach files to TA applicants to store additional data about them (e.g. resume, transcript).
+ * Justification: This feature allows the users to avoid needing to copy and paste information from files submitted by the applicants into the comments field of each TA applicant, instead attaching them directly. This saves the users a lot of time and effort.
+ * Highlights: This enhancement required an understanding of how Java interacts with users' filesystems. The implementation was challenging as there were many corner cases to deal with when interacting with the filesystem, so there had to be additional tests added to cover them.
+
+* **New Feature**: Added the `import` command. (PR [\#101](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/101), [\#196](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/196))
+ * What it does: allows the user to attach files to TA applicants to store additional data about them (e.g. resume, transcript).
+ * Justification: This feature allows the users to avoid needing to manually transfer the details of all the TA applicants from mySoC to TAfinder. Instead, the users can simply export the data from mySoC and import it directly into TAfinder.
+ * Highlights: Parsing the CSV file format was a challenge. This enhancement required figuring out a way of reading the order of the columns in the CSV file, so as to correctly interpret the data contained within it.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=ravern&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByAuthors&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=true&tabType=authorship&zFR=false&tabAuthor=ravern&tabRepo=AY2324S1-CS2103T-W10-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+* **Project management**:
+ * Managed releases `v1.2` - `v1.4` (3 releases) on GitHub
+
+* **Enhancements to existing features**:
+ * Update the GUI to include a detailed view of an applicant (PR [\#90](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/90))
+ * Add student number field to each applicant to uniquely identify them (PR [\#71](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/71))
+ * Add the ability to store and show previous module grade and an arbitrary interview score (PR [\#100](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/100))
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `attach` and `import` (PR [\#110](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/110))
+ * Added the automatic numbering of headings and table of contents generation (PR [\#187](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/187))
+ * Developer Guide:
+ * Added implementation details of the `attach` and `import` feature (PR [\#98](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/98), [\#200](https://github.com/AY2324S1-CS2103T-W10-1/tp/pull/200))
+
+* **Tools**:
+ * Added the CodeCov badge to the README in the team repo
diff --git a/docs/team/ylyma.md b/docs/team/ylyma.md
new file mode 100644
index 00000000000..7f9bef343d3
--- /dev/null
+++ b/docs/team/ylyma.md
@@ -0,0 +1,37 @@
+---
+title: Amy's Project Portfolio Page
+---
+
+## Project: TAfinder
+
+TAfinder is a desktop resource management application used by NUS SOC professors to choose TAs from a large pool of applicants. The user interacts with it using a CLI, and it has a GUI created with JavaFX. It is written in Java, and has about 10 kLoC.
+
+Given below are my contributions to the project.
+
+* **Code contributed**: [RepoSense link](https://nus-cs2103-ay2324s1.github.io/tp-dashboard/?search=ylyma&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByAuthors&breakdown=true&checkedFileTypes=docs~functional-code~test-code&since=2023-09-22&tabOpen=true&tabType=authorship&tabAuthor=ylyma&tabRepo=AY2324S1-CS2103T-W10-1%2Ftp%5Bmaster%5D&authorshipIsMergeGroup=false&authorshipFileTypes=docs~functional-code~test-code&authorshipIsBinaryFileTypeChecked=false&authorshipIsIgnoredFilesChecked=false)
+
+* **New Feature**: Added the 'hide', 'unhide' and 'unhide-all' commands to the app.
+ * What it does: Allows the user to hide and unhide applicants from the list of applicants in the app.
+ * Justification: This feature is useful for the user to hide applicants that are not suitable for the job, and unhide them when they are suitable, helping to declutter the list of applicants.
+ * Highlights: This feature required a modification of several models to include new fields. I had to take care to preserve the immutability of the classes while doing so. I also had to make sure this command is applied to all future lists.
+
+* **New Feature**: Added a 'sort' command
+ * What it does: Allows the user to sort applicants by a specified field.
+ * Justification: This feature allows the most suitable applicants to be listed in order based on the user's criteria, helping them to make a decision more efficiently.
+ * Highlights: To allow flexibility of command when more fields are added in the future, I had to make sure the class is easily extendable to include new fields, while ensuring I stuck to the OOP principles. I also had to deliberate on how each field should be sorted.
+
+* **Enhancement to existing feature**: Enhanced 'list' command
+ * What it does: Allow the 'list' command to filter applicants based on a specified field, in addition to listing all applicants.
+ * Justification: This feature allows the user to see a filtered list of applicants based on what they are looking for (e.g. bookmarked applicants), making their search for the right applicant more organised.
+ * Highlights: I had to make this feature easily extendable to new fields as well. I also had to modify some existing classes.
+
+* **Documentation**:
+ * User Guide:
+ * Added documentation for the features `hide`, `unhide`, `unhide-all`, `sort` and `list`
+ * Added the corresponding screenshots for each command
+ * Developer Guide:
+ * Added the hide command section under implementation
+
+* **Contributions to team-based tasks**:
+ * Reviewed PRs for team members
+
diff --git a/docs/tutorials/AddRemark.md b/docs/tutorials/AddRemark.md
index d98f38982e7..5e6493f29ad 100644
--- a/docs/tutorials/AddRemark.md
+++ b/docs/tutorials/AddRemark.md
@@ -1,5 +1,4 @@
---
-layout: page
title: "Tutorial: Adding a command"
---
@@ -257,7 +256,7 @@ Then insert the following into [`main/resources/view/PersonListCard.fxml`](https
**`PersonListCard.fxml`:**
``` xml
-
+
```
That’s it! Fire up the application again and you should see something like this:
diff --git a/docs/tutorials/RemovingFields.md b/docs/tutorials/RemovingFields.md
index f29169bc924..161a9a56053 100644
--- a/docs/tutorials/RemovingFields.md
+++ b/docs/tutorials/RemovingFields.md
@@ -1,8 +1,9 @@
---
-layout: page
title: "Tutorial: Removing Fields"
---
+# 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
@@ -18,10 +19,6 @@ This tutorial aims to give you some practice on such a code 'removal' activity b
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`
IntelliJ IDEA provides a refactoring tool that can identify *most* parts of a removal easily. Let’s try to use it as much as we can.
@@ -82,9 +79,9 @@ private Label address;
``` xml
...
-
-
-
+
+
+
...
```
diff --git a/docs/tutorials/TracingCode.md b/docs/tutorials/TracingCode.md
index 4fb62a83ef6..eb906c38e66 100644
--- a/docs/tutorials/TracingCode.md
+++ b/docs/tutorials/TracingCode.md
@@ -1,17 +1,15 @@
---
-layout: page
title: "Tutorial: Tracing code"
---
+# 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.
diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java
index 3d6bd06d5af..1d414936d80 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, 4, 0, false);
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 TAfinder ]===========================");
super.init();
AppParameters appParameters = AppParameters.parse(getParameters());
@@ -68,9 +68,12 @@ public void init() throws Exception {
}
/**
- * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
- * The data from the sample address book will be used instead if {@code storage}'s address book is not found,
- * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book.
+ * Returns a {@code ModelManager} with the data from {@code storage}'s address
+ * book and {@code userPrefs}.
+ * The data from the sample applicant list will be used instead if
+ * {@code storage}'s applicant list is not found,
+ * or an empty applicant list will be used instead if errors occur when reading
+ * {@code storage}'s applicant list.
*/
private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
logger.info("Using data file : " + storage.getAddressBookFilePath());
@@ -81,12 +84,12 @@ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
addressBookOptional = storage.readAddressBook();
if (!addressBookOptional.isPresent()) {
logger.info("Creating a new data file " + storage.getAddressBookFilePath()
- + " populated with a sample AddressBook.");
+ + " populated with a sample applicant list.");
}
initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
} catch (DataLoadingException e) {
logger.warning("Data file at " + storage.getAddressBookFilePath() + " could not be loaded."
- + " Will be starting with an empty AddressBook.");
+ + " Will be starting with an empty applicant list.");
initialData = new AddressBook();
}
@@ -127,7 +130,8 @@ protected Config initConfig(Path configFilePath) {
initializedConfig = new Config();
}
- //Update config file in case it was missing to begin with or there are new/unused fields
+ // Update config file in case it was missing to begin with or there are
+ // new/unused fields
try {
ConfigUtil.saveConfig(initializedConfig, configFilePathUsed);
} catch (IOException e) {
@@ -137,7 +141,8 @@ protected Config initConfig(Path configFilePath) {
}
/**
- * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path,
+ * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs
+ * file path,
* or a new {@code UserPrefs} with default configuration if errors occur when
* reading from the file.
*/
@@ -158,7 +163,8 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
initializedPrefs = new UserPrefs();
}
- //Update prefs file in case it was missing to begin with or there are new/unused fields
+ // Update prefs file in case it was missing to begin with or there are
+ // new/unused fields
try {
storage.saveUserPrefs(initializedPrefs);
} catch (IOException e) {
@@ -170,13 +176,13 @@ protected UserPrefs initPrefs(UserPrefsStorage storage) {
@Override
public void start(Stage primaryStage) {
- logger.info("Starting AddressBook " + MainApp.VERSION);
+ logger.info("Starting TAfinder " + MainApp.VERSION);
ui.start(primaryStage);
}
@Override
public void stop() {
- logger.info("============================ [ Stopping Address Book ] =============================");
+ logger.info("============================ [ Stopping TAfinder ] =============================");
try {
storage.saveUserPrefs(model.getUserPrefs());
} catch (IOException e) {
diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java
index 92cd8fa605a..13cf650ae76 100644
--- a/src/main/java/seedu/address/logic/Logic.java
+++ b/src/main/java/seedu/address/logic/Logic.java
@@ -1,7 +1,9 @@
package seedu.address.logic;
import java.nio.file.Path;
+import java.util.Optional;
+import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.logic.commands.CommandResult;
@@ -33,6 +35,9 @@ public interface Logic {
/** Returns an unmodifiable view of the filtered list of persons */
ObservableList getFilteredPersonList();
+ /** Returns the current person being viewed in detail */
+ ObservableValue> getCurrentPerson();
+
/**
* 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..47635be7155 100644
--- a/src/main/java/seedu/address/logic/LogicManager.java
+++ b/src/main/java/seedu/address/logic/LogicManager.java
@@ -3,8 +3,10 @@
import java.io.IOException;
import java.nio.file.AccessDeniedException;
import java.nio.file.Path;
+import java.util.Optional;
import java.util.logging.Logger;
+import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
@@ -71,6 +73,11 @@ public ObservableList getFilteredPersonList() {
return model.getFilteredPersonList();
}
+ @Override
+ public ObservableValue> getCurrentPerson() {
+ return model.getCurrentPerson();
+ }
+
@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..39c40558b35 100644
--- a/src/main/java/seedu/address/logic/Messages.java
+++ b/src/main/java/seedu/address/logic/Messages.java
@@ -12,9 +12,10 @@
*/
public class Messages {
- public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command";
+ public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command.";
public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s";
- public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid";
+ public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "Error: Invalid index. "
+ + "Please enter an index within range.";
public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!";
public static final String MESSAGE_DUPLICATE_FIELDS =
"Multiple values specified for the following single-valued field(s): ";
@@ -41,8 +42,10 @@ public static String format(Person person) {
.append(person.getPhone())
.append("; Email: ")
.append(person.getEmail())
- .append("; Address: ")
- .append(person.getAddress())
+ .append("; GPA: ")
+ .append(person.getGpa())
+ .append("; Comment: ")
+ .append(person.getComment())
.append("; Tags: ");
person.getTags().forEach(builder::append);
return builder.toString();
diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java
index 5d7185a9680..c446491d364 100644
--- a/src/main/java/seedu/address/logic/commands/AddCommand.java
+++ b/src/main/java/seedu/address/logic/commands/AddCommand.java
@@ -1,10 +1,14 @@
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_COMMENT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GPA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEW_SCORE;
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_PREVIOUS_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STUDENT_NUMBER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import seedu.address.commons.util.ToStringBuilder;
@@ -14,29 +18,39 @@
import seedu.address.model.person.Person;
/**
- * Adds a person to the address book.
+ * Adds a applicant to the applicant list.
*/
public class AddCommand extends Command {
public static final String COMMAND_WORD = "add";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds an applicant to the list. "
+ // Parameters
+ "Parameters: "
+ + PREFIX_STUDENT_NUMBER + "STUDENT NUMBER "
+ PREFIX_NAME + "NAME "
+ PREFIX_PHONE + "PHONE "
+ PREFIX_EMAIL + "EMAIL "
- + PREFIX_ADDRESS + "ADDRESS "
- + "[" + PREFIX_TAG + "TAG]...\n"
+ + PREFIX_GPA + "GPA "
+ + PREFIX_PREVIOUS_GRADE + "PREV GRADE "
+ + "[" + PREFIX_INTERVIEW_SCORE + "INTERVIEW SCORE] " // optional
+ + "[" + PREFIX_COMMENT + "COMMENT] " // optional
+ + "[" + PREFIX_INTERVIEW_SCORE + "INTERVIEW SCORE] " // optional
+ + "[" + PREFIX_TAG + "TAG]...\n" // optional
+ // Example
+ "Example: " + COMMAND_WORD + " "
+ + PREFIX_STUDENT_NUMBER + "A0343434C "
+ 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_GPA + "4.9 "
+ + PREFIX_PREVIOUS_GRADE + "A "
+ + PREFIX_INTERVIEW_SCORE + "9.1 "
+ + PREFIX_COMMENT + "Hardworking and diligent "
+ + PREFIX_TAG + "pastTA ";
- public static final String MESSAGE_SUCCESS = "New person added: %1$s";
- public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book";
+ public static final String MESSAGE_SUCCESS = "New applicant added: %1$s.";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This applicant already exists in the applicant list.";
private final Person toAdd;
diff --git a/src/main/java/seedu/address/logic/commands/AttachCommand.java b/src/main/java/seedu/address/logic/commands/AttachCommand.java
new file mode 100644
index 00000000000..16fe77286da
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/AttachCommand.java
@@ -0,0 +1,152 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_FILE;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+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.attachment.Attachment;
+import seedu.address.model.person.Person;
+
+/**
+ * Attaches a file/files to an applicant (e.g. resume, certification).
+ */
+public class AttachCommand extends Command {
+
+ public static final String COMMAND_WORD = "attach";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Attaches a file to the applicant "
+ + "by the index number used in the displayed applicant list. This is done by copying\n "
+ + "the files into the data directory.\n "
+ + "Existing values will not be overwritten by new attachments.\n"
+ + "Cannot have attachments with the same filename, rename files before attaching them.\n"
+ + "Use the unattach command in order to remove attachments.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + "[" + PREFIX_FILE + "FILE]...\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_FILE + "samples/resume.pdf";
+ public static final String MESSAGE_ATTACH_SUCCESS = "Attached %1$s attachments to %2$s!";
+ public static final String MESSAGE_FAILED_TO_COPY = "Failed to copy attachment.";
+ public static final String MESSAGE_ATTACHMENT_ALREADY_EXISTS = "Attachment names should be unique.";
+
+ private final Index index;
+ private final List attachments;
+
+ /**
+ * Creates a new attach command, representing the attachment of a file/files to
+ * an
+ * applicant at the specified index on the visible applicant list.
+ *
+ * @param index index of the applicant to attach the file to
+ * @param attachments the path(s) of the file/files to be attached
+ */
+ public AttachCommand(Index index, List attachments) {
+ this.index = index;
+ this.attachments = attachments;
+ }
+
+ @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 personToAttachTo = lastShownList.get(index.getZeroBased());
+
+ List updatedAttachments = new ArrayList<>(personToAttachTo.getAttachments());
+ try {
+ for (Attachment attachment : attachments) {
+ if (attachment.file.isDirectory()) {
+ throw new CommandException(MESSAGE_FAILED_TO_COPY);
+ }
+ checkAttachmentUnique(attachment, updatedAttachments);
+ Attachment copiedAttachment = copyAttachment(
+ model.getUserPrefs().getAttachmentsBasePath(), attachment, personToAttachTo);
+ updatedAttachments.add(copiedAttachment);
+ }
+ } catch (IOException e) {
+ throw new CommandException(MESSAGE_FAILED_TO_COPY);
+ }
+
+ Person attachedPerson = new Person(
+ personToAttachTo.getStudentNumber(),
+ personToAttachTo.getName(),
+ personToAttachTo.getPhone(),
+ personToAttachTo.getEmail(),
+ personToAttachTo.getGpa(),
+ personToAttachTo.getPreviousGrade(),
+ personToAttachTo.getInterviewScore(),
+ personToAttachTo.getComment(),
+ personToAttachTo.getTags(),
+ updatedAttachments,
+ personToAttachTo.getIsHidden(),
+ personToAttachTo.getIsBookmarked()
+ );
+ model.setPerson(personToAttachTo, attachedPerson);
+
+ return new CommandResult(
+ String.format(MESSAGE_ATTACH_SUCCESS, attachments.size(), personToAttachTo.getName()));
+ }
+
+ private void checkAttachmentUnique(
+ Attachment attachment,
+ List existingAttachments) throws CommandException {
+ for (Attachment existingAttachment : existingAttachments) {
+ Path attachmentFileName = attachment.file.toPath().getFileName();
+ Path existingAttachmentFileName = existingAttachment.file.toPath().getFileName();
+ if (attachmentFileName.equals(existingAttachmentFileName)) {
+ throw new CommandException(MESSAGE_ATTACHMENT_ALREADY_EXISTS);
+ }
+ }
+ }
+
+ private Attachment copyAttachment(
+ Path basePath,
+ Attachment attachment,
+ Person personToAttachTo) throws IOException {
+ Path sourcePath = attachment.file.toPath();
+ String fileName = sourcePath.getFileName().toString();
+ Path destPath = Paths.get(basePath.toString(), personToAttachTo.getStudentNumber().toString(), fileName);
+ destPath.getParent().toFile().mkdirs();
+ Files.copy(sourcePath, destPath, StandardCopyOption.REPLACE_EXISTING);
+ return new Attachment(destPath.toString());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof AttachCommand)) {
+ return false;
+ }
+
+ AttachCommand otherAttachCommand = (AttachCommand) other;
+ return index.equals(otherAttachCommand.index)
+ && attachments.equals(otherAttachCommand.attachments);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", index)
+ .add("attachments", attachments)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/BookmarkCommand.java b/src/main/java/seedu/address/logic/commands/BookmarkCommand.java
new file mode 100644
index 00000000000..827c0920c74
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/BookmarkCommand.java
@@ -0,0 +1,91 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+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.person.IsBookmarked;
+import seedu.address.model.person.Person;
+
+
+/**
+ * Bookmarks an applicant from the list of all applicants.
+ */
+public class BookmarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "bookmark";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Bookmarks an applicant, identified by the index number "
+ + "used in the last list, from all future lists of applicants.\n"
+ + "Parameter: INDEX (must be a positive integer) \n"
+ + "Example: " + COMMAND_WORD + " 1 ";
+
+ public static final String MESSAGE_BOOKMARK_APPLICANT_SUCCESS = "Applicant at index %1$s has been "
+ + "successfully bookmarked.";
+
+ public final Index targetIndex;
+
+ /**
+ * Constructor for BookmarkCommand.
+ * @param targetIndex
+ */
+ public BookmarkCommand(Index targetIndex) {
+ requireAllNonNull(targetIndex);
+
+ this.targetIndex = targetIndex;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (this.targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToBookmark = lastShownList.get(targetIndex.getZeroBased());
+ Person bookmarkedPerson = new Person(
+ personToBookmark.getStudentNumber(),
+ personToBookmark.getName(),
+ personToBookmark.getPhone(),
+ personToBookmark.getEmail(),
+ personToBookmark.getGpa(),
+ personToBookmark.getPreviousGrade(),
+ personToBookmark.getInterviewScore(),
+ personToBookmark.getComment(),
+ personToBookmark.getTags(),
+ personToBookmark.getAttachments(),
+ personToBookmark.getIsHidden(),
+ new IsBookmarked(true));
+ model.setPerson(personToBookmark, bookmarkedPerson);
+ return new CommandResult(String.format(MESSAGE_BOOKMARK_APPLICANT_SUCCESS, targetIndex.getOneBased()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof BookmarkCommand)) {
+ return false;
+ }
+
+ BookmarkCommand e = (BookmarkCommand) other;
+ return targetIndex.equals(e.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java
index 9c86b1fa6e4..2f09bb29450 100644
--- a/src/main/java/seedu/address/logic/commands/ClearCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java
@@ -6,18 +6,19 @@
import seedu.address.model.Model;
/**
- * Clears the address book.
+ * Clears the applicant list.
*/
public class ClearCommand extends Command {
public static final String COMMAND_WORD = "clear";
- public static final String MESSAGE_SUCCESS = "Address book has been cleared!";
+ public static final String MESSAGE_SUCCESS = "Applicant list has been cleared!";
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
model.setAddressBook(new AddressBook());
+ model.clearPersonDetails();
return new CommandResult(MESSAGE_SUCCESS);
}
}
diff --git a/src/main/java/seedu/address/logic/commands/CommentCommand.java b/src/main/java/seedu/address/logic/commands/CommentCommand.java
new file mode 100644
index 00000000000..b6a29a3444f
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/CommentCommand.java
@@ -0,0 +1,103 @@
+package seedu.address.logic.commands;
+
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMENT;
+import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+
+import java.util.List;
+import java.util.Optional;
+
+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.Comment;
+import seedu.address.model.person.Person;
+
+
+/**
+ * Changes the comment of an existing person in the address book.
+ */
+public class CommentCommand extends Command {
+ public static final String COMMAND_WORD = "comment";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Edits the comment of the person identified "
+ + "by the index number used in the last person listing. "
+ + "Existing comment will be overwritten by the input.\n"
+ + "Parameters: INDEX (must be a positive integer) "
+ + "c/ [COMMENT]\n"
+ + "Example: " + COMMAND_WORD + " 1 "
+ + PREFIX_COMMENT + "Hardworking student";
+
+ public static final String MESSAGE_ADD_COMMENT_SUCCESS = "Applicant has been successfully commented on.";
+ public static final String MESSAGE_DELETE_COMMENT_SUCCESS = "Removed comment from Person: %1$s.";
+
+ private final Index index;
+ private final Comment comment;
+
+ /**
+ * @param index of the person in the filtered person list to edit the comment
+ * @param comment of the person to be updated to
+ */
+ public CommentCommand(Index index, Comment comment) {
+ requireAllNonNull(index, comment);
+
+ this.index = index;
+ this.comment = comment;
+ }
+
+ @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());
+ Person editedPerson = new Person(
+ personToEdit.getStudentNumber(),
+ personToEdit.getName(),
+ personToEdit.getPhone(),
+ personToEdit.getEmail(),
+ personToEdit.getGpa(),
+ personToEdit.getPreviousGrade(),
+ personToEdit.getInterviewScore(),
+ Optional.of(comment),
+ personToEdit.getTags(),
+ personToEdit.getAttachments(),
+ personToEdit.getIsHidden(),
+ personToEdit.getIsBookmarked());
+ 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 = !comment.comment.isEmpty() ? MESSAGE_ADD_COMMENT_SUCCESS : MESSAGE_DELETE_COMMENT_SUCCESS;
+ return String.format(message, personToEdit);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof CommentCommand)) {
+ return false;
+ }
+
+ CommentCommand e = (CommentCommand) other;
+ return index.equals(e.index)
+ && comment.equals(e.comment);
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/CompareCommand.java b/src/main/java/seedu/address/logic/commands/CompareCommand.java
new file mode 100644
index 00000000000..7039d13fa59
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/CompareCommand.java
@@ -0,0 +1,67 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+import seedu.address.ui.CompareWindow;
+
+
+/**
+ * Compare two applicants side by side.
+ * It takes two indices as parameters and compares the GPA of the applicants at those indices.
+ */
+public class CompareCommand extends Command {
+
+ public static final String COMMAND_WORD = "compare";
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Compares two applicants side by side.\n"
+ + "Parameters: INDEX1 and INDEX2 (must be a positive integers)\n"
+ + "Example: " + COMMAND_WORD + " 1 2";
+
+ private final Index index1;
+ private final Index index2;
+
+ /**
+ * Constructs a `CompareCommand` with the given indices.
+ *
+ * @param index1 The index of the first applicant to compare.
+ * @param index2 The index of the second applicant to compare.
+ */
+ public CompareCommand(Index index1, Index index2) {
+ requireNonNull(index1);
+ requireNonNull(index2);
+
+ this.index1 = index1;
+ this.index2 = index2;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (index1.equals(index2)) {
+ throw new CommandException("Error: Please provide distinct indices. "
+ + "You cannot compare the same applicant.");
+ }
+
+ try {
+ Person personToCompare1 = lastShownList.get(index1.getZeroBased());
+ Person personToCompare2 = lastShownList.get(index2.getZeroBased());
+
+ new CompareWindow(personToCompare1, personToCompare2).show();
+
+ return new CommandResult("Comparison successful! ");
+
+ } catch (IndexOutOfBoundsException e) {
+ throw new CommandException("Error: One or both of the specified applicants"
+ + " were not found in the list.");
+ }
+ }
+}
+
diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
index 1135ac19b74..85ef934eea8 100644
--- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java
+++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java
@@ -12,18 +12,18 @@
import seedu.address.model.person.Person;
/**
- * Deletes a person identified using it's displayed index from the address book.
+ * Deletes a applicant identified using it's displayed index from the applicant list.
*/
public class DeleteCommand extends Command {
public static final String COMMAND_WORD = "delete";
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Deletes the person identified by the index number used in the displayed person list.\n"
+ + ": Deletes the applicant identified by the index number used in the displayed applicant list.\n"
+ "Parameters: INDEX (must be a positive integer)\n"
+ "Example: " + COMMAND_WORD + " 1";
- public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s";
+ public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted applicant: %1$s.";
private final Index targetIndex;
diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java
index 4b581c7331e..46983a2e161 100644
--- a/src/main/java/seedu/address/logic/commands/EditCommand.java
+++ b/src/main/java/seedu/address/logic/commands/EditCommand.java
@@ -1,12 +1,12 @@
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_COMMENT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GPA;
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;
@@ -21,43 +21,50 @@
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.attachment.Attachment;
+import seedu.address.model.person.Comment;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.InterviewScore;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
+import seedu.address.model.person.StudentNumber;
import seedu.address.model.tag.Tag;
/**
- * Edits the details of an existing person in the address book.
+ * Edits the details of an existing person in the applicant list.
*/
public class EditCommand extends Command {
public static final String COMMAND_WORD = "edit";
- public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified "
- + "by the index number used in the displayed person list. "
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the applicant identified "
+ + "by the index number used in the displayed applicant 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_GPA + "GPA] "
+ + "[" + PREFIX_COMMENT + "COMMENT] "
+ "[" + 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_EDIT_PERSON_SUCCESS = "Edited applicant: %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.";
+ public static final String MESSAGE_DUPLICATE_PERSON = "This applicant already exists in the list.";
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
+ * @param index of the applicant in the filtered applicant list
+ * to edit
+ * @param editPersonDescriptor details to edit the applicant with
*/
public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) {
requireNonNull(index);
@@ -84,7 +91,9 @@ public CommandResult execute(Model model) throws CommandException {
}
model.setPerson(personToEdit, editedPerson);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_UNHIDDEN_PERSONS);
+ model.showPersonAtIndex(index);
+
return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, Messages.format(editedPerson)));
}
@@ -98,10 +107,20 @@ private static Person createEditedPerson(Person personToEdit, EditPersonDescript
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());
+ Gpa updatedGpa = editPersonDescriptor.getGpa().orElse(personToEdit.getGpa());
+ PreviousGrade updatedPreviousGrade = editPersonDescriptor.getPreviousGrade()
+ .orElse(personToEdit.getPreviousGrade());
+ Optional updatedInterviewScore = editPersonDescriptor.getInterviewScore()
+ .or(() -> personToEdit.getInterviewScore());
+ Optional updatedComment = editPersonDescriptor.getComment().or(() -> personToEdit.getComment());
Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags());
- return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags);
+ StudentNumber studentNo = personToEdit.getStudentNumber();
+ List attachments = personToEdit.getAttachments();
+
+ return new Person(studentNo, updatedName, updatedPhone, updatedEmail,
+ updatedGpa, updatedPreviousGrade, updatedInterviewScore, updatedComment,
+ updatedTags, attachments, personToEdit.getIsHidden(), personToEdit.getIsBookmarked());
}
@Override
@@ -129,17 +148,22 @@ public String toString() {
}
/**
- * Stores the details to edit the person with. Each non-empty field value will replace the
- * corresponding field value of the person.
+ * Stores the details to edit the applicant with. Each non-empty field value
+ * will replace the
+ * corresponding field value of the applicant.
*/
public static class EditPersonDescriptor {
private Name name;
private Phone phone;
private Email email;
- private Address address;
+ private Gpa gpa;
+ private PreviousGrade previousGrade;
+ private InterviewScore interviewScore;
+ private Comment comment;
private Set tags;
- public EditPersonDescriptor() {}
+ public EditPersonDescriptor() {
+ }
/**
* Copy constructor.
@@ -149,7 +173,10 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) {
setName(toCopy.name);
setPhone(toCopy.phone);
setEmail(toCopy.email);
- setAddress(toCopy.address);
+ setGpa(toCopy.gpa);
+ setPreviousGrade(toCopy.previousGrade);
+ setInterviewScore(toCopy.interviewScore);
+ setComment(toCopy.comment);
setTags(toCopy.tags);
}
@@ -157,7 +184,8 @@ public EditPersonDescriptor(EditPersonDescriptor toCopy) {
* Returns true if at least one field is edited.
*/
public boolean isAnyFieldEdited() {
- return CollectionUtil.isAnyNonNull(name, phone, email, address, tags);
+ return CollectionUtil.isAnyNonNull(
+ name, phone, email, gpa, previousGrade, interviewScore, comment, tags);
}
public void setName(Name name) {
@@ -184,12 +212,36 @@ public Optional getEmail() {
return Optional.ofNullable(email);
}
- public void setAddress(Address address) {
- this.address = address;
+ public void setGpa(Gpa gpa) {
+ this.gpa = gpa;
+ }
+
+ public Optional getGpa() {
+ return Optional.ofNullable(gpa);
+ }
+
+ public void setPreviousGrade(PreviousGrade previousGrade) {
+ this.previousGrade = previousGrade;
+ }
+
+ public Optional getPreviousGrade() {
+ return Optional.ofNullable(previousGrade);
+ }
+
+ public void setInterviewScore(InterviewScore interviewScore) {
+ this.interviewScore = interviewScore;
+ }
+
+ public Optional getInterviewScore() {
+ return Optional.ofNullable(interviewScore);
+ }
+
+ public void setComment(Comment comment) {
+ this.comment = comment;
}
- public Optional getAddress() {
- return Optional.ofNullable(address);
+ public Optional getComment() {
+ return Optional.ofNullable(comment);
}
/**
@@ -201,7 +253,8 @@ public void setTags(Set tags) {
}
/**
- * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException}
+ * Returns an unmodifiable tag set, which throws
+ * {@code UnsupportedOperationException}
* if modification is attempted.
* Returns {@code Optional#empty()} if {@code tags} is null.
*/
@@ -224,7 +277,10 @@ public boolean equals(Object other) {
return Objects.equals(name, otherEditPersonDescriptor.name)
&& Objects.equals(phone, otherEditPersonDescriptor.phone)
&& Objects.equals(email, otherEditPersonDescriptor.email)
- && Objects.equals(address, otherEditPersonDescriptor.address)
+ && Objects.equals(gpa, otherEditPersonDescriptor.gpa)
+ && Objects.equals(previousGrade, otherEditPersonDescriptor.previousGrade)
+ && Objects.equals(interviewScore, otherEditPersonDescriptor.interviewScore)
+ && Objects.equals(comment, otherEditPersonDescriptor.comment)
&& Objects.equals(tags, otherEditPersonDescriptor.tags);
}
@@ -234,8 +290,11 @@ public String toString() {
.add("name", name)
.add("phone", phone)
.add("email", email)
- .add("address", address)
+ .add("gpa", gpa)
+ .add("previousGrade", previousGrade)
+ .add("interviewScore", interviewScore)
.add("tags", tags)
+ .add("comment", comment)
.toString();
}
}
diff --git a/src/main/java/seedu/address/logic/commands/ExitCommand.java b/src/main/java/seedu/address/logic/commands/ExitCommand.java
index 3dd85a8ba90..b3879e4ad3a 100644
--- a/src/main/java/seedu/address/logic/commands/ExitCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ExitCommand.java
@@ -9,7 +9,7 @@ public class ExitCommand extends Command {
public static final String COMMAND_WORD = "exit";
- public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting Address Book as requested ...";
+ public static final String MESSAGE_EXIT_ACKNOWLEDGEMENT = "Exiting TAfinder as requested ...";
@Override
public CommandResult execute(Model model) {
diff --git a/src/main/java/seedu/address/logic/commands/FindCommand.java b/src/main/java/seedu/address/logic/commands/FindCommand.java
index 72b9eddd3a7..2d2b62d8aa1 100644
--- a/src/main/java/seedu/address/logic/commands/FindCommand.java
+++ b/src/main/java/seedu/address/logic/commands/FindCommand.java
@@ -9,7 +9,7 @@
/**
* Finds and lists all persons in address book whose name contains any of the argument keywords.
- * Keyword matching is case insensitive.
+ * Keyword matching is case-insensitive.
*/
public class FindCommand extends Command {
diff --git a/src/main/java/seedu/address/logic/commands/HideCommand.java b/src/main/java/seedu/address/logic/commands/HideCommand.java
new file mode 100644
index 00000000000..84937cac3b5
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/HideCommand.java
@@ -0,0 +1,101 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+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.person.IsHidden;
+import seedu.address.model.person.Person;
+
+
+/**
+ * Hides an applicant from the list of all applicants.
+ */
+public class HideCommand extends Command {
+
+ public static final String COMMAND_WORD = "hide";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Hides an applicant, identified by the index number "
+ + "used in the last list, from all future lists of applicants.\n"
+ + "Parameter: INDEX (must be a positive integer) \n"
+ + "Example: " + COMMAND_WORD + " 1 ";
+
+ public static final String MESSAGE_HIDE_APPLICANT_SUCCESS = "Applicant %1$s hidden from lists.";
+
+ public final Index targetIndex;
+
+ /**
+ * Constructor for HideCommand.
+ * @param targetIndex
+ */
+ public HideCommand(Index targetIndex) {
+ requireAllNonNull(targetIndex);
+
+ this.targetIndex = targetIndex;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (this.targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToHide = lastShownList.get(targetIndex.getZeroBased());
+ model.setPerson(personToHide, createHiddenPerson(personToHide));
+ model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_UNHIDDEN_PERSONS);
+ return new CommandResult(String.format(MESSAGE_HIDE_APPLICANT_SUCCESS, Messages.format(personToHide)));
+ }
+
+ /**
+ * Creates and returns a hidden {@code Person} with the details of {@code personToHide}
+ * @param personToHide {@code Person} to hide
+ * @return {@code Person} with {@code IsHidden} set to true
+ */
+ private static Person createHiddenPerson(Person personToHide) {
+ assert personToHide != null;
+
+ return new Person(
+ personToHide.getStudentNumber(),
+ personToHide.getName(),
+ personToHide.getPhone(),
+ personToHide.getEmail(),
+ personToHide.getGpa(),
+ personToHide.getPreviousGrade(),
+ personToHide.getInterviewScore(),
+ personToHide.getComment(),
+ personToHide.getTags(),
+ personToHide.getAttachments(),
+ new IsHidden(true),
+ personToHide.getIsBookmarked());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof HideCommand)) {
+ return false;
+ }
+
+ HideCommand e = (HideCommand) other;
+ return targetIndex.equals(e.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ImportCommand.java b/src/main/java/seedu/address/logic/commands/ImportCommand.java
new file mode 100644
index 00000000000..47a1d8e037d
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ImportCommand.java
@@ -0,0 +1,271 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.logic.parser.CliSyntax.FIELD_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.FIELD_GPA;
+import static seedu.address.logic.parser.CliSyntax.FIELD_NAME;
+import static seedu.address.logic.parser.CliSyntax.FIELD_PHONE;
+import static seedu.address.logic.parser.CliSyntax.FIELD_PREVIOUS_GRADE;
+import static seedu.address.logic.parser.CliSyntax.FIELD_STUDENT_NUMBER;
+import static seedu.address.logic.parser.CliSyntax.FIELD_TAGS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_FILE;
+
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.attachment.Attachment;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.IsBookmarked;
+import seedu.address.model.person.IsHidden;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
+import seedu.address.model.person.StudentNumber;
+import seedu.address.model.tag.Tag;
+
+/**
+ * Imports new applicants from the given file.
+ */
+public class ImportCommand extends Command {
+
+ public static final String COMMAND_WORD = "import";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Imports applicants from the file "
+ + "specified, reading it in CSV format. The CSV header row is required, any\n "
+ + "additional fields other than those supported will be ignored, and any missing "
+ + "fields will be reported as an error.\n"
+ + "Supported fields: studentNo, name, phone, email, gpa, previousGrade, tags.\n"
+ + "Parameters: " + PREFIX_FILE + "FILE\n"
+ + "Example: " + COMMAND_WORD + " " + PREFIX_FILE + "samples/applicants.csv";
+ public static final String MESSAGE_IMPORT_SUCCESS = "Imported %1$s applicants successfully!";
+ public static final String MESSAGE_IMPORT_NONE = "No applicants were imported.";
+ public static final String MESSAGE_IMPORT_FAILURE = "Failed to import %1$s applicants, on line %2$s.";
+ public static final String MESSAGE_FAILED_TO_OPEN = "Failed to open and load applicant file.";
+ public static final String MESSAGE_INVALID_FILE_FORMAT = "File format is invalid.";
+ public static final String MESSAGE_MISSING_FIELDS = "Missing fields from file: %1$s.";
+
+ private static final String CELL_DELIM = ",";
+ private static final String DATA_ARRAY_DELIM = ";";
+ private static final Set REQUIRED_FIELDS = Set.of(
+ FIELD_STUDENT_NUMBER, FIELD_NAME,
+ FIELD_PHONE, FIELD_EMAIL, FIELD_GPA,
+ FIELD_PREVIOUS_GRADE, FIELD_TAGS);
+
+ private final Attachment attachment;
+
+ /**
+ * Creates a new import command, which will import applicants listed in the
+ * given
+ * file (read in using CSV format).
+ *
+ * @param attachment The path of the file to be imported
+ */
+ public ImportCommand(Attachment attachment) {
+ this.attachment = attachment;
+ }
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+
+ List applicants = new ArrayList<>();
+ List failureLineNos = new ArrayList<>();
+
+ Scanner scanner = null;
+ try {
+ scanner = new Scanner(attachment.file);
+
+ if (!scanner.hasNextLine()) {
+ throw new CommandException(MESSAGE_INVALID_FILE_FORMAT);
+ }
+ String header = scanner.nextLine();
+ Map fieldIndices = buildFieldIndices(header);
+
+ int lineNo = 1;
+ while (scanner.hasNextLine()) {
+ lineNo++;
+ String line = scanner.nextLine();
+ try {
+ Person applicant = parseLine(fieldIndices, line);
+ applicants.add(applicant);
+ } catch (ParsePersonException e) {
+ failureLineNos.add(lineNo);
+ continue;
+ }
+ }
+ } catch (FileNotFoundException e) {
+ throw new CommandException(MESSAGE_FAILED_TO_OPEN, e);
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+
+ int newApplicantCount = applicants.size();
+ for (Person applicant : applicants) {
+ if (model.hasPerson(applicant)) {
+ newApplicantCount--;
+ continue;
+ }
+ model.addPerson(applicant);
+ }
+
+ String successMessage = null;
+ if (!applicants.isEmpty()) {
+ successMessage = String.format(MESSAGE_IMPORT_SUCCESS, newApplicantCount);
+ }
+
+ String failureMessage = null;
+ if (!failureLineNos.isEmpty()) {
+ List lineNos = failureLineNos.stream().map(Object::toString).collect(Collectors.toList());
+ failureMessage = String.format(
+ MESSAGE_IMPORT_FAILURE,
+ failureLineNos.size(),
+ String.join(", ", lineNos));
+ }
+
+ String message;
+ if (successMessage != null && failureMessage != null) {
+ message = successMessage + " " + failureMessage;
+ } else if (successMessage != null) {
+ message = successMessage;
+ } else if (failureMessage != null) {
+ message = failureMessage;
+ } else {
+ message = MESSAGE_IMPORT_NONE;
+ }
+
+ return new CommandResult(message);
+ }
+
+ private Map buildFieldIndices(String header) throws CommandException {
+ String[] fields = header.split(CELL_DELIM);
+
+ Set fieldSet = Set.of(fields);
+ if (!fieldSet.containsAll(REQUIRED_FIELDS)) {
+ Set requiredFieldsCopy = new HashSet<>(REQUIRED_FIELDS);
+ requiredFieldsCopy.removeAll(fieldSet);
+ throw new CommandException(
+ String.format(MESSAGE_MISSING_FIELDS, String.join(", ", requiredFieldsCopy)));
+ }
+
+ Map fieldIndices = new HashMap<>(fields.length);
+ for (int i = 0; i < fields.length; i++) {
+ fieldIndices.put(fields[i], i);
+ }
+
+ return fieldIndices;
+ }
+
+ private Person parseLine(Map fieldIndices, String line) throws ParsePersonException {
+ String[] data = line.split(CELL_DELIM, -1);
+
+ String studentNoString = data[fieldIndices.get(FIELD_STUDENT_NUMBER)];
+ if (!StudentNumber.isValidStudentNumber(studentNoString)) {
+ throw new ParsePersonException("Invalid student number");
+ }
+ StudentNumber studentNo = new StudentNumber(studentNoString);
+
+ String nameString = data[fieldIndices.get(FIELD_NAME)];
+ if (!Name.isValidName(nameString)) {
+ throw new ParsePersonException("Invalid name");
+ }
+ Name name = new Name(nameString);
+
+ String phoneString = data[fieldIndices.get(FIELD_PHONE)];
+ if (!Phone.isValidPhone(phoneString)) {
+ throw new ParsePersonException("Invalid phone");
+ }
+ Phone phone = new Phone(phoneString);
+
+ String emailString = data[fieldIndices.get(FIELD_EMAIL)];
+ if (!Email.isValidEmail(emailString)) {
+ throw new ParsePersonException("Invalid email");
+ }
+ Email email = new Email(emailString);
+
+ String gpaString = data[fieldIndices.get(FIELD_GPA)];
+ double gpaDouble;
+ try {
+ gpaDouble = Double.parseDouble(gpaString);
+ } catch (NumberFormatException e) {
+ throw new ParsePersonException("Invalid GPA");
+ }
+ if (!Gpa.isValidGpa(gpaDouble)) {
+ throw new ParsePersonException("Invalid GPA");
+ }
+ Gpa gpa = new Gpa(gpaDouble);
+
+ String previousGradeString = data[fieldIndices.get(FIELD_PREVIOUS_GRADE)];
+ if (!PreviousGrade.isValidGrade(previousGradeString)) {
+ throw new ParsePersonException("Invalid previous grade");
+ }
+ PreviousGrade previousGrade = new PreviousGrade(previousGradeString);
+
+ String tagsString = data[fieldIndices.get(FIELD_TAGS)];
+ String[] tagStrings = tagsString.split(DATA_ARRAY_DELIM);
+ Set tags = Set.of();
+ if (tagStrings.length > 1 || tagStrings.length == 0 || !tagStrings[0].trim().equals("")) {
+ boolean isValid = List.of(tagStrings).stream().map(Tag::isValidTagName).allMatch(v -> v);
+ if (!isValid) {
+ throw new ParsePersonException("Invalid tag");
+ }
+ tags = List.of(tagStrings).stream().map(Tag::new).collect(Collectors.toSet());
+ }
+
+ Person applicant = new Person(
+ studentNo, name, phone, email, gpa, previousGrade,
+ Optional.empty(), Optional.empty(), tags, List.of(),
+ new IsHidden(false), new IsBookmarked(false));
+
+ return applicant;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ImportCommand)) {
+ return false;
+ }
+
+ ImportCommand otherImportCommand = (ImportCommand) other;
+ return attachment.equals(otherImportCommand.attachment);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("attachment", attachment)
+ .toString();
+ }
+
+ private static class ParsePersonException extends IllegalValueException {
+
+ public ParsePersonException(String message) {
+ super(message);
+ }
+
+ public ParsePersonException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/logic/commands/ListCommand.java b/src/main/java/seedu/address/logic/commands/ListCommand.java
index 84be6ad2596..34b2702d9cd 100644
--- a/src/main/java/seedu/address/logic/commands/ListCommand.java
+++ b/src/main/java/seedu/address/logic/commands/ListCommand.java
@@ -1,24 +1,69 @@
package seedu.address.logic.commands;
import static java.util.Objects.requireNonNull;
-import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS;
+import java.util.function.Predicate;
+
+import seedu.address.commons.util.ToStringBuilder;
import seedu.address.model.Model;
+import seedu.address.model.person.ListPredicate;
+import seedu.address.model.person.Person;
/**
- * Lists all persons in the address book to the user.
+ * Lists all applicants in the applicant list to the user.
*/
public class ListCommand extends Command {
public static final String COMMAND_WORD = "list";
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Lists all applicants filtered by the given field.\n"
+ + "Parameters: FIELD_NAME\n"
+ + "Example: " + COMMAND_WORD + " hidden";
+
+ public static final String MESSAGE_SUCCESS = "Listed applicants.";
+ public static final String MESSAGE_EMPTY_LIST = "No applicants to list.";
+ public final String fieldName;
- public static final String MESSAGE_SUCCESS = "Listed all persons";
+ public ListCommand(String fieldName) {
+ this.fieldName = fieldName;
+ }
+ public ListCommand() {
+ this.fieldName = "";
+ }
@Override
public CommandResult execute(Model model) {
requireNonNull(model);
- model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+
+ if (fieldName.isEmpty()) {
+ model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_UNHIDDEN_PERSONS);
+ } else {
+ Predicate predicate = new ListPredicate(fieldName, true);
+ model.updateFilteredPersonList(predicate);
+ }
+ if (model.getFilteredPersonList().isEmpty()) {
+ return new CommandResult(MESSAGE_EMPTY_LIST);
+ }
+
+
return new CommandResult(MESSAGE_SUCCESS);
}
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ // instanceof handles nulls
+ if (!(other instanceof ListCommand)) {
+ return false;
+ }
+ ListCommand otherListCommand = (ListCommand) other;
+ return fieldName.equals(otherListCommand.fieldName);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("fieldName", fieldName).toString();
+ }
}
diff --git a/src/main/java/seedu/address/logic/commands/SortCommand.java b/src/main/java/seedu/address/logic/commands/SortCommand.java
new file mode 100644
index 00000000000..84f89b07bb1
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/SortCommand.java
@@ -0,0 +1,65 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Comparator;
+
+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.SortComparator;
+
+/**
+ * Sorts all applicants by the given field name.
+ */
+public class SortCommand extends Command {
+ public static final String COMMAND_WORD = "sort";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD + ": Sorts all applicants by the given field name.\n"
+ + "Parameters: FIELD_NAME\n"
+ + "Example: " + COMMAND_WORD + " gpa";
+
+ public static final String MESSAGE_SUCCESS = "Sorted all applicants";
+
+ public static final String MESSAGE_EMPTY_LIST = "No applicants to sort.";
+
+ public final String fieldName;
+
+ /**
+ * Constructor for SortCommand.
+ * @param fieldName the field name to sort by.
+ */
+ public SortCommand(String fieldName) {
+ requireNonNull(fieldName);
+ this.fieldName = fieldName;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ if (model.getFilteredPersonList().isEmpty()) {
+ throw new CommandException(MESSAGE_EMPTY_LIST);
+ }
+ Comparator comparator = new SortComparator(fieldName);
+ model.sortFilteredPersonList(comparator);
+ return new CommandResult(MESSAGE_SUCCESS);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+ // instanceof handles nulls
+ if (!(other instanceof SortCommand)) {
+ return false;
+ }
+ SortCommand otherSortCommand = (SortCommand) other;
+ return fieldName.equals(otherSortCommand.fieldName);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).add("fieldName", fieldName).toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/UnbookmarkCommand.java b/src/main/java/seedu/address/logic/commands/UnbookmarkCommand.java
new file mode 100644
index 00000000000..7a21525fd23
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UnbookmarkCommand.java
@@ -0,0 +1,91 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+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.person.IsBookmarked;
+import seedu.address.model.person.Person;
+
+
+/**
+ * Unbookmarks an applicant from the list of all applicants.
+ */
+public class UnbookmarkCommand extends Command {
+
+ public static final String COMMAND_WORD = "unbookmark";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unbookmarks an applicant, identified by the index number "
+ + "used in the last list, from all future lists of applicants.\n"
+ + "Parameter: INDEX (must be a positive integer) \n"
+ + "Example: " + COMMAND_WORD + " 1 ";
+
+ public static final String MESSAGE_UNBOOKMARK_APPLICANT_SUCCESS = "Applicant at index %1$s has been "
+ + "successfully unbookmarked.";
+
+ public final Index targetIndex;
+
+ /**
+ * Constructor for UnbookmarkCommand.
+ * @param targetIndex
+ */
+ public UnbookmarkCommand(Index targetIndex) {
+ requireAllNonNull(targetIndex);
+
+ this.targetIndex = targetIndex;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (this.targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToUnbookmark = lastShownList.get(targetIndex.getZeroBased());
+ Person unbookmarkedPerson = new Person(
+ personToUnbookmark.getStudentNumber(),
+ personToUnbookmark.getName(),
+ personToUnbookmark.getPhone(),
+ personToUnbookmark.getEmail(),
+ personToUnbookmark.getGpa(),
+ personToUnbookmark.getPreviousGrade(),
+ personToUnbookmark.getInterviewScore(),
+ personToUnbookmark.getComment(),
+ personToUnbookmark.getTags(),
+ personToUnbookmark.getAttachments(),
+ personToUnbookmark.getIsHidden(),
+ new IsBookmarked(false));
+ model.setPerson(personToUnbookmark, unbookmarkedPerson);
+ return new CommandResult(String.format(MESSAGE_UNBOOKMARK_APPLICANT_SUCCESS, targetIndex.getOneBased()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof UnbookmarkCommand)) {
+ return false;
+ }
+
+ UnbookmarkCommand e = (UnbookmarkCommand) other;
+ return targetIndex.equals(e.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/UnhideAllCommand.java b/src/main/java/seedu/address/logic/commands/UnhideAllCommand.java
new file mode 100644
index 00000000000..b488e7a0721
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UnhideAllCommand.java
@@ -0,0 +1,60 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.person.IsHidden;
+import seedu.address.model.person.Person;
+
+
+/**
+ * Unhides all applicants from the list of all applicants.
+ */
+public class UnhideAllCommand extends Command {
+
+ public static final String COMMAND_WORD = "unhide-all";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unhides all applicants in all future lists of applicants.\n"
+ + "Example: " + COMMAND_WORD;
+
+ public static final String MESSAGE_SUCCESS = "All applicants unhidden from lists.";
+
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ Model modelCopy = new ModelManager(model.getAddressBook(), model.getUserPrefs());
+ modelCopy.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_HIDDEN_PERSONS);
+ List hiddenList = modelCopy.getFilteredPersonList();
+ hiddenList.forEach(p -> model.setPerson(p, createUnhiddenPerson(p)));
+ model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_UNHIDDEN_PERSONS);
+ return new CommandResult(String.format(MESSAGE_SUCCESS));
+ }
+
+ /**
+ * Creates and returns an unhidden {@code Person} with the details of {@code personToUnhide}
+ * @param personToUnhide {@code Person} to unhide
+ * @return {@code Person} with {@code IsHidden} set to false
+ */
+ private static Person createUnhiddenPerson(Person personToUnhide) {
+ assert personToUnhide != null;
+
+ return new Person(
+ personToUnhide.getStudentNumber(),
+ personToUnhide.getName(),
+ personToUnhide.getPhone(),
+ personToUnhide.getEmail(),
+ personToUnhide.getGpa(),
+ personToUnhide.getPreviousGrade(),
+ personToUnhide.getInterviewScore(),
+ personToUnhide.getComment(),
+ personToUnhide.getTags(),
+ personToUnhide.getAttachments(),
+ new IsHidden(false),
+ personToUnhide.getIsBookmarked());
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/UnhideCommand.java b/src/main/java/seedu/address/logic/commands/UnhideCommand.java
new file mode 100644
index 00000000000..7025a300a48
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/UnhideCommand.java
@@ -0,0 +1,101 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+
+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.person.IsHidden;
+import seedu.address.model.person.Person;
+
+
+/**
+ * Unhides an applicant from the list of all applicants.
+ */
+public class UnhideCommand extends Command {
+
+ public static final String COMMAND_WORD = "unhide";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ": Unhides an applicant, identified by the index number "
+ + "used in the last list, in all future lists of applicants.\n"
+ + "Parameter: INDEX (must be a positive integer) \n"
+ + "Example: " + COMMAND_WORD + " 1 ";
+
+ public static final String MESSAGE_UNHIDE_APPLICANT_SUCCESS = "Applicant %1$s unhidden from lists.";
+
+ public final Index targetIndex;
+
+ /**
+ * Constructor for UnhideCommand.
+ * @param targetIndex
+ */
+ public UnhideCommand(Index targetIndex) {
+ requireAllNonNull(targetIndex);
+
+ this.targetIndex = targetIndex;
+ }
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (this.targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToUnhide = lastShownList.get(targetIndex.getZeroBased());
+ model.setPerson(personToUnhide, createUnhiddenPerson(personToUnhide));
+ model.updateFilteredPersonList(Model.PREDICATE_SHOW_ALL_UNHIDDEN_PERSONS);
+ return new CommandResult(String.format(MESSAGE_UNHIDE_APPLICANT_SUCCESS, Messages.format(personToUnhide)));
+ }
+
+ /**
+ * Creates and returns an unhidden {@code Person} with the details of {@code personToUnhide}
+ * @param personToUnhide {@code Person} to unhide
+ * @return {@code Person} with {@code IsHidden} set to false
+ */
+ private static Person createUnhiddenPerson(Person personToUnhide) {
+ assert personToUnhide != null;
+
+ return new Person(
+ personToUnhide.getStudentNumber(),
+ personToUnhide.getName(),
+ personToUnhide.getPhone(),
+ personToUnhide.getEmail(),
+ personToUnhide.getGpa(),
+ personToUnhide.getPreviousGrade(),
+ personToUnhide.getInterviewScore(),
+ personToUnhide.getComment(),
+ personToUnhide.getTags(),
+ personToUnhide.getAttachments(),
+ new IsHidden(false),
+ personToUnhide.getIsBookmarked());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ if (!(other instanceof UnhideCommand)) {
+ return false;
+ }
+
+ UnhideCommand e = (UnhideCommand) other;
+ return targetIndex.equals(e.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/commands/ViewCommand.java b/src/main/java/seedu/address/logic/commands/ViewCommand.java
new file mode 100644
index 00000000000..4be8ee9c47e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/commands/ViewCommand.java
@@ -0,0 +1,70 @@
+package seedu.address.logic.commands;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.logic.Messages;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.person.Person;
+
+/**
+ * Deletes a person identified using its displayed index from the applicant list.
+ */
+public class ViewCommand extends Command {
+
+ public static final String COMMAND_WORD = "view";
+
+ public static final String MESSAGE_USAGE = COMMAND_WORD
+ + ":\nDisplays the applicant identified by the index number used in the displayed applicant list.\n"
+ + "Parameters: INDEX (must be a positive integer)\n"
+ + "Example: " + COMMAND_WORD + " 1";
+
+ public static final String MESSAGE_VIEW_PERSON_SUCCESS = "Displaying: %1$s.";
+
+ private final Index targetIndex;
+
+ public ViewCommand(Index targetIndex) {
+ this.targetIndex = targetIndex;
+ }
+
+ //TODO
+ @Override
+ public CommandResult execute(Model model) throws CommandException {
+ requireNonNull(model);
+ List lastShownList = model.getFilteredPersonList();
+
+ if (targetIndex.getZeroBased() >= lastShownList.size()) {
+ throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ Person personToView = lastShownList.get(targetIndex.getZeroBased());
+ model.showPersonAtIndex(targetIndex);
+ return new CommandResult(String.format(MESSAGE_VIEW_PERSON_SUCCESS, personToView.getName()));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof ViewCommand)) {
+ return false;
+ }
+
+ ViewCommand otherViewCommand = (ViewCommand) other;
+ return targetIndex.equals(otherViewCommand.targetIndex);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .add("targetIndex", targetIndex)
+ .toString();
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/AddCommandParser.java b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
index 4ff1a97ed77..90e18c45527 100644
--- a/src/main/java/seedu/address/logic/parser/AddCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddCommandParser.java
@@ -1,57 +1,111 @@
package seedu.address.logic.parser;
import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMENT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GPA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEW_SCORE;
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_PREVIOUS_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STUDENT_NUMBER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
+import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import seedu.address.logic.commands.AddCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
+import seedu.address.model.attachment.Attachment;
+import seedu.address.model.person.Comment;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.InterviewScore;
+import seedu.address.model.person.IsBookmarked;
+import seedu.address.model.person.IsHidden;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
+import seedu.address.model.person.StudentNumber;
import seedu.address.model.tag.Tag;
/**
- * Parses input arguments and creates a new AddCommand object
+ * Parses input arguments and creates a new AddCommand object.
*/
public class AddCommandParser implements Parser {
/**
* Parses the given {@code String} of arguments in the context of the AddCommand
* and returns an AddCommand object for execution.
+ *
* @throws ParseException if the user input does not conform the expected format
*/
public AddCommand parse(String args) throws ParseException {
- ArgumentMultimap argMultimap =
- ArgumentTokenizer.tokenize(args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS, PREFIX_TAG);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(
+ args, PREFIX_STUDENT_NUMBER, PREFIX_NAME, PREFIX_PHONE,
+ PREFIX_EMAIL, PREFIX_GPA, PREFIX_PREVIOUS_GRADE, PREFIX_INTERVIEW_SCORE,
+ PREFIX_COMMENT, PREFIX_TAG);
- if (!arePrefixesPresent(argMultimap, PREFIX_NAME, PREFIX_ADDRESS, PREFIX_PHONE, PREFIX_EMAIL)
+ if (!arePrefixesPresent(
+ argMultimap,
+ PREFIX_STUDENT_NUMBER,
+ PREFIX_NAME,
+ PREFIX_GPA,
+ PREFIX_PREVIOUS_GRADE,
+ PREFIX_PHONE,
+ PREFIX_EMAIL)
|| !argMultimap.getPreamble().isEmpty()) {
- throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(
+ PREFIX_STUDENT_NUMBER, PREFIX_NAME, PREFIX_PHONE,
+ PREFIX_EMAIL, PREFIX_GPA, PREFIX_COMMENT, PREFIX_PREVIOUS_GRADE,
+ PREFIX_INTERVIEW_SCORE);
+
+ StudentNumber studentNo = ParserUtil.parseStudentNumber(argMultimap.getValue(PREFIX_STUDENT_NUMBER).get());
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());
+ Gpa gpa = ParserUtil.parseGpa(argMultimap.getValue(PREFIX_GPA).get());
+ PreviousGrade previousGrade = ParserUtil.parsePreviousGrade(argMultimap.getValue(PREFIX_PREVIOUS_GRADE).get());
+ Optional maybeInterviewScore = argMultimap.getValue(PREFIX_INTERVIEW_SCORE);
+ Optional interviewScore = maybeInterviewScore.isPresent()
+ ? Optional.of(ParserUtil.parseInterviewScore(maybeInterviewScore.get()))
+ : Optional.empty();
+ Optional maybeComment = argMultimap.getValue(PREFIX_COMMENT);
+ Optional comment = maybeComment.isPresent()
+ ? Optional.of(ParserUtil.parseComment(maybeComment.get()))
+ : Optional.empty();
Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_TAG));
+ IsHidden isHidden = new IsHidden(false);
+ List attachments = List.of();
+ IsBookmarked isBookmarked = new IsBookmarked(false);
- Person person = new Person(name, phone, email, address, tagList);
+ Person person = new Person(
+ studentNo,
+ name,
+ phone,
+ email,
+ gpa,
+ previousGrade,
+ interviewScore,
+ comment,
+ tagList,
+ attachments,
+ isHidden,
+ isBookmarked);
return new AddCommand(person);
}
/**
- * Returns true if none of the prefixes contains empty {@code Optional} values in the given
+ * 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) {
diff --git a/src/main/java/seedu/address/logic/parser/AddressBookParser.java b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
index 3149ee07e0b..b3a8fa5d485 100644
--- a/src/main/java/seedu/address/logic/parser/AddressBookParser.java
+++ b/src/main/java/seedu/address/logic/parser/AddressBookParser.java
@@ -9,14 +9,24 @@
import seedu.address.commons.core.LogsCenter;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AttachCommand;
+import seedu.address.logic.commands.BookmarkCommand;
import seedu.address.logic.commands.ClearCommand;
import seedu.address.logic.commands.Command;
+import seedu.address.logic.commands.CommentCommand;
+import seedu.address.logic.commands.CompareCommand;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
+import seedu.address.logic.commands.HideCommand;
+import seedu.address.logic.commands.ImportCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.SortCommand;
+import seedu.address.logic.commands.UnbookmarkCommand;
+import seedu.address.logic.commands.UnhideAllCommand;
+import seedu.address.logic.commands.UnhideCommand;
+import seedu.address.logic.commands.ViewCommand;
import seedu.address.logic.parser.exceptions.ParseException;
/**
@@ -46,7 +56,8 @@ public Command parseCommand(String userInput) throws ParseException {
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)
+ // 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);
@@ -65,11 +76,14 @@ public Command parseCommand(String userInput) throws ParseException {
case ClearCommand.COMMAND_WORD:
return new ClearCommand();
- case FindCommand.COMMAND_WORD:
- return new FindCommandParser().parse(arguments);
+ case AttachCommand.COMMAND_WORD:
+ return new AttachCommandParser().parse(arguments);
+
+ case ImportCommand.COMMAND_WORD:
+ return new ImportCommandParser().parse(arguments);
case ListCommand.COMMAND_WORD:
- return new ListCommand();
+ return new ListCommandParser().parse(arguments);
case ExitCommand.COMMAND_WORD:
return new ExitCommand();
@@ -77,6 +91,33 @@ public Command parseCommand(String userInput) throws ParseException {
case HelpCommand.COMMAND_WORD:
return new HelpCommand();
+ case ViewCommand.COMMAND_WORD:
+ return new ViewCommandParser().parse(arguments);
+
+ case HideCommand.COMMAND_WORD:
+ return new HideCommandParser().parse(arguments);
+
+ case UnhideCommand.COMMAND_WORD:
+ return new UnhideCommandParser().parse(arguments);
+
+ case BookmarkCommand.COMMAND_WORD:
+ return new BookmarkCommandParser().parse(arguments);
+
+ case UnbookmarkCommand.COMMAND_WORD:
+ return new UnbookmarkCommandParser().parse(arguments);
+
+ case UnhideAllCommand.COMMAND_WORD:
+ return new UnhideAllCommand();
+
+ case SortCommand.COMMAND_WORD:
+ return new SortCommandParser().parse(arguments);
+
+ case CommentCommand.COMMAND_WORD:
+ return new CommentCommandParser().parse(arguments);
+
+ case CompareCommand.COMMAND_WORD:
+ return new CompareCommandParser().parse(arguments);
+
default:
logger.finer("This user input caused a ParseException: " + userInput);
throw new ParseException(MESSAGE_UNKNOWN_COMMAND);
diff --git a/src/main/java/seedu/address/logic/parser/AttachCommandParser.java b/src/main/java/seedu/address/logic/parser/AttachCommandParser.java
new file mode 100644
index 00000000000..3428416b982
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/AttachCommandParser.java
@@ -0,0 +1,70 @@
+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_FILE;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.AttachCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.attachment.Attachment;
+
+/**
+ * Parses input arguments and creates a new {@code AttachCommand} object
+ */
+public class AttachCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the
+ * {@code AttachCommand} and returns a {@code AttachCommand} object for
+ * execution.
+ *
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public AttachCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_FILE);
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AttachCommand.MESSAGE_USAGE), pe);
+ }
+
+ if (!argMultimap.getValue(PREFIX_FILE).isPresent()) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AttachCommand.MESSAGE_USAGE));
+ }
+ List attachments = parseAttachments(argMultimap.getAllValues(PREFIX_FILE));
+
+ return new AttachCommand(index, attachments);
+ }
+
+ /**
+ * Parses {@code Collection pathStrings} into a {@code List} if
+ * {@code pathStrings} is non-empty.
+ * If {@code pathStrings} contain only one element which is an empty string, it
+ * will be parsed into a
+ * {@code List} containing zero tags.
+ */
+ private List parseAttachments(Collection pathStrings) throws ParseException {
+ assert pathStrings != null;
+
+ if (pathStrings.isEmpty()) {
+ return List.of();
+ }
+
+ Collection pathSet;
+ if (pathStrings.size() == 1 && pathStrings.contains("")) {
+ pathSet = Collections.emptySet();
+ } else {
+ pathSet = pathStrings;
+ }
+
+ return ParserUtil.parseAttachments(pathSet);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/BookmarkCommandParser.java b/src/main/java/seedu/address/logic/parser/BookmarkCommandParser.java
new file mode 100644
index 00000000000..c525e9321ff
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/BookmarkCommandParser.java
@@ -0,0 +1,28 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.BookmarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+
+/**
+ * Parses input arguments and creates a new BookmarkCommand object.
+ */
+public class BookmarkCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the BookmarkCommand
+ * and returns a BookmarkCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public BookmarkCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new BookmarkCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, BookmarkCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java
index 75b1a9bf119..93830efc807 100644
--- a/src/main/java/seedu/address/logic/parser/CliSyntax.java
+++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java
@@ -6,10 +6,25 @@
public class CliSyntax {
/* Prefix definitions */
+ public static final Prefix PREFIX_STUDENT_NUMBER = new Prefix("s/");
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_GPA = new Prefix("g/");
+ public static final Prefix PREFIX_PREVIOUS_GRADE = new Prefix("pg/");
+ public static final Prefix PREFIX_INTERVIEW_SCORE = new Prefix("is/");
public static final Prefix PREFIX_TAG = new Prefix("t/");
-
+ public static final Prefix PREFIX_FILE = new Prefix("f/");
+ public static final Prefix PREFIX_COMMENT = new Prefix("c/");
+ public static final String FIELD_GPA = "gpa";
+ public static final String FIELD_PREVIOUS_GRADE = "previousGrade";
+ public static final String FIELD_INTERVIEW_SCORE = "interviewScore";
+ public static final String FIELD_STUDENT_NUMBER = "studentNo";
+ public static final String FIELD_NAME = "name";
+ public static final String FIELD_PHONE = "phone";
+ public static final String FIELD_EMAIL = "email";
+ public static final String FIELD_COMMENT = "comment";
+ public static final String FIELD_TAGS = "tags";
+ public static final String FIELD_HIDDEN = "hidden";
+ public static final String FIELD_BOOKMARKED = "bookmarked";
}
diff --git a/src/main/java/seedu/address/logic/parser/CommentCommandParser.java b/src/main/java/seedu/address/logic/parser/CommentCommandParser.java
new file mode 100644
index 00000000000..555e73cd287
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/CommentCommandParser.java
@@ -0,0 +1,40 @@
+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_COMMENT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.logic.commands.CommentCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.person.Comment;
+
+/**
+ * Parses input arguments and creates a new CommentCommand object
+ */
+public class CommentCommandParser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the CommentCommand
+ * and returns a CommentCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public CommentCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args,
+ PREFIX_COMMENT);
+
+ Index index;
+ try {
+ index = ParserUtil.parseIndex(argMultimap.getPreamble());
+ } catch (IllegalValueException ive) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ CommentCommand.MESSAGE_USAGE), ive);
+ }
+
+ String comment = argMultimap.getValue(PREFIX_COMMENT).orElse("");
+
+ return new CommentCommand(index, new Comment(comment));
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/CompareCommandParser.java b/src/main/java/seedu/address/logic/parser/CompareCommandParser.java
new file mode 100644
index 00000000000..12f21990a30
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/CompareCommandParser.java
@@ -0,0 +1,35 @@
+package seedu.address.logic.parser;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.CompareCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new CompareCommand object
+ */
+public class CompareCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the CompareCommand
+ * and returns a CompareCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ @Override
+ public CompareCommand parse(String args) throws ParseException {
+ try {
+ String[] splitArgs = args.trim().split("\\s+");
+ if (splitArgs.length != 2) {
+ throw new ParseException("Invalid command format!\n"
+ + "Please follow the format: compare INDEX1 INDEX2.\n"
+ + "Parameters: INDEX (must be positive integers)");
+ }
+ Index index1 = ParserUtil.parseIndex(splitArgs[0]);
+ Index index2 = ParserUtil.parseIndex(splitArgs[1]);
+ return new CompareCommand(index1, index2);
+ } catch (ParseException pe) {
+ throw new ParseException("Invalid command format!\n"
+ + "Please follow the format: compare INDEX1 INDEX2.\n"
+ + "Parameters: INDEX (must be positive integers)");
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/EditCommandParser.java b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
index 46b3309a78b..c50fece099c 100644
--- a/src/main/java/seedu/address/logic/parser/EditCommandParser.java
+++ b/src/main/java/seedu/address/logic/parser/EditCommandParser.java
@@ -2,10 +2,13 @@
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_COMMENT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GPA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEW_SCORE;
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_PREVIOUS_GRADE;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import java.util.Collection;
@@ -25,14 +28,17 @@
public class EditCommandParser implements Parser {
/**
- * Parses the given {@code String} of arguments in the context of the EditCommand
+ * 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);
+ ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(
+ args, PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_GPA, PREFIX_PREVIOUS_GRADE,
+ PREFIX_INTERVIEW_SCORE, PREFIX_COMMENT, PREFIX_TAG);
Index index;
@@ -42,7 +48,7 @@ public EditCommand parse(String args) throws ParseException {
throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, EditCommand.MESSAGE_USAGE), pe);
}
- argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS);
+ argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_NAME, PREFIX_PHONE, PREFIX_EMAIL, PREFIX_GPA, PREFIX_COMMENT);
EditPersonDescriptor editPersonDescriptor = new EditPersonDescriptor();
@@ -55,8 +61,19 @@ public EditCommand parse(String args) throws ParseException {
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()));
+ if (argMultimap.getValue(PREFIX_GPA).isPresent()) {
+ editPersonDescriptor.setGpa(ParserUtil.parseGpa(argMultimap.getValue(PREFIX_GPA).get()));
+ }
+ if (argMultimap.getValue(PREFIX_PREVIOUS_GRADE).isPresent()) {
+ editPersonDescriptor.setPreviousGrade(ParserUtil.parsePreviousGrade(
+ argMultimap.getValue(PREFIX_PREVIOUS_GRADE).get()));
+ }
+ if (argMultimap.getValue(PREFIX_INTERVIEW_SCORE).isPresent()) {
+ editPersonDescriptor.setInterviewScore(ParserUtil.parseInterviewScore(
+ argMultimap.getValue(PREFIX_INTERVIEW_SCORE).get()));
+ }
+ if (argMultimap.getValue(PREFIX_COMMENT).isPresent()) {
+ editPersonDescriptor.setComment(ParserUtil.parseComment(argMultimap.getValue(PREFIX_COMMENT).get()));
}
parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editPersonDescriptor::setTags);
@@ -68,8 +85,10 @@ public EditCommand parse(String args) throws ParseException {
}
/**
- * 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
+ * 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 {
diff --git a/src/main/java/seedu/address/logic/parser/HideCommandParser.java b/src/main/java/seedu/address/logic/parser/HideCommandParser.java
new file mode 100644
index 00000000000..a0ec1afcb3c
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/HideCommandParser.java
@@ -0,0 +1,28 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.HideCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+
+/**
+ * Parses input arguments and creates a new HideCommand object.
+ */
+public class HideCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the HideCommand
+ * and returns a HideCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public HideCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new HideCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, HideCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ImportCommandParser.java b/src/main/java/seedu/address/logic/parser/ImportCommandParser.java
new file mode 100644
index 00000000000..970bd6e7d4e
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ImportCommandParser.java
@@ -0,0 +1,24 @@
+package seedu.address.logic.parser;
+
+import static java.util.Objects.requireNonNull;
+
+import seedu.address.logic.commands.ImportCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+import seedu.address.model.attachment.Attachment;
+
+/**
+ * Parses input arguments and creates a new {@code ImportCommand} object
+ */
+public class ImportCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the {@code ImportCommand}
+ * and returns a {@code ImportCommand} object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public ImportCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ Attachment attachment = ParserUtil.parseAttachment(args);
+ return new ImportCommand(attachment);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ListCommandParser.java b/src/main/java/seedu/address/logic/parser/ListCommandParser.java
new file mode 100644
index 00000000000..6650655c9ac
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ListCommandParser.java
@@ -0,0 +1,38 @@
+package seedu.address.logic.parser;
+
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CliSyntax.FIELD_BOOKMARKED;
+import static seedu.address.logic.parser.CliSyntax.FIELD_HIDDEN;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new ListCommand object.
+ */
+public class ListCommandParser implements Parser {
+ /**
+ * Parses the given {@code String} of arguments in the context of the ListCommand
+ * and returns an ListCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public ListCommand parse(String args) throws ParseException {
+ if (args.isEmpty()) {
+ return new ListCommand();
+ }
+ String fieldName = args.trim();
+ if (!isFieldNameValid(fieldName, FIELD_HIDDEN, FIELD_BOOKMARKED)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE));
+ }
+ return new ListCommand(fieldName);
+ }
+ /**
+ * Returns true if all the field names are valid.
+ */
+ private static boolean isFieldNameValid(String fieldName, String... fieldNames) {
+ return Arrays.asList(fieldNames).contains(fieldName);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java
index b117acb9c55..4cec96e9836 100644
--- a/src/main/java/seedu/address/logic/parser/ParserUtil.java
+++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java
@@ -2,30 +2,42 @@
import static java.util.Objects.requireNonNull;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import seedu.address.commons.core.index.Index;
import seedu.address.commons.util.StringUtil;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.Address;
+import seedu.address.model.attachment.Attachment;
+import seedu.address.model.person.Comment;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.InterviewScore;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
+import seedu.address.model.person.StudentNumber;
import seedu.address.model.tag.Tag;
/**
- * Contains utility methods used for parsing strings in the various *Parser classes.
+ * Contains utility methods used for parsing strings in the various *Parser
+ * classes.
*/
public class ParserUtil {
public static final String MESSAGE_INVALID_INDEX = "Index is not a non-zero unsigned integer.";
+ public static final String MESSAGE_INVALID_PATH = "Please provide a valid file path.";
/**
- * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading and trailing whitespaces will be
+ * Parses {@code oneBasedIndex} into an {@code Index} and returns it. Leading
+ * and trailing whitespaces will be
* trimmed.
- * @throws ParseException if the specified index is invalid (not non-zero unsigned integer).
+ *
+ * @throws ParseException if the specified index is invalid (not non-zero
+ * unsigned integer).
*/
public static Index parseIndex(String oneBasedIndex) throws ParseException {
String trimmedIndex = oneBasedIndex.trim();
@@ -35,6 +47,21 @@ public static Index parseIndex(String oneBasedIndex) throws ParseException {
return Index.fromOneBased(Integer.parseInt(trimmedIndex));
}
+ /**
+ * Parses a {@code String studentNo} into a {@code StudentNumber}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code name} is invalid.
+ */
+ public static StudentNumber parseStudentNumber(String studentNo) throws ParseException {
+ requireNonNull(studentNo);
+ String trimmedStudentNo = studentNo.trim();
+ if (!StudentNumber.isValidStudentNumber(trimmedStudentNo)) {
+ throw new ParseException(StudentNumber.MESSAGE_CONSTRAINTS);
+ }
+ return new StudentNumber(trimmedStudentNo);
+ }
+
/**
* Parses a {@code String name} into a {@code Name}.
* Leading and trailing whitespaces will be trimmed.
@@ -66,18 +93,72 @@ public static Phone parsePhone(String phone) throws ParseException {
}
/**
- * Parses a {@code String address} into an {@code Address}.
+ * Parses a {@code String gpa} into a {@code Gpa}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code gpa} is invalid.
+ */
+ public static Gpa parseGpa(String gpaString) throws ParseException {
+ requireNonNull(gpaString);
+ String trimmedGpaString = gpaString.trim();
+ double gpa;
+ try {
+ gpa = Double.parseDouble(trimmedGpaString);
+ } catch (NumberFormatException e) {
+ throw new ParseException(Gpa.MESSAGE_CONSTRAINTS);
+ }
+ if (!Gpa.isValidGpa(gpa)) {
+ throw new ParseException(Gpa.MESSAGE_CONSTRAINTS);
+ }
+ return new Gpa(gpa);
+ }
+
+ /**
+ * Parses a {@code String previousGradeString} into a {@code PreviousGradeString}.
+ *
+ * @throws ParseException if the given {@code previousGradeString} is invalid.
+ */
+ public static PreviousGrade parsePreviousGrade(String previousGradeString) throws ParseException {
+ requireNonNull(previousGradeString);
+ if (!PreviousGrade.isValidGrade(previousGradeString)) {
+ throw new ParseException(PreviousGrade.MESSAGE_CONSTRAINTS);
+ }
+ return new PreviousGrade(previousGradeString);
+ }
+
+ /**
+ * Parses a {@code String interviewScoreString} into an {@code InterviewScore}.
+ *
+ * @throws ParseException if the given {@code interviewScoreString} is invalid.
+ */
+ public static InterviewScore parseInterviewScore(String interviewScoreString) throws ParseException {
+ requireNonNull(interviewScoreString);
+ String trimmedInterviewScoreString = interviewScoreString.trim();
+ double interviewScore;
+ try {
+ interviewScore = Double.parseDouble(trimmedInterviewScoreString);
+ } catch (NumberFormatException e) {
+ throw new ParseException(InterviewScore.MESSAGE_CONSTRAINTS);
+ }
+ if (!InterviewScore.isValidInterviewScore(interviewScore)) {
+ throw new ParseException(InterviewScore.MESSAGE_CONSTRAINTS);
+ }
+ return new InterviewScore(interviewScore);
+ }
+
+ /**
+ * Parses a {@code String name} into a {@code Name}.
* Leading and trailing whitespaces will be trimmed.
*
- * @throws ParseException if the given {@code address} is invalid.
+ * @throws ParseException if the given {@code name} 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);
+ public static Comment parseComment(String name) throws ParseException {
+ requireNonNull(name);
+ String trimmedComment = name.trim();
+ if (!Comment.isValidComment(trimmedComment)) {
+ throw new ParseException(Name.MESSAGE_CONSTRAINTS);
}
- return new Address(trimmedAddress);
+ return new Comment(trimmedComment);
}
/**
@@ -121,4 +202,32 @@ public static Set parseTags(Collection tags) throws ParseException
}
return tagSet;
}
+
+ /**
+ * Parses a {@code String pathString} into a {@code Attachment}.
+ * Leading and trailing whitespaces will be trimmed.
+ *
+ * @throws ParseException if the given {@code pathString} is invalid.
+ */
+ public static Attachment parseAttachment(String pathString) throws ParseException {
+ requireNonNull(pathString);
+ String trimmedPathString = pathString.trim();
+ if (!Attachment.isValidAttachment(pathString)) {
+ throw new ParseException(MESSAGE_INVALID_PATH);
+ }
+ return new Attachment(trimmedPathString);
+ }
+
+ /**
+ * Parses {@code Collection pathStrings} into a
+ * {@code List}.
+ */
+ public static List parseAttachments(Collection pathStrings) throws ParseException {
+ requireNonNull(pathStrings);
+ final List pathList = new ArrayList<>();
+ for (String pathString : pathStrings) {
+ pathList.add(parseAttachment(pathString));
+ }
+ return pathList;
+ }
}
diff --git a/src/main/java/seedu/address/logic/parser/SortCommandParser.java b/src/main/java/seedu/address/logic/parser/SortCommandParser.java
new file mode 100644
index 00000000000..af2398674c2
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/SortCommandParser.java
@@ -0,0 +1,46 @@
+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.FIELD_COMMENT;
+import static seedu.address.logic.parser.CliSyntax.FIELD_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.FIELD_GPA;
+import static seedu.address.logic.parser.CliSyntax.FIELD_INTERVIEW_SCORE;
+import static seedu.address.logic.parser.CliSyntax.FIELD_NAME;
+import static seedu.address.logic.parser.CliSyntax.FIELD_PHONE;
+import static seedu.address.logic.parser.CliSyntax.FIELD_PREVIOUS_GRADE;
+import static seedu.address.logic.parser.CliSyntax.FIELD_STUDENT_NUMBER;
+import static seedu.address.logic.parser.CliSyntax.FIELD_TAGS;
+
+import java.util.Arrays;
+
+import seedu.address.logic.commands.SortCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new SortCommand object.
+ */
+public class SortCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the SortCommand
+ * and returns an SortCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public SortCommand parse(String args) throws ParseException {
+ requireNonNull(args);
+ String fieldName = args.trim();
+ if (!isFieldNameValid(fieldName, FIELD_STUDENT_NUMBER, FIELD_NAME, FIELD_PHONE, FIELD_EMAIL, FIELD_GPA,
+ FIELD_COMMENT, FIELD_PREVIOUS_GRADE, FIELD_INTERVIEW_SCORE, FIELD_TAGS)) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
+ }
+ return new SortCommand(fieldName);
+ }
+
+ /**
+ * Returns true if all the field names are valid.
+ */
+ private static boolean isFieldNameValid(String fieldName, String... fieldNames) {
+ return Arrays.asList(fieldNames).contains(fieldName);
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/UnbookmarkCommandParser.java b/src/main/java/seedu/address/logic/parser/UnbookmarkCommandParser.java
new file mode 100644
index 00000000000..054395ed0a3
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/UnbookmarkCommandParser.java
@@ -0,0 +1,29 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.UnbookmarkCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+
+/**
+ * Parses input arguments and creates a new UnookmarkCommand object.
+ */
+public class UnbookmarkCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the UnbookmarkCommand
+ * and returns a UnbookmarkCommand object for execution.
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public UnbookmarkCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new UnbookmarkCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UnbookmarkCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/UnhideCommandParser.java b/src/main/java/seedu/address/logic/parser/UnhideCommandParser.java
new file mode 100644
index 00000000000..c7221de3645
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/UnhideCommandParser.java
@@ -0,0 +1,30 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.UnhideCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+
+
+/**
+ * Parses input arguments and creates a new UnhideCommand object.
+ */
+public class UnhideCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the UnhideCommand
+ * and returns a UnhideCommand object for execution.
+ *
+ * @throws ParseException if the user input does not conform to the expected format
+ */
+ public UnhideCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new UnhideCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnhideCommand.MESSAGE_USAGE), pe);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/logic/parser/ViewCommandParser.java b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java
new file mode 100644
index 00000000000..6dfe9e6aa49
--- /dev/null
+++ b/src/main/java/seedu/address/logic/parser/ViewCommandParser.java
@@ -0,0 +1,29 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.ViewCommand;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * Parses input arguments and creates a new ViewCommand object
+ */
+public class ViewCommandParser implements Parser {
+
+ /**
+ * Parses the given {@code String} of arguments in the context of the ViewCommand
+ * and returns a ViewCommand object for execution.
+ * @throws ParseException if the user input does not conform the expected format
+ */
+ public ViewCommand parse(String args) throws ParseException {
+ try {
+ Index index = ParserUtil.parseIndex(args);
+ return new ViewCommand(index);
+ } catch (ParseException pe) {
+ throw new ParseException(
+ String.format(MESSAGE_INVALID_COMMAND_FORMAT, ViewCommand.MESSAGE_USAGE), pe);
+ }
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/Model.java b/src/main/java/seedu/address/model/Model.java
index d54df471c1f..015088f0fb1 100644
--- a/src/main/java/seedu/address/model/Model.java
+++ b/src/main/java/seedu/address/model/Model.java
@@ -1,10 +1,17 @@
package seedu.address.model;
+import static seedu.address.logic.parser.CliSyntax.FIELD_HIDDEN;
+
import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.Optional;
import java.util.function.Predicate;
+import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.commons.core.index.Index;
+import seedu.address.model.person.ListPredicate;
import seedu.address.model.person.Person;
/**
@@ -14,6 +21,20 @@ public interface Model {
/** {@code Predicate} that always evaluate to true */
Predicate PREDICATE_SHOW_ALL_PERSONS = unused -> true;
+ /**
+ * {@code Predicate} that filters for all hidden persons
+ */
+ Predicate PREDICATE_SHOW_ALL_HIDDEN_PERSONS = new ListPredicate(FIELD_HIDDEN, true);
+ /**
+ * {@code Predicate} that filters for all unhidden persons
+ */
+ Predicate PREDICATE_SHOW_ALL_UNHIDDEN_PERSONS = new ListPredicate(FIELD_HIDDEN, false);
+
+ /**
+ * {@code Predicate} that filters for all bookmarked applicants
+ */
+ Predicate PREDICATE_SHOW_ALL_BOOKMARKED_PERSONS = new ListPredicate(FIELD_HIDDEN, true);
+
/**
* Replaces user prefs data with the data in {@code userPrefs}.
*/
@@ -76,6 +97,17 @@ public interface Model {
*/
void setPerson(Person target, Person editedPerson);
+ /**
+ * Displays the given person.
+ * The person must exist in the address book.
+ */
+ void showPersonAtIndex(Index index);
+
+ /**
+ * CLears details of person displayed in detail view of UI.
+ */
+ void clearPersonDetails();
+
/** Returns an unmodifiable view of the filtered person list */
ObservableList getFilteredPersonList();
@@ -84,4 +116,13 @@ public interface Model {
* @throws NullPointerException if {@code predicate} is null.
*/
void updateFilteredPersonList(Predicate predicate);
+
+ /**
+ * Updates the sorted person list to sort by the given {@code comparator}.
+ * @throws NullPointerException if {@code predicate} is null.
+ */
+ void sortFilteredPersonList(Comparator comparator);
+
+ /** Returns the current person being viewed in detail */
+ ObservableValue> getCurrentPerson();
}
diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java
index 57bc563fde6..d61656edabc 100644
--- a/src/main/java/seedu/address/model/ModelManager.java
+++ b/src/main/java/seedu/address/model/ModelManager.java
@@ -4,13 +4,19 @@
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
import java.nio.file.Path;
+import java.util.Comparator;
+import java.util.Optional;
import java.util.function.Predicate;
import java.util.logging.Logger;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
+import javafx.collections.transformation.SortedList;
import seedu.address.commons.core.GuiSettings;
import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.index.Index;
import seedu.address.model.person.Person;
/**
@@ -22,9 +28,10 @@ public class ModelManager implements Model {
private final AddressBook addressBook;
private final UserPrefs userPrefs;
private final FilteredList filteredPersons;
+ private final SimpleObjectProperty> currentPerson;
/**
- * Initializes a ModelManager with the given addressBook and userPrefs.
+ * Initializes a ModelManager with the given addressBook, userPrefs and attachments interface.
*/
public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs) {
requireAllNonNull(addressBook, userPrefs);
@@ -34,6 +41,7 @@ public ModelManager(ReadOnlyAddressBook addressBook, ReadOnlyUserPrefs userPrefs
this.addressBook = new AddressBook(addressBook);
this.userPrefs = new UserPrefs(userPrefs);
filteredPersons = new FilteredList<>(this.addressBook.getPersonList());
+ currentPerson = new SimpleObjectProperty<>(Optional.empty());
}
public ModelManager() {
@@ -93,6 +101,21 @@ public boolean hasPerson(Person person) {
return addressBook.hasPerson(person);
}
+ @Override
+ public void showPersonAtIndex(Index index) {
+ requireNonNull(index);
+ if (index.getZeroBased() >= filteredPersons.size()) {
+ currentPerson.set(Optional.empty());
+ } else {
+ currentPerson.set(Optional.of(filteredPersons.get(index.getZeroBased())));
+ }
+ }
+
+ @Override
+ public void clearPersonDetails() {
+ currentPerson.set(Optional.empty());
+ }
+
@Override
public void deletePerson(Person target) {
addressBook.removePerson(target);
@@ -101,7 +124,7 @@ public void deletePerson(Person target) {
@Override
public void addPerson(Person person) {
addressBook.addPerson(person);
- updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS);
+ updateFilteredPersonList(PREDICATE_SHOW_ALL_UNHIDDEN_PERSONS);
}
@Override
@@ -109,6 +132,10 @@ public void setPerson(Person target, Person editedPerson) {
requireAllNonNull(target, editedPerson);
addressBook.setPerson(target, editedPerson);
+
+ if (currentPerson.get().isPresent() && target.isSamePerson(currentPerson.get().get())) {
+ currentPerson.setValue(Optional.of(editedPerson));
+ }
}
//=========== Filtered Person List Accessors =============================================================
@@ -126,6 +153,22 @@ public ObservableList getFilteredPersonList() {
public void updateFilteredPersonList(Predicate predicate) {
requireNonNull(predicate);
filteredPersons.setPredicate(predicate);
+ currentPerson.set(Optional.empty());
+ }
+
+ @Override
+ public void sortFilteredPersonList(Comparator comparator) {
+ requireNonNull(comparator);
+ SortedList sortedList = new SortedList<>(addressBook.getPersonList());
+ sortedList.setComparator(comparator);
+ addressBook.setPersons(sortedList);
+ updateFilteredPersonList(PREDICATE_SHOW_ALL_UNHIDDEN_PERSONS);
+ }
+
+ /** Returns the current person being viewed in detail */
+ @Override
+ public ObservableValue> getCurrentPerson() {
+ return currentPerson;
}
@Override
diff --git a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
index befd58a4c73..afc92ca49e4 100644
--- a/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
+++ b/src/main/java/seedu/address/model/ReadOnlyUserPrefs.java
@@ -13,4 +13,6 @@ public interface ReadOnlyUserPrefs {
Path getAddressBookFilePath();
+ Path getAttachmentsBasePath();
+
}
diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java
index 6be655fb4c7..22ca8f2ef44 100644
--- a/src/main/java/seedu/address/model/UserPrefs.java
+++ b/src/main/java/seedu/address/model/UserPrefs.java
@@ -14,7 +14,8 @@
public class UserPrefs implements ReadOnlyUserPrefs {
private GuiSettings guiSettings = new GuiSettings();
- private Path addressBookFilePath = Paths.get("data" , "addressbook.json");
+ private Path addressBookFilePath = Paths.get("data" , "tafinder.json");
+ private Path attachmentsBasePath = Paths.get("data" , "attachments");
/**
* Creates a {@code UserPrefs} with default values.
@@ -56,6 +57,15 @@ public void setAddressBookFilePath(Path addressBookFilePath) {
this.addressBookFilePath = addressBookFilePath;
}
+ public Path getAttachmentsBasePath() {
+ return attachmentsBasePath;
+ }
+
+ public void setAttachmentsBasePath(Path attachmentsBasePath) {
+ requireNonNull(attachmentsBasePath);
+ this.attachmentsBasePath = attachmentsBasePath;
+ }
+
@Override
public boolean equals(Object other) {
if (other == this) {
diff --git a/src/main/java/seedu/address/model/attachment/Attachment.java b/src/main/java/seedu/address/model/attachment/Attachment.java
new file mode 100644
index 00000000000..fb098df7cc5
--- /dev/null
+++ b/src/main/java/seedu/address/model/attachment/Attachment.java
@@ -0,0 +1,74 @@
+package seedu.address.model.attachment;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.io.File;
+import java.nio.file.InvalidPathException;
+import java.nio.file.Paths;
+
+/**
+ * Represents an attachment in the address book.
+ * Guarantees: immutable; name is valid as declared in
+ * {@link #isValidAttachment(String)}
+ */
+public class Attachment {
+
+ public static final String MESSAGE_CONSTRAINTS = "Attachments should be valid paths to files";
+
+ public final File file;
+
+ /**
+ * Constructs an {@code Attachment}.
+ *
+ * @param tagName A valid file path to the attachment contents.
+ */
+ public Attachment(String path) {
+ requireNonNull(path);
+ checkArgument(isValidAttachment(path), MESSAGE_CONSTRAINTS);
+ File file = new File(path);
+ this.file = file;
+ }
+
+ /**
+ * Returns true if a given string is a valid path for an attachment.
+ */
+ public static boolean isValidAttachment(String test) {
+ try {
+ Paths.get(test);
+ return true;
+ } catch (InvalidPathException e) {
+ return false;
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Attachment)) {
+ return false;
+ }
+
+ Attachment otherFile = (Attachment) other;
+ return file.equals(otherFile.file);
+ }
+
+ @Override
+ public int hashCode() {
+ return file.getAbsolutePath().hashCode();
+ }
+
+ /**
+ * Format state as text for viewing.
+ */
+ public String toString() {
+ return '[' + file.getAbsolutePath() + ']';
+ }
+
+}
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/Comment.java b/src/main/java/seedu/address/model/person/Comment.java
new file mode 100644
index 00000000000..cc78b1913c6
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Comment.java
@@ -0,0 +1,63 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a comment for an applicant in the applicant list.
+ * Guarantees: immutable; is valid as declared in
+ * {@link #isValidComment(String)}
+ */
+public class Comment {
+ public static final String MESSAGE_CONSTRAINTS = "Comment takes in any string. Comment can be blank";
+
+ /*
+ * Takes in any string
+ */
+ public static final String VALIDATION_REGEX = "^.*$";
+
+ public final String comment;
+
+ /**
+ * Constructs an {@code Comment}.
+ *
+ * @param comment A possible comment for a person.
+ */
+ public Comment(String comment) {
+ requireNonNull(comment);
+ checkArgument(isValidComment(comment), MESSAGE_CONSTRAINTS);
+ this.comment = comment;
+ }
+
+ /**
+ * Returns true if a given string is a valid comment.
+ */
+ public static boolean isValidComment(String test) {
+ return test.matches(VALIDATION_REGEX);
+ }
+
+ @Override
+ public String toString() {
+ return comment;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Comment)) {
+ return false;
+ }
+
+ Comment otherComment = (Comment) other;
+ return comment.equals(otherComment.comment);
+ }
+
+ @Override
+ public int hashCode() {
+ return comment.hashCode();
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Gpa.java b/src/main/java/seedu/address/model/person/Gpa.java
new file mode 100644
index 00000000000..925bf3af059
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/Gpa.java
@@ -0,0 +1,71 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Person's Grade Point Average (Gpa) in the applicant list.
+ * Guarantees: immutable; is valid as declared in {@link #isValidGpa(double)}
+ */
+public class Gpa implements Comparable {
+
+ public static final String MESSAGE_CONSTRAINTS = "Gpa can take values from 0.00 to 5.00, and cannot be blank";
+
+ public final double value;
+
+ /**
+ * Constructs a {@code Gpa}.
+ *
+ * @param gpa A possible gpa of a person.
+ */
+ public Gpa(double gpa) {
+ requireNonNull(gpa);
+ checkArgument(isValidGpa(gpa), MESSAGE_CONSTRAINTS);
+ value = gpa;
+ }
+
+ /**
+ * Returns true if a given double is a valid Gpa.
+ */
+ public static boolean isValidGpa(double test) {
+ return test >= 0.0 && test <= 5.0;
+ }
+
+ /**
+ * Compares this GPA with another GPA and returns an integer representing the comparison.
+ *
+ * @param other The other Gpa object to compare with.
+ * @return A negative value if this GPA is less than the other GPA, zero if they are equal,
+ * and a positive value if this GPA is greater than the other GPA.
+ */
+ @Override
+ public int compareTo(Gpa other) {
+ return Double.compare(this.value, other.value);
+ }
+
+ @Override
+ public String toString() {
+ return Double.toString(value);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof Gpa)) {
+ return false;
+ }
+
+ Gpa otherGpa = (Gpa) other;
+ return value == otherGpa.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Double.hashCode(value);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/InterviewScore.java b/src/main/java/seedu/address/model/person/InterviewScore.java
new file mode 100644
index 00000000000..48bc319ec10
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/InterviewScore.java
@@ -0,0 +1,71 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents a Person's Grade Point Average (InterviewScore) in the applicant list.
+ * Guarantees: immutable; is valid as declared in {@link #isValidInterviewScore(double)}
+ */
+public class InterviewScore implements Comparable {
+
+ public static final String MESSAGE_CONSTRAINTS = "The interview score must be between 0.0 and 10.0";
+
+ public final double value;
+
+ /**
+ * Constructs a {@code InterviewScore}.
+ *
+ * @param score A possible interview score of a person.
+ */
+ public InterviewScore(double score) {
+ requireNonNull(score);
+ checkArgument(isValidInterviewScore(score), MESSAGE_CONSTRAINTS);
+ value = score;
+ }
+
+ /**
+ * Returns true if a given double is a valid {@code InterviewScore}.
+ */
+ public static boolean isValidInterviewScore(double test) {
+ return test >= 0.0 && test <= 10.0;
+ }
+
+ /**
+ * Compares this score with another score and returns an integer representing the comparison.
+ *
+ * @param other The other {@code InterviewScore} object to compare with.
+ * @return A negative value if this score is less than the other score, zero if they are equal,
+ * and a positive value if this score is greater than the other score.
+ */
+ @Override
+ public int compareTo(InterviewScore other) {
+ return Double.compare(this.value, other.value);
+ }
+
+ @Override
+ public String toString() {
+ return Double.toString(value);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof InterviewScore)) {
+ return false;
+ }
+
+ InterviewScore otherInterviewScore = (InterviewScore) other;
+ return value == otherInterviewScore.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Double.hashCode(value);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/IsBookmarked.java b/src/main/java/seedu/address/model/person/IsBookmarked.java
new file mode 100644
index 00000000000..45ffb516903
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/IsBookmarked.java
@@ -0,0 +1,41 @@
+package seedu.address.model.person;
+
+/**
+ * Represents whether a Person is bookmarked in the address book.
+ */
+public class IsBookmarked {
+ public final boolean value;
+
+ /**
+ * Constructs a {@code Bookmark}.
+ */
+ public IsBookmarked(boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return Boolean.toString(value);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof IsBookmarked)) {
+ return false;
+ }
+
+ IsBookmarked otherBookmark = (IsBookmarked) other;
+ return value == otherBookmark.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Boolean.hashCode(value);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/IsHidden.java b/src/main/java/seedu/address/model/person/IsHidden.java
new file mode 100644
index 00000000000..91c79a160bd
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/IsHidden.java
@@ -0,0 +1,41 @@
+package seedu.address.model.person;
+
+/**
+ * Represents whether a Person is hidden in the address book.
+ */
+public class IsHidden {
+ public final boolean value;
+
+ /**
+ * Constructs a {@code IsHidden}.
+ */
+ public IsHidden(boolean value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return value ? "true" : "false";
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof IsHidden)) {
+ return false;
+ }
+
+ IsHidden otherIsHidden = (IsHidden) other;
+ return value == otherIsHidden.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Boolean.hashCode(value);
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/person/ListPredicate.java b/src/main/java/seedu/address/model/person/ListPredicate.java
new file mode 100644
index 00000000000..307a2a2a71f
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/ListPredicate.java
@@ -0,0 +1,36 @@
+package seedu.address.model.person;
+
+import static seedu.address.logic.parser.CliSyntax.FIELD_BOOKMARKED;
+import static seedu.address.logic.parser.CliSyntax.FIELD_HIDDEN;
+
+import java.util.function.Predicate;
+
+
+/**
+ * Tests that a {@code Person}'s specified field matches the boolean given.
+ */
+public class ListPredicate implements Predicate {
+ private final String fieldName;
+ private final boolean filter;
+
+ /**
+ * Constructor for ListPredicate.
+ * @param fieldName
+ * @param filter
+ */
+ public ListPredicate(String fieldName, boolean filter) {
+ this.fieldName = fieldName;
+ this.filter = filter;
+ }
+ @Override
+ public boolean test(Person person) {
+ switch (fieldName) {
+ case FIELD_HIDDEN:
+ return person.getIsHidden().value == filter;
+ case FIELD_BOOKMARKED:
+ return person.getIsBookmarked().value == filter;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java
index abe8c46b535..0fb2ec3d579 100644
--- a/src/main/java/seedu/address/model/person/Person.java
+++ b/src/main/java/seedu/address/model/person/Person.java
@@ -2,59 +2,109 @@
import static seedu.address.commons.util.CollectionUtil.requireAllNonNull;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import seedu.address.commons.util.ToStringBuilder;
+import seedu.address.model.attachment.Attachment;
import seedu.address.model.tag.Tag;
/**
- * Represents a Person in the address book.
- * Guarantees: details are present and not null, field values are validated, immutable.
+ * Represents an applicant in the TA applicant list.
+ * Guarantees: details are present and not null, field values are validated,
+ * immutable.
*/
public class Person {
// Identity fields
+ private final StudentNumber studentNo;
private final Name name;
private final Phone phone;
private final Email email;
// Data fields
- private final Address address;
+ private final Gpa gpa;
+ private final PreviousGrade previousGrade;
+ private final Optional interviewScore;
+ private final Optional comment;
private final Set tags = new HashSet<>();
+ private final List attachments = new ArrayList<>();
+
+ // Flag fields
+ private final IsHidden isHidden;
+ private final IsBookmarked isBookmarked;
/**
* 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(
+ StudentNumber studentNo,
+ Name name,
+ Phone phone,
+ Email email,
+ Gpa gpa,
+ PreviousGrade previousGrade,
+ Optional interviewScore,
+ Optional comment,
+ Set tags,
+ List attachments,
+ IsHidden isHidden,
+ IsBookmarked isBookmarked) {
+ requireAllNonNull(studentNo, name, phone, email, gpa, previousGrade, tags, isHidden);
+ this.studentNo = studentNo;
this.name = name;
this.phone = phone;
this.email = email;
- this.address = address;
+ this.gpa = gpa;
+ this.previousGrade = previousGrade;
+ this.interviewScore = interviewScore;
+ this.comment = comment;
this.tags.addAll(tags);
+ this.attachments.addAll(attachments);
+ this.isHidden = isHidden;
+ this.isBookmarked = isBookmarked;
+ }
+
+ public StudentNumber getStudentNumber() {
+ return studentNo;
}
public Name getName() {
- return name;
+ return this.name;
}
public Phone getPhone() {
- return phone;
+ return this.phone;
}
public Email getEmail() {
- return email;
+ return this.email;
+ }
+
+ public Gpa getGpa() {
+ return this.gpa;
+ }
+
+ public PreviousGrade getPreviousGrade() {
+ return this.previousGrade;
}
- public Address getAddress() {
- return address;
+ public Optional getInterviewScore() {
+ return this.interviewScore;
+ }
+
+ public Optional getComment() {
+ return this.comment;
}
/**
- * Returns an immutable tag set, which throws {@code UnsupportedOperationException}
+ * Returns an immutable tag set, which throws
+ * {@code UnsupportedOperationException}
* if modification is attempted.
*/
public Set getTags() {
@@ -62,7 +112,34 @@ public Set getTags() {
}
/**
- * Returns true if both persons have the same name.
+ * Returns an immutable attachment list, which throws
+ * {@code UnsupportedOperationException}
+ * if modification is attempted.
+ */
+ public List getAttachments() {
+ return Collections.unmodifiableList(attachments);
+ }
+
+ /**
+ * Returns an immutable isHidden value, which throws
+ * {@code UnsupportedOperationException}
+ * if modification is attempted.
+ */
+ public IsHidden getIsHidden() {
+ return this.isHidden;
+ }
+
+ /**
+ * Returns an immutable isBookmarked value, which throws
+ * {@code UnsupportedOperationException}
+ * if modification is attempted.
+ */
+ public IsBookmarked getIsBookmarked() {
+ return this.isBookmarked;
+ }
+
+ /**
+ * Returns true if both persons have the same student number.
* This defines a weaker notion of equality between two persons.
*/
public boolean isSamePerson(Person otherPerson) {
@@ -71,7 +148,7 @@ public boolean isSamePerson(Person otherPerson) {
}
return otherPerson != null
- && otherPerson.getName().equals(getName());
+ && otherPerson.getStudentNumber().equals(getStudentNumber());
}
/**
@@ -90,28 +167,42 @@ public boolean equals(Object other) {
}
Person otherPerson = (Person) other;
- return name.equals(otherPerson.name)
+ return studentNo.equals(otherPerson.studentNo)
+ && name.equals(otherPerson.name)
&& phone.equals(otherPerson.phone)
&& email.equals(otherPerson.email)
- && address.equals(otherPerson.address)
- && tags.equals(otherPerson.tags);
+ && gpa.equals(otherPerson.gpa)
+ && previousGrade.equals(otherPerson.previousGrade)
+ && interviewScore.equals(otherPerson.interviewScore)
+ && comment.equals(otherPerson.comment)
+ && tags.equals(otherPerson.tags)
+ && attachments.equals(otherPerson.attachments)
+ && isHidden.equals(otherPerson.isHidden)
+ && isBookmarked.equals(otherPerson.isBookmarked);
}
@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(studentNo, name, phone, email, gpa, previousGrade, interviewScore, comment, tags, isHidden,
+ attachments);
}
@Override
public String toString() {
return new ToStringBuilder(this)
+ .add("studentNo", studentNo)
.add("name", name)
.add("phone", phone)
.add("email", email)
- .add("address", address)
+ .add("gpa", gpa)
+ .add("previousGrade", previousGrade)
+ .add("interviewScore", interviewScore)
+ .add("comment", comment)
.add("tags", tags)
+ .add("attachments", attachments)
+ .add("isHidden", isHidden)
+ .add("isBookmarked", isBookmarked)
.toString();
}
-
}
diff --git a/src/main/java/seedu/address/model/person/PreviousGrade.java b/src/main/java/seedu/address/model/person/PreviousGrade.java
new file mode 100644
index 00000000000..afa1d0df374
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/PreviousGrade.java
@@ -0,0 +1,128 @@
+package seedu.address.model.person;
+
+import static java.util.Map.entry;
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+import java.util.Map;
+
+/**
+ * Represents the previous grade an applicant obtained in the module i.e. when they were
+ * a student of the module.
+ */
+public class PreviousGrade implements Comparable {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Grades must be one of the following: A+, A, A-, B+, B, B-, C+, C, D+, D, F";
+
+ public final Grade grade;
+
+ /**
+ * Creates a new "previous grade" that the TA applicant has obtained in the same module.
+ *
+ * @param grade The grade the applicant previously obtained as a participant of the module.
+ */
+ public PreviousGrade(String grade) {
+ requireNonNull(grade);
+ checkArgument(isValidGrade(grade), MESSAGE_CONSTRAINTS);
+ this.grade = Grade.parse(grade);
+ }
+
+ public static boolean isValidGrade(String test) {
+ return Grade.parse(test) != null;
+ }
+
+ @Override
+ public int compareTo(PreviousGrade other) {
+ return grade.compareTo(other.grade);
+ }
+
+ @Override
+ public String toString() {
+ return grade.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == this) {
+ return true;
+ }
+
+ // instanceof handles nulls
+ if (!(other instanceof PreviousGrade)) {
+ return false;
+ }
+
+ PreviousGrade otherPrevousGrade = (PreviousGrade) other;
+ return grade == otherPrevousGrade.grade;
+ }
+
+ @Override
+ public int hashCode() {
+ return grade.hashCode();
+ }
+
+ /**
+ * Represents a grade that an NUS student can obtain under the GPA system.
+ */
+ public static enum Grade {
+ A_PLUS,
+ A,
+ A_MINUS,
+ B_PLUS,
+ B,
+ B_MINUS,
+ C_PLUS,
+ C,
+ D_PLUS,
+ D,
+ F;
+
+ private static final Map GRADES = Map.ofEntries(
+ entry("A+", Grade.A_PLUS),
+ entry("A", Grade.A),
+ entry("A-", Grade.A_MINUS),
+ entry("B+", Grade.B_PLUS),
+ entry("B", Grade.B),
+ entry("B-", Grade.B_MINUS),
+ entry("C+", Grade.C_PLUS),
+ entry("C", Grade.C),
+ entry("D+", Grade.D_PLUS),
+ entry("D", Grade.D),
+ entry("F", Grade.F));
+
+ public static Grade parse(String grade) {
+ return GRADES.get(grade);
+ }
+
+ @Override
+ public String toString() {
+ switch (this) {
+ case A_PLUS:
+ return "A+";
+ case A:
+ return "A";
+ case A_MINUS:
+ return "A-";
+ case B_PLUS:
+ return "B+";
+ case B:
+ return "B";
+ case B_MINUS:
+ return "B-";
+ case C_PLUS:
+ return "C+";
+ case C:
+ return "C";
+ case D_PLUS:
+ return "D+";
+ case D:
+ return "D";
+ case F:
+ return "F";
+ default:
+ throw new Error("Unreachable");
+ }
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/SortComparator.java b/src/main/java/seedu/address/model/person/SortComparator.java
new file mode 100644
index 00000000000..a2ce7f137cd
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/SortComparator.java
@@ -0,0 +1,66 @@
+package seedu.address.model.person;
+
+import static seedu.address.logic.parser.CliSyntax.FIELD_COMMENT;
+import static seedu.address.logic.parser.CliSyntax.FIELD_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.FIELD_GPA;
+import static seedu.address.logic.parser.CliSyntax.FIELD_INTERVIEW_SCORE;
+import static seedu.address.logic.parser.CliSyntax.FIELD_NAME;
+import static seedu.address.logic.parser.CliSyntax.FIELD_PHONE;
+import static seedu.address.logic.parser.CliSyntax.FIELD_PREVIOUS_GRADE;
+import static seedu.address.logic.parser.CliSyntax.FIELD_STUDENT_NUMBER;
+import static seedu.address.logic.parser.CliSyntax.FIELD_TAGS;
+
+import java.util.Comparator;
+
+/**
+ * Compares two persons based on the specified field.
+ */
+public class SortComparator implements Comparator {
+ private final String fieldName;
+
+ public SortComparator(String fieldName) {
+ this.fieldName = fieldName;
+ }
+
+ @Override
+ public int compare(Person p1, Person p2) {
+ switch (fieldName) {
+ case FIELD_NAME:
+ return p1.getName().fullName.compareTo(p2.getName().fullName);
+ case FIELD_STUDENT_NUMBER:
+ return p1.getStudentNumber().value.compareTo(p2.getStudentNumber().value);
+ case FIELD_GPA:
+ return (int) ((p2.getGpa().value - p1.getGpa().value) * 100);
+ case FIELD_PREVIOUS_GRADE:
+ return p1.getPreviousGrade().grade.compareTo(p2.getPreviousGrade().grade);
+ case FIELD_INTERVIEW_SCORE:
+ if (p1.getInterviewScore().isPresent() && p2.getInterviewScore().isPresent()) {
+ return Double.compare(p2.getInterviewScore().get().value, p1.getInterviewScore().get().value);
+ } else if (p1.getInterviewScore().isPresent()) {
+ return -1;
+ } else if (p2.getInterviewScore().isPresent()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ case FIELD_COMMENT:
+ if (p1.getComment().isPresent() && p2.getComment().isPresent()) {
+ return p2.getComment().get().comment.length() - p1.getComment().get().comment.length();
+ } else if (p1.getComment().isPresent()) {
+ return -1;
+ } else if (p2.getComment().isPresent()) {
+ return 1;
+ } else {
+ return 0;
+ }
+ case FIELD_PHONE:
+ return p1.getPhone().value.compareTo(p2.getPhone().value);
+ case FIELD_EMAIL:
+ return p1.getEmail().value.compareTo(p2.getEmail().value);
+ case FIELD_TAGS:
+ return p2.getTags().size() - p1.getTags().size();
+ default:
+ return 0;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/model/person/StudentNumber.java b/src/main/java/seedu/address/model/person/StudentNumber.java
new file mode 100644
index 00000000000..105d768fd49
--- /dev/null
+++ b/src/main/java/seedu/address/model/person/StudentNumber.java
@@ -0,0 +1,61 @@
+package seedu.address.model.person;
+
+import static java.util.Objects.requireNonNull;
+import static seedu.address.commons.util.AppUtil.checkArgument;
+
+/**
+ * Represents an applicant's student number in the applicant list.
+ * Guarantees: immutable; is valid as declared in {@link #isValidStudentNumber(String)}
+ */
+public class StudentNumber {
+
+ public static final String MESSAGE_CONSTRAINTS =
+ "Student numbers should look like A0123456G. 1 letter, 7 digits, then 1 more letter.";
+ public static final String VALIDATION_REGEX = "[A-Z]\\d{7}[A-Z]";
+
+ public final String value;
+
+ /**
+ * Constructs a {@code StudentNumber}.
+ *
+ * @param studentNo A valid student number.
+ */
+ public StudentNumber(String studentNo) {
+ requireNonNull(studentNo);
+ checkArgument(isValidStudentNumber(studentNo), MESSAGE_CONSTRAINTS);
+ value = studentNo;
+ }
+
+ /**
+ * Returns true if a given string is a valid student number.
+ */
+ public static boolean isValidStudentNumber(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 StudentNumber)) {
+ return false;
+ }
+
+ StudentNumber otherStudentNo = (StudentNumber) other;
+ return value.equals(otherStudentNo.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+}
diff --git a/src/main/java/seedu/address/model/util/SampleDataUtil.java b/src/main/java/seedu/address/model/util/SampleDataUtil.java
index 1806da4facf..46db729f293 100644
--- a/src/main/java/seedu/address/model/util/SampleDataUtil.java
+++ b/src/main/java/seedu/address/model/util/SampleDataUtil.java
@@ -1,42 +1,116 @@
package seedu.address.model.util;
import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import seedu.address.model.AddressBook;
import seedu.address.model.ReadOnlyAddressBook;
-import seedu.address.model.person.Address;
+import seedu.address.model.attachment.Attachment;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.IsBookmarked;
+import seedu.address.model.person.IsHidden;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
+import seedu.address.model.person.StudentNumber;
import seedu.address.model.tag.Tag;
/**
* Contains utility methods for populating {@code AddressBook} with sample data.
*/
public class SampleDataUtil {
+
public static Person[] getSamplePersons() {
return new Person[] {
- new Person(new Name("Alex Yeoh"), new Phone("87438807"), new Email("alexyeoh@example.com"),
- new Address("Blk 30 Geylang Street 29, #06-40"),
- getTagSet("friends")),
- new Person(new Name("Bernice Yu"), new Phone("99272758"), new Email("berniceyu@example.com"),
- new Address("Blk 30 Lorong 3 Serangoon Gardens, #07-18"),
- getTagSet("colleagues", "friends")),
- new Person(new Name("Charlotte Oliveiro"), new Phone("93210283"), new Email("charlotte@example.com"),
- new Address("Blk 11 Ang Mo Kio Street 74, #11-04"),
- getTagSet("neighbours")),
- new Person(new Name("David Li"), new Phone("91031282"), new Email("lidavid@example.com"),
- new Address("Blk 436 Serangoon Gardens Street 26, #16-43"),
- getTagSet("family")),
- new Person(new Name("Irfan Ibrahim"), new Phone("92492021"), new Email("irfan@example.com"),
- new Address("Blk 47 Tampines Street 20, #17-35"),
- getTagSet("classmates")),
- new Person(new Name("Roy Balakrishnan"), new Phone("92624417"), new Email("royb@example.com"),
- new Address("Blk 45 Aljunied Street 85, #11-31"),
- getTagSet("colleagues"))
+ new Person(
+ new StudentNumber("A0123456A"),
+ new Name("Alex Yeoh"),
+ new Phone("87438807"),
+ new Email("alexyeoh@example.com"),
+ new Gpa(4.2),
+ new PreviousGrade("A+"),
+ Optional.empty(),
+ Optional.empty(),
+ getTagSet("pastTA"),
+ getAttachments(),
+ new IsHidden(false),
+ new IsBookmarked(false)
+ ),
+ new Person(
+ new StudentNumber("A0321654B"),
+ new Name("Bernice Yu"),
+ new Phone("99272758"),
+ new Email("berniceyu@example.com"),
+ new Gpa(5.0),
+ new PreviousGrade("A+"),
+ Optional.empty(),
+ Optional.empty(),
+ getTagSet("pastTA", "deansList"),
+ getAttachments(),
+ new IsHidden(false),
+ new IsBookmarked(true)
+ ),
+ new Person(
+ new StudentNumber("A0654123C"),
+ new Name("Charlotte Oliveiro"),
+ new Phone("93210283"),
+ new Email("charlotte@example.com"),
+ new Gpa(3.6),
+ new PreviousGrade("A+"),
+ Optional.empty(),
+ Optional.empty(),
+ getTagSet("pastTA"),
+ getAttachments(),
+ new IsHidden(false),
+ new IsBookmarked(false)
+ ),
+ new Person(
+ new StudentNumber("A0654321D"),
+ new Name("David Li"),
+ new Phone("91031282"),
+ new Email("lidavid@example.com"),
+ new Gpa(4.71),
+ new PreviousGrade("A+"),
+ Optional.empty(),
+ Optional.empty(),
+ getTagSet("pastTA"),
+ getAttachments(),
+ new IsHidden(false),
+ new IsBookmarked(false)
+ ),
+ new Person(
+ new StudentNumber("A0321456E"),
+ new Name("Irfan Ibrahim"),
+ new Phone("92492021"),
+ new Email("irfan@example.com"),
+ new Gpa(3.67),
+ new PreviousGrade("A+"),
+ Optional.empty(),
+ Optional.empty(),
+ getTagSet("topInModule"),
+ getAttachments(),
+ new IsHidden(false),
+ new IsBookmarked(true)
+ ),
+ new Person(
+ new StudentNumber("A0374384F"),
+ new Name("Roy Balakrishnan"),
+ new Phone("92624417"),
+ new Email("royb@example.com"),
+ new Gpa(4.3),
+ new PreviousGrade("A+"),
+ Optional.empty(),
+ Optional.empty(),
+ getTagSet("pastTA"),
+ getAttachments(),
+ new IsHidden(false),
+ new IsBookmarked(false)
+ )
};
}
@@ -57,4 +131,13 @@ public static Set getTagSet(String... strings) {
.collect(Collectors.toSet());
}
+ /**
+ * Returns an attachment list containing the list of strings given.
+ */
+ public static List getAttachments(String... strings) {
+ return Arrays.stream(strings)
+ .map(Attachment::new)
+ .collect(Collectors.toList());
+ }
+
}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedAttachment.java b/src/main/java/seedu/address/storage/JsonAdaptedAttachment.java
new file mode 100644
index 00000000000..7d9ded7adf3
--- /dev/null
+++ b/src/main/java/seedu/address/storage/JsonAdaptedAttachment.java
@@ -0,0 +1,48 @@
+package seedu.address.storage;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+import seedu.address.commons.exceptions.IllegalValueException;
+import seedu.address.model.attachment.Attachment;
+
+/**
+ * Jackson-friendly version of {@link Attachment}.
+ */
+class JsonAdaptedAttachment {
+
+ private final String path;
+
+ /**
+ * Constructs a {@code JsonAdaptedFile} with the given {@code path}.
+ */
+ @JsonCreator
+ public JsonAdaptedAttachment(String path) {
+ this.path = path;
+ }
+
+ /**
+ * Converts a given {@code File} into this class for Jackson use.
+ */
+ public JsonAdaptedAttachment(Attachment source) {
+ path = source.file.getAbsolutePath();
+ }
+
+ @JsonValue
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * Converts this Jackson-friendly adapted tag object into the model's {@code Attachment} object.
+ *
+ * @throws IllegalValueException if there were any data constraints violated in the adapted tag.
+ */
+ public Attachment toModelType() throws IllegalValueException {
+ if (!Attachment.isValidAttachment(path)) {
+ throw new IllegalValueException(Attachment.MESSAGE_CONSTRAINTS);
+ }
+ return new Attachment(path);
+ }
+
+}
diff --git a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
index bd1ca0f56c8..36d93744e2e 100644
--- a/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
+++ b/src/main/java/seedu/address/storage/JsonAdaptedPerson.java
@@ -3,6 +3,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -10,11 +11,18 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
+import seedu.address.model.attachment.Attachment;
+import seedu.address.model.person.Comment;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.InterviewScore;
+import seedu.address.model.person.IsBookmarked;
+import seedu.address.model.person.IsHidden;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
+import seedu.address.model.person.StudentNumber;
import seedu.address.model.tag.Tag;
/**
@@ -24,45 +32,83 @@ class JsonAdaptedPerson {
public static final String MISSING_FIELD_MESSAGE_FORMAT = "Person's %s field is missing!";
+ private final String studentNo;
private final String name;
private final String phone;
private final String email;
- private final String address;
+ private final Double gpa;
+ private final String previousGrade;
+ private final Double interviewScore; // this is uppercase Double because it can be null
+ private final String comment;
private final List tags = new ArrayList<>();
+ private final List attachments = new ArrayList<>();
+ private final Boolean isHidden;
+ private final Boolean isBookmarked;
/**
* 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) {
+
+ public JsonAdaptedPerson(
+ @JsonProperty("studentNo") String studentNo,
+ @JsonProperty("name") String name,
+ @JsonProperty("phone") String phone,
+ @JsonProperty("email") String email,
+ @JsonProperty("gpa") Double gpa,
+ @JsonProperty("previousGrade") String previousGrade,
+ @JsonProperty("interviewScore") Double interviewScore,
+ @JsonProperty("comment") String comment,
+ @JsonProperty("tags") List tags,
+ @JsonProperty("attachments") List attachments,
+ @JsonProperty("isHidden") boolean isHidden,
+ @JsonProperty("isBookmarked") boolean isBookmarked) {
+ this.studentNo = studentNo;
this.name = name;
this.phone = phone;
this.email = email;
- this.address = address;
+ this.gpa = gpa;
+ this.previousGrade = previousGrade;
+ this.interviewScore = interviewScore;
+ this.comment = comment;
if (tags != null) {
this.tags.addAll(tags);
}
+ this.isHidden = isHidden;
+ if (attachments != null) {
+ this.attachments.addAll(attachments);
+ }
+ this.isBookmarked = isBookmarked;
}
/**
* Converts a given {@code Person} into this class for Jackson use.
*/
public JsonAdaptedPerson(Person source) {
+ studentNo = source.getStudentNumber().value;
name = source.getName().fullName;
phone = source.getPhone().value;
email = source.getEmail().value;
- address = source.getAddress().value;
+ gpa = source.getGpa().value;
+ previousGrade = source.getPreviousGrade().toString();
+ interviewScore = source.getInterviewScore().map(score -> score.value).orElse(null);
+ comment = source.getComment().map(comment -> comment.comment).orElse(null);
tags.addAll(source.getTags().stream()
.map(JsonAdaptedTag::new)
.collect(Collectors.toList()));
+ isHidden = source.getIsHidden().value;
+ attachments.addAll(source.getAttachments().stream()
+ .map(JsonAdaptedAttachment::new)
+ .collect(Collectors.toList()));
+ isBookmarked = source.getIsBookmarked().value;
}
/**
- * Converts this Jackson-friendly adapted person object into the model's {@code Person} object.
+ * Converts this Jackson-friendly adapted person object into the model's
+ * {@code Person} object.
*
- * @throws IllegalValueException if there were any data constraints violated in the adapted person.
+ * @throws IllegalValueException if there were any data constraints violated in
+ * the adapted person.
*/
public Person toModelType() throws IllegalValueException {
final List personTags = new ArrayList<>();
@@ -70,6 +116,21 @@ public Person toModelType() throws IllegalValueException {
personTags.add(tag.toModelType());
}
+ final List personAttachments = new ArrayList<>();
+ for (JsonAdaptedAttachment attachment : attachments) {
+ personAttachments.add(attachment.toModelType());
+ }
+
+ if (studentNo == null) {
+ throw new IllegalValueException(String.format(
+ MISSING_FIELD_MESSAGE_FORMAT,
+ StudentNumber.class.getSimpleName()));
+ }
+ if (!StudentNumber.isValidStudentNumber(studentNo)) {
+ throw new IllegalValueException(StudentNumber.MESSAGE_CONSTRAINTS);
+ }
+ final StudentNumber modelStudentNo = new StudentNumber(studentNo);
+
if (name == null) {
throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName()));
}
@@ -94,16 +155,64 @@ 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 (gpa == null) {
+ throw new IllegalValueException(String.format(MISSING_FIELD_MESSAGE_FORMAT, Gpa.class.getSimpleName()));
+ }
+ if (!Gpa.isValidGpa(gpa)) {
+ throw new IllegalValueException(Gpa.MESSAGE_CONSTRAINTS);
+ }
+ final Gpa modelGpa = new Gpa(gpa);
+
+ if (previousGrade == null) {
+ throw new IllegalValueException(
+ String.format(MISSING_FIELD_MESSAGE_FORMAT, PreviousGrade.class.getSimpleName()));
+ }
+ if (!PreviousGrade.isValidGrade(previousGrade)) {
+ throw new IllegalValueException(PreviousGrade.MESSAGE_CONSTRAINTS);
}
- if (!Address.isValidAddress(address)) {
- throw new IllegalValueException(Address.MESSAGE_CONSTRAINTS);
+ final PreviousGrade modelPreviousGrade = new PreviousGrade(previousGrade);
+
+ if (interviewScore != null && !InterviewScore.isValidInterviewScore(interviewScore)) {
+ throw new IllegalValueException(InterviewScore.MESSAGE_CONSTRAINTS);
+ }
+ final Optional modelInterviewScore = interviewScore != null
+ ? Optional.of(new InterviewScore(interviewScore))
+ : Optional.empty();
+
+ if (comment != null && !Comment.isValidComment(comment)) {
+ throw new IllegalValueException(Comment.MESSAGE_CONSTRAINTS);
}
- final Address modelAddress = new Address(address);
+ final Optional modelComment = comment != null
+ ? Optional.of(new Comment(comment))
+ : Optional.empty();
final Set modelTags = new HashSet<>(personTags);
- return new Person(modelName, modelPhone, modelEmail, modelAddress, modelTags);
+
+ if (isHidden == null) {
+ throw new IllegalValueException(
+ String.format(MISSING_FIELD_MESSAGE_FORMAT, IsHidden.class.getSimpleName()));
+ }
+ final IsHidden modelIsHidden = new IsHidden(isHidden);
+
+ if (isBookmarked == null) {
+ throw new IllegalValueException(
+ String.format(MISSING_FIELD_MESSAGE_FORMAT, IsBookmarked.class.getSimpleName()));
+ }
+ final IsBookmarked modelBookmark = new IsBookmarked(isBookmarked);
+
+ return new Person(
+ modelStudentNo,
+ modelName,
+ modelPhone,
+ modelEmail,
+ modelGpa,
+ modelPreviousGrade,
+ modelInterviewScore,
+ modelComment,
+ modelTags,
+ personAttachments,
+ modelIsHidden,
+ modelBookmark);
}
}
diff --git a/src/main/java/seedu/address/storage/StorageManager.java b/src/main/java/seedu/address/storage/StorageManager.java
index 8b84a9024d5..e0ada5867e5 100644
--- a/src/main/java/seedu/address/storage/StorageManager.java
+++ b/src/main/java/seedu/address/storage/StorageManager.java
@@ -45,7 +45,6 @@ public void saveUserPrefs(ReadOnlyUserPrefs userPrefs) throws IOException {
userPrefsStorage.saveUserPrefs(userPrefs);
}
-
// ================ AddressBook methods ==============================
@Override
diff --git a/src/main/java/seedu/address/ui/CompareWindow.java b/src/main/java/seedu/address/ui/CompareWindow.java
new file mode 100644
index 00000000000..1b8eceda0b7
--- /dev/null
+++ b/src/main/java/seedu/address/ui/CompareWindow.java
@@ -0,0 +1,160 @@
+package seedu.address.ui;
+
+import java.io.IOException;
+
+import javafx.fxml.FXML;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Scene;
+import javafx.scene.control.Label;
+import javafx.scene.shape.Rectangle;
+import javafx.stage.Stage;
+import seedu.address.MainApp;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.InterviewScore;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.PreviousGrade;
+
+/**
+ * The Compare Pop-up Window. Provides a separate JavaFX stage when a "compare" command is inputted.
+ * This window is used to display and compare the details of two applicants,
+ * including student number, name, and their relevant achievements.
+ */
+public class CompareWindow extends Stage {
+ @FXML
+ private Label person1StuNum;
+ @FXML
+ private Label person1Name;
+ @FXML
+ private Label person1Gpa;
+ @FXML
+ private Rectangle gpa1Highlight;
+ @FXML
+ private Label person1IScore;
+ @FXML
+ private Rectangle iScore1Highlight;
+ @FXML
+ private Label person1MGrade;
+ @FXML
+ private Rectangle mGrade1Highlight;
+ @FXML
+ private Label person1Comment;
+ @FXML
+ private Label person2StuNum;
+ @FXML
+ private Label person2Name;
+ @FXML
+ private Label person2Gpa;
+ @FXML
+ private Rectangle gpa2Highlight;
+ @FXML
+ private Label person2IScore;
+ @FXML
+ private Rectangle iScore2Highlight;
+ @FXML
+ private Label person2MGrade;
+ @FXML
+ private Rectangle mGrade2Highlight;
+ @FXML
+ private Label person2Comment;
+
+ /**
+ * Creates a new instance of the CompareWindow with information about two persons to be compared.
+ *
+ * @param person1 The first person for comparison.
+ * @param person2 The second person for comparison.
+ */
+ public CompareWindow(Person person1, Person person2) {
+ FXMLLoader fxmlLoader = new FXMLLoader(MainApp.class.getResource("/view/CompareWindow.fxml"));
+ fxmlLoader.setController(this);
+ try {
+ fxmlLoader.load();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ setTitle("Comparison Result");
+
+ //person1 stuff
+ Gpa gpa1 = person1.getGpa();
+ PreviousGrade mGrade1 = person1.getPreviousGrade();
+ InterviewScore interviewScore1 = null;
+ String interviewScoreString1;
+
+ if (person1.getInterviewScore().isPresent()) {
+ interviewScore1 = person1.getInterviewScore().get();
+ interviewScoreString1 = interviewScore1.toString();
+ } else {
+ interviewScoreString1 = "-";
+ }
+
+ String comment1;
+ if (person1.getComment().isPresent()) {
+ String commentString1 = person1.getComment().toString();
+ comment1 = commentString1.substring("Optional[".length(),
+ commentString1.length() - 1);
+ } else {
+ comment1 = "-";
+ }
+
+ //person2 stuff
+ Gpa gpa2 = person2.getGpa();
+ PreviousGrade mGrade2 = person2.getPreviousGrade();
+ InterviewScore interviewScore2 = null;
+ String interviewScoreString2;
+
+ if (person2.getInterviewScore().isPresent()) {
+ interviewScore2 = person2.getInterviewScore().get();
+ interviewScoreString2 = interviewScore2.toString();
+ } else {
+ interviewScoreString2 = "-";
+ }
+
+ String comment2;
+ if (person2.getComment().isPresent()) {
+ String commentString2 = person2.getComment().toString();
+ comment2 = commentString2.substring("Optional[".length(),
+ commentString2.length() - 1);
+ } else {
+ comment2 = "-";
+ }
+
+ person1StuNum.setText(person1.getStudentNumber().toString());
+ person1Name.setText(person1.getName().toString());
+ person1Gpa.setText(gpa1.toString());
+ person1IScore.setText(interviewScoreString1);
+ person1MGrade.setText(mGrade1.toString());
+ person1Comment.setText(comment1);
+
+ person2StuNum.setText(person2.getStudentNumber().toString());
+ person2Name.setText(person2.getName().toString());
+ person2Gpa.setText(gpa2.toString());
+ person2IScore.setText(interviewScoreString2);
+ person2MGrade.setText(mGrade2.toString());
+ person2Comment.setText(comment2);
+
+ if (gpa1.compareTo(gpa2) > 0) {
+ gpa1Highlight.setOpacity(0.33);
+ } else if (gpa1.compareTo(gpa2) < 0) {
+ gpa2Highlight.setOpacity(0.33);
+ }
+
+ if (interviewScoreString1 != "-" && interviewScoreString2 != "-") {
+ if (interviewScore1.compareTo(interviewScore2) > 0) {
+ iScore1Highlight.setOpacity(0.33);
+ } else if (interviewScore1.compareTo(interviewScore2) < 0) {
+ iScore2Highlight.setOpacity(0.33);
+ }
+ }
+
+ if (mGrade1.compareTo(mGrade2) < 0) {
+ mGrade1Highlight.setOpacity(0.33);
+ } else if (mGrade1.compareTo(mGrade2) > 0) {
+ mGrade2Highlight.setOpacity(0.33);
+ }
+
+ Scene scene = new Scene(fxmlLoader.getRoot());
+ setWidth(900);
+ setHeight(600);
+ setScene(scene);
+ }
+}
diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java
index 3f16b2fcf26..ccfb0108100 100644
--- a/src/main/java/seedu/address/ui/HelpWindow.java
+++ b/src/main/java/seedu/address/ui/HelpWindow.java
@@ -15,7 +15,7 @@
*/
public class HelpWindow extends UiPart {
- public static final String USERGUIDE_URL = "https://se-education.org/addressbook-level3/UserGuide.html";
+ public static final String USERGUIDE_URL = "https://ay2324s1-cs2103t-w10-1.github.io/tp/UserGuide.html";
public static final String HELP_MESSAGE = "Refer to the user guide: " + USERGUIDE_URL;
private static final Logger logger = LogsCenter.getLogger(HelpWindow.class);
diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java
index 79e74ef37c0..379c1390fb7 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 PersonDetailPanel personDetailPanel;
private ResultDisplay resultDisplay;
private HelpWindow helpWindow;
@@ -44,6 +45,9 @@ public class MainWindow extends UiPart {
@FXML
private StackPane personListPanelPlaceholder;
+ @FXML
+ private StackPane personDetailPanelPlaceholder;
+
@FXML
private StackPane resultDisplayPlaceholder;
@@ -113,6 +117,9 @@ void fillInnerParts() {
personListPanel = new PersonListPanel(logic.getFilteredPersonList());
personListPanelPlaceholder.getChildren().add(personListPanel.getRoot());
+ personDetailPanel = new PersonDetailPanel(logic.getCurrentPerson());
+ personDetailPanelPlaceholder.getChildren().add(personDetailPanel.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/PersonDetailContent.java
similarity index 54%
rename from src/main/java/seedu/address/ui/PersonCard.java
rename to src/main/java/seedu/address/ui/PersonDetailContent.java
index 094c42cda82..54df5df485c 100644
--- a/src/main/java/seedu/address/ui/PersonCard.java
+++ b/src/main/java/seedu/address/ui/PersonDetailContent.java
@@ -12,48 +12,63 @@
/**
* An UI component that displays information of a {@code Person}.
*/
-public class PersonCard extends UiPart {
+public class PersonDetailContent extends UiPart {
- private static final String FXML = "PersonListCard.fxml";
+ private static final String FXML = "PersonDetailContent.fxml";
/**
- * Note: Certain keywords such as "location" and "resources" are reserved keywords in JavaFX.
+ * 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
+ * @see The
+ * issue on AddressBook level 4
*/
public final Person person;
@FXML
- private HBox cardPane;
+ private HBox detailsPane;
@FXML
private Label name;
@FXML
- private Label id;
- @FXML
private Label phone;
@FXML
- private Label address;
+ private Label gpa;
@FXML
private Label email;
@FXML
private FlowPane tags;
+ @FXML
+ private Label previousGrade;
+ @FXML
+ private Label interviewScore;
+ @FXML
+ private Label comment;
+ @FXML
+ private FlowPane attachments;
/**
- * Creates a {@code PersonCode} with the given {@code Person} and index to display.
+ * Creates a {@code PersonCode} with the given {@code Person} and index to
+ * display.
*/
- public PersonCard(Person person, int displayedIndex) {
+ public PersonDetailContent(Person person) {
super(FXML);
this.person = person;
- id.setText(displayedIndex + ". ");
name.setText(person.getName().fullName);
phone.setText(person.getPhone().value);
- address.setText(person.getAddress().value);
+ gpa.setText(person.getGpa().toString());
+ previousGrade.setText(person.getPreviousGrade().toString());
+ interviewScore.setText(person.getInterviewScore().map(score -> score.toString()).orElse(""));
+ comment.setText(person.getComment().map(comment -> comment.comment).orElse(""));
email.setText(person.getEmail().value);
person.getTags().stream()
.sorted(Comparator.comparing(tag -> tag.tagName))
.forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
+ person.getAttachments().stream()
+ .sorted(Comparator.comparing(attachment -> attachment.toString()))
+ .forEach(attachment -> attachments.getChildren()
+ .add(new Label(attachment.file.toPath().getFileName().toString())));
}
}
diff --git a/src/main/java/seedu/address/ui/PersonDetailEmptyState.java b/src/main/java/seedu/address/ui/PersonDetailEmptyState.java
new file mode 100644
index 00000000000..4e029668de9
--- /dev/null
+++ b/src/main/java/seedu/address/ui/PersonDetailEmptyState.java
@@ -0,0 +1,32 @@
+package seedu.address.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.VBox;
+
+/**
+ * An UI component that displays information of a {@code Person}.
+ */
+public class PersonDetailEmptyState extends UiPart {
+
+ private static final String FXML = "PersonDetailEmptyState.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
+ */
+
+ @FXML
+ private VBox contentPane;
+
+ /**
+ * Creates a {@code PersonCode} with the given {@code Person} and index to
+ * display.
+ */
+ public PersonDetailEmptyState() {
+ super(FXML);
+ }
+}
diff --git a/src/main/java/seedu/address/ui/PersonDetailPanel.java b/src/main/java/seedu/address/ui/PersonDetailPanel.java
new file mode 100644
index 00000000000..2e9ba444e7a
--- /dev/null
+++ b/src/main/java/seedu/address/ui/PersonDetailPanel.java
@@ -0,0 +1,46 @@
+package seedu.address.ui;
+
+import java.util.Optional;
+
+import javafx.beans.value.ObservableValue;
+import javafx.fxml.FXML;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.VBox;
+import seedu.address.model.person.Person;
+
+/**
+ * An UI component that displays information of a {@code Person}.
+ */
+public class PersonDetailPanel extends UiPart {
+
+ private static final String FXML = "PersonDetailPanel.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
+ */
+
+ @FXML
+ private VBox contentPane;
+
+ /**
+ * Creates a {@code PersonCode} with the given {@code Person} and index to
+ * display.
+ */
+ public PersonDetailPanel(ObservableValue> observablePerson) {
+ super(FXML);
+
+ observablePerson.addListener((observable, oldValue, newValue) -> {
+ if (newValue.isPresent()) {
+ PersonDetailContent content = new PersonDetailContent(newValue.get());
+ contentPane.getChildren().setAll(content.getRoot());
+ } else {
+ PersonDetailEmptyState emptyState = new PersonDetailEmptyState();
+ contentPane.getChildren().setAll(emptyState.getRoot());
+ }
+ });
+ }
+}
diff --git a/src/main/java/seedu/address/ui/PersonListCard.java b/src/main/java/seedu/address/ui/PersonListCard.java
new file mode 100644
index 00000000000..c2abc8708b2
--- /dev/null
+++ b/src/main/java/seedu/address/ui/PersonListCard.java
@@ -0,0 +1,78 @@
+package seedu.address.ui;
+
+import java.util.Comparator;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.FlowPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import seedu.address.model.person.Person;
+
+/**
+ * An UI component that displays information of a {@code Person}.
+ */
+public class PersonListCard extends UiPart {
+
+ private static final String FXML = "PersonListCard.fxml";
+
+ private static final Image BOOKMARKED = new Image(
+ PersonListCard.class.getResourceAsStream("/images/bookmarked.png"));
+ private static final Image NOT_BOOKMARKED = new Image(
+ PersonListCard.class.getResourceAsStream("/images/unbookmarked.png"));
+
+ /**
+ * Note: Certain keywords such as "location" and "resources" are reserved
+ * keywords in JavaFX.
+ * As a consequence, UI elements' variable names cannot be set to such keywords
+ * or an exception will be thrown by JavaFX during runtime.
+ *
+ * @see The
+ * issue on AddressBook level 4
+ */
+
+ public final Person person;
+
+ @FXML
+ private HBox cardPane;
+ @FXML
+ private Label name;
+ @FXML
+ private Label id;
+ @FXML
+ private Label studentNo;
+ @FXML
+ private FlowPane tags;
+ @FXML
+ private ImageView bookmark;
+
+ /**
+ * Creates a {@code PersonCode} with the given {@code Person} and index to
+ * display.
+ */
+ public PersonListCard(Person person, int displayedIndex) {
+ super(FXML);
+ this.person = person;
+ id.setText(displayedIndex + ". ");
+ name.setText(person.getName().fullName);
+ studentNo.setText(person.getStudentNumber().value);
+ updateBookmarkImage(person.getIsBookmarked().value);
+ person.getTags()
+ .stream()
+ .sorted(Comparator.comparing(tag -> tag.tagName))
+ .forEach(tag -> tags.getChildren().add(new Label(tag.tagName)));
+ }
+
+ /**
+ * Updates the bookmark image view based on the provided bookmark value.
+ */
+ private void updateBookmarkImage(boolean isBookmarked) {
+ if (isBookmarked) {
+ bookmark.setImage(BOOKMARKED);
+ } else {
+ bookmark.setImage(NOT_BOOKMARKED);
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java
index f4c501a897b..89e3f468a87 100644
--- a/src/main/java/seedu/address/ui/PersonListPanel.java
+++ b/src/main/java/seedu/address/ui/PersonListPanel.java
@@ -30,7 +30,8 @@ public PersonListPanel(ObservableList personList) {
}
/**
- * Custom {@code ListCell} that displays the graphics of a {@code Person} using a {@code PersonCard}.
+ * Custom {@code ListCell} that displays the graphics of a {@code Person} using
+ * a {@code PersonCard}.
*/
class PersonListViewCell extends ListCell {
@Override
@@ -41,7 +42,7 @@ protected void updateItem(Person person, boolean empty) {
setGraphic(null);
setText(null);
} else {
- setGraphic(new PersonCard(person, getIndex() + 1).getRoot());
+ setGraphic(new PersonListCard(person, getIndex() + 1).getRoot());
}
}
}
diff --git a/src/main/resources/images/bookmarked.png b/src/main/resources/images/bookmarked.png
new file mode 100644
index 00000000000..d0bea6a051e
Binary files /dev/null and b/src/main/resources/images/bookmarked.png differ
diff --git a/src/main/resources/images/unbookmarked.png b/src/main/resources/images/unbookmarked.png
new file mode 100644
index 00000000000..8a7de89634a
Binary files /dev/null and b/src/main/resources/images/unbookmarked.png differ
diff --git a/src/main/resources/view/AltTheme.css b/src/main/resources/view/AltTheme.css
new file mode 100644
index 00000000000..bf3584f85ec
--- /dev/null
+++ b/src/main/resources/view/AltTheme.css
@@ -0,0 +1,395 @@
+.background {
+ -fx-background-color: derive(#1a6186, 20%);
+ background-color: #1a6186;
+ /* Used in the default.html file */
+}
+
+.label {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: #ffffff;
+ -fx-opacity: 0.9;
+}
+
+.label-bright {
+ -fx-font-size: 11pt;
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.label-header {
+ -fx-font-size: 32pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 1;
+}
+
+.text-field {
+ -fx-font-size: 12pt;
+ -fx-font-family: "Segoe UI Semibold";
+}
+
+.tab-pane {
+ -fx-padding: 0 0 0 1;
+}
+
+.tab-pane .tab-header-area {
+ -fx-padding: 0 0 0 0;
+ -fx-min-height: 0;
+ -fx-max-height: 0;
+}
+
+.table-view {
+ -fx-base: #a5a6e1;
+ -fx-control-inner-background: #a5a6e1;
+ -fx-background-color: #a5a6e1;
+ -fx-table-cell-border-color: transparent;
+ -fx-table-header-border-color: transparent;
+ -fx-padding: 5;
+}
+
+.table-view .column-header-background {
+ -fx-background-color: transparent;
+}
+
+.table-view .column-header,
+.table-view .filler {
+ -fx-size: 35;
+ -fx-border-width: 0 0 1 0;
+ -fx-background-color: transparent;
+ -fx-border-color:
+ transparent transparent derive(-fx-base, 80%) transparent;
+ -fx-border-insets: 0 10 1 0;
+}
+
+.table-view .column-header .label {
+ -fx-font-size: 20pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: #ffffff;
+ -fx-alignment: center-left;
+ -fx-opacity: 1;
+}
+
+.table-view:focused .table-row-cell:filled:focused:selected {
+ -fx-background-color: -fx-focus-color;
+}
+
+.split-pane:horizontal .split-pane-divider {
+ -fx-background-color: derive(#ffffff, 20%);
+ -fx-border-color: transparent transparent transparent #ffffff;
+}
+
+.split-pane {
+ -fx-border-radius: 1;
+ -fx-border-width: 1;
+ -fx-background-color: derive(#ffffff, 20%);
+}
+
+.list-view {
+ -fx-background-insets: 0;
+ -fx-padding: 4;
+ -fx-background-color: derive(#5a71bb, 20%);
+}
+
+.list-cell {
+ -fx-padding: 2;
+ -fx-border-radius: 5;
+ -fx-border-width: 1;
+ -fx-background-insets: 3;
+ -fx-border-insets: 3;
+ -fx-background-radius: 8;
+}
+
+.list-cell:filled:even {
+ -fx-background-color: #311c67;
+}
+
+.list-cell:filled:odd {
+ -fx-background-color: #422e77;
+}
+
+.list-cell:filled:selected {
+ -fx-background-color: #200c50;
+}
+
+.list-cell:filled:selected #cardPane {
+ -fx-border-color: #200c50;
+ -fx-border-width: 1;
+}
+
+.list-cell .label {
+ -fx-text-fill: white;
+}
+
+.detail-content .label {
+ -fx-text-fill: #ffffff;
+}
+
+.detail-content-label {
+ -fx-font-size: 18px;
+}
+
+.detail-content-value {
+ -fx-font-weight: bold;
+ -fx-font-size: 18px;
+}
+
+.cell-huge-label {
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 42px;
+ -fx-text-fill: #010504;
+}
+
+.cell-big-label {
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 16px;
+ -fx-text-fill: #010504;
+}
+
+.cell-small-label {
+ -fx-font-family: "Segoe UI";
+ -fx-font-size: 13px;
+ -fx-text-fill: #010504;
+}
+
+.stack-pane {
+ -fx-background-color: derive(#4d518d, 20%); //text bar
+}
+
+.pane-with-border {
+ -fx-background-color: derive(#5a71bb, 20%); //overall bg
+ -fx-border-color: derive(#5a71bb, 10%);
+ -fx-border-top-width: 1px;
+}
+
+.status-bar {
+ -fx-background-color: derive(#423a5b, 30%); //bottom bar
+}
+
+.result-display {
+ -fx-background-color: transparent;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: white;
+}
+
+.result-display .label {
+ -fx-text-fill: black !important;
+}
+
+.status-bar .label {
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-padding: 4px;
+ -fx-pref-height: 30px;
+}
+
+.status-bar-with-border {
+ -fx-background-color: derive(#4f585e, 30%);
+ -fx-border-color: derive(#4464c5, 25%);
+ -fx-border-width: 1px;
+}
+
+.status-bar-with-border .label {
+ -fx-text-fill: white;
+}
+
+.grid-pane {
+ -fx-background-color: derive(#452967, 30%);
+ -fx-border-color: derive(#452967, 30%);
+ -fx-border-width: 1px;
+}
+
+.grid-pane .stack-pane {
+ -fx-background-color: derive(#452967, 30%);
+}
+
+.context-menu {
+ -fx-background-color: derive(#452967, 50%);
+}
+
+.context-menu .label {
+ -fx-text-fill: white;
+}
+
+.menu-bar {
+ -fx-background-color: derive(#423a5b, 20%); //top bar
+}
+
+.menu-bar .label {
+ -fx-font-size: 14pt;
+ -fx-font-family: "Segoe UI Light";
+ -fx-text-fill: white;
+ -fx-opacity: 0.9;
+}
+
+.menu .left-container {
+ -fx-background-color: black;
+}
+
+/*
+ * Metro style Push Button
+ * Author: Pedro Duque Vieira
+ * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/
+ */
+.button {
+ -fx-padding: 5 22 5 22;
+ -fx-border-color: #e2e2e2;
+ -fx-border-width: 2;
+ -fx-background-radius: 0;
+ -fx-background-color: #452967;
+ -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif;
+ -fx-font-size: 11pt;
+ -fx-text-fill: #d8d8d8;
+ -fx-background-insets: 0 0 0 0, 0, 1, 2;
+}
+
+.button:hover {
+ -fx-background-color: #9a5467;
+}
+
+.button:pressed,
+.button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #863b50;
+}
+
+.button:focused {
+ -fx-border-color: white, white;
+ -fx-border-width: 1, 1;
+ -fx-border-style: solid, segments(1, 1);
+ -fx-border-radius: 0, 0;
+ -fx-border-insets: 1 1 1 1, 0;
+}
+
+.button:disabled,
+.button:default:disabled {
+ -fx-opacity: 0.4;
+ -fx-background-color: #a13e59;
+ -fx-text-fill: white;
+}
+
+.button:default {
+ -fx-background-color: -fx-focus-color;
+ -fx-text-fill: #ffffff;
+}
+
+.button:default:hover {
+ -fx-background-color: derive(-fx-focus-color, 30%);
+}
+
+.dialog-pane {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane>*.button-bar>*.container {
+ -fx-background-color: #ffffff;
+}
+
+.dialog-pane>*.label.content {
+ -fx-font-size: 14px;
+ -fx-font-weight: bold;
+ -fx-text-fill: black;
+}
+
+.dialog-pane:header *.header-panel {
+ -fx-background-color: derive(#ffffff, 25%);
+}
+
+.dialog-pane:header *.header-panel *.label {
+ -fx-font-size: 18px;
+ -fx-font-style: italic;
+ -fx-fill: white;
+ -fx-text-fill: white;
+}
+
+.scroll-bar {
+ -fx-background-color: transparent;
+}
+
+.scroll-bar .thumb {
+ -fx-background-color: derive(#7e82b2, 50%);
+ -fx-background-insets: 3;
+}
+
+.scroll-bar .increment-button,
+.scroll-bar .decrement-button {
+ -fx-background-color: transparent;
+ -fx-padding: 0 0 0 0;
+}
+
+.scroll-bar .increment-arrow,
+.scroll-bar .decrement-arrow {
+ -fx-shape: " ";
+}
+
+.scroll-bar:vertical .increment-arrow,
+.scroll-bar:vertical .decrement-arrow {
+ -fx-padding: 1 8 1 8;
+}
+
+.scroll-bar:horizontal .increment-arrow,
+.scroll-bar:horizontal .decrement-arrow {
+ -fx-padding: 8 1 8 1;
+}
+
+#cardPane {
+ -fx-background-color: transparent;
+ -fx-border-width: 0;
+}
+
+#commandTypeLabel {
+ -fx-font-size: 11px;
+ -fx-text-fill: #ff0007;
+}
+
+#commandTextField {
+ -fx-background-color: transparent #4d518d transparent #4d518d;
+ -fx-background-insets: 0;
+ -fx-border-color: #4d518d #4d518d #ffffff #4d518d;
+ -fx-border-insets: 0;
+ -fx-border-width: 1;
+ -fx-font-family: "Segoe UI Light";
+ -fx-font-size: 13pt;
+ -fx-text-fill: white;
+}
+
+#filterField,
+#personListPanel,
+#personWebpage {
+ -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
+}
+
+#resultDisplay .content {
+ -fx-background-color: transparent, #4d518d, transparent, #4d518d;
+ -fx-background-radius: 0;
+}
+
+#tags {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#tags .label {
+ -fx-text-fill: white;
+ -fx-background-color: #4d518d;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
+
+#attachments {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#attachments .label {
+ -fx-text-fill: white;
+ -fx-background-color: #91773e;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
diff --git a/src/main/resources/view/CompareWindow.fxml b/src/main/resources/view/CompareWindow.fxml
new file mode 100644
index 00000000000..102f10eb541
--- /dev/null
+++ b/src/main/resources/view/CompareWindow.fxml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css
index 36e6b001cd8..a3d9d695149 100644
--- a/src/main/resources/view/DarkTheme.css
+++ b/src/main/resources/view/DarkTheme.css
@@ -1,6 +1,7 @@
.background {
-fx-background-color: derive(#1d1d1d, 20%);
- background-color: #383838; /* Used in the default.html file */
+ background-color: #383838;
+ /* Used in the default.html file */
}
.label {
@@ -52,15 +53,13 @@
-fx-background-color: transparent;
}
-.table-view .column-header, .table-view .filler {
+.table-view .column-header,
+.table-view .filler {
-fx-size: 35;
-fx-border-width: 0 0 1 0;
-fx-background-color: transparent;
-fx-border-color:
- transparent
- transparent
- derive(-fx-base, 80%)
- transparent;
+ transparent transparent derive(-fx-base, 80%) transparent;
-fx-border-insets: 0 10 1 0;
}
@@ -95,7 +94,7 @@
.list-cell {
-fx-label-padding: 0 0 0 0;
- -fx-graphic-text-gap : 0;
+ -fx-graphic-text-gap: 0;
-fx-padding: 0 0 0 0;
}
@@ -120,26 +119,45 @@
-fx-text-fill: white;
}
-.cell_big_label {
+.detail-content .label {
+ -fx-text-fill: white;
+}
+
+.detail-content-label {
+ -fx-font-size: 18px;
+}
+
+.detail-content-value {
+ -fx-font-weight: bold;
+ -fx-font-size: 18px;
+}
+
+.cell-huge-label {
+ -fx-font-family: "Segoe UI Semibold";
+ -fx-font-size: 42px;
+ -fx-text-fill: #010504;
+}
+
+.cell-big-label {
-fx-font-family: "Segoe UI Semibold";
-fx-font-size: 16px;
-fx-text-fill: #010504;
}
-.cell_small_label {
+.cell-small-label {
-fx-font-family: "Segoe UI";
-fx-font-size: 13px;
-fx-text-fill: #010504;
}
.stack-pane {
- -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-background-color: derive(#1d1d1d, 20%);
}
.pane-with-border {
- -fx-background-color: derive(#1d1d1d, 20%);
- -fx-border-color: derive(#1d1d1d, 10%);
- -fx-border-top-width: 1px;
+ -fx-background-color: derive(#1d1d1d, 20%);
+ -fx-border-color: derive(#1d1d1d, 10%);
+ -fx-border-top-width: 1px;
}
.status-bar {
@@ -228,9 +246,10 @@
-fx-background-color: #3a3a3a;
}
-.button:pressed, .button:default:hover:pressed {
- -fx-background-color: white;
- -fx-text-fill: #1d1d1d;
+.button:pressed,
+.button:default:hover:pressed {
+ -fx-background-color: white;
+ -fx-text-fill: #1d1d1d;
}
.button:focused {
@@ -241,7 +260,8 @@
-fx-border-insets: 1 1 1 1, 0;
}
-.button:disabled, .button:default:disabled {
+.button:disabled,
+.button:default:disabled {
-fx-opacity: 0.4;
-fx-background-color: #1d1d1d;
-fx-text-fill: white;
@@ -260,11 +280,11 @@
-fx-background-color: #1d1d1d;
}
-.dialog-pane > *.button-bar > *.container {
+.dialog-pane>*.button-bar>*.container {
-fx-background-color: #1d1d1d;
}
-.dialog-pane > *.label.content {
+.dialog-pane>*.label.content {
-fx-font-size: 14px;
-fx-font-weight: bold;
-fx-text-fill: white;
@@ -290,20 +310,24 @@
-fx-background-insets: 3;
}
-.scroll-bar .increment-button, .scroll-bar .decrement-button {
+.scroll-bar .increment-button,
+.scroll-bar .decrement-button {
-fx-background-color: transparent;
-fx-padding: 0 0 0 0;
}
-.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow {
+.scroll-bar .increment-arrow,
+.scroll-bar .decrement-arrow {
-fx-shape: " ";
}
-.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow {
+.scroll-bar:vertical .increment-arrow,
+.scroll-bar:vertical .decrement-arrow {
-fx-padding: 1 8 1 8;
}
-.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow {
+.scroll-bar:horizontal .increment-arrow,
+.scroll-bar:horizontal .decrement-arrow {
-fx-padding: 8 1 8 1;
}
@@ -328,7 +352,9 @@
-fx-text-fill: white;
}
-#filterField, #personListPanel, #personWebpage {
+#filterField,
+#personListPanel,
+#personWebpage {
-fx-effect: innershadow(gaussian, black, 10, 0, 0, 0);
}
@@ -350,3 +376,17 @@
-fx-background-radius: 2;
-fx-font-size: 11;
}
+
+#attachments {
+ -fx-hgap: 7;
+ -fx-vgap: 3;
+}
+
+#attachments .label {
+ -fx-text-fill: white;
+ -fx-background-color: #91773e;
+ -fx-padding: 1 3 1 3;
+ -fx-border-radius: 2;
+ -fx-background-radius: 2;
+ -fx-font-size: 11;
+}
diff --git a/src/main/resources/view/Extensions.css b/src/main/resources/view/Extensions.css
index bfe82a85964..a54d3746365 100644
--- a/src/main/resources/view/Extensions.css
+++ b/src/main/resources/view/Extensions.css
@@ -1,11 +1,11 @@
.error {
- -fx-text-fill: #d06651 !important; /* The error class should always override the default text-fill style */
+ -fx-text-fill: #e88071 !important; /* The error class should always override the default text-fill style */
}
.list-cell:empty {
/* Empty cells will not have alternating colours */
- -fx-background: #383838;
+ -fx-background: derive(#5a71bb, 20%);
}
.tag-selector {
diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml
index 7778f666a0a..4a757fce9c4 100644
--- a/src/main/resources/view/MainWindow.fxml
+++ b/src/main/resources/view/MainWindow.fxml
@@ -10,16 +10,17 @@
+
+ title="TAfinder" minWidth="450" minHeight="600" onCloseRequest="#handleExit">
-
+
@@ -46,12 +47,21 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonDetailContent.fxml b/src/main/resources/view/PersonDetailContent.fxml
new file mode 100644
index 00000000000..cc3c588cf58
--- /dev/null
+++ b/src/main/resources/view/PersonDetailContent.fxml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonDetailEmptyState.fxml b/src/main/resources/view/PersonDetailEmptyState.fxml
new file mode 100644
index 00000000000..da386759c2a
--- /dev/null
+++ b/src/main/resources/view/PersonDetailEmptyState.fxml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonDetailPanel.fxml b/src/main/resources/view/PersonDetailPanel.fxml
new file mode 100644
index 00000000000..002ac40f1fe
--- /dev/null
+++ b/src/main/resources/view/PersonDetailPanel.fxml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml
index f5e812e25e6..df9e44deab1 100644
--- a/src/main/resources/view/PersonListCard.fxml
+++ b/src/main/resources/view/PersonListCard.fxml
@@ -2,6 +2,7 @@
+
@@ -9,28 +10,36 @@
-
+
+
-
+
-
+
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/test/data/ImportCommandTest/invalid.csv b/src/test/data/ImportCommandTest/invalid.csv
new file mode 100644
index 00000000000..76056931083
--- /dev/null
+++ b/src/test/data/ImportCommandTest/invalid.csv
@@ -0,0 +1,5 @@
+name,phone,email,gpa,tags,studentNo,previousGrade
+Jasmine David,9847283,jasmine_david@u.nus.edu,4.3,deansList;pastTA,A0123486A,D-
+Sandeep Kopparthi,8753746,sandeep@u.nus.edu,5.0,pastTA,A0456123A,C-
+Lim Boon Kong,9777777,boonkong@u.nus.edu,3.5.2,deansList,A0775848D,C
+Mohammed Taufiq bin Rozaini,85535252,taufiqu.nus.edu,4.2,,A0483910A,A+
diff --git a/src/test/data/ImportCommandTest/invalid_format.csv b/src/test/data/ImportCommandTest/invalid_format.csv
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/src/test/data/ImportCommandTest/missing_fields.csv b/src/test/data/ImportCommandTest/missing_fields.csv
new file mode 100644
index 00000000000..ea06b3596b7
--- /dev/null
+++ b/src/test/data/ImportCommandTest/missing_fields.csv
@@ -0,0 +1,5 @@
+studentNo,name,phone,email,gpaa,previousGrade,tags
+A0123486A,Jasmine David,98472983,jasmine_david@u.nus.edu,4.3,B+,deansList;pastTA
+A0456123A,Sandeep Kopparthi,86753746,sandeep@u.nus.edu,5.0,B+,pastTA
+A0775848D,Lim Boon Kong,97777777,boonkong@u.nus.edu,3.5,C,deansList
+A0483910A,Mohammed Taufiq bin Rozaini,85535252,taufiq@u.nus.edu,4.2,A+,
diff --git a/src/test/data/ImportCommandTest/partial_valid.csv b/src/test/data/ImportCommandTest/partial_valid.csv
new file mode 100644
index 00000000000..cf08d44c1ac
--- /dev/null
+++ b/src/test/data/ImportCommandTest/partial_valid.csv
@@ -0,0 +1,5 @@
+name,phone,email,gpa,tags,studentNo,previousGrade
+Jasmine David,9847283,jasmine_david@u.nus.edu,4.3,deansList;pastTA,A0123486A,D-
+Sandeep Kopparthi,8753746,sandeep@u.nus.edu,5.0,pastTA,A0456123A,C-
+Lim Boon Kong,97777777,boonkong@u.nus.edu,3.5,deansList,A0775848D,C
+Mohammed Taufiq bin Rozaini,85535252,taufiq@u.nus.edu,4.2,,A0483910A,A+
diff --git a/src/test/data/ImportCommandTest/valid.csv b/src/test/data/ImportCommandTest/valid.csv
new file mode 100644
index 00000000000..46bf2a6c6e4
--- /dev/null
+++ b/src/test/data/ImportCommandTest/valid.csv
@@ -0,0 +1,5 @@
+studentNo,name,phone,email,gpa,previousGrade,tags
+A0123486A,Jasmine David,98472983,jasmine_david@u.nus.edu,4.3,B+,deansList;pastTA
+A0456123A,Sandeep Kopparthi,86753746,sandeep@u.nus.edu,5.0,B+,pastTA
+A0775848D,Lim Boon Kong,97777777,boonkong@u.nus.edu,3.5,C,deansList
+A0483910A,Mohammed Taufiq bin Rozaini,85535252,taufiq@u.nus.edu,4.2,A+,
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
index 6a4d2b7181c..4e48df4fe46 100644
--- a/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
+++ b/src/test/data/JsonAddressBookStorageTest/invalidAndValidPersonAddressBook.json
@@ -3,11 +3,11 @@
"name": "Valid Person",
"phone": "9482424",
"email": "hans@example.com",
- "address": "4th street"
+ "gpa": "5.0"
}, {
"name": "Person With Invalid Phone Field",
"phone": "948asdf2424",
"email": "hans@example.com",
- "address": "4th street"
+ "gpa": "3.0"
} ]
}
diff --git a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
index ccd21f7d1a9..c7b8c10fc1f 100644
--- a/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonAddressBookStorageTest/invalidPersonAddressBook.json
@@ -3,6 +3,6 @@
"name": "Person with invalid name field: Ha!ns Mu@ster",
"phone": "9482424",
"email": "hans@example.com",
- "address": "4th street"
+ "gpa": "2.6"
} ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
index a7427fe7aa2..c5a84bbd19b 100644
--- a/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/duplicatePersonAddressBook.json
@@ -1,14 +1,22 @@
{
"persons": [ {
+ "studentNo": "A0222222A",
"name": "Alice Pauline",
"phone": "94351253",
"email": "alice@example.com",
- "address": "123, Jurong West Ave 6, #08-111",
- "tags": [ "friends" ]
+ "gpa": "3.1",
+ "previousGrade": "A+",
+ "interviewScore": "3.2",
+ "tags": [ "friends" ],
+ "comment": ""
}, {
+ "studentNo": "A0222222A",
"name": "Alice Pauline",
"phone": "94351253",
"email": "pauline@example.com",
- "address": "4th street"
+ "gpa": "4.0",
+ "previousGrade": "A+",
+ "interviewScore": "3.2",
+ "comment": ""
} ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
index ad3f135ae42..2ee66192cbf 100644
--- a/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/invalidPersonAddressBook.json
@@ -3,6 +3,6 @@
"name": "Hans Muster",
"phone": "9482424",
"email": "invalid@email!3e",
- "address": "4th street"
+ "gpa": "3.0"
} ]
}
diff --git a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
index 72262099d35..c1c90692967 100644
--- a/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
+++ b/src/test/data/JsonSerializableAddressBookTest/typicalPersonsAddressBook.json
@@ -1,46 +1,68 @@
{
"_comment": "AddressBook save file which contains the same Person values as in TypicalPersons#getTypicalAddressBook()",
"persons" : [ {
+ "studentNo": "A0343345A",
"name" : "Alice Pauline",
"phone" : "94351253",
"email" : "alice@example.com",
- "address" : "123, Jurong West Ave 6, #08-111",
- "tags" : [ "friends" ]
+ "gpa" : "2.9",
+ "tags" : [ "friends" ],
+ "previousGrade": "A+",
+ "interviewScore": "3.2"
}, {
+ "studentNo": "A9473847C",
"name" : "Benson Meier",
"phone" : "98765432",
"email" : "johnd@example.com",
- "address" : "311, Clementi Ave 2, #02-25",
- "tags" : [ "owesMoney", "friends" ]
+ "gpa" : "4.9",
+ "previousGrade": "A+",
+ "interviewScore": "3.2",
+ "tags" : [ "owesMoney", "friends" ],
+ "comment": "This is a comment"
}, {
+ "studentNo": "A0437563D",
"name" : "Carl Kurz",
"phone" : "95352563",
"email" : "heinz@example.com",
- "address" : "wall street",
+ "gpa" : "4.0",
+ "previousGrade": "A+",
+ "interviewScore": "3.2",
"tags" : [ ]
}, {
+ "studentNo": "A0453552G",
"name" : "Daniel Meier",
"phone" : "87652533",
"email" : "cornelia@example.com",
- "address" : "10th street",
+ "gpa" : "3.0",
+ "previousGrade": "A+",
+ "interviewScore": "3.2",
"tags" : [ "friends" ]
}, {
+ "studentNo": "A0348574E",
"name" : "Elle Meyer",
"phone" : "9482224",
"email" : "werner@example.com",
- "address" : "michegan ave",
+ "gpa" : "1.0",
+ "previousGrade": "A+",
+ "interviewScore": "3.2",
"tags" : [ ]
}, {
+ "studentNo": "A0434534C",
"name" : "Fiona Kunz",
"phone" : "9482427",
"email" : "lydia@example.com",
- "address" : "little tokyo",
+ "gpa" : "2.0",
+ "previousGrade": "A+",
+ "interviewScore": "3.2",
"tags" : [ ]
}, {
+ "studentNo": "A0483728F",
"name" : "George Best",
"phone" : "9482442",
"email" : "anna@example.com",
- "address" : "4th street",
+ "gpa" : "4.0",
+ "previousGrade": "A+",
+ "interviewScore": "3.2",
"tags" : [ ]
} ]
}
diff --git a/src/test/java/seedu/address/logic/LogicManagerTest.java b/src/test/java/seedu/address/logic/LogicManagerTest.java
index baf8ce336a2..6ae08b9341c 100644
--- a/src/test/java/seedu/address/logic/LogicManagerTest.java
+++ b/src/test/java/seedu/address/logic/LogicManagerTest.java
@@ -3,10 +3,14 @@
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_UNKNOWN_COMMAND;
-import static seedu.address.logic.commands.CommandTestUtil.ADDRESS_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.COMMENT_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.EMAIL_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.GPA_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEW_SCORE_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.NAME_DESC_AMY;
import static seedu.address.logic.commands.CommandTestUtil.PHONE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.PREVIOUS_GRADE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.STUDENT_NUMBER_DESC_AMY;
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalPersons.AMY;
@@ -67,7 +71,7 @@ public void execute_commandExecutionError_throwsCommandException() {
@Test
public void execute_validCommand_success() throws Exception {
String listCommand = ListCommand.COMMAND_WORD;
- assertCommandSuccess(listCommand, ListCommand.MESSAGE_SUCCESS, model);
+ assertCommandSuccess(listCommand, ListCommand.MESSAGE_EMPTY_LIST, model);
}
@Test
@@ -165,9 +169,9 @@ 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 addCommand = AddCommand.COMMAND_WORD + STUDENT_NUMBER_DESC_AMY + NAME_DESC_AMY + PHONE_DESC_AMY
+ + EMAIL_DESC_AMY + GPA_DESC_AMY + PREVIOUS_GRADE_DESC_AMY + INTERVIEW_SCORE_DESC_AMY + COMMENT_DESC_AMY;
+ Person expectedPerson = new PersonBuilder(AMY).withTags().withAttachments().build();
ModelManager expectedModel = new ModelManager();
expectedModel.addPerson(expectedPerson);
assertCommandFailure(addCommand, CommandException.class, expectedMessage, expectedModel);
diff --git a/src/test/java/seedu/address/logic/commands/AddCommandTest.java b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
index 90e8253f48e..82dd74415f8 100644
--- a/src/test/java/seedu/address/logic/commands/AddCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/AddCommandTest.java
@@ -10,12 +10,16 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Optional;
import java.util.function.Predicate;
import org.junit.jupiter.api.Test;
+import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import seedu.address.commons.core.GuiSettings;
+import seedu.address.commons.core.index.Index;
import seedu.address.logic.Messages;
import seedu.address.logic.commands.exceptions.CommandException;
import seedu.address.model.AddressBook;
@@ -148,15 +152,35 @@ public void setPerson(Person target, Person editedPerson) {
throw new AssertionError("This method should not be called.");
}
+ @Override
+ public void showPersonAtIndex(Index index) {
+ throw new AssertionError("This method should not be called.");
+ }
+
+ @Override
+ public void clearPersonDetails() {
+ throw new AssertionError("This method should not be called.");
+ }
+
@Override
public ObservableList getFilteredPersonList() {
throw new AssertionError("This method should not be called.");
}
+ @Override
+ public ObservableValue> getCurrentPerson() {
+ 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 sortFilteredPersonList(Comparator comparator) {
+ throw new AssertionError("This method should not be called.");
+ }
}
/**
diff --git a/src/test/java/seedu/address/logic/commands/AttachCommandTest.java b/src/test/java/seedu/address/logic/commands/AttachCommandTest.java
new file mode 100644
index 00000000000..f8c32c6e882
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/AttachCommandTest.java
@@ -0,0 +1,94 @@
+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.assertCommandSuccess;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.attachment.Attachment;
+import seedu.address.model.person.Person;
+import seedu.address.testutil.PersonBuilder;
+
+/**
+ * Contains integration tests (interaction with the Model) for
+ * {@code AttachCommand}.
+ */
+public class AttachCommandTest {
+ private static Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ private static Person firstPerson = model.getFilteredPersonList().get(0);
+
+ private static final String ATTACHMENT_SAMPLE_PATH = Paths
+ .get("src", "test", "resources", "attachments", "sample.txt")
+ .toString();
+ private static final String ATTACHMENT_ANOTHER_SAMPLE_PATH = Paths
+ .get("src", "test", "resources", "attachments", "another-sample.txt")
+ .toString();
+
+ private static final String ATTACHMENT_SAMPLE_EXPECTED_PATH = Paths
+ .get("data", "attachments", firstPerson.getStudentNumber().value, "sample.txt")
+ .toString();
+ private static final String ATTACHMENT_ANOTHER_SAMPLE_EXPECTED_PATH = Paths
+ .get("data", "attachments", firstPerson.getStudentNumber().value, "another-sample.txt")
+ .toString();
+
+ @Test
+ public void equals() {
+ List attachments = List.of(new Attachment(ATTACHMENT_SAMPLE_PATH));
+ List otherAttachments = List.of(new Attachment(ATTACHMENT_ANOTHER_SAMPLE_PATH));
+
+ AttachCommand findFirstCommand = new AttachCommand(Index.fromZeroBased(3), attachments);
+ AttachCommand findSecondCommand = new AttachCommand(Index.fromZeroBased(4), attachments);
+ AttachCommand findThirdCommand = new AttachCommand(Index.fromZeroBased(3), otherAttachments);
+
+ // same object -> returns true
+ assertTrue(findFirstCommand.equals(findFirstCommand));
+
+ // same values -> returns true
+ AttachCommand findFirstCommandCopy = new AttachCommand(Index.fromZeroBased(3), attachments);
+ assertTrue(findFirstCommand.equals(findFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(findFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(findFirstCommand.equals(null));
+
+ // different index -> returns false
+ assertFalse(findFirstCommand.equals(findSecondCommand));
+
+ // different attachments -> returns false
+ assertFalse(findFirstCommand.equals(findThirdCommand));
+ }
+
+ @Test
+ public void execute_manyAttachments_success() {
+ List attachments = List.of(
+ new Attachment(ATTACHMENT_SAMPLE_PATH),
+ new Attachment(ATTACHMENT_ANOTHER_SAMPLE_PATH));
+
+ Person person = model.getFilteredPersonList().get(0);
+
+ String expectedMessage = String.format(AttachCommand.MESSAGE_ATTACH_SUCCESS, 2,
+ person.getName());
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(person,
+ new PersonBuilder(person)
+ .withAttachments(ATTACHMENT_SAMPLE_EXPECTED_PATH, ATTACHMENT_ANOTHER_SAMPLE_EXPECTED_PATH)
+ .build());
+
+ AttachCommand command = new AttachCommand(Index.fromZeroBased(0), attachments);
+
+ assertCommandSuccess(command, model, expectedMessage, expectedModel);
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/BookmarkCommandTest.java b/src/test/java/seedu/address/logic/commands/BookmarkCommandTest.java
new file mode 100644
index 00000000000..14696b8128a
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/BookmarkCommandTest.java
@@ -0,0 +1,71 @@
+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.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.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.IsBookmarked;
+import seedu.address.model.person.Person;
+
+public class BookmarkCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_personBookmarked_bookmarkSuccessful() throws CommandException {
+ Person bookmarkedPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ CommandResult commandResult = new BookmarkCommand(INDEX_FIRST_PERSON).execute(model);
+
+ String expectedMessage = String.format(BookmarkCommand.MESSAGE_BOOKMARK_APPLICANT_SUCCESS,
+ INDEX_FIRST_PERSON.getOneBased());
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ assertEquals(model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()).getIsBookmarked(),
+ new IsBookmarked(true));
+ }
+
+ @Test
+ public void execute_indexOutOfRange_throwsCommandException() {
+ Index outOfRangeIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ BookmarkCommand bookmarkCommand = new BookmarkCommand(outOfRangeIndex);
+ assertCommandFailure(bookmarkCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ BookmarkCommand bookmarkFirstCommand = new BookmarkCommand(INDEX_FIRST_PERSON);
+ BookmarkCommand bookmarkSecondCommand = new BookmarkCommand(INDEX_SECOND_PERSON);
+
+ assertTrue(bookmarkFirstCommand.equals(bookmarkFirstCommand));
+
+ BookmarkCommand bookmarkFirstCommandCopy = new BookmarkCommand(INDEX_FIRST_PERSON);
+ assertTrue(bookmarkFirstCommand.equals(bookmarkFirstCommandCopy));
+
+ assertFalse(bookmarkFirstCommand.equals(1));
+
+ assertFalse(bookmarkFirstCommand.equals(null));
+
+ assertFalse(bookmarkFirstCommand.equals(bookmarkSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ BookmarkCommand bookmarkCommand = new BookmarkCommand(targetIndex);
+ String expected = BookmarkCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, bookmarkCommand.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
index 643a1d08069..e1aa736e61e 100644
--- a/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
+++ b/src/test/java/seedu/address/logic/commands/CommandTestUtil.java
@@ -2,10 +2,14 @@
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_COMMENT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GPA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEW_SCORE;
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_PREVIOUS_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STUDENT_NUMBER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import static seedu.address.testutil.Assert.assertThrows;
@@ -26,32 +30,50 @@
*/
public class CommandTestUtil {
+ public static final String VALID_STUDENT_NUMBER_AMY = "A0344333B";
+ public static final String VALID_STUDENT_NUMBER_BOB = "A0984834C";
public static final String VALID_NAME_AMY = "Amy Bee";
public static final String VALID_NAME_BOB = "Bob Choo";
public static final String VALID_PHONE_AMY = "11111111";
public static final String VALID_PHONE_BOB = "22222222";
+ public static final String VALID_PREVIOUS_GRADE_AMY = "A+";
+ public static final String VALID_PREVIOUS_GRADE_BOB = "B-";
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 double VALID_GPA_AMY = 5.0;
+ public static final double VALID_GPA_BOB = 3.6;
+ public static final double VALID_INTERVIEW_SCORE_AMY = 9.99;
+ public static final double VALID_INTERVIEW_SCORE_BOB = 9.99;
+ public static final String VALID_COMMENT_AMY = "studious";
+ public static final String VALID_COMMENT_BOB = "hardworking";
public static final String VALID_TAG_HUSBAND = "husband";
public static final String VALID_TAG_FRIEND = "friend";
+ public static final boolean VALID_IS_HIDDEN = false;
+ public static final String STUDENT_NUMBER_DESC_AMY = " " + PREFIX_STUDENT_NUMBER + VALID_STUDENT_NUMBER_AMY;
+ public static final String STUDENT_NUMBER_DESC_BOB = " " + PREFIX_STUDENT_NUMBER + VALID_STUDENT_NUMBER_BOB;
+ public static final String INTERVIEW_SCORE_DESC_AMY = " " + PREFIX_INTERVIEW_SCORE + VALID_INTERVIEW_SCORE_AMY;
+ public static final String INTERVIEW_SCORE_DESC_BOB = " " + PREFIX_INTERVIEW_SCORE + VALID_INTERVIEW_SCORE_BOB;
public static final String NAME_DESC_AMY = " " + PREFIX_NAME + VALID_NAME_AMY;
public static final String NAME_DESC_BOB = " " + PREFIX_NAME + VALID_NAME_BOB;
public static final String PHONE_DESC_AMY = " " + PREFIX_PHONE + VALID_PHONE_AMY;
public static final String PHONE_DESC_BOB = " " + PREFIX_PHONE + VALID_PHONE_BOB;
+ public static final String PREVIOUS_GRADE_DESC_AMY = " " + PREFIX_PREVIOUS_GRADE + VALID_PREVIOUS_GRADE_AMY;
+ public static final String PREVIOUS_GRADE_DESC_BOB = " " + PREFIX_PREVIOUS_GRADE + VALID_PREVIOUS_GRADE_BOB;
public static final String EMAIL_DESC_AMY = " " + PREFIX_EMAIL + VALID_EMAIL_AMY;
public static final String EMAIL_DESC_BOB = " " + PREFIX_EMAIL + VALID_EMAIL_BOB;
- public static final String ADDRESS_DESC_AMY = " " + PREFIX_ADDRESS + VALID_ADDRESS_AMY;
- public static final String ADDRESS_DESC_BOB = " " + PREFIX_ADDRESS + VALID_ADDRESS_BOB;
+ public static final String GPA_DESC_AMY = " " + PREFIX_GPA + VALID_GPA_AMY;
+ public static final String GPA_DESC_BOB = " " + PREFIX_GPA + VALID_GPA_BOB;
+ public static final String COMMENT_DESC_AMY = " " + PREFIX_COMMENT + VALID_COMMENT_AMY;
+ public static final String COMMENT_DESC_BOB = " " + PREFIX_COMMENT + VALID_COMMENT_BOB;
public static final String TAG_DESC_FRIEND = " " + PREFIX_TAG + VALID_TAG_FRIEND;
public static final String TAG_DESC_HUSBAND = " " + PREFIX_TAG + VALID_TAG_HUSBAND;
+ public static final String INVALID_STUDENT_NUMBER_DESC = " " + PREFIX_STUDENT_NUMBER + "A0343G"; // too short
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_GPA_DESC = " " + PREFIX_GPA; // 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";
@@ -62,16 +84,17 @@ public class CommandTestUtil {
static {
DESC_AMY = new EditPersonDescriptorBuilder().withName(VALID_NAME_AMY)
- .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY)
+ .withPhone(VALID_PHONE_AMY).withEmail(VALID_EMAIL_AMY).withGpa(VALID_GPA_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)
+ .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_BOB).withGpa(VALID_GPA_BOB)
.withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
}
/**
* Executes the given {@code command}, confirms that
- * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ * - the returned {@link CommandResult} matches {@code expectedCommandResult}
+ *
* - the {@code actualModel} matches {@code expectedModel}
*/
public static void assertCommandSuccess(Command command, Model actualModel, CommandResult expectedCommandResult,
@@ -86,7 +109,8 @@ public static void assertCommandSuccess(Command command, Model actualModel, Comm
}
/**
- * Convenience wrapper to {@link #assertCommandSuccess(Command, Model, CommandResult, Model)}
+ * Convenience wrapper to
+ * {@link #assertCommandSuccess(Command, Model, CommandResult, Model)}
* that takes a string {@code expectedMessage}.
*/
public static void assertCommandSuccess(Command command, Model actualModel, String expectedMessage,
@@ -99,7 +123,8 @@ public static void assertCommandSuccess(Command command, Model actualModel, Stri
* Executes the given {@code command}, confirms that
* - a {@code CommandException} is thrown
* - the CommandException message matches {@code expectedMessage}
- * - the address book, filtered person list and selected person in {@code actualModel} remain unchanged
+ * - the address book, filtered person list and selected person in
+ * {@code actualModel} remain unchanged
*/
public static void assertCommandFailure(Command command, Model actualModel, String expectedMessage) {
// we are unable to defensively copy the model for comparison later, so we can
@@ -111,8 +136,10 @@ public static void assertCommandFailure(Command command, Model actualModel, Stri
assertEquals(expectedAddressBook, actualModel.getAddressBook());
assertEquals(expectedFilteredList, actualModel.getFilteredPersonList());
}
+
/**
- * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the
+ * Updates {@code model}'s filtered list to show only the person at the given
+ * {@code targetIndex} in the
* {@code model}'s address book.
*/
public static void showPersonAtIndex(Model model, Index targetIndex) {
diff --git a/src/test/java/seedu/address/logic/commands/CommentCommandTest.java b/src/test/java/seedu/address/logic/commands/CommentCommandTest.java
new file mode 100644
index 00000000000..1193ba4dd11
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/CommentCommandTest.java
@@ -0,0 +1,125 @@
+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_COMMENT_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_COMMENT_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.Comment;
+import seedu.address.model.person.Person;
+import seedu.address.testutil.PersonBuilder;
+
+class CommentCommandTest {
+ private static final String COMMENT_STUB = "Some comment";
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ void execute_addCommentUnfilteredList_success() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person editedPerson = new PersonBuilder(firstPerson).withComment(COMMENT_STUB).build();
+
+ CommentCommand commentCommand = new CommentCommand(INDEX_FIRST_PERSON,
+ new Comment(editedPerson.getComment().get().comment));
+
+ String expectedMessage = String.format(CommentCommand.MESSAGE_ADD_COMMENT_SUCCESS, editedPerson);
+
+ Model expectedModel = new ModelManager(new AddressBook(model.getAddressBook()), new UserPrefs());
+ expectedModel.setPerson(firstPerson, editedPerson);
+
+ assertCommandSuccess(commentCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_deleteRemarkUnfilteredList_success() {
+ Person firstPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person editedPerson = new PersonBuilder(firstPerson).withComment("").build();
+
+ CommentCommand remarkCommand = new CommentCommand(INDEX_FIRST_PERSON,
+ new Comment(editedPerson.getComment().get().toString()));
+
+ String expectedMessage = String.format(CommentCommand.MESSAGE_DELETE_COMMENT_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()))
+ .withComment(COMMENT_STUB).build();
+
+ CommentCommand remarkCommand = new CommentCommand(INDEX_FIRST_PERSON,
+ new Comment(editedPerson.getComment().get().comment));
+
+ String expectedMessage = String.format(CommentCommand.MESSAGE_ADD_COMMENT_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);
+ CommentCommand remarkCommand = new CommentCommand(outOfBoundIndex, new Comment(VALID_COMMENT_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());
+
+ CommentCommand remarkCommand = new CommentCommand(outOfBoundIndex, new Comment(VALID_COMMENT_BOB));
+ assertCommandFailure(remarkCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ final CommentCommand standardCommand = new CommentCommand(INDEX_FIRST_PERSON,
+ new Comment(VALID_COMMENT_AMY));
+ // same values -> returns true
+ CommentCommand commandWithSameValues = new CommentCommand(INDEX_FIRST_PERSON,
+ new Comment(VALID_COMMENT_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 CommentCommand(INDEX_SECOND_PERSON,
+ new Comment(VALID_COMMENT_AMY))));
+ // different comment -> returns false
+ assertFalse(standardCommand.equals(new CommentCommand(INDEX_FIRST_PERSON,
+ new Comment(VALID_COMMENT_BOB))));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/CompareCommandTest.java b/src/test/java/seedu/address/logic/commands/CompareCommandTest.java
new file mode 100644
index 00000000000..7e029bbe13f
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/CompareCommandTest.java
@@ -0,0 +1,38 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.commons.core.index.Index;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.testutil.AnotherPersonBuilder;
+import seedu.address.testutil.PersonBuilder;
+
+public class CompareCommandTest {
+
+ @Test
+ public void execute_sameIndex_throwsCommandException() {
+ Model model = new ModelManager();
+ model.addPerson(new PersonBuilder().build());
+ model.addPerson(new AnotherPersonBuilder().build());
+
+ CompareCommand compareCommand = new CompareCommand(Index.fromOneBased(1),
+ Index.fromOneBased(1));
+ assertThrows(CommandException.class, () -> compareCommand.execute(model),
+ "Error: Please provide distinct indices. " + "You cannot compare the same applicant.");
+ }
+
+ @Test
+ public void execute_invalidIndex_throwsCommandException() {
+ Model model = new ModelManager();
+ model.addPerson(new PersonBuilder().build());
+ model.addPerson(new AnotherPersonBuilder().build());
+
+ CompareCommand compareCommand = new CompareCommand(Index.fromOneBased(1), Index.fromOneBased(3));
+ assertThrows(CommandException.class, () -> compareCommand.execute(model),
+ "Error: One or both of the specified applicants were not found in the list.");
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/EditCommandTest.java b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
index 469dd97daa7..80403cbba64 100644
--- a/src/test/java/seedu/address/logic/commands/EditCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditCommandTest.java
@@ -37,7 +37,16 @@ public class EditCommandTest {
@Test
public void execute_allFieldsSpecifiedUnfilteredList_success() {
- Person editedPerson = new PersonBuilder().build();
+ Person currentPerson = model.getFilteredPersonList().get(0);
+ Person editedPerson = new PersonBuilder()
+ .withInterviewScore(4.4)
+ .withStudentNumber(currentPerson.getStudentNumber().toString())
+ .build();
+
+
+ System.out.println(currentPerson);
+ System.out.println(editedPerson);
+
EditPersonDescriptor descriptor = new EditPersonDescriptorBuilder(editedPerson).build();
EditCommand editCommand = new EditCommand(INDEX_FIRST_PERSON, descriptor);
@@ -99,27 +108,6 @@ public void execute_filteredList_success() {
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);
diff --git a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
index b17c1f3d5c2..c02e092baef 100644
--- a/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
+++ b/src/test/java/seedu/address/logic/commands/EditPersonDescriptorTest.java
@@ -5,8 +5,8 @@
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_GPA_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;
@@ -49,7 +49,7 @@ public void equals() {
assertFalse(DESC_AMY.equals(editedAmy));
// different address -> returns false
- editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withAddress(VALID_ADDRESS_BOB).build();
+ editedAmy = new EditPersonDescriptorBuilder(DESC_AMY).withGpa(VALID_GPA_BOB).build();
assertFalse(DESC_AMY.equals(editedAmy));
// different tags -> returns false
@@ -63,9 +63,12 @@ public void toStringMethod() {
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) + "}";
+ + editPersonDescriptor.getEmail().orElse(null) + ", gpa="
+ + editPersonDescriptor.getGpa().orElse(null) + ", previousGrade="
+ + editPersonDescriptor.getPreviousGrade().orElse(null) + ", interviewScore="
+ + editPersonDescriptor.getInterviewScore().orElse(null) + ", tags="
+ + editPersonDescriptor.getTags().orElse(null) + ", comment="
+ + editPersonDescriptor.getComment().orElse(null) + "}";
assertEquals(expected, editPersonDescriptor.toString());
}
}
diff --git a/src/test/java/seedu/address/logic/commands/HideCommandTest.java b/src/test/java/seedu/address/logic/commands/HideCommandTest.java
new file mode 100644
index 00000000000..b1507ee75c9
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/HideCommandTest.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.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.commands.CommandTestUtil.assertCommandFailure;
+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.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+
+public class HideCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_personHidden_hideSuccessful() throws CommandException {
+ Person hiddenPerson = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ Person secondPerson = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+ CommandResult commandResult = new HideCommand(INDEX_FIRST_PERSON).execute(model);
+
+ String expectedMessage = String.format(HideCommand.MESSAGE_HIDE_APPLICANT_SUCCESS,
+ Messages.format(hiddenPerson));
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ assertEquals(model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()), secondPerson);
+ }
+
+ @Test
+ public void execute_indexOutOfRange_throwsCommandException() {
+ Index outOfRangeIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ HideCommand hideCommand = new HideCommand(outOfRangeIndex);
+ assertCommandFailure(hideCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ HideCommand hideFirstCommand = new HideCommand(INDEX_FIRST_PERSON);
+ HideCommand hideSecondCommand = new HideCommand(INDEX_SECOND_PERSON);
+
+ assertTrue(hideFirstCommand.equals(hideFirstCommand));
+
+ HideCommand hideFirstCommandCopy = new HideCommand(INDEX_FIRST_PERSON);
+ assertTrue(hideFirstCommand.equals(hideFirstCommandCopy));
+
+ assertFalse(hideFirstCommand.equals(1));
+
+ assertFalse(hideFirstCommand.equals(null));
+
+ assertFalse(hideFirstCommand.equals(hideSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ HideCommand hideCommand = new HideCommand(targetIndex);
+ String expected = HideCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, hideCommand.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/ImportCommandTest.java b/src/test/java/seedu/address/logic/commands/ImportCommandTest.java
new file mode 100644
index 00000000000..09645d07892
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ImportCommandTest.java
@@ -0,0 +1,121 @@
+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.testutil.TypicalPersons.getTypicalAddressBook;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.attachment.Attachment;
+
+public class ImportCommandTest {
+
+ private static final Path TEST_DATA_FOLDER = Paths.get("src", "test", "data", "ImportCommandTest");
+ private static final Path VALID_CSV_FILE = TEST_DATA_FOLDER.resolve("valid.csv");
+ private static final Path PARTIAL_VALID_CSV_FILE = TEST_DATA_FOLDER.resolve("partial_valid.csv");
+ private static final Path INVALID_CSV_FILE = TEST_DATA_FOLDER.resolve("invalid.csv");
+ private static final Path MISSING_FIELDS_CSV_FILE = TEST_DATA_FOLDER.resolve("missing_fields.csv");
+ private static final Path INVALID_FORMAT_CSV_FILE = TEST_DATA_FOLDER.resolve("invalid_format.csv");
+ private static final Path INVALID_PATH_FILE = Paths.get("invalid", "directory", "file.csv");
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validCsv_importSuccessful() throws CommandException {
+ Attachment validCsv = new Attachment(VALID_CSV_FILE.toString());
+ CommandResult commandResult = new ImportCommand(validCsv).execute(model);
+
+ String expectedMessage = String.format(ImportCommand.MESSAGE_IMPORT_SUCCESS, 4);
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_partialValidCsv_importPartiallySuccessful() throws CommandException {
+ Attachment partialValidCsv = new Attachment(PARTIAL_VALID_CSV_FILE.toString());
+ CommandResult commandResult = new ImportCommand(partialValidCsv).execute(model);
+
+ String expectedMessage = String.format(ImportCommand.MESSAGE_IMPORT_SUCCESS, 2) + " "
+ + String.format(ImportCommand.MESSAGE_IMPORT_FAILURE, 2, "2, 3");
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_invalidCsv_importFailure() throws CommandException {
+ Attachment invalidCsv = new Attachment(INVALID_CSV_FILE.toString());
+ CommandResult commandResult = new ImportCommand(invalidCsv).execute(model);
+
+ String expectedMessage = String.format(ImportCommand.MESSAGE_IMPORT_FAILURE, 4, "2, 3, 4, 5");
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ }
+
+ @Test
+ public void execute_invalidFileFormatCsv_importFailure() throws CommandException {
+ Attachment invalidFileFormatCsv = new Attachment(INVALID_FORMAT_CSV_FILE.toString());
+ ImportCommand importCommand = new ImportCommand(invalidFileFormatCsv);
+
+ String expectedMessage = ImportCommand.MESSAGE_INVALID_FILE_FORMAT;
+
+ assertCommandFailure(importCommand, model, expectedMessage);
+ }
+
+ @Test
+ public void execute_missingFieldsCsv_importFailure() throws CommandException {
+ Attachment missingFieldsCsv = new Attachment(MISSING_FIELDS_CSV_FILE.toString());
+ ImportCommand importCommand = new ImportCommand(missingFieldsCsv);
+
+ String expectedMessage = String.format(ImportCommand.MESSAGE_MISSING_FIELDS, "gpa");
+
+ assertCommandFailure(importCommand, model, expectedMessage);
+ }
+
+ @Test
+ public void execute_invalidPath_importFailure() throws CommandException {
+ Attachment invalidPathFile = new Attachment(INVALID_PATH_FILE.toString());
+ ImportCommand importCommand = new ImportCommand(invalidPathFile);
+
+ String expectedMessage = ImportCommand.MESSAGE_FAILED_TO_OPEN;
+
+ assertCommandFailure(importCommand, model, expectedMessage);
+ }
+
+ @Test
+ public void equals() {
+ Attachment firstAttachment = new Attachment(VALID_CSV_FILE.toString());
+ Attachment secondAttachment = new Attachment(INVALID_CSV_FILE.toString());
+
+ ImportCommand importFirstCommand = new ImportCommand(firstAttachment);
+ ImportCommand importSecondCommand = new ImportCommand(secondAttachment);
+
+ assertTrue(importFirstCommand.equals(importFirstCommand));
+
+ ImportCommand importFirstCommandCopy = new ImportCommand(firstAttachment);
+ assertTrue(importFirstCommand.equals(importFirstCommandCopy));
+
+ assertFalse(importFirstCommand.equals(1));
+
+ assertFalse(importFirstCommand.equals(null));
+
+ assertFalse(importFirstCommand.equals(importSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Attachment attachment = new Attachment(VALID_CSV_FILE.toString());
+ ImportCommand importCommand = new ImportCommand(attachment);
+ String expected = ImportCommand.class.getCanonicalName() + "{attachment=" + attachment + "}";
+ assertEquals(expected, importCommand.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/ListCommandTest.java b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
index 435ff1f7275..38245bf2b10 100644
--- a/src/test/java/seedu/address/logic/commands/ListCommandTest.java
+++ b/src/test/java/seedu/address/logic/commands/ListCommandTest.java
@@ -1,7 +1,12 @@
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.assertCommandSuccess;
import static seedu.address.logic.commands.CommandTestUtil.showPersonAtIndex;
+import static seedu.address.logic.parser.CliSyntax.FIELD_BOOKMARKED;
+import static seedu.address.logic.parser.CliSyntax.FIELD_HIDDEN;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
@@ -11,12 +16,13 @@
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) and unit tests for ListCommand.
*/
public class ListCommandTest {
-
private Model model;
private Model expectedModel;
@@ -36,4 +42,48 @@ public void execute_listIsFiltered_showsEverything() {
showPersonAtIndex(model, INDEX_FIRST_PERSON);
assertCommandSuccess(new ListCommand(), model, ListCommand.MESSAGE_SUCCESS, expectedModel);
}
+
+ @Test
+ public void execute_listHiddenPersons_listHiddenSuccessful() {
+ Person hiddenPerson = new PersonBuilder().withHidden(true).build();
+ model.setPerson(model.getFilteredPersonList().get(0), hiddenPerson);
+ CommandResult commandResult = new ListCommand(FIELD_HIDDEN).execute(model);
+ String expectedMessage = ListCommand.MESSAGE_SUCCESS;
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ assertEquals(model.getFilteredPersonList().get(0), hiddenPerson);
+ }
+
+ @Test
+ public void execute_listBookmarkedPersons_listBookmarkedSuccessful() {
+ Person bookmarkedPerson = new PersonBuilder().withBookmark(true).build();
+ model.setPerson(model.getFilteredPersonList().get(0), bookmarkedPerson);
+ CommandResult commandResult = new ListCommand(FIELD_BOOKMARKED).execute(model);
+ String expectedMessage = ListCommand.MESSAGE_SUCCESS;
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ assertEquals(model.getFilteredPersonList().get(0), bookmarkedPerson);
+ }
+
+ @Test
+ public void equals() {
+ ListCommand listFirstCommand = new ListCommand();
+ ListCommand listSecondCommand = new ListCommand(FIELD_HIDDEN);
+
+ assertTrue(listFirstCommand.equals(listFirstCommand));
+
+ ListCommand listFirstCommandCopy = new ListCommand();
+ assertTrue(listFirstCommand.equals(listFirstCommandCopy));
+
+ assertFalse(listFirstCommand.equals(1));
+
+ assertFalse(listFirstCommand.equals(null));
+
+ assertFalse(listFirstCommand.equals(listSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ ListCommand listCommand = new ListCommand();
+ String expected = ListCommand.class.getCanonicalName() + "{fieldName=" + "}";
+ assertEquals(expected, listCommand.toString());
+ }
}
diff --git a/src/test/java/seedu/address/logic/commands/SortCommandTest.java b/src/test/java/seedu/address/logic/commands/SortCommandTest.java
new file mode 100644
index 00000000000..bb495d50385
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/SortCommandTest.java
@@ -0,0 +1,73 @@
+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.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+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) and unit tests for SortCommand.
+ */
+public class SortCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_sortByGpa_success() throws CommandException {
+ int indexOfLastPerson = model.getFilteredPersonList().size() - 1;
+ Person personWithHighestGpa = new PersonBuilder(model.getFilteredPersonList().get(indexOfLastPerson))
+ .withGpa(5.0).build();
+ Person personWithLowestGpa = new PersonBuilder(model.getFilteredPersonList().get(0))
+ .withGpa(0.0).build();
+
+ model.setPerson(model.getFilteredPersonList().get(0), personWithLowestGpa);
+ model.setPerson(model.getFilteredPersonList().get(indexOfLastPerson), personWithHighestGpa);
+
+ CommandResult commandResult = new SortCommand("gpa").execute(model);
+ String expectedMessage = SortCommand.MESSAGE_SUCCESS;
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ assertTrue(model.getFilteredPersonList().get(0).equals(personWithHighestGpa)
+ && model.getFilteredPersonList().get(indexOfLastPerson).equals(personWithLowestGpa));
+ }
+ @Test
+ public void execute_sortEmptyList_throwsCommandException() {
+ Model emptyModel = new ModelManager();
+ SortCommand sortCommand = new SortCommand("gpa");
+ assertCommandFailure(sortCommand, emptyModel, SortCommand.MESSAGE_EMPTY_LIST);
+ }
+
+ @Test
+ public void equals() {
+ SortCommand sortFirstCommand = new SortCommand("gpa");
+ SortCommand sortSecondCommand = new SortCommand("name");
+
+ assertTrue(sortFirstCommand.equals(sortFirstCommand));
+
+ SortCommand sortFirstCommandCopy = new SortCommand("gpa");
+ assertTrue(sortFirstCommand.equals(sortFirstCommandCopy));
+
+ assertFalse(sortFirstCommand.equals(1));
+
+ assertFalse(sortFirstCommand.equals(null));
+
+ assertFalse(sortFirstCommand.equals(sortSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ String fieldName = "gpa";
+ SortCommand sortCommand = new SortCommand(fieldName);
+ String expected = SortCommand.class.getCanonicalName() + "{fieldName=" + fieldName + "}";
+ assertEquals(expected, sortCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/UnbookmarkCommandTest.java b/src/test/java/seedu/address/logic/commands/UnbookmarkCommandTest.java
new file mode 100644
index 00000000000..5b8cc1fc5d9
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/UnbookmarkCommandTest.java
@@ -0,0 +1,71 @@
+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.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.exceptions.CommandException;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.IsBookmarked;
+import seedu.address.model.person.Person;
+
+public class UnbookmarkCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_personUnbookmarked_unbookmarkSuccessful() throws CommandException {
+ Person unbookmarkedPerson = model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased());
+ CommandResult commandResult = new UnbookmarkCommand(INDEX_SECOND_PERSON).execute(model);
+
+ String expectedMessage = String.format(UnbookmarkCommand.MESSAGE_UNBOOKMARK_APPLICANT_SUCCESS,
+ INDEX_SECOND_PERSON.getOneBased());
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ assertEquals(model.getFilteredPersonList().get(INDEX_SECOND_PERSON.getZeroBased()).getIsBookmarked(),
+ new IsBookmarked(false));
+ }
+
+ @Test
+ public void execute_indexOutOfRange_throwsCommandException() {
+ Index outOfRangeIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ UnbookmarkCommand unbookmarkCommand = new UnbookmarkCommand(outOfRangeIndex);
+ assertCommandFailure(unbookmarkCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ UnbookmarkCommand unbookmarkFirstCommand = new UnbookmarkCommand(INDEX_FIRST_PERSON);
+ UnbookmarkCommand unbookmarkSecondCommand = new UnbookmarkCommand(INDEX_SECOND_PERSON);
+
+ assertTrue(unbookmarkFirstCommand.equals(unbookmarkFirstCommand));
+
+ UnbookmarkCommand unbookmarkFirstCommandCopy = new UnbookmarkCommand(INDEX_FIRST_PERSON);
+ assertTrue(unbookmarkFirstCommand.equals(unbookmarkFirstCommandCopy));
+
+ assertFalse(unbookmarkFirstCommand.equals(1));
+
+ assertFalse(unbookmarkFirstCommand.equals(null));
+
+ assertFalse(unbookmarkFirstCommand.equals(unbookmarkSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ UnbookmarkCommand unbookmarkCommand = new UnbookmarkCommand(targetIndex);
+ String expected = UnbookmarkCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, unbookmarkCommand.toString());
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/UnhideAllCommandTest.java b/src/test/java/seedu/address/logic/commands/UnhideAllCommandTest.java
new file mode 100644
index 00000000000..af8a6faad56
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/UnhideAllCommandTest.java
@@ -0,0 +1,37 @@
+package seedu.address.logic.commands;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.testutil.TypicalPersons.getTypicalAddressBook;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.exceptions.CommandException;
+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;
+
+
+public class UnhideAllCommandTest {
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+ @Test
+ public void execute_unhideAll_unhideAllSuccessful() throws CommandException {
+ Person hiddenPersonOne = new PersonBuilder(model.getFilteredPersonList().get(0))
+ .withHidden(true).build();
+ Person hiddenPersonTwo = new PersonBuilder(model.getFilteredPersonList().get(1))
+ .withHidden(true).build();
+ model.setPerson(model.getFilteredPersonList().get(0), hiddenPersonOne);
+ model.setPerson(model.getFilteredPersonList().get(1), hiddenPersonTwo);
+
+ CommandResult commandResult = new UnhideAllCommand().execute(model);
+
+ String expectedMessage = UnhideAllCommand.MESSAGE_SUCCESS;
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ assertTrue(!model.getFilteredPersonList().get(0).getIsHidden().value
+ && !model.getFilteredPersonList().get(1).getIsHidden().value);
+ }
+
+}
diff --git a/src/test/java/seedu/address/logic/commands/UnhideCommandTest.java b/src/test/java/seedu/address/logic/commands/UnhideCommandTest.java
new file mode 100644
index 00000000000..dcb3e0ac500
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/UnhideCommandTest.java
@@ -0,0 +1,71 @@
+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.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.exceptions.CommandException;
+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;
+
+
+public class UnhideCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_personUnhidden_unhideSuccessful() throws CommandException {
+ Person hiddenPerson = new PersonBuilder().withHidden(true).build();
+ model.setPerson(model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()), hiddenPerson);
+ CommandResult commandResult = new UnhideCommand(INDEX_FIRST_PERSON).execute(model);
+
+ String expectedMessage = String.format(UnhideCommand.MESSAGE_UNHIDE_APPLICANT_SUCCESS,
+ Messages.format(hiddenPerson));
+
+ assertEquals(expectedMessage, commandResult.getFeedbackToUser());
+ assertFalse(model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased()).getIsHidden().value);
+ }
+
+ @Test
+ public void execute_indexOutOfRange_throwsCommandException() {
+ Index outOfRangeIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ UnhideCommand unhideCommand = new UnhideCommand(outOfRangeIndex);
+ assertCommandFailure(unhideCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ HideCommand hideFirstCommand = new HideCommand(INDEX_FIRST_PERSON);
+ HideCommand hideSecondCommand = new HideCommand(INDEX_SECOND_PERSON);
+
+ assertTrue(hideFirstCommand.equals(hideFirstCommand));
+
+ HideCommand hideFirstCommandCopy = new HideCommand(INDEX_FIRST_PERSON);
+ assertTrue(hideFirstCommand.equals(hideFirstCommandCopy));
+
+ assertFalse(hideFirstCommand.equals(1));
+
+ assertFalse(hideFirstCommand.equals(null));
+
+ assertFalse(hideFirstCommand.equals(hideSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ HideCommand hideCommand = new HideCommand(targetIndex);
+ String expected = HideCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, hideCommand.toString());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/commands/ViewCommandTest.java b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java
new file mode 100644
index 00000000000..39991295646
--- /dev/null
+++ b/src/test/java/seedu/address/logic/commands/ViewCommandTest.java
@@ -0,0 +1,103 @@
+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.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.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.person.Person;
+
+/**
+ * Contains integration tests (interaction with the Model) and unit tests for
+ * {@code ViewCommand}.
+ */
+public class ViewCommandTest {
+
+ private Model model = new ModelManager(getTypicalAddressBook(), new UserPrefs());
+
+ @Test
+ public void execute_validIndexUnfilteredList_success() {
+ Person personToView = model.getFilteredPersonList().get(INDEX_FIRST_PERSON.getZeroBased());
+ ViewCommand viewCommand = new ViewCommand(INDEX_FIRST_PERSON);
+
+ String expectedMessage = String.format(ViewCommand.MESSAGE_VIEW_PERSON_SUCCESS,
+ String.format(personToView.getName().toString()));
+
+ ModelManager expectedModel = new ModelManager(model.getAddressBook(), new UserPrefs());
+ expectedModel.showPersonAtIndex(INDEX_FIRST_PERSON);
+
+ assertCommandSuccess(viewCommand, model, expectedMessage, expectedModel);
+ }
+
+ @Test
+ public void execute_invalidIndexUnfilteredList_throwsCommandException() {
+ Index outOfBoundIndex = Index.fromOneBased(model.getFilteredPersonList().size() + 1);
+ ViewCommand viewCommand = new ViewCommand(outOfBoundIndex);
+
+ assertCommandFailure(viewCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void execute_invalidIndexFilteredList_throwsCommandException() {
+ showPersonAtIndex(model, INDEX_FIRST_PERSON);
+
+ Index outOfBoundIndex = INDEX_SECOND_PERSON;
+ // ensures that outOfBoundIndex is still in bounds of address book list
+ assertTrue(outOfBoundIndex.getZeroBased() < model.getAddressBook().getPersonList().size());
+
+ ViewCommand viewCommand = new ViewCommand(outOfBoundIndex);
+
+ assertCommandFailure(viewCommand, model, Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX);
+ }
+
+ @Test
+ public void equals() {
+ ViewCommand viewFirstCommand = new ViewCommand(INDEX_FIRST_PERSON);
+ ViewCommand viewSecondCommand = new ViewCommand(INDEX_SECOND_PERSON);
+
+ // same object -> returns true
+ assertTrue(viewFirstCommand.equals(viewFirstCommand));
+
+ // same values -> returns true
+ ViewCommand viewFirstCommandCopy = new ViewCommand(INDEX_FIRST_PERSON);
+ assertTrue(viewFirstCommand.equals(viewFirstCommandCopy));
+
+ // different types -> returns false
+ assertFalse(viewFirstCommand.equals(1));
+
+ // null -> returns false
+ assertFalse(viewFirstCommand.equals(null));
+
+ // different person -> returns false
+ assertFalse(viewFirstCommand.equals(viewSecondCommand));
+ }
+
+ @Test
+ public void toStringMethod() {
+ Index targetIndex = Index.fromOneBased(1);
+ ViewCommand viewCommand = new ViewCommand(targetIndex);
+ String expected = ViewCommand.class.getCanonicalName() + "{targetIndex=" + targetIndex + "}";
+ assertEquals(expected, viewCommand.toString());
+ }
+
+ /**
+ * Updates {@code model}'s filtered list to show no one.
+ */
+ private void showNoPerson(Model model) {
+ model.updateFilteredPersonList(p -> false);
+
+ assertTrue(model.getFilteredPersonList().isEmpty());
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
index 5bc11d3cdaa..4f9fba14bcb 100644
--- a/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddCommandParserTest.java
@@ -1,14 +1,19 @@
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.COMMENT_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.COMMENT_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.GPA_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.GPA_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEW_SCORE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.INTERVIEW_SCORE_DESC_BOB;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_GPA_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_STUDENT_NUMBER_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;
@@ -16,18 +21,27 @@
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.PREVIOUS_GRADE_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.PREVIOUS_GRADE_DESC_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.STUDENT_NUMBER_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.STUDENT_NUMBER_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_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_COMMENT_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_GPA_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_COMMENT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GPA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEW_SCORE;
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_PREVIOUS_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STUDENT_NUMBER;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
import static seedu.address.testutil.TypicalPersons.AMY;
@@ -37,11 +51,12 @@
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.Gpa;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.StudentNumber;
import seedu.address.model.tag.Tag;
import seedu.address.testutil.PersonBuilder;
@@ -53,22 +68,30 @@ 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));
-
+ assertParseSuccess(parser, PREAMBLE_WHITESPACE + STUDENT_NUMBER_DESC_BOB
+ + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + COMMENT_DESC_BOB
+ + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB + GPA_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,
+ STUDENT_NUMBER_DESC_BOB + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + GPA_DESC_BOB + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB + COMMENT_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;
+ String validExpectedPersonString = STUDENT_NUMBER_DESC_BOB + NAME_DESC_BOB + PHONE_DESC_BOB
+ + EMAIL_DESC_BOB + GPA_DESC_BOB + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_DESC_BOB + TAG_DESC_FRIEND;
+
+ // multiple student numbers
+ assertParseFailure(parser, STUDENT_NUMBER_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_STUDENT_NUMBER));
// multiple names
assertParseFailure(parser, NAME_DESC_AMY + validExpectedPersonString,
@@ -82,15 +105,21 @@ public void parse_repeatedNonTagValue_failure() {
assertParseFailure(parser, EMAIL_DESC_AMY + validExpectedPersonString,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_EMAIL));
- // multiple addresses
- assertParseFailure(parser, ADDRESS_DESC_AMY + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ // multiple GPAs
+ assertParseFailure(parser, GPA_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GPA));
+
+ // multiple comments
+ assertParseFailure(parser, COMMENT_DESC_AMY + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_COMMENT));
// multiple fields repeated
assertParseFailure(parser,
- validExpectedPersonString + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY + ADDRESS_DESC_AMY
+ validExpectedPersonString + PHONE_DESC_AMY + EMAIL_DESC_AMY + NAME_DESC_AMY + GPA_DESC_AMY
+ validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_NAME, PREFIX_ADDRESS, PREFIX_EMAIL, PREFIX_PHONE));
+ Messages.getErrorMessageForDuplicatePrefixes(
+ PREFIX_STUDENT_NUMBER, PREFIX_NAME, PREFIX_GPA, PREFIX_PREVIOUS_GRADE,
+ PREFIX_INTERVIEW_SCORE, PREFIX_COMMENT, PREFIX_EMAIL, PREFIX_PHONE));
// invalid value followed by valid value
@@ -107,8 +136,8 @@ public void parse_repeatedNonTagValue_failure() {
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
// invalid address
- assertParseFailure(parser, INVALID_ADDRESS_DESC + validExpectedPersonString,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ assertParseFailure(parser, INVALID_GPA_DESC + validExpectedPersonString,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GPA));
// valid value followed by invalid value
@@ -124,16 +153,18 @@ public void parse_repeatedNonTagValue_failure() {
assertParseFailure(parser, validExpectedPersonString + INVALID_PHONE_DESC,
Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
- // invalid address
- assertParseFailure(parser, validExpectedPersonString + INVALID_ADDRESS_DESC,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_ADDRESS));
+ // invalid GPAs
+ assertParseFailure(parser, validExpectedPersonString + INVALID_GPA_DESC,
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_GPA));
}
@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,
+ assertParseSuccess(parser, STUDENT_NUMBER_DESC_AMY + NAME_DESC_AMY + PHONE_DESC_AMY
+ + EMAIL_DESC_AMY + GPA_DESC_AMY + PREVIOUS_GRADE_DESC_AMY + INTERVIEW_SCORE_DESC_AMY
+ + COMMENT_DESC_AMY,
new AddCommand(expectedPerson));
}
@@ -142,55 +173,94 @@ public void parse_compulsoryFieldMissing_failure() {
String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE);
// missing name prefix
- assertParseFailure(parser, VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser,
+ VALID_NAME_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + GPA_DESC_BOB + COMMENT_DESC_BOB,
expectedMessage);
// missing phone prefix
- assertParseFailure(parser, NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser,
+ NAME_DESC_BOB + VALID_PHONE_BOB + EMAIL_DESC_BOB + GPA_DESC_BOB + COMMENT_DESC_BOB,
expectedMessage);
// missing email prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + ADDRESS_DESC_BOB,
+ assertParseFailure(parser,
+ NAME_DESC_BOB + PHONE_DESC_BOB + VALID_EMAIL_BOB + GPA_DESC_BOB + COMMENT_DESC_BOB,
+ expectedMessage);
+
+ // missing gpa prefix
+ assertParseFailure(parser,
+ NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_GPA_BOB + COMMENT_DESC_BOB,
expectedMessage);
- // missing address prefix
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + VALID_ADDRESS_BOB,
+ // missing comment prefix
+ assertParseFailure(parser,
+ NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + GPA_DESC_BOB + VALID_COMMENT_BOB,
expectedMessage);
// all prefixes missing
- assertParseFailure(parser, VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_ADDRESS_BOB,
+ assertParseFailure(parser,
+ VALID_NAME_BOB + VALID_PHONE_BOB + VALID_EMAIL_BOB + VALID_GPA_BOB + VALID_COMMENT_BOB,
expectedMessage);
}
@Test
public void parse_invalidValue_failure() {
+ // invalid student number
+ assertParseFailure(parser,
+ INVALID_STUDENT_NUMBER_DESC + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + GPA_DESC_BOB + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ StudentNumber.MESSAGE_CONSTRAINTS);
+
// invalid name
- assertParseFailure(parser, INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Name.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser,
+ STUDENT_NUMBER_DESC_BOB + INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + GPA_DESC_BOB + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_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);
+ assertParseFailure(parser,
+ STUDENT_NUMBER_DESC_BOB + NAME_DESC_BOB + INVALID_PHONE_DESC + EMAIL_DESC_BOB
+ + GPA_DESC_BOB + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ Phone.MESSAGE_CONSTRAINTS);
// invalid email
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC + ADDRESS_DESC_BOB
- + TAG_DESC_HUSBAND + TAG_DESC_FRIEND, Email.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser,
+ STUDENT_NUMBER_DESC_BOB + NAME_DESC_BOB + PHONE_DESC_BOB + INVALID_EMAIL_DESC
+ + GPA_DESC_BOB + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_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 GPA
+ assertParseFailure(parser,
+ STUDENT_NUMBER_DESC_BOB + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + INVALID_GPA_DESC + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ Gpa.MESSAGE_CONSTRAINTS);
+
+ // invalid comment (empty)
// invalid tag
- assertParseFailure(parser, NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB + ADDRESS_DESC_BOB
- + INVALID_TAG_DESC + VALID_TAG_FRIEND, Tag.MESSAGE_CONSTRAINTS);
+ assertParseFailure(parser,
+ STUDENT_NUMBER_DESC_BOB + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + GPA_DESC_BOB + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_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,
+ assertParseFailure(parser,
+ STUDENT_NUMBER_DESC_BOB + INVALID_NAME_DESC + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + INVALID_GPA_DESC + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_DESC_BOB + TAG_DESC_FRIEND,
Name.MESSAGE_CONSTRAINTS);
// non-empty preamble
- assertParseFailure(parser, PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
- + ADDRESS_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
+ assertParseFailure(parser,
+ PREAMBLE_NON_EMPTY + NAME_DESC_BOB + PHONE_DESC_BOB + EMAIL_DESC_BOB
+ + GPA_DESC_BOB + PREVIOUS_GRADE_DESC_BOB + INTERVIEW_SCORE_DESC_BOB
+ + COMMENT_DESC_BOB + TAG_DESC_HUSBAND + TAG_DESC_FRIEND,
String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddCommand.MESSAGE_USAGE));
}
}
diff --git a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
index 5a1ab3dbc0c..e81cabd1b51 100644
--- a/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/AddressBookParserTest.java
@@ -7,23 +7,31 @@
import static seedu.address.testutil.Assert.assertThrows;
import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
-import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import seedu.address.logic.commands.AddCommand;
+import seedu.address.logic.commands.AttachCommand;
+import seedu.address.logic.commands.BookmarkCommand;
import seedu.address.logic.commands.ClearCommand;
+import seedu.address.logic.commands.CommentCommand;
import seedu.address.logic.commands.DeleteCommand;
import seedu.address.logic.commands.EditCommand;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
import seedu.address.logic.commands.ExitCommand;
-import seedu.address.logic.commands.FindCommand;
import seedu.address.logic.commands.HelpCommand;
+import seedu.address.logic.commands.HideCommand;
+import seedu.address.logic.commands.ImportCommand;
import seedu.address.logic.commands.ListCommand;
+import seedu.address.logic.commands.SortCommand;
+import seedu.address.logic.commands.UnbookmarkCommand;
+import seedu.address.logic.commands.UnhideAllCommand;
+import seedu.address.logic.commands.UnhideCommand;
+import seedu.address.logic.commands.ViewCommand;
import seedu.address.logic.parser.exceptions.ParseException;
-import seedu.address.model.person.NameContainsKeywordsPredicate;
+import seedu.address.model.attachment.Attachment;
+import seedu.address.model.person.Comment;
import seedu.address.model.person.Person;
import seedu.address.testutil.EditPersonDescriptorBuilder;
import seedu.address.testutil.PersonBuilder;
@@ -68,14 +76,6 @@ public void parseCommand_exit() throws Exception {
assertTrue(parser.parseCommand(ExitCommand.COMMAND_WORD + " 3") instanceof ExitCommand);
}
- @Test
- public void parseCommand_find() throws Exception {
- List keywords = Arrays.asList("foo", "bar", "baz");
- FindCommand command = (FindCommand) parser.parseCommand(
- FindCommand.COMMAND_WORD + " " + keywords.stream().collect(Collectors.joining(" ")));
- assertEquals(new FindCommand(new NameContainsKeywordsPredicate(keywords)), command);
- }
-
@Test
public void parseCommand_help() throws Exception {
assertTrue(parser.parseCommand(HelpCommand.COMMAND_WORD) instanceof HelpCommand);
@@ -85,17 +85,86 @@ public void parseCommand_help() throws Exception {
@Test
public void parseCommand_list() throws Exception {
assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD) instanceof ListCommand);
- assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " 3") instanceof ListCommand);
+ assertTrue(parser.parseCommand(ListCommand.COMMAND_WORD + " hidden") instanceof ListCommand);
}
@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_FORMAT, HelpCommand.MESSAGE_USAGE), () -> parser.parseCommand(""));
}
@Test
public void parseCommand_unknownCommand_throwsParseException() {
assertThrows(ParseException.class, MESSAGE_UNKNOWN_COMMAND, () -> parser.parseCommand("unknownCommand"));
}
+
+ @Test
+ public void parseCommand_hide() throws Exception {
+ HideCommand command = (HideCommand) parser.parseCommand(
+ HideCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new HideCommand(INDEX_FIRST_PERSON), command);
+ }
+
+ @Test
+ public void parseCommand_unhide() throws Exception {
+ UnhideCommand command = (UnhideCommand) parser.parseCommand(
+ UnhideCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new UnhideCommand(INDEX_FIRST_PERSON), command);
+ }
+
+ @Test
+ public void parseCommand_bookmark() throws Exception {
+ BookmarkCommand command = (BookmarkCommand) parser.parseCommand(
+ BookmarkCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new BookmarkCommand(INDEX_FIRST_PERSON), command);
+ }
+
+ @Test
+ public void parseCommand_unbookmark() throws Exception {
+ UnbookmarkCommand command = (UnbookmarkCommand) parser.parseCommand(
+ UnbookmarkCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new UnbookmarkCommand(INDEX_FIRST_PERSON), command);
+ }
+
+ @Test
+ public void parseCommand_sort() throws Exception {
+ SortCommand command = (SortCommand) parser.parseCommand(
+ SortCommand.COMMAND_WORD + " name");
+ assertEquals(new SortCommand("name"), command);
+ }
+
+ @Test
+ public void parseCommand_unhideAll() throws Exception {
+ assertTrue(parser.parseCommand(UnhideAllCommand.COMMAND_WORD) instanceof UnhideAllCommand);
+ assertTrue(parser.parseCommand(UnhideAllCommand.COMMAND_WORD + " 3") instanceof UnhideAllCommand);
+ }
+
+ @Test
+ public void parseCommand_comment() throws Exception {
+ CommentCommand command = (CommentCommand) parser.parseCommand(
+ CommentCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased() + " c/Hello");
+ assertEquals(new CommentCommand(INDEX_FIRST_PERSON, new Comment("Hello")), command);
+ }
+
+ @Test
+ public void parseCommand_view() throws Exception {
+ ViewCommand command = (ViewCommand) parser.parseCommand(
+ ViewCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased());
+ assertEquals(new ViewCommand(INDEX_FIRST_PERSON), command);
+ }
+
+ @Test
+ public void parseCommand_import() throws Exception {
+ ImportCommand command = (ImportCommand) parser.parseCommand(
+ ImportCommand.COMMAND_WORD + " sample.csv");
+ assertEquals(new ImportCommand(new Attachment("sample.csv")), command);
+ }
+
+ @Test
+ public void parseCommand_attach() throws Exception {
+ AttachCommand command = (AttachCommand) parser.parseCommand(
+ AttachCommand.COMMAND_WORD + " " + INDEX_FIRST_PERSON.getOneBased() + " f/sample.pdf");
+ assertEquals(new AttachCommand(INDEX_FIRST_PERSON, List.of(new Attachment("sample.pdf"))), command);
+ }
}
diff --git a/src/test/java/seedu/address/logic/parser/BookmarkCommandParserTest.java b/src/test/java/seedu/address/logic/parser/BookmarkCommandParserTest.java
new file mode 100644
index 00000000000..2744fc28af9
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/BookmarkCommandParserTest.java
@@ -0,0 +1,26 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.BookmarkCommand;
+
+public class BookmarkCommandParserTest {
+
+ private BookmarkCommandParser parser = new BookmarkCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsBookmarkCommand() {
+ assertParseSuccess(parser, "1", new BookmarkCommand(INDEX_FIRST_PERSON));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ BookmarkCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/CompareCommandParserTest.java b/src/test/java/seedu/address/logic/parser/CompareCommandParserTest.java
new file mode 100644
index 00000000000..5911abbf995
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/CompareCommandParserTest.java
@@ -0,0 +1,33 @@
+package seedu.address.logic.parser;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.parser.exceptions.ParseException;
+
+public class CompareCommandParserTest {
+
+ private final CompareCommandParser parser = new CompareCommandParser();
+
+
+ @Test
+ public void parse_invalidFormat_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("invalidArgs"));
+ }
+
+ @Test
+ public void parse_missingIndex_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("1"));
+ }
+
+ @Test
+ public void parse_invalidIndexFormat_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("one two"));
+ }
+
+ @Test
+ public void parse_extraArgs_throwsParseException() {
+ assertThrows(ParseException.class, () -> parser.parse("1 2 3"));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
index cc7175172d4..bd12159cb5d 100644
--- a/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
+++ b/src/test/java/seedu/address/logic/parser/EditCommandParserTest.java
@@ -1,12 +1,12 @@
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.GPA_DESC_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.GPA_DESC_BOB;
import static seedu.address.logic.commands.CommandTestUtil.INVALID_EMAIL_DESC;
+import static seedu.address.logic.commands.CommandTestUtil.INVALID_GPA_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;
@@ -15,15 +15,15 @@
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_GPA_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_GPA;
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;
@@ -38,8 +38,8 @@
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.Gpa;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
import seedu.address.model.tag.Tag;
@@ -78,7 +78,7 @@ public void parse_invalidPreamble_failure() {
assertParseFailure(parser, "1 some random string", MESSAGE_INVALID_FORMAT);
// invalid prefix being parsed as preamble
- assertParseFailure(parser, "1 i/ string", MESSAGE_INVALID_FORMAT);
+ assertParseFailure(parser, "1 z/ string", MESSAGE_INVALID_FORMAT);
}
@Test
@@ -86,7 +86,7 @@ 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_GPA_DESC, Gpa.MESSAGE_CONSTRAINTS); // invalid address
assertParseFailure(parser, "1" + INVALID_TAG_DESC, Tag.MESSAGE_CONSTRAINTS); // invalid tag
// invalid phone followed by valid email
@@ -99,7 +99,7 @@ public void parse_invalidValue_failure() {
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,
+ assertParseFailure(parser, "1" + INVALID_NAME_DESC + INVALID_EMAIL_DESC + VALID_GPA_AMY + VALID_PHONE_AMY,
Name.MESSAGE_CONSTRAINTS);
}
@@ -107,10 +107,10 @@ public void parse_invalidValue_failure() {
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;
+ + EMAIL_DESC_AMY + GPA_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)
+ .withPhone(VALID_PHONE_BOB).withEmail(VALID_EMAIL_AMY).withGpa(VALID_GPA_AMY)
.withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).build();
EditCommand expectedCommand = new EditCommand(targetIndex, descriptor);
@@ -151,8 +151,8 @@ public void parse_oneFieldSpecified_success() {
assertParseSuccess(parser, userInput, expectedCommand);
// address
- userInput = targetIndex.getOneBased() + ADDRESS_DESC_AMY;
- descriptor = new EditPersonDescriptorBuilder().withAddress(VALID_ADDRESS_AMY).build();
+ userInput = targetIndex.getOneBased() + GPA_DESC_AMY;
+ descriptor = new EditPersonDescriptorBuilder().withGpa(VALID_GPA_AMY).build();
expectedCommand = new EditCommand(targetIndex, descriptor);
assertParseSuccess(parser, userInput, expectedCommand);
@@ -180,19 +180,19 @@ public void parse_multipleRepeatedFields_failure() {
assertParseFailure(parser, userInput, Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE));
// mulltiple valid fields repeated
- userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY
- + TAG_DESC_FRIEND + PHONE_DESC_AMY + ADDRESS_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND
- + PHONE_DESC_BOB + ADDRESS_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND;
+ userInput = targetIndex.getOneBased() + PHONE_DESC_AMY + GPA_DESC_AMY + EMAIL_DESC_AMY
+ + TAG_DESC_FRIEND + PHONE_DESC_AMY + GPA_DESC_AMY + EMAIL_DESC_AMY + TAG_DESC_FRIEND
+ + PHONE_DESC_BOB + GPA_DESC_BOB + EMAIL_DESC_BOB + TAG_DESC_HUSBAND;
assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_GPA));
// multiple invalid values
- userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC
- + INVALID_PHONE_DESC + INVALID_ADDRESS_DESC + INVALID_EMAIL_DESC;
+ userInput = targetIndex.getOneBased() + INVALID_PHONE_DESC + INVALID_GPA_DESC + INVALID_EMAIL_DESC
+ + INVALID_PHONE_DESC + INVALID_GPA_DESC + INVALID_EMAIL_DESC;
assertParseFailure(parser, userInput,
- Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_ADDRESS));
+ Messages.getErrorMessageForDuplicatePrefixes(PREFIX_PHONE, PREFIX_EMAIL, PREFIX_GPA));
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/HideCommandParserTest.java b/src/test/java/seedu/address/logic/parser/HideCommandParserTest.java
new file mode 100644
index 00000000000..c0bb1798b76
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/HideCommandParserTest.java
@@ -0,0 +1,25 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.HideCommand;
+
+public class HideCommandParserTest {
+
+ private HideCommandParser parser = new HideCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsHideCommand() {
+ assertParseSuccess(parser, "1", new HideCommand(INDEX_FIRST_PERSON));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, HideCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java
new file mode 100644
index 00000000000..c0de9e0becf
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ListCommandParserTest.java
@@ -0,0 +1,25 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.ListCommand;
+
+public class ListCommandParserTest {
+ private ListCommandParser parser = new ListCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsListCommand() {
+ assertParseSuccess(parser, "", new ListCommand());
+ assertParseSuccess(parser, "hidden", new ListCommand("hidden"));
+ assertParseSuccess(parser, "bookmarked", new ListCommand("bookmarked"));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
index 4256788b1a7..86177f142c9 100644
--- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
+++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
@@ -14,8 +14,8 @@
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.Gpa;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
import seedu.address.model.tag.Tag;
@@ -23,13 +23,13 @@
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_GPA = " ";
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 double VALID_GPA = 5.0;
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";
@@ -104,25 +104,25 @@ public void parsePhone_validValueWithWhitespace_returnsTrimmedPhone() throws Exc
@Test
public void parseAddress_null_throwsNullPointerException() {
- assertThrows(NullPointerException.class, () -> ParserUtil.parseAddress((String) null));
+ assertThrows(NullPointerException.class, () -> ParserUtil.parseGpa((String) null));
}
@Test
- public void parseAddress_invalidValue_throwsParseException() {
- assertThrows(ParseException.class, () -> ParserUtil.parseAddress(INVALID_ADDRESS));
+ public void parseGpa_invalidValue_throwsParseException() {
+ assertThrows(ParseException.class, () -> ParserUtil.parseGpa(INVALID_GPA));
}
@Test
public void parseAddress_validValueWithoutWhitespace_returnsAddress() throws Exception {
- Address expectedAddress = new Address(VALID_ADDRESS);
- assertEquals(expectedAddress, ParserUtil.parseAddress(VALID_ADDRESS));
+ Gpa expectedGpa = new Gpa(VALID_GPA);
+ assertEquals(expectedGpa, ParserUtil.parseGpa(Double.toString(VALID_GPA)));
}
@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));
+ String addressWithWhitespace = WHITESPACE + VALID_GPA + WHITESPACE;
+ Gpa expectedAddress = new Gpa(VALID_GPA);
+ assertEquals(expectedAddress, ParserUtil.parseGpa(addressWithWhitespace));
}
@Test
diff --git a/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java b/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java
new file mode 100644
index 00000000000..9f1a8ca3a77
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/SortCommandParserTest.java
@@ -0,0 +1,24 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalFieldNames.FIELDNAME_GPA;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.SortCommand;
+
+public class SortCommandParserTest {
+ private SortCommandParser parser = new SortCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsSortCommand() {
+ assertParseSuccess(parser, "gpa", new SortCommand(FIELDNAME_GPA));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, SortCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/UnbookmarkCommandParserTest.java b/src/test/java/seedu/address/logic/parser/UnbookmarkCommandParserTest.java
new file mode 100644
index 00000000000..193be8b7af7
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/UnbookmarkCommandParserTest.java
@@ -0,0 +1,26 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.UnbookmarkCommand;
+
+public class UnbookmarkCommandParserTest {
+
+ private UnbookmarkCommandParser parser = new UnbookmarkCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsUnbookmarkCommand() {
+ assertParseSuccess(parser, "1", new UnbookmarkCommand(INDEX_FIRST_PERSON));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ UnbookmarkCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/UnhideCommandParserTest.java b/src/test/java/seedu/address/logic/parser/UnhideCommandParserTest.java
new file mode 100644
index 00000000000..06e554e7cb5
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/UnhideCommandParserTest.java
@@ -0,0 +1,25 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.UnhideCommand;
+
+public class UnhideCommandParserTest {
+
+ private UnhideCommandParser parser = new UnhideCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsUnhideCommand() {
+ assertParseSuccess(parser, "1", new UnhideCommand(INDEX_FIRST_PERSON));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT, UnhideCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java
new file mode 100644
index 00000000000..a85d8ba0948
--- /dev/null
+++ b/src/test/java/seedu/address/logic/parser/ViewCommandParserTest.java
@@ -0,0 +1,26 @@
+package seedu.address.logic.parser;
+
+import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseFailure;
+import static seedu.address.logic.parser.CommandParserTestUtil.assertParseSuccess;
+import static seedu.address.testutil.TypicalIndexes.INDEX_FIRST_PERSON;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.logic.commands.ViewCommand;
+
+public class ViewCommandParserTest {
+
+ private ViewCommandParser parser = new ViewCommandParser();
+
+ @Test
+ public void parse_validArgs_returnsViewCommand() {
+ assertParseSuccess(parser, "1", new ViewCommand(INDEX_FIRST_PERSON));
+ }
+
+ @Test
+ public void parse_invalidArgs_throwsParseException() {
+ assertParseFailure(parser, "a", String.format(MESSAGE_INVALID_COMMAND_FORMAT,
+ ViewCommand.MESSAGE_USAGE));
+ }
+}
diff --git a/src/test/java/seedu/address/model/AddressBookTest.java b/src/test/java/seedu/address/model/AddressBookTest.java
index 68c8c5ba4d5..d9747392923 100644
--- a/src/test/java/seedu/address/model/AddressBookTest.java
+++ b/src/test/java/seedu/address/model/AddressBookTest.java
@@ -3,7 +3,7 @@
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_GPA_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;
@@ -46,7 +46,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).withGpa(VALID_GPA_BOB).withTags(VALID_TAG_HUSBAND)
.build();
List newPersons = Arrays.asList(ALICE, editedAlice);
AddressBookStub newData = new AddressBookStub(newPersons);
@@ -73,7 +73,7 @@ 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).withGpa(VALID_GPA_BOB).withTags(VALID_TAG_HUSBAND)
.build();
assertTrue(addressBook.hasPerson(editedAlice));
}
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/AttachmentTest.java b/src/test/java/seedu/address/model/person/AttachmentTest.java
new file mode 100644
index 00000000000..91ffc4de466
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/AttachmentTest.java
@@ -0,0 +1,28 @@
+package seedu.address.model.person;
+
+import static seedu.address.testutil.Assert.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.model.attachment.Attachment;
+
+public class AttachmentTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new Attachment(null));
+ }
+
+ @Test
+ public void constructor_invalidPathString_throwsIllegalArgumentException() {
+ String invalidPath = "[E:\\Temp\0\\564\\324\\123.txt]";
+ assertThrows(IllegalArgumentException.class, () -> new Attachment(invalidPath));
+ }
+
+ @Test
+ public void isValidPathString() {
+ // null path string
+ assertThrows(NullPointerException.class, () -> Attachment.isValidAttachment(null));
+ }
+
+}
diff --git a/src/test/java/seedu/address/model/person/GpaTest.java b/src/test/java/seedu/address/model/person/GpaTest.java
new file mode 100644
index 00000000000..5078dbe8ce6
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/GpaTest.java
@@ -0,0 +1,62 @@
+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 org.junit.jupiter.api.Test;
+
+public class GpaTest {
+
+ @Test
+ public void isValidGpa() {
+ // invalid GPAs
+ assertFalse(Gpa.isValidGpa(-1.0));
+ assertFalse(Gpa.isValidGpa(5.1));
+
+ // valid GPAs
+ assertTrue(Gpa.isValidGpa(0.0));
+ assertTrue(Gpa.isValidGpa(4.0));
+ assertTrue(Gpa.isValidGpa(5.0));
+ }
+
+ @Test
+ public void compareTo() {
+ Gpa gpa1 = new Gpa(3.2);
+ Gpa gpa2 = new Gpa(3.2);
+ Gpa gpa3 = new Gpa(3.3);
+ Gpa gpa4 = new Gpa(3.1);
+
+ // gpa1 and gpa2 have the same values, so should return 0
+ assertEquals(0, gpa1.compareTo(gpa2));
+
+ // gpa2 is equal to gpa1, so should return 0
+ assertEquals(0, gpa2.compareTo(gpa1));
+
+ // gpa1 is less than gpa3, so should return a negative value
+ assertTrue(gpa1.compareTo(gpa3) < 0);
+
+ // gpa1 is greater than gpa4, so should return a positive value
+ assertTrue(gpa1.compareTo(gpa4) > 0);
+
+ }
+ @Test
+ public void equals() {
+ Gpa gpa = new Gpa(3.2);
+
+ // same values -> returns true
+ assertTrue(gpa.equals(new Gpa(3.2)));
+
+ // same object -> returns true
+ assertTrue(gpa.equals(gpa));
+
+ // null -> returns false
+ assertFalse(gpa.equals(null));
+
+ // different types -> returns false
+ assertFalse(gpa.equals("Test"));
+
+ // different values -> returns false
+ assertFalse(gpa.equals(new Gpa(3.3)));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/InterviewScoreTest.java b/src/test/java/seedu/address/model/person/InterviewScoreTest.java
new file mode 100644
index 00000000000..481cf82861b
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/InterviewScoreTest.java
@@ -0,0 +1,62 @@
+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 org.junit.jupiter.api.Test;
+
+public class InterviewScoreTest {
+
+ @Test
+ public void isValidInterviewScore() {
+ // invalid InterviewScores
+ assertFalse(InterviewScore.isValidInterviewScore(-1.0));
+ assertFalse(InterviewScore.isValidInterviewScore(10.1));
+
+ // valid InterviewScores
+ assertTrue(InterviewScore.isValidInterviewScore(0.0));
+ assertTrue(InterviewScore.isValidInterviewScore(4.0));
+ assertTrue(InterviewScore.isValidInterviewScore(5.0));
+ }
+
+ @Test
+ public void compareTo() {
+ InterviewScore score1 = new InterviewScore(3.2);
+ InterviewScore score2 = new InterviewScore(3.2);
+ InterviewScore score3 = new InterviewScore(3.3);
+ InterviewScore score4 = new InterviewScore(3.1);
+
+ // score1 and score2 have the same values, so should return 0
+ assertEquals(0, score1.compareTo(score2));
+
+ // score2 is equal to score1, so should return 0
+ assertEquals(0, score2.compareTo(score1));
+
+ // score1 is less than score3, so should return a negative value
+ assertTrue(score1.compareTo(score3) < 0);
+
+ // score1 is greater than score4, so should return a positive value
+ assertTrue(score1.compareTo(score4) > 0);
+
+ }
+ @Test
+ public void equals() {
+ InterviewScore interviewScore = new InterviewScore(3.2);
+
+ // same values -> returns true
+ assertTrue(interviewScore.equals(new InterviewScore(3.2)));
+
+ // same object -> returns true
+ assertTrue(interviewScore.equals(interviewScore));
+
+ // null -> returns false
+ assertFalse(interviewScore.equals(null));
+
+ // different types -> returns false
+ assertFalse(interviewScore.equals("Test"));
+
+ // different values -> returns false
+ assertFalse(interviewScore.equals(new InterviewScore(3.3)));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/IsBookmarkedTest.java b/src/test/java/seedu/address/model/person/IsBookmarkedTest.java
new file mode 100644
index 00000000000..a05761852dd
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/IsBookmarkedTest.java
@@ -0,0 +1,29 @@
+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 IsBookmarkedTest {
+
+ @Test
+ public void equals() {
+ IsBookmarked isBookmarked = new IsBookmarked(true);
+
+ // same values -> returns true
+ assertTrue(isBookmarked.equals(new IsBookmarked(true)));
+
+ // same object -> returns true
+ assertTrue(isBookmarked.equals(isBookmarked));
+
+ // null -> returns false
+ assertFalse(isBookmarked.equals(null));
+
+ // different types -> returns false
+ assertFalse(isBookmarked.equals("Test"));
+
+ // different values -> returns false
+ assertFalse(isBookmarked.equals(new IsBookmarked(false)));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/IsHiddenTest.java b/src/test/java/seedu/address/model/person/IsHiddenTest.java
new file mode 100644
index 00000000000..2a2a2c2dc95
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/IsHiddenTest.java
@@ -0,0 +1,19 @@
+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 IsHiddenTest {
+
+ @Test
+ public void equals() {
+ IsHidden isHidden = new IsHidden(true);
+ assertTrue(isHidden.equals(new IsHidden(true)));
+ assertTrue(isHidden.equals(isHidden));
+ assertFalse(isHidden.equals(null));
+ assertFalse(isHidden.equals("Test"));
+ assertFalse(isHidden.equals(new IsHidden(false)));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/ListPredicateTest.java b/src/test/java/seedu/address/model/person/ListPredicateTest.java
new file mode 100644
index 00000000000..d61039f3bac
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/ListPredicateTest.java
@@ -0,0 +1,43 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static seedu.address.logic.parser.CliSyntax.FIELD_BOOKMARKED;
+import static seedu.address.logic.parser.CliSyntax.FIELD_HIDDEN;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+public class ListPredicateTest {
+
+ @Test
+ public void test_filterHidden_returnsTrue() {
+ ListPredicate listPredicate = new ListPredicate(FIELD_HIDDEN, true);
+ assertTrue(listPredicate.test(new PersonBuilder().withHidden(true).build()));
+ }
+
+ @Test
+ public void test_filterHidden_returnsFalse() {
+ ListPredicate listPredicate = new ListPredicate(FIELD_HIDDEN, true);
+ assertFalse(listPredicate.test(new PersonBuilder().withHidden(false).build()));
+ }
+
+ @Test
+ public void test_filterBookmarked_returnsTrue() {
+ ListPredicate listPredicate = new ListPredicate(FIELD_BOOKMARKED, true);
+ assertTrue(listPredicate.test(new PersonBuilder().withBookmark(true).build()));
+ }
+
+ @Test
+ public void test_filterBookmarked_returnsFalse() {
+ ListPredicate listPredicate = new ListPredicate(FIELD_BOOKMARKED, true);
+ assertFalse(listPredicate.test(new PersonBuilder().withBookmark(false).build()));
+ }
+
+ @Test
+ public void test_filterUndefined_returnsFalse() {
+ ListPredicate listPredicate = new ListPredicate("undefined", true);
+ assertFalse(listPredicate.test(new PersonBuilder().withBookmark(false).build()));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
index 6b3fd90ade7..f4a797abb66 100644
--- a/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
+++ b/src/test/java/seedu/address/model/person/NameContainsKeywordsPredicateTest.java
@@ -71,7 +71,7 @@ public void test_nameDoesNotContainKeywords_returnsFalse() {
// Keywords match phone, email and address, but does not match name
predicate = new NameContainsKeywordsPredicate(Arrays.asList("12345", "alice@email.com", "Main", "Street"));
assertFalse(predicate.test(new PersonBuilder().withName("Alice").withPhone("12345")
- .withEmail("alice@email.com").withAddress("Main Street").build()));
+ .withEmail("alice@email.com").withGpa(5.0).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..d4d8dc14a9c 100644
--- a/src/test/java/seedu/address/model/person/PersonTest.java
+++ b/src/test/java/seedu/address/model/person/PersonTest.java
@@ -3,10 +3,12 @@
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_COMMENT_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_EMAIL_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_GPA_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_STUDENT_NUMBER_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;
@@ -32,23 +34,14 @@ 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();
+ // same student number, all other attributes different -> returns true
+ Person editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB)
+ .withEmail(VALID_EMAIL_BOB).withGpa(VALID_GPA_BOB).withTags(VALID_TAG_HUSBAND).build();
assertTrue(ALICE.isSamePerson(editedAlice));
- // different name, all other attributes same -> returns false
- editedAlice = new PersonBuilder(ALICE).withName(VALID_NAME_BOB).build();
+ // different student number, all other attributes same -> returns false
+ editedAlice = new PersonBuilder(ALICE).withStudentNumber(VALID_STUDENT_NUMBER_BOB).build();
assertFalse(ALICE.isSamePerson(editedAlice));
-
- // name differs in case, all other attributes same -> returns false
- Person editedBob = new PersonBuilder(BOB).withName(VALID_NAME_BOB.toLowerCase()).build();
- assertFalse(BOB.isSamePerson(editedBob));
-
- // name has trailing spaces, all other attributes same -> returns false
- String nameWithTrailingSpaces = VALID_NAME_BOB + " ";
- editedBob = new PersonBuilder(BOB).withName(nameWithTrailingSpaces).build();
- assertFalse(BOB.isSamePerson(editedBob));
}
@Test
@@ -81,8 +74,12 @@ 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();
+ // different gpa -> returns false
+ editedAlice = new PersonBuilder(ALICE).withGpa(VALID_GPA_BOB).build();
+ assertFalse(ALICE.equals(editedAlice));
+
+ // different comment -> returns false
+ editedAlice = new PersonBuilder(ALICE).withComment(VALID_COMMENT_BOB).build();
assertFalse(ALICE.equals(editedAlice));
// different tags -> returns false
@@ -92,8 +89,14 @@ public void equals() {
@Test
public void toStringMethod() {
- String expected = Person.class.getCanonicalName() + "{name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
- + ", email=" + ALICE.getEmail() + ", address=" + ALICE.getAddress() + ", tags=" + ALICE.getTags() + "}";
+ String expected = Person.class.getCanonicalName() + "{studentNo=" + ALICE.getStudentNumber()
+ + ", name=" + ALICE.getName() + ", phone=" + ALICE.getPhone()
+ + ", email=" + ALICE.getEmail() + ", gpa=" + ALICE.getGpa()
+ + ", previousGrade=" + ALICE.getPreviousGrade() + ", interviewScore=" + ALICE.getInterviewScore()
+ + ", comment=" + ALICE.getComment() + ", tags=" + ALICE.getTags()
+ + ", attachments=" + ALICE.getAttachments() + ", isHidden=" + ALICE.getIsHidden()
+ + ", isBookmarked=" + ALICE.getIsBookmarked() + "}";
+
assertEquals(expected, ALICE.toString());
}
}
diff --git a/src/test/java/seedu/address/model/person/PreviousGradeTest.java b/src/test/java/seedu/address/model/person/PreviousGradeTest.java
new file mode 100644
index 00000000000..ec0a1467133
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/PreviousGradeTest.java
@@ -0,0 +1,86 @@
+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 org.junit.jupiter.api.Test;
+
+public class PreviousGradeTest {
+
+ @Test
+ public void isValidPreviousGrade() {
+ // invalid PreviousGrades
+ assertFalse(PreviousGrade.isValidGrade("D-"));
+ assertFalse(PreviousGrade.isValidGrade("lol"));
+
+ // valid PreviousGrades
+ assertTrue(PreviousGrade.isValidGrade("A+"));
+ assertTrue(PreviousGrade.isValidGrade("B"));
+ assertTrue(PreviousGrade.isValidGrade("C"));
+ }
+
+ @Test
+ public void compareTo() {
+ PreviousGrade score1 = new PreviousGrade("A");
+ PreviousGrade score2 = new PreviousGrade("A");
+ PreviousGrade score3 = new PreviousGrade("D");
+ PreviousGrade score4 = new PreviousGrade("A+");
+
+ // score1 and score2 have the same values, so should return 0
+ assertEquals(0, score1.compareTo(score2));
+
+ // score2 is equal to score1, so should return 0
+ assertEquals(0, score2.compareTo(score1));
+
+ // score1 is less than score3, so should return a negative value
+ assertTrue(score1.compareTo(score3) < 0);
+
+ // score1 is greater than score4, so should return a positive value
+ assertTrue(score1.compareTo(score4) > 0);
+
+ }
+ @Test
+ public void equals() {
+ PreviousGrade previousGrade = new PreviousGrade("B-");
+
+ // same values -> returns true
+ assertTrue(previousGrade.equals(new PreviousGrade("B-")));
+
+ // same object -> returns true
+ assertTrue(previousGrade.equals(previousGrade));
+
+ // null -> returns false
+ assertFalse(previousGrade.equals(null));
+
+ // different types -> returns false
+ assertFalse(previousGrade.equals("Test"));
+
+ // different values -> returns false
+ assertFalse(previousGrade.equals(new PreviousGrade("B")));
+ }
+
+ @Test
+ public void testToString() {
+ PreviousGrade score1 = new PreviousGrade("A");
+ PreviousGrade score2 = new PreviousGrade("B+");
+ PreviousGrade score3 = new PreviousGrade("B");
+ PreviousGrade score4 = new PreviousGrade("B-");
+ PreviousGrade score5 = new PreviousGrade("C+");
+ PreviousGrade score6 = new PreviousGrade("C");
+ PreviousGrade score7 = new PreviousGrade("D+");
+ PreviousGrade score8 = new PreviousGrade("D");
+ PreviousGrade score9 = new PreviousGrade("F");
+
+ assertEquals(score1.toString(), "A");
+ assertEquals(score2.toString(), "B+");
+ assertEquals(score3.toString(), "B");
+ assertEquals(score4.toString(), "B-");
+ assertEquals(score5.toString(), "C+");
+ assertEquals(score6.toString(), "C");
+ assertEquals(score7.toString(), "D+");
+ assertEquals(score8.toString(), "D");
+ assertEquals(score9.toString(), "F");
+
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/SortComparatorTest.java b/src/test/java/seedu/address/model/person/SortComparatorTest.java
new file mode 100644
index 00000000000..43955801a7f
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/SortComparatorTest.java
@@ -0,0 +1,181 @@
+package seedu.address.model.person;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import seedu.address.testutil.PersonBuilder;
+
+public class SortComparatorTest {
+
+ @Test
+ public void compare_nameIsSame_returnsZero() {
+ SortComparator comparator = new SortComparator("name");
+ Person person1 = new PersonBuilder().withName("Alice").build();
+ Person person2 = new PersonBuilder().withName("Alice").build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_nameIsDifferent_returnsNonZero() {
+ SortComparator comparator = new SortComparator("name");
+ Person person1 = new PersonBuilder().withName("Alice").build();
+ Person person2 = new PersonBuilder().withName("Bob").build();
+ assertEquals(-1, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_studentNoIsSame_returnsZero() {
+ SortComparator comparator = new SortComparator("studentNo");
+ Person person1 = new PersonBuilder().withStudentNumber("A1234567X").build();
+ Person person2 = new PersonBuilder().withStudentNumber("A1234567X").build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_studentNoIsDifferent_returnsNonZero() {
+ SortComparator comparator = new SortComparator("studentNo");
+ Person person1 = new PersonBuilder().withStudentNumber("A1234567X").build();
+ Person person2 = new PersonBuilder().withStudentNumber("A1234567Y").build();
+ assertEquals(-1, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_emailIsSame_returnsZero() {
+ SortComparator comparator = new SortComparator("email");
+ Person person1 = new PersonBuilder().withEmail("alice@example.com").build();
+ Person person2 = new PersonBuilder().withEmail("alice@example.com").build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_emailIsDifferent_returnsNonZero() {
+ SortComparator comparator = new SortComparator("email");
+ Person person1 = new PersonBuilder().withEmail("alice@example.com").build();
+ Person person2 = new PersonBuilder().withEmail("bob@example.com").build();
+ assertEquals(-1, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_phoneIsSame_returnsZero() {
+ SortComparator comparator = new SortComparator("phone");
+ Person person1 = new PersonBuilder().withPhone("12345678").build();
+ Person person2 = new PersonBuilder().withPhone("12345678").build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_phoneIsDifferent_returnsNonZero() {
+ SortComparator comparator = new SortComparator("phone");
+ Person person1 = new PersonBuilder().withPhone("12345678").build();
+ Person person2 = new PersonBuilder().withPhone("87654321").build();
+ assertEquals(-7, comparator.compare(person1, person2));
+ }
+ @Test
+ public void compare_commentsAreEmpty_returnsZero() {
+ SortComparator comparator = new SortComparator("comment");
+ Person person1 = new PersonBuilder().build();
+ Person person2 = new PersonBuilder().build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+ @Test
+ public void compare_firstCommentIsEmpty_returnsNonZero() {
+ SortComparator comparator = new SortComparator("comment");
+ Person person1 = new PersonBuilder().build();
+ Person person2 = new PersonBuilder().withComment("hello").build();
+ assertEquals(1, comparator.compare(person1, person2));
+ }
+ @Test
+ public void compare_secondCommentIsEmpty_returnsNonZero() {
+ SortComparator comparator = new SortComparator("comment");
+ Person person1 = new PersonBuilder().withComment("hello").build();
+ Person person2 = new PersonBuilder().build();
+ assertEquals(-1, comparator.compare(person1, person2));
+ }
+ @Test
+ public void compare_commentIsSame_returnsZero() {
+ SortComparator comparator = new SortComparator("comment");
+ Person person1 = new PersonBuilder().withComment("hello").build();
+ Person person2 = new PersonBuilder().withComment("hello").build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_tagsIsSame_returnsZero() {
+ SortComparator comparator = new SortComparator("tags");
+ Person person1 = new PersonBuilder().withTags("friends").build();
+ Person person2 = new PersonBuilder().withTags("owesMoney").build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_tagsIsDifferent_returnsNonZero() {
+ SortComparator comparator = new SortComparator("tags");
+ Person person1 = new PersonBuilder().withTags("friends", "owesMoney").build();
+ Person person2 = new PersonBuilder().withTags("friends").build();
+ assertEquals(-1, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_interviewScoresAreEmpty_returnsZero() {
+ SortComparator comparator = new SortComparator("interviewScore");
+ Person person1 = new PersonBuilder().build();
+ Person person2 = new PersonBuilder().build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_firstInterviewScoreIsEmpty_returnsNonZero() {
+ SortComparator comparator = new SortComparator("interviewScore");
+ Person person1 = new PersonBuilder().build();
+ Person person2 = new PersonBuilder().withInterviewScore(1).build();
+ assertEquals(1, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_secondInterviewScoreIsEmpty_returnsNonZero() {
+ SortComparator comparator = new SortComparator("interviewScore");
+ Person person1 = new PersonBuilder().withInterviewScore(1).build();
+ Person person2 = new PersonBuilder().build();
+ assertEquals(-1, comparator.compare(person1, person2));
+ }
+ @Test
+ public void compare_interviewScoreIsSame_returnsZero() {
+ SortComparator comparator = new SortComparator("interviewScore");
+ Person person1 = new PersonBuilder().withInterviewScore(1).build();
+ Person person2 = new PersonBuilder().withInterviewScore(1).build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+ @Test
+ public void compare_interviewScoreIsDifferent_returnsNonZero() {
+ SortComparator comparator = new SortComparator("interviewScore");
+ Person person1 = new PersonBuilder().withInterviewScore(1).build();
+ Person person2 = new PersonBuilder().withInterviewScore(2).build();
+ assertEquals(1, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_previousGradeIsSame_returnsZero() {
+ SortComparator comparator = new SortComparator("previousGrade");
+ Person person1 = new PersonBuilder().withPreviousGrade("A").build();
+ Person person2 = new PersonBuilder().withPreviousGrade("A").build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_previousGradeIsDifferent_returnsNonZero() {
+ SortComparator comparator = new SortComparator("previousGrade");
+ Person person1 = new PersonBuilder().withPreviousGrade("A").build();
+ Person person2 = new PersonBuilder().withPreviousGrade("B+").build();
+ assertEquals(-2, comparator.compare(person1, person2));
+ }
+
+ @Test
+ public void compare_undefinedField_returnsZero() {
+ SortComparator comparator = new SortComparator("undefined");
+ Person person1 = new PersonBuilder().build();
+ Person person2 = new PersonBuilder().build();
+ assertEquals(0, comparator.compare(person1, person2));
+ }
+
+}
diff --git a/src/test/java/seedu/address/model/person/StudentNumberTest.java b/src/test/java/seedu/address/model/person/StudentNumberTest.java
new file mode 100644
index 00000000000..ad6039a504b
--- /dev/null
+++ b/src/test/java/seedu/address/model/person/StudentNumberTest.java
@@ -0,0 +1,60 @@
+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 StudentNumberTest {
+
+ @Test
+ public void constructor_null_throwsNullPointerException() {
+ assertThrows(NullPointerException.class, () -> new StudentNumber(null));
+ }
+
+ @Test
+ public void constructor_invalidStudentNumber_throwsIllegalArgumentException() {
+ String invalidStudentNumber = "";
+ assertThrows(IllegalArgumentException.class, () -> new StudentNumber(invalidStudentNumber));
+ }
+
+ @Test
+ public void isValidStudentNumber() {
+ // null student number
+ assertThrows(NullPointerException.class, () -> StudentNumber.isValidStudentNumber(null));
+
+ // invalid student numbers
+ assertFalse(StudentNumber.isValidStudentNumber("")); // empty string
+ assertFalse(StudentNumber.isValidStudentNumber(" ")); // spaces only
+ assertFalse(StudentNumber.isValidStudentNumber("A0324A"));
+ assertFalse(StudentNumber.isValidStudentNumber("studentNo"));
+ assertFalse(StudentNumber.isValidStudentNumber("3284933")); // only digits
+ assertFalse(StudentNumber.isValidStudentNumber("A838 3877C")); // spaces within
+
+ // valid student numbers
+ assertTrue(StudentNumber.isValidStudentNumber("A0837473D"));
+ assertTrue(StudentNumber.isValidStudentNumber("A1111111G"));
+ assertTrue(StudentNumber.isValidStudentNumber("A8478373Z"));
+ }
+
+ @Test
+ public void equals() {
+ StudentNumber studentNo = new StudentNumber("A1111111G");
+
+ // same values -> returns true
+ assertTrue(studentNo.equals(new StudentNumber("A1111111G")));
+
+ // same object -> returns true
+ assertTrue(studentNo.equals(studentNo));
+
+ // null -> returns false
+ assertFalse(studentNo.equals(null));
+
+ // different types -> returns false
+ assertFalse(studentNo.equals(5.0f));
+
+ // different values -> returns false
+ assertFalse(studentNo.equals(new StudentNumber("A1111112G")));
+ }
+}
diff --git a/src/test/java/seedu/address/model/person/UniquePersonListTest.java b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
index 17ae501df08..bb6902c3e95 100644
--- a/src/test/java/seedu/address/model/person/UniquePersonListTest.java
+++ b/src/test/java/seedu/address/model/person/UniquePersonListTest.java
@@ -3,7 +3,7 @@
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_GPA_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 +42,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).withGpa(VALID_GPA_BOB).withTags(VALID_TAG_HUSBAND)
.build();
assertTrue(uniquePersonList.contains(editedAlice));
}
@@ -85,7 +85,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).withGpa(VALID_GPA_BOB).withTags(VALID_TAG_HUSBAND)
.build();
uniquePersonList.setPerson(ALICE, editedAlice);
UniquePersonList expectedUniquePersonList = new UniquePersonList();
diff --git a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
index 83b11331cdb..cab6c4f427f 100644
--- a/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
+++ b/src/test/java/seedu/address/storage/JsonAdaptedPersonTest.java
@@ -12,25 +12,36 @@
import org.junit.jupiter.api.Test;
import seedu.address.commons.exceptions.IllegalValueException;
-import seedu.address.model.person.Address;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
import seedu.address.model.person.Name;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.StudentNumber;
public class JsonAdaptedPersonTest {
+ private static final String INVALID_STUDENT_NUMBER = "A023C";
private static final String INVALID_NAME = "R@chel";
private static final String INVALID_PHONE = "+651234";
- private static final String INVALID_ADDRESS = " ";
+ private static final double INVALID_GPA = -1.0;
private static final String INVALID_EMAIL = "example.com";
private static final String INVALID_TAG = "#friend";
+ private static final String VALID_STUDENT_NUMBER = BENSON.getStudentNumber().toString();
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 double VALID_GPA = BENSON.getGpa().value;
+ private static final String VALID_PREVIOUS_GRADE = BENSON.getPreviousGrade().toString();
+ private static final double VALID_INTERVIEW_SCORE = BENSON.getInterviewScore().get().value;
+ private static final String VALID_COMMENT = BENSON.getComment().get().comment;
private static final List VALID_TAGS = BENSON.getTags().stream()
.map(JsonAdaptedTag::new)
.collect(Collectors.toList());
+ private static final boolean VALID_IS_HIDDEN = BENSON.getIsHidden().value;
+ private static final List VALID_ATTACHMENTS = BENSON.getAttachments().stream()
+ .map(JsonAdaptedAttachment::new)
+ .collect(Collectors.toList());
+ private static final boolean VALID_IS_BOOKMARKED = BENSON.getIsBookmarked().value;
@Test
public void toModelType_validPersonDetails_returnsPerson() throws Exception {
@@ -38,63 +49,204 @@ public void toModelType_validPersonDetails_returnsPerson() throws Exception {
assertEquals(BENSON, person.toModelType());
}
+ @Test
+ public void toModelType_invalidStudentNumber_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(
+ INVALID_STUDENT_NUMBER,
+ VALID_NAME,
+ VALID_PHONE,
+ VALID_EMAIL,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
+ String expectedMessage = StudentNumber.MESSAGE_CONSTRAINTS;
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+ @Test
+ public void toModelType_nullStudentNumber_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(
+ null,
+ VALID_NAME,
+ VALID_PHONE,
+ VALID_EMAIL,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, StudentNumber.class.getSimpleName());
+ assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
+ }
+
+
@Test
public void toModelType_invalidName_throwsIllegalValueException() {
- JsonAdaptedPerson person =
- new JsonAdaptedPerson(INVALID_NAME, VALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(
+ VALID_STUDENT_NUMBER,
+ INVALID_NAME,
+ VALID_PHONE,
+ VALID_EMAIL,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
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(
+ VALID_STUDENT_NUMBER,
+ null,
+ VALID_PHONE,
+ VALID_EMAIL,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Name.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_invalidPhone_throwsIllegalValueException() {
- JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, INVALID_PHONE, VALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(
+ VALID_STUDENT_NUMBER,
+ VALID_NAME,
+ INVALID_PHONE,
+ VALID_EMAIL,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
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_STUDENT_NUMBER,
+ VALID_NAME,
+ null,
+ VALID_EMAIL,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Phone.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
public void toModelType_invalidEmail_throwsIllegalValueException() {
- JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, INVALID_EMAIL, VALID_ADDRESS, VALID_TAGS);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(
+ VALID_STUDENT_NUMBER,
+ VALID_NAME,
+ VALID_PHONE,
+ INVALID_EMAIL,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
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_STUDENT_NUMBER,
+ VALID_NAME,
+ VALID_PHONE,
+ null,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Email.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@Test
- public void toModelType_invalidAddress_throwsIllegalValueException() {
- JsonAdaptedPerson person =
- new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, INVALID_ADDRESS, VALID_TAGS);
- String expectedMessage = Address.MESSAGE_CONSTRAINTS;
+ public void toModelType_invalidGpa_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(
+ VALID_STUDENT_NUMBER,
+ VALID_NAME,
+ VALID_PHONE,
+ VALID_EMAIL,
+ INVALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
+ String expectedMessage = Gpa.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_nullGpa_throwsIllegalValueException() {
+ JsonAdaptedPerson person = new JsonAdaptedPerson(
+ VALID_STUDENT_NUMBER,
+ VALID_NAME,
+ VALID_PHONE,
+ VALID_EMAIL,
+ null,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ VALID_TAGS,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
+ String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Gpa.class.getSimpleName());
assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
}
@@ -102,8 +254,20 @@ public void toModelType_nullAddress_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_ADDRESS, invalidTags);
+ JsonAdaptedPerson person = new JsonAdaptedPerson(
+ VALID_STUDENT_NUMBER,
+ VALID_NAME,
+ VALID_PHONE,
+ VALID_EMAIL,
+ VALID_GPA,
+ VALID_PREVIOUS_GRADE,
+ VALID_INTERVIEW_SCORE,
+ VALID_COMMENT,
+ invalidTags,
+ VALID_ATTACHMENTS,
+ VALID_IS_HIDDEN,
+ VALID_IS_BOOKMARKED
+ );
assertThrows(IllegalValueException.class, person::toModelType);
}
diff --git a/src/test/java/seedu/address/testutil/AnotherPersonBuilder.java b/src/test/java/seedu/address/testutil/AnotherPersonBuilder.java
new file mode 100644
index 00000000000..6b74e607f50
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/AnotherPersonBuilder.java
@@ -0,0 +1,195 @@
+package seedu.address.testutil;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+import seedu.address.model.attachment.Attachment;
+import seedu.address.model.person.Comment;
+import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.InterviewScore;
+import seedu.address.model.person.IsBookmarked;
+import seedu.address.model.person.IsHidden;
+import seedu.address.model.person.Name;
+import seedu.address.model.person.Person;
+import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
+import seedu.address.model.person.StudentNumber;
+import seedu.address.model.tag.Tag;
+import seedu.address.model.util.SampleDataUtil;
+
+/**
+ * A utility class to help with building another Person object besides the first Person.
+ */
+public class AnotherPersonBuilder {
+
+ public static final String DEFAULT_STUDENT_NUMBER = "A0616616B";
+ public static final String DEFAULT_NAME = "Bob Cee";
+ public static final String DEFAULT_PHONE = "91234567";
+ public static final String DEFAULT_EMAIL = "bob@gmail.com";
+ public static final double DEFAULT_GPA = 3.5;
+ public static final String DEFAULT_PREVIOUS_GRADE = "B+";
+ public static final boolean DEFAULT_IS_HIDDEN = false;
+ public static final boolean DEFAULT_BOOKMARK = false;
+
+ private StudentNumber studentNo;
+ private Name name;
+ private Phone phone;
+ private Email email;
+ private Gpa gpa;
+ private PreviousGrade previousGrade;
+ private Optional interviewScore;
+ private Optional comment;
+ private Set tags;
+ private List attachments;
+ private IsHidden isHidden;
+ private IsBookmarked isBookmarked;
+
+ /**
+ * Creates a {@code AnotherPersonBuilder} with the default details.
+ */
+ public AnotherPersonBuilder() {
+ studentNo = new StudentNumber(DEFAULT_STUDENT_NUMBER);
+ name = new Name(DEFAULT_NAME);
+ phone = new Phone(DEFAULT_PHONE);
+ email = new Email(DEFAULT_EMAIL);
+ gpa = new Gpa(DEFAULT_GPA);
+ previousGrade = new PreviousGrade(DEFAULT_PREVIOUS_GRADE);
+ interviewScore = Optional.empty();
+ comment = Optional.empty();
+ tags = new HashSet<>();
+ isHidden = new IsHidden(DEFAULT_IS_HIDDEN);
+ attachments = List.of();
+ isBookmarked = new IsBookmarked(DEFAULT_BOOKMARK);
+ }
+
+ /**
+ * Initializes the PersonBuilder with the data of {@code personToCopy}.
+ */
+ public AnotherPersonBuilder(Person personToCopy) {
+ studentNo = personToCopy.getStudentNumber();
+ name = personToCopy.getName();
+ phone = personToCopy.getPhone();
+ email = personToCopy.getEmail();
+ gpa = personToCopy.getGpa();
+ previousGrade = personToCopy.getPreviousGrade();
+ interviewScore = personToCopy.getInterviewScore();
+ comment = personToCopy.getComment();
+ tags = new HashSet<>(personToCopy.getTags());
+ isHidden = personToCopy.getIsHidden();
+ attachments = personToCopy.getAttachments();
+ isBookmarked = personToCopy.getIsBookmarked();
+ }
+
+ /**
+ * Sets the {@code Name} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withName(String name) {
+ this.name = new Name(name);
+ return this;
+ }
+
+ /**
+ * Sets the {@code StudentNumber} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withStudentNumber(String studentNo) {
+ this.studentNo = new StudentNumber(studentNo);
+ return this;
+ }
+
+ /**
+ * Parses the {@code tags} into a {@code Set} and set it to the
+ * {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withTags(String... tags) {
+ this.tags = SampleDataUtil.getTagSet(tags);
+ return this;
+ }
+
+ /**
+ * Parses the {@code pathStrings} into a {@code List} and set it to the
+ * {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withAttachments(String... pathStrings) {
+ this.attachments = SampleDataUtil.getAttachments(pathStrings);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Gpa} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withGpa(double gpa) {
+ this.gpa = new Gpa(gpa);
+ return this;
+ }
+
+ /**
+ * Sets the {@code PreviousGrade} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withPreviousGrade(String grade) {
+ this.previousGrade = new PreviousGrade(grade);
+ return this;
+ }
+
+ /**
+ * Sets the {@code InterviewScore} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withInterviewScore(double interviewScore) {
+ this.interviewScore = Optional.of(new InterviewScore(interviewScore));
+ return this;
+ }
+
+ /**
+ * Sets the {@code Comment} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withComment(String comment) {
+ this.comment = Optional.of(new Comment(comment));
+ return this;
+ }
+
+ /**
+ * Sets the {@code Phone} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withPhone(String phone) {
+ this.phone = new Phone(phone);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Email} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withEmail(String email) {
+ this.email = new Email(email);
+ return this;
+ }
+
+ /**
+ * Sets the {@code isHidden} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withHidden(boolean isHidden) {
+ this.isHidden = new IsHidden(isHidden);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Bookmark} of the {@code Person} that we are building.
+ */
+ public AnotherPersonBuilder withBookmark(boolean isBookmarked) {
+ this.isBookmarked = new IsBookmarked(isBookmarked);
+ return this;
+ }
+
+ /**
+ * Builds a person from the attributes configured before this.
+ *
+ * @return The person.
+ */
+ public Person build() {
+ return new Person(studentNo, name, phone, email, gpa, previousGrade, interviewScore, comment, tags, attachments,
+ isHidden, isBookmarked);
+ }
+
+}
+
diff --git a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
index 4584bd5044e..a5961007a81 100644
--- a/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
+++ b/src/test/java/seedu/address/testutil/EditPersonDescriptorBuilder.java
@@ -5,13 +5,17 @@
import java.util.stream.Stream;
import seedu.address.logic.commands.EditCommand.EditPersonDescriptor;
-import seedu.address.model.person.Address;
+import seedu.address.model.person.Comment;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.InterviewScore;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
import seedu.address.model.tag.Tag;
+
/**
* A utility class to help with building EditPersonDescriptor objects.
*/
@@ -35,7 +39,14 @@ public EditPersonDescriptorBuilder(Person person) {
descriptor.setName(person.getName());
descriptor.setPhone(person.getPhone());
descriptor.setEmail(person.getEmail());
- descriptor.setAddress(person.getAddress());
+ descriptor.setGpa(person.getGpa());
+ descriptor.setPreviousGrade(person.getPreviousGrade());
+ if (person.getInterviewScore().isPresent()) {
+ descriptor.setInterviewScore(person.getInterviewScore().get());
+ }
+ if (person.getComment().isPresent()) {
+ descriptor.setComment(person.getComment().get());
+ }
descriptor.setTags(person.getTags());
}
@@ -64,10 +75,34 @@ public EditPersonDescriptorBuilder withEmail(String email) {
}
/**
- * Sets the {@code Address} of the {@code EditPersonDescriptor} that we are building.
+ * Sets the {@code Gpa} of the {@code EditPersonDescriptor} that we are building.
+ */
+ public EditPersonDescriptorBuilder withGpa(double gpa) {
+ descriptor.setGpa(new Gpa(gpa));
+ return this;
+ }
+
+ /**
+ * Sets the {@code PreviousGrade} of the {@code EditPersonDescriptor} that we are building.
+ */
+ public EditPersonDescriptorBuilder withPreviousGrade(String grade) {
+ descriptor.setPreviousGrade(new PreviousGrade(grade));
+ return this;
+ }
+
+ /**
+ * Sets the {@code InterviewScore} of the {@code EditPersonDescriptor} that we are building.
+ */
+ public EditPersonDescriptorBuilder withInterviewScore(double score) {
+ descriptor.setInterviewScore(new InterviewScore(score));
+ return this;
+ }
+
+ /**
+ * Sets the {@code Comment} of the {@code EditPersonDescriptor} that we are building.
*/
- public EditPersonDescriptorBuilder withAddress(String address) {
- descriptor.setAddress(new Address(address));
+ public EditPersonDescriptorBuilder withComment(String comment) {
+ descriptor.setComment(new Comment(comment));
return this;
}
diff --git a/src/test/java/seedu/address/testutil/PersonBuilder.java b/src/test/java/seedu/address/testutil/PersonBuilder.java
index 6be381d39ba..4f3b6d82436 100644
--- a/src/test/java/seedu/address/testutil/PersonBuilder.java
+++ b/src/test/java/seedu/address/testutil/PersonBuilder.java
@@ -1,13 +1,22 @@
package seedu.address.testutil;
import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
import java.util.Set;
-import seedu.address.model.person.Address;
+import seedu.address.model.attachment.Attachment;
+import seedu.address.model.person.Comment;
import seedu.address.model.person.Email;
+import seedu.address.model.person.Gpa;
+import seedu.address.model.person.InterviewScore;
+import seedu.address.model.person.IsBookmarked;
+import seedu.address.model.person.IsHidden;
import seedu.address.model.person.Name;
import seedu.address.model.person.Person;
import seedu.address.model.person.Phone;
+import seedu.address.model.person.PreviousGrade;
+import seedu.address.model.person.StudentNumber;
import seedu.address.model.tag.Tag;
import seedu.address.model.util.SampleDataUtil;
@@ -16,37 +25,62 @@
*/
public class PersonBuilder {
+ public static final String DEFAULT_STUDENT_NUMBER = "A0616616A";
public static final String DEFAULT_NAME = "Amy Bee";
public static final String DEFAULT_PHONE = "85355255";
public static final String DEFAULT_EMAIL = "amy@gmail.com";
- public static final String DEFAULT_ADDRESS = "123, Jurong West Ave 6, #08-111";
+ public static final double DEFAULT_GPA = 4.0;
+ public static final String DEFAULT_PREVIOUS_GRADE = "A-";
+ public static final boolean DEFAULT_IS_HIDDEN = false;
+ public static final boolean DEFAULT_BOOKMARK = false;
+ private StudentNumber studentNo;
private Name name;
private Phone phone;
private Email email;
- private Address address;
+ private Gpa gpa;
+ private PreviousGrade previousGrade;
+ private Optional interviewScore;
+ private Optional comment;
private Set tags;
+ private List attachments;
+ private IsHidden isHidden;
+ private IsBookmarked isBookmarked;
/**
* Creates a {@code PersonBuilder} with the default details.
*/
public PersonBuilder() {
+ studentNo = new StudentNumber(DEFAULT_STUDENT_NUMBER);
name = new Name(DEFAULT_NAME);
phone = new Phone(DEFAULT_PHONE);
email = new Email(DEFAULT_EMAIL);
- address = new Address(DEFAULT_ADDRESS);
+ gpa = new Gpa(DEFAULT_GPA);
+ previousGrade = new PreviousGrade(DEFAULT_PREVIOUS_GRADE);
+ interviewScore = Optional.empty();
+ comment = Optional.empty();
tags = new HashSet<>();
+ isHidden = new IsHidden(DEFAULT_IS_HIDDEN);
+ attachments = List.of();
+ isBookmarked = new IsBookmarked(DEFAULT_BOOKMARK);
}
/**
* Initializes the PersonBuilder with the data of {@code personToCopy}.
*/
public PersonBuilder(Person personToCopy) {
+ studentNo = personToCopy.getStudentNumber();
name = personToCopy.getName();
phone = personToCopy.getPhone();
email = personToCopy.getEmail();
- address = personToCopy.getAddress();
+ gpa = personToCopy.getGpa();
+ previousGrade = personToCopy.getPreviousGrade();
+ interviewScore = personToCopy.getInterviewScore();
+ comment = personToCopy.getComment();
tags = new HashSet<>(personToCopy.getTags());
+ isHidden = personToCopy.getIsHidden();
+ attachments = personToCopy.getAttachments();
+ isBookmarked = personToCopy.getIsBookmarked();
}
/**
@@ -58,18 +92,60 @@ public PersonBuilder withName(String name) {
}
/**
- * Parses the {@code tags} into a {@code Set} and set it to the {@code Person} that we are building.
+ * Sets the {@code StudentNumber} of the {@code Person} that we are building.
*/
- public PersonBuilder withTags(String ... tags) {
+ public PersonBuilder withStudentNumber(String studentNo) {
+ this.studentNo = new StudentNumber(studentNo);
+ return this;
+ }
+
+ /**
+ * Parses the {@code tags} into a {@code Set} and set it to the
+ * {@code Person} that we are building.
+ */
+ public PersonBuilder withTags(String... tags) {
this.tags = SampleDataUtil.getTagSet(tags);
return this;
}
/**
- * Sets the {@code Address} of the {@code Person} that we are building.
+ * Parses the {@code pathStrings} into a {@code List} and set it to the
+ * {@code Person} that we are building.
+ */
+ public PersonBuilder withAttachments(String... pathStrings) {
+ this.attachments = SampleDataUtil.getAttachments(pathStrings);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Gpa} of the {@code Person} that we are building.
*/
- public PersonBuilder withAddress(String address) {
- this.address = new Address(address);
+ public PersonBuilder withGpa(double gpa) {
+ this.gpa = new Gpa(gpa);
+ return this;
+ }
+
+ /**
+ * Sets the {@code PreviousGrade} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withPreviousGrade(String grade) {
+ this.previousGrade = new PreviousGrade(grade);
+ return this;
+ }
+
+ /**
+ * Sets the {@code InterviewScore} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withInterviewScore(double interviewScore) {
+ this.interviewScore = Optional.of(new InterviewScore(interviewScore));
+ return this;
+ }
+
+ /**
+ * Sets the {@code Comment} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withComment(String comment) {
+ this.comment = Optional.of(new Comment(comment));
return this;
}
@@ -89,8 +165,30 @@ public PersonBuilder withEmail(String email) {
return this;
}
+ /**
+ * Sets the {@code isHidden} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withHidden(boolean isHidden) {
+ this.isHidden = new IsHidden(isHidden);
+ return this;
+ }
+
+ /**
+ * Sets the {@code Bookmark} of the {@code Person} that we are building.
+ */
+ public PersonBuilder withBookmark(boolean isBookmarked) {
+ this.isBookmarked = new IsBookmarked(isBookmarked);
+ return this;
+ }
+
+ /**
+ * Builds a person from the attributes configured before this.
+ *
+ * @return The person.
+ */
public Person build() {
- return new Person(name, phone, email, address, tags);
+ return new Person(studentNo, name, phone, email, gpa, previousGrade, interviewScore, comment, tags, attachments,
+ isHidden, isBookmarked);
}
}
diff --git a/src/test/java/seedu/address/testutil/PersonUtil.java b/src/test/java/seedu/address/testutil/PersonUtil.java
index 90849945183..51a59dabcbc 100644
--- a/src/test/java/seedu/address/testutil/PersonUtil.java
+++ b/src/test/java/seedu/address/testutil/PersonUtil.java
@@ -1,9 +1,13 @@
package seedu.address.testutil;
-import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_COMMENT;
import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_GPA;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_INTERVIEW_SCORE;
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_PREVIOUS_GRADE;
+import static seedu.address.logic.parser.CliSyntax.PREFIX_STUDENT_NUMBER;
import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG;
import java.util.Set;
@@ -30,25 +34,38 @@ public static String getAddCommand(Person person) {
*/
public static String getPersonDetails(Person person) {
StringBuilder sb = new StringBuilder();
+ sb.append(PREFIX_STUDENT_NUMBER + person.getStudentNumber().value + " ");
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 + " ");
+ sb.append(PREFIX_GPA + "" + person.getGpa().value + " ");
+ sb.append(PREFIX_PREVIOUS_GRADE + "" + person.getPreviousGrade().toString() + " ");
+ if (person.getInterviewScore().isPresent()) {
+ sb.append(PREFIX_INTERVIEW_SCORE + "" + person.getInterviewScore().get().value + " ");
+ }
+ if (person.getComment().isPresent()) {
+ sb.append(PREFIX_COMMENT + person.getComment().get().comment + " ");
+ }
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.
+ * 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(" "));
+ descriptor.getGpa().ifPresent(gpa -> sb.append(PREFIX_GPA).append(gpa.value).append(" "));
+ descriptor.getPreviousGrade()
+ .ifPresent(grade -> sb.append(PREFIX_PREVIOUS_GRADE).append(grade.toString()).append(" "));
+ descriptor.getInterviewScore()
+ .ifPresent(score -> sb.append(PREFIX_INTERVIEW_SCORE).append(score.value).append(" "));
+ descriptor.getComment().ifPresent(comment -> sb.append(PREFIX_COMMENT).append(comment.comment).append(" "));
if (descriptor.getTags().isPresent()) {
Set tags = descriptor.getTags().get();
if (tags.isEmpty()) {
diff --git a/src/test/java/seedu/address/testutil/TypicalFieldNames.java b/src/test/java/seedu/address/testutil/TypicalFieldNames.java
new file mode 100644
index 00000000000..1521d064367
--- /dev/null
+++ b/src/test/java/seedu/address/testutil/TypicalFieldNames.java
@@ -0,0 +1,16 @@
+package seedu.address.testutil;
+
+/**
+ * A utility class containing a list of field names to be used in tests.
+ */
+public class TypicalFieldNames {
+ public static final String FIELDNAME_GPA = "gpa";
+ public static final String FIELDNAME_NAME = "name";
+ public static final String FIELDNAME_PHONE = "phone";
+ public static final String FIELDNAME_EMAIL = "email";
+ public static final String FIELDNAME_COMMENT = "comment";
+ public static final String FIELDNAME_TAG = "tag";
+ public static final String FIELDNAME_HIDDEN = "hidden";
+ public static final String FIELDNAME_BOOKMARKED = "bookmarked";
+
+}
diff --git a/src/test/java/seedu/address/testutil/TypicalPersons.java b/src/test/java/seedu/address/testutil/TypicalPersons.java
index fec76fb7129..ca4ee82a8f1 100644
--- a/src/test/java/seedu/address/testutil/TypicalPersons.java
+++ b/src/test/java/seedu/address/testutil/TypicalPersons.java
@@ -1,13 +1,21 @@
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_COMMENT_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_COMMENT_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_GPA_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_GPA_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_INTERVIEW_SCORE_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_INTERVIEW_SCORE_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_PREVIOUS_GRADE_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_PREVIOUS_GRADE_BOB;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_STUDENT_NUMBER_AMY;
+import static seedu.address.logic.commands.CommandTestUtil.VALID_STUDENT_NUMBER_BOB;
import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_FRIEND;
import static seedu.address.logic.commands.CommandTestUtil.VALID_TAG_HUSBAND;
@@ -19,45 +27,111 @@
import seedu.address.model.person.Person;
/**
- * A utility class containing a list of {@code Person} objects to be used in tests.
+ * A utility class containing a list of {@code Person} objects to be used in
+ * tests.
*/
public class TypicalPersons {
- public static final Person ALICE = new PersonBuilder().withName("Alice Pauline")
- .withAddress("123, Jurong West Ave 6, #08-111").withEmail("alice@example.com")
+ public static final Person ALICE = new PersonBuilder()
+ .withStudentNumber("A0343345A")
+ .withName("Alice Pauline")
+ .withGpa(2.9)
+ .withPreviousGrade("A+")
+ .withInterviewScore(3.2)
+ .withEmail("alice@example.com")
.withPhone("94351253")
- .withTags("friends").build();
- public static final Person BENSON = new PersonBuilder().withName("Benson Meier")
- .withAddress("311, Clementi Ave 2, #02-25")
- .withEmail("johnd@example.com").withPhone("98765432")
- .withTags("owesMoney", "friends").build();
- public static final Person CARL = new PersonBuilder().withName("Carl Kurz").withPhone("95352563")
- .withEmail("heinz@example.com").withAddress("wall street").build();
- public static final Person DANIEL = new PersonBuilder().withName("Daniel Meier").withPhone("87652533")
- .withEmail("cornelia@example.com").withAddress("10th street").withTags("friends").build();
- public static final Person ELLE = new PersonBuilder().withName("Elle Meyer").withPhone("9482224")
- .withEmail("werner@example.com").withAddress("michegan ave").build();
- public static final Person FIONA = new PersonBuilder().withName("Fiona Kunz").withPhone("9482427")
- .withEmail("lydia@example.com").withAddress("little tokyo").build();
- public static final Person GEORGE = new PersonBuilder().withName("George Best").withPhone("9482442")
- .withEmail("anna@example.com").withAddress("4th street").build();
+ .withTags("friends")
+ .withAttachments().build();
+ public static final Person BENSON = new PersonBuilder()
+ .withStudentNumber("A9473847C")
+ .withName("Benson Meier")
+ .withGpa(4.9)
+ .withPreviousGrade("A+")
+ .withInterviewScore(3.2)
+ .withComment("This is a comment")
+ .withEmail("johnd@example.com")
+ .withPhone("98765432")
+ .withTags("owesMoney", "friends")
+ .withAttachments().build();
+ public static final Person CARL = new PersonBuilder()
+ .withStudentNumber("A0437563D")
+ .withName("Carl Kurz")
+ .withPhone("95352563")
+ .withEmail("heinz@example.com")
+ .withGpa(4.0)
+ .withPreviousGrade("A+")
+ .withInterviewScore(3.2)
+ .withAttachments()
+ .build();
+ public static final Person DANIEL = new PersonBuilder()
+ .withStudentNumber("A0453552G")
+ .withName("Daniel Meier")
+ .withPhone("87652533")
+ .withEmail("cornelia@example.com")
+ .withGpa(3.0)
+ .withPreviousGrade("A+")
+ .withInterviewScore(3.2)
+ .withTags("friends")
+ .withAttachments()
+ .build();
+ public static final Person ELLE = new PersonBuilder()
+ .withStudentNumber("A0348574E")
+ .withName("Elle Meyer")
+ .withPhone("9482224")
+ .withEmail("werner@example.com")
+ .withGpa(1.0)
+ .withPreviousGrade("A+")
+ .withInterviewScore(3.2)
+ .withAttachments()
+ .build();
+ public static final Person FIONA = new PersonBuilder()
+ .withStudentNumber("A0434534C")
+ .withName("Fiona Kunz")
+ .withPhone("9482427")
+ .withEmail("lydia@example.com")
+ .withGpa(2.0)
+ .withPreviousGrade("A+")
+ .withInterviewScore(3.2)
+ .withAttachments()
+ .build();
+ public static final Person GEORGE = new PersonBuilder()
+ .withStudentNumber("A0483728F")
+ .withName("George Best")
+ .withPhone("9482442")
+ .withEmail("anna@example.com")
+ .withGpa(4.0)
+ .withPreviousGrade("A+")
+ .withInterviewScore(3.2)
+ .withAttachments()
+ .build();
// Manually added
- public static final Person HOON = new PersonBuilder().withName("Hoon Meier").withPhone("8482424")
- .withEmail("stefan@example.com").withAddress("little india").build();
- public static final Person IDA = new PersonBuilder().withName("Ida Mueller").withPhone("8482131")
- .withEmail("hans@example.com").withAddress("chicago ave").build();
+ public static final Person HOON = new PersonBuilder().withStudentNumber("A0348483G").withName("Hoon Meier")
+ .withPhone("8482424").withEmail("stefan@example.com").withGpa(0.1)
+ .withAttachments().build();
+
+ public static final Person IDA = new PersonBuilder().withStudentNumber("A3974743C").withName("Ida Mueller")
+ .withPhone("8482131").withEmail("hans@example.com").withGpa(0.5)
+ .withAttachments().build();
// Manually added - Person's details found in {@code CommandTestUtil}
- public static final Person AMY = new PersonBuilder().withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY)
- .withEmail(VALID_EMAIL_AMY).withAddress(VALID_ADDRESS_AMY).withTags(VALID_TAG_FRIEND).build();
- public static final Person BOB = new PersonBuilder().withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB)
- .withEmail(VALID_EMAIL_BOB).withAddress(VALID_ADDRESS_BOB).withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND)
- .build();
+ public static final Person AMY = new PersonBuilder().withStudentNumber(VALID_STUDENT_NUMBER_AMY)
+ .withName(VALID_NAME_AMY).withPhone(VALID_PHONE_AMY).withHidden(false)
+ .withEmail(VALID_EMAIL_AMY).withGpa(VALID_GPA_AMY).withPreviousGrade(VALID_PREVIOUS_GRADE_AMY)
+ .withInterviewScore(VALID_INTERVIEW_SCORE_AMY).withComment(VALID_COMMENT_AMY)
+ .withTags(VALID_TAG_FRIEND).withAttachments().withBookmark(false).build();
+
+ public static final Person BOB = new PersonBuilder().withStudentNumber(VALID_STUDENT_NUMBER_BOB)
+ .withName(VALID_NAME_BOB).withPhone(VALID_PHONE_BOB).withHidden(false)
+ .withEmail(VALID_EMAIL_BOB).withGpa(VALID_GPA_BOB).withPreviousGrade(VALID_PREVIOUS_GRADE_BOB)
+ .withInterviewScore(VALID_INTERVIEW_SCORE_BOB).withComment(VALID_COMMENT_BOB)
+ .withTags(VALID_TAG_HUSBAND, VALID_TAG_FRIEND).withHidden(false)
+ .withAttachments().withBookmark(false).build();
public static final String KEYWORD_MATCHING_MEIER = "Meier"; // A keyword that matches MEIER
- private TypicalPersons() {} // prevents instantiation
+ private TypicalPersons() {
+ } // prevents instantiation
/**
* Returns an {@code AddressBook} with all the typical persons.
diff --git a/src/test/resources/attachments/another-sample.txt b/src/test/resources/attachments/another-sample.txt
new file mode 100644
index 00000000000..cb970e69486
--- /dev/null
+++ b/src/test/resources/attachments/another-sample.txt
@@ -0,0 +1 @@
+This is another sample attachment.
diff --git a/src/test/resources/attachments/sample.txt b/src/test/resources/attachments/sample.txt
new file mode 100644
index 00000000000..96feda20c1c
--- /dev/null
+++ b/src/test/resources/attachments/sample.txt
@@ -0,0 +1 @@
+This is a sample attachment.