diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..6aa952c5ae8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "java.help.shareDiagnostics": true, + "java.configuration.updateBuildConfiguration": "interactive" +} diff --git a/README.md b/README.md index 13f5c77403f..77418950406 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,8 @@ -[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/se-edu/addressbook-level3/actions) +## HRConnect -![Ui](docs/images/Ui.png) +[![CI Status](https://github.com/se-edu/addressbook-level3/workflows/Java%20CI/badge.svg)](https://github.com/AY2122S2-CS2103T-W11-2/tp/actions) +[![codecov](https://codecov.io/gh/AY2122S2-CS2103T-W11-2/tp/branch/master/graph/badge.svg?token=NLSR34Z1Y2)](https://codecov.io/gh/AY2122S2-CS2103T-W11-2/tp) -* 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. +![demo screenshot](docs/images/Ui.png) + +This product is for tech HR recruiters who prefer to use CLI to store contacts of job applicants and their respective interviews as well as keep track of the status of each applicant through the application process. diff --git a/build.gradle b/build.gradle index be2d2905dde..a08cd3af471 100644 --- a/build.gradle +++ b/build.gradle @@ -66,7 +66,11 @@ dependencies { } shadowJar { - archiveName = 'addressbook.jar' + archiveName = 'HRConnect.jar' +} + +run { + enableAssertions = true } defaultTasks 'clean', 'test' diff --git a/docs/AboutUs.md b/docs/AboutUs.md index 1c9514e966a..e41eb6b1016 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -5,55 +5,47 @@ title: About Us We are a team based in the [School of Computing, National University of Singapore](http://www.comp.nus.edu.sg). -You can reach us at the email `seer[at]comp.nus.edu.sg` +You can reach us at the email `e0544350@comp.nus.edu.sg` ## Project team -### John Doe +### Brigitte Puteri Santoso - -[[homepage](http://www.comp.nus.edu.sg/~damithch)] -[[github](https://github.com/johndoe)] -[[portfolio](team/johndoe.md)] + -* Role: Project Advisor +[[github](https://github.com/brigittesantoso)] +[[portfolio](team/brigittesantoso.md)] -### Jane Doe - - - -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] - -* Role: Team Lead -* Responsibilities: UI +* Role: Developer +* Responsibilities: Job and Stage fields, Interview features and Types -### Johnny Doe +### Ryan Chang - + -[[github](http://github.com/johndoe)] [[portfolio](team/johndoe.md)] +[[github](http://github.com/rcjj98)] +[[portfolio](team/rcjj98.md)] * Role: Developer -* Responsibilities: Data +* Responsibilities: Export and edit feature. -### Jean Doe +### Emmanuel Mok - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/eman-kom)] +[[portfolio](team/eman-kom.md)] * Role: Developer -* Responsibilities: Dev Ops + Threading +* Responsibilities: Find and Import Feature -### James Doe +### Imraj Singh Sandhu - + -[[github](http://github.com/johndoe)] -[[portfolio](team/johndoe.md)] +[[github](http://github.com/imrajsinghsandhu)] +[[portfolio](team/imrajsinghsandhu.md)] * Role: Developer -* Responsibilities: UI +* Responsibilities: Task feature diff --git a/docs/DevOps.md b/docs/DevOps.md index 26354050fa4..09da3b20e3b 100644 --- a/docs/DevOps.md +++ b/docs/DevOps.md @@ -74,7 +74,7 @@ Any warnings or errors will be printed out to the console. Here are the steps to create a new release. -1. Update the version number in [`MainApp.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). +1. Update the version number in [`MainApp.java`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/java/seedu/address/MainApp.java). 1. Generate a fat JAR file using Gradle (i.e., `gradlew shadowJar`). 1. Tag the repo with the version number. e.g. `v0.1` 1. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/). Upload the JAR file you created. diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 46eae8ee565..7e112c7177b 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -9,7 +9,7 @@ title: Developer Guide ## **Acknowledgements** -* {list here sources of all reused/adapted ideas, code, documentation, and third-party libraries -- include links to the original source as well} +* Built upon and referenced from [AB-3](https://github.com/nus-cs2103-AY2122S2/tp) -------------------------------------------------------------------------------------------------------------------- @@ -18,14 +18,16 @@ title: Developer Guide Refer to the guide [_Setting up and getting started_](SettingUp.md). -------------------------------------------------------------------------------------------------------------------- +
## **Design**
-:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/se-edu/addressbook-level3/tree/master/docs/diagrams/) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams. +:bulb: **Tip:** The `.puml` files used to create diagrams in this document can be found in the [diagrams](https://github.com/AY2122S2-CS2103T-W11-2/tp/tree/master/docs/diagrams) folder. Refer to the [_PlantUML Tutorial_ at se-edu/guides](https://se-education.org/guides/tutorials/plantUml.html) to learn how to create and edit diagrams.
+ ### Architecture @@ -36,7 +38,7 @@ Given below is a quick overview of main components and how they interact with ea **Main components of the architecture** -**`Main`** has two classes called [`Main`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/MainApp.java). It is responsible for, +**`Main`** has two classes called [`Main`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/java/seedu/address/Main.java) and [`MainApp`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/java/seedu/address/MainApp.java). It is responsible for, * At app launch: Initializes the components in the correct sequence, and connects them up with each other. * At shut down: Shuts down the components and invokes cleanup methods where necessary. @@ -52,7 +54,7 @@ The rest of the App consists of four components. **How the architecture components interact with each other** -The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete 1`. +The *Sequence Diagram* below shows how the components interact with each other for the scenario where the user issues the command `delete [p] 1`. @@ -61,32 +63,38 @@ Each of the four main components (also shown in the diagram above), * defines its *API* in an `interface` with the same name as the Component. * implements its functionality using a concrete `{Component Name}Manager` class (which follows the corresponding API `interface` mentioned in the previous point. +
+ For example, the `Logic` component defines its API in the `Logic.java` interface and implements its functionality using the `LogicManager.java` class which follows the `Logic` interface. Other components interact with a given component through its interface rather than the concrete class (reason: to prevent outside component's being coupled to the implementation of a component), as illustrated in the (partial) class diagram below. The sections below give more details of each component. +
+ ### UI component -The **API** of this component is specified in [`Ui.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/Ui.java) +The **API** of this component is specified in [`Ui.java`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/java/seedu/address/ui/Ui.java) ![Structure of the UI Component](images/UiClassDiagram.png) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, `InterviewListPanel`, `TaskListPanel`,`StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class which captures the commonalities between classes that represent parts of the visible GUI. -The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/resources/view/MainWindow.fxml) +The `UI` component uses the JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files that are in the `src/main/resources/view` folder. For example, the layout of the [`MainWindow`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/java/seedu/address/ui/MainWindow.java) is specified in [`MainWindow.fxml`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/resources/view/MainWindow.fxml) The `UI` component, * executes user commands using the `Logic` component. * listens for changes to `Model` data so that the UI can be updated with the modified data. * keeps a reference to the `Logic` component, because the `UI` relies on the `Logic` to execute commands. -* depends on some classes in the `Model` component, as it displays `Person` object residing in the `Model`. +* depends on some classes in the `Model` component, as it displays `Person`, `Interview`, `Task` objects residing in the `Model`. + +
### Logic component -**API** : [`Logic.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/logic/Logic.java) +**API** : [`Logic.java`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/java/seedu/address/logic/Logic.java) Here's a (partial) class diagram of the `Logic` component: @@ -94,13 +102,13 @@ Here's a (partial) class diagram of the `Logic` component: How the `Logic` component works: 1. When `Logic` is called upon to execute a command, it uses the `AddressBookParser` class to parse the user command. -1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `AddCommand`) which is executed by the `LogicManager`. -1. The command can communicate with the `Model` when it is executed (e.g. to add a person). +1. This results in a `Command` object (more precisely, an object of one of its subclasses e.g., `HelpCommand`) which is executed by the `LogicManager`. +1. The command can communicate with the `Model` when it is executed (e.g. to add an applicant). 1. The result of the command execution is encapsulated as a `CommandResult` object which is returned back from `Logic`. -The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete 1")` API call. +The Sequence Diagram below illustrates the interactions within the `Logic` component for the `execute("delete [p] 1")` API call. -![Interactions Inside the Logic Component for the `delete 1` Command](images/DeleteSequenceDiagram.png) +![Interactions Inside the Logic Component for the `delete [p] 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.
@@ -111,135 +119,195 @@ Here are the other classes in `Logic` (omitted from the class diagram above) tha How the parsing works: * When called upon to parse a user command, the `AddressBookParser` class creates an `XYZCommandParser` (`XYZ` is a placeholder for the specific command name e.g., `AddCommandParser`) which uses the other classes shown above to parse the user command and create a `XYZCommand` object (e.g., `AddCommand`) which the `AddressBookParser` returns back as a `Command` object. -* All `XYZCommandParser` classes (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. +* All `XYZCommandParser` (e.g., `AddCommandParser`, `DeleteCommandParser`, ...) inherit from the `Parser` interface so that they can be treated similarly where possible e.g, during testing. +* All `XYZTYPECommandParser` classes (`XYZ` refers to the specific command, and `TYPE` refers to either `Person`, `Task` or `Interview` objects e.g., `AddTaskCommandParser`, `DeletePersonCommandParser`, ...) are created by the `XYZCommandParser` classes, which return an `XYZTYPECommand` (`XYZTYPECommand` referring to the specific command e.g. `AddPersonCommand`, `DeleteTaskCommand`, ...). +* `XYZCommand` like `AddCommand` and `DeleteCommand` are abstract classes, which inherits from the abstract `Command` class, so they may be treated similarly where possible. However `XYZCommand` like `HelpCommand` and `ExitCommand` are non-abstract classes but also inherits from abstract class Command. + +
### Model component -**API** : [`Model.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/model/Model.java) +**API** : [`Model.java`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/java/seedu/address/model/Model.java) The `Model` component, -* stores the address book data i.e., all `Person` objects (which are contained in a `UniquePersonList` object). +* stores the HRConnect data i.e., all `Person` objects (which are contained in a `UniquePersonList` object), all `Interview` objects (which are contained in a `UniqueInterviewList` object), all `Task` objects (which are contained in a `UniqueTaskList` object). * stores the currently 'selected' `Person` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the currently 'selected' `Interview` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. +* stores the currently 'selected' `Task` objects (e.g., results of a search query) as a separate _filtered_ list which is exposed to outsiders as an unmodifiable `ObservableList` that can be 'observed' e.g. the UI can be bound to this list so that the UI automatically updates when the data in the list change. * stores a `UserPref` object that represents the user’s preferences. This is exposed to the outside as a `ReadOnlyUserPref` objects. * does not depend on any of the other three components (as the `Model` represents data entities of the domain, they should make sense on their own without depending on other components) -
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below. It has a `Tag` list in the `AddressBook`, which `Person` references. This allows `AddressBook` to only require one `Tag` object per unique tag, instead of each `Person` needing their own `Tag` objects.
+
:information_source: **Note:** An alternative (arguably, a more OOP) model is given below for `Person`, `Interview` and `Task` separately.
+
### Storage component -**API** : [`Storage.java`](https://github.com/se-edu/addressbook-level3/tree/master/src/main/java/seedu/address/storage/Storage.java) +**API** : [`Storage.java`](https://github.com/AY2122S2-CS2103T-W11-2/tp/blob/master/src/main/java/seedu/address/storage/Storage.java) The `Storage` component, -* can save both address book data and user preference data in json format, and read them back into corresponding objects. +* can save both HRConnect data and user preference data in json format, and read them back into corresponding objects. * inherits from both `AddressBookStorage` and `UserPrefStorage`, which means it can be treated as either one (if only the functionality of only one is needed). * 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. +Classes used by multiple components are in the `seedu.address.commons` package. -------------------------------------------------------------------------------------------------------------------- +
+ ## **Implementation** This section describes some noteworthy details on how certain features are implemented. -### \[Proposed\] Undo/redo feature +### Export Feature -#### Proposed Implementation +The export feature takes the current HRConnect data stored in memory and exports the job applicants data into a user-specified +csv file that is tab-delimited. -The proposed undo/redo mechanism is facilitated by `VersionedAddressBook`. It extends `AddressBook` with an undo/redo history, stored internally as an `addressBookStateList` and `currentStatePointer`. Additionally, it implements the following operations: +The structure for the csv file is defined as follows: -* `VersionedAddressBook#commit()` — Saves the current address book state in its history. -* `VersionedAddressBook#undo()` — Restores the previous address book state from its history. -* `VersionedAddressBook#redo()` — Restores a previously undone address book state from its history. +name | phone number | email | address | job position | stage -These operations are exposed in the `Model` interface as `Model#commitAddressBook()`, `Model#undoAddressBook()` and `Model#redoAddressBook()` respectively. +#### Implementation -Given below is an example usage scenario and how the undo/redo mechanism behaves at each step. +The export feature is facilitated by the `ExportCommand` while the necessary checks for the export +feature is facilitated by the `ExportCommandParser`. -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. +  -![UndoRedoState0](images/UndoRedoState0.png) +Given below is an example usage scenario and how the export mechanism behaves at each step. -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 1. User enters `export ./mydata.csv` into the application. -![UndoRedoState1](images/UndoRedoState1.png) +Step 2. The file path is passed to `ExportCommandParser#parse()` and `ExportCommandParser#checkFilePath()` checks the validity of the file path. -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 3. After checking that the file path is valid, the data type of the file path is converted from its *string* representation into a *Path* representation. -![UndoRedoState2](images/UndoRedoState2.png) +Step 4. A new `ExportCommand` object is created with the file path as its parameter. -
: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`. +Step 5. The `ExportCommand#execute()` method is called, and it calls `Model#getFilteredPersonList()` to get the current list of job applicants. -
+Step 6. For each person in the persons list, the *string* representation of each field is obtained and concatenated into +the aformentioned format above, then it is written into the user-specified csv file. -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. +Step 7. A new `CommandResult` object is returned signifying that the command has executed successfully. -![UndoRedoState3](images/UndoRedoState3.png) +  -
:information_source: **Note:** If the `currentStatePointer` is at index 0, pointing to the initial AddressBook state, then there are no previous AddressBook states to restore. The `undo` command uses `Model#canUndoAddressBook()` to check if this is the case. If so, it will return an error to the user rather -than attempting to perform the undo. +The following sequence diagram summarises how `export ./mydata.csv` works. -
+

+ Interactions for Export Command +

-The following sequence diagram shows how the undo operation works: +
-![UndoSequenceDiagram](images/UndoSequenceDiagram.png) +### Import Feature -
: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. +The import feature takes in a csv file and adds all the job applicants stored in the csv file back into the HRConnect. -
+The csv file structure needs to follow the file structure as defined by the [export feature](#export-feature). -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. +#### Implementation -
: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. +The import feature is facilitated by the `ImportCommand` while the necessary checks for the import feature is facilitated by the `ImportCommandParser`. -
+  + +Given below is an example usage scenario and how the import mechanism behaves at each step. + +Step 1. User enters `import ./mydata.csv` into the application. + +Step 2. The file path is passed to `ImportCommandParser#parse()` and `ImportCommandParser#checkFilePath()` checks the validity of the file path. + +Step 3. After checking that the file path is valid, the data type of the file path is converted from its *string* representation into a *Path* representation. + +Step 4. The `ImportCommandParser#readCsv()` is called to parse the csv file. From there, 3 methods are called to ensure the correctness of the csv file. + * `ImportCommandParser#getFields()`: ensures that the number of fields in each line matches the number of fields required by the application + * `ImportCommandParser#createPerson()`: ensures that each field is valid and correct and converts the line into a person object. + * `ImportCommandParser#updatePersons()`: ensures that each person in the csv file is unique with regards to the csv file. + +Step 5. The newly created person is added to a temporary list. + +Step 6. A new `ImportCommand` object is created with the aforementioned temporary list as its parameter. + +Step 7. The `ImportCommand#execute()` method is called. It checks that there are no persons in the temporary list that already exists in the current HRConnect before adding each person into the current HRConnect. -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 8. A new `CommandResult` object is returned signifying that the command has executed successfully. -![UndoRedoState4](images/UndoRedoState4.png) +  -Step 6. The user executes `clear`, which calls `Model#commitAddressBook()`. Since the `currentStatePointer` is not pointing at the end of the `addressBookStateList`, all address book states after the `currentStatePointer` will be purged. Reason: It no longer makes sense to redo the `add n/David …​` command. This is the behavior that most modern desktop applications follow. +The following sequence diagram summarises how `import ./mydata.csv` works. -![UndoRedoState5](images/UndoRedoState5.png) +

+ Interactions for Import Command +

+

+ Interactions for Import checking Command +

-The following activity diagram summarizes what happens when a user executes a new command: +
- +### Find Feature -#### Design considerations: +The find feature finds all data within a specified section (Person/Interview/Task) such that it matches the search criteria. -**Aspect: How undo & redo executes:** +#### Implementation -* **Alternative 1 (current choice):** Saves the entire address book. - * Pros: Easy to implement. - * Cons: May have performance issues in terms of memory usage. +The find feature is facilitated by the subclasses of the `FindCommand` while the necessary validity checks for the find feature is facilitated by the subclasses of `FindCommandParser`. The actual filtering is faciliated by the `{Section}ContainsKeywordsPredicate` -* **Alternative 2:** Individual command knows how to undo/redo by - itself. - * Pros: Will use less memory (e.g. for `delete`, just save the person being deleted). - * Cons: We must ensure that the implementation of each individual command are correct. +  -_{more aspects and alternatives to be added}_ +Given below is an example usage scenario and how the find mechanism behaves at each step. -### \[Proposed\] Data archiving +Step 1. User enters `find [t] g/i/update` into the application. -_{Explain here how the data archiving feature will be implemented}_ +Step 2. The input is passed to `FindCommandParser#parse()` and its section is determined. +Step 3. Next, the input is parsed and checked for any invalid groups. + +Step 4. The list of groups are then passed into `FindTaskCommandParser#parse()` to check for any invalid flags or formats. + +Step 5. A new `TaskContainsKeywordsPredicate` predicate object is created using the list of groups as its parameter. + +Step 6. A new `FindTaskCommand` object is created with the aforementioned predicate object as its parameter. + +Step 7. The `FindTaskCommand#execute()` method is called. It filters the current HRConnect such that it satisfies the predicate object and displays it on the screen. + +Step 8. A new `CommandResult` object is returned signifying that the command has executed successfully. + + +  + +The following sequence diagram summarises how `find [t] g/i/update` works + +

+ Interactions for Find Task Command +

+ +#### Design Considerations + +* **Alternative 1:** Using AND, OR, NOT operators (i.e. `find john AND tom OR (gmail.com AND NOT 111)`). + * Pros: More intuitive to the technically inclined and more control over the search results. + * Cons: Harder to parse and implement. +* **Alternative 2** Using free text queries (i.e. `find john tom gmail.com`) + * Pros: More intuitive as user expects queried person to contain all the search terms. + * Cons: Lacks the flexibility provided by AND and OR operators. -------------------------------------------------------------------------------------------------------------------- +
## **Documentation, logging, testing, configuration, dev-ops** @@ -255,75 +323,582 @@ _{Explain here how the data archiving feature will be implemented}_ ### Product scope -**Target user profile**: +**Target user profile**: Tech HR Recruiters -* 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 +* Required to manage a significant number of contacts. +* Prefer desktop apps over other types. +* Proficient typists. +* Prefers typing to mouse interactions. +* Reasonably comfortable using CLI apps. -**Value proposition**: manage contacts faster than a typical mouse/GUI driven app +**Value proposition**: +* Manage contacts more efficiently using CLI as compared to a typical mouse/GUI driven app. +* Find applicants based on their details, e.g. name, position applied, stage of application. +* Archive/Retrieve applicants' details for long term storage/ easy transfer of data into HRConnect. +* Keep track of applicant's upcoming and past interviews. +* A task list to keep track of miscellaneous tasks. +
### User stories Priorities: High (must have) - `* * *`, Medium (nice to have) - `* *`, Low (unlikely to have) - `*` -| Priority | As a …​ | I want to …​ | So that I can…​ | -| -------- | ------------------------------------------ | ------------------------------ | ---------------------------------------------------------------------- | -| `* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App | -| `* * *` | user | add a new person | | -| `* * *` | user | delete a person | remove entries that I no longer need | -| `* * *` | user | find a person by name | locate details of persons without having to go through the entire list | -| `* *` | user | hide private contact details | minimize chance of someone else seeing them by accident | -| `*` | user with many persons in the address book | sort persons by name | locate a person easily | - -*{More to be added}* +| Priority | As a …​ | I want to …​ | So that I can…​ | +|----------|-------------------|--------------------------------------------------|-----------------------------------------------------| +| `* * *` | Tech HR Recruiter | add applicants to my HRConnect | record their details | +| `* * *` | Tech HR Recruiter | take note of scheduled interviews for applicants | keep track of their interview dates | +| `* * *` | Tech HR Recruiter | record miscellaneous task | keep track of any important details | +| `* * *` | Tech HR Recruiter | view the details of all contacts | see all my contacts at a glance | +| `* * *` | Tech HR Recruiter | view all scheduled interviews | see all upcoming and past interviews | +| `* * *` | Tech HR Recruiter | view all recorded miscellaneous task | see all important tasks at a glance | +| `* * *` | Tech HR Recruiter | remove any applicant from my contact list | delete any applicant that is no longer of interest | +| `* * *` | Tech HR Recruiter | remove a scheduled interview | delete any interview that is cancelled | +| `* * *` | Tech HR Recruiter | remove any recorded tasks | delete tasks off my task list | +| `* * *` | Tech HR Recruiter | update any applicant's details | edit any mistakes in the applicant's detail | +| `* * *` | Tech HR Recruiter | update the details of a scheduled interview | edit any mistakes in the scheduled interview | +| `* * *` | Tech HR Recruiter | update any task details | edit any mistakes in the miscellaneous tasks | +| `* *` | Tech HR Recruiter | find an applicant in my HRConnect | locate them easily | +| `* *` | Tech HR Recruiter | find a scheduled interview | locate a specific interview | +| `* *` | Tech HR Recruiter | find specific tasks I recorded | locate the quickly | +| `* * *` | Tech HR Recruiter | clear all applicants from my HRConnect | easily empty my HRConnect | +| `* * *` | Tech HR Recruiter | clear all schedule interviews | easily remove all schedule interviews | +| `* * *` | Tech HR Recruiter | clear all recorded miscellaneous task | easily remove all recorded tasks | +| `* *` | Tech HR Recruiter | export details of all applicants | archive the data for future reference | +| `* *` | Tech HR Recruiter | import details of applicants | easily transfer applicant details into my HRConnect | + + +
### Use cases +For all use cases below, the **System** is the `HRConnect` and the **Actor** is the `Tech HR Recruiter`, unless specified otherwise. -(For all use cases below, the **System** is the `AddressBook` and the **Actor** is the `user`, unless specified otherwise) -**Use case: Delete a person** +**Use case: UC1 - Add an applicant** **MSS** -1. User requests to list persons -2. AddressBook shows a list of persons -3. User requests to delete a specific person in the list -4. AddressBook deletes the person +1. User requests to add an applicant in the HRConnect. +2. HRConnect adds the applicant to applicant list. Use case ends. **Extensions** -* 2a. The list is empty. +* 1a. The type provided is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

- Use case ends. +* 1b. The details for some field(s) provided are invalid. -* 3a. The given index is invalid. + * 1b1. HRConnect requests for valid details. + * 1b2. User enters new details for field(s).
+ Steps 1b1-1b2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

- * 3a1. AddressBook shows an error message. +**Use case: UC2 - Add an interview** - Use case resumes at step 2. +**MSS** -*{More to be added}* +1. User requests to add an interview to the interview list. +2. HRConnect adds the interview to interview list. -### Non-Functional Requirements + Use case ends. + +
+ +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The INDEX is out of bounds. + + * 1b1. HRConnect requests for a valid INDEX. + * 1b2. User enters new INDEX.
+ Steps 1b1-1b2 are repeated until the INDEX entered is valid.
+ Use case resumes from step 2.

+ +**Use case: UC3 - Add a task** + +**MSS** + +1. User requests to add a task to the task list. +2. HRConnect adds the task to task list. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The details for some field(s) provided are invalid. + + * 1b1. HRConnect requests for valid details. + * 1b2. User enters new details for field(s).
+ Steps 1b1-1b2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC4 - Delete an applicant** + +**MSS** + +1. User requests to delete an applicant from the HRConnect. +2. HRConnect deletes the applicant from the applicant list. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The INDEX is out of bounds. + + * 1b1. HRConnect requests for a valid INDEX. + * 1b2. User enters new INDEX.
+ Steps 1b1-1b2 are repeated until the INDEX entered is valid.
+ Use case resumes from step 2.

+ +* 1c. The given applicant has an interview. + + * 1c1. HRConnect requests for the user to delete the interview. + * 1c2. User deletes interview.
+ Steps 1c1-1c2 are repeated until the correct interview has been deleted.
+ Use case resumes from step 2.

+ +**Use case: UC5 - Delete an interview** + +**MSS** + +1. User requests to delete a interview in the HRConnect. +2. AddressBook deletes the interview from the interview list. + + Use case ends. + +
+ +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The INDEX is out of bounds. + + * 1b1. HRConnect requests for a valid INDEX. + * 1b2. User enters new INDEX.
+ Steps 1b1-1b2 are repeated until the INDEX entered is valid.
+ Use case resumes from step 2.

+ +**Use case: UC6 - Delete a task** + +**MSS** + +1. User requests to delete a task from the task list. +2. HRConnect deletes the task from the task list. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The INDEX is out of bounds. + + * 1b1. HRConnect requests for a valid INDEX. + * 1b2. User enters new INDEX.
+ Steps 1b1-1b2 are repeated until the INDEX entered is valid.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC7 - List applicant list** + +**MSS** + +1. User requests to list all applicants in the HRConnect. +2. HRConnect displays list of applicants. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +**Use case: UC8 - List interview list** + +**MSS** + +1. User requests to see all scheduled interviews in the HRConnect. +2. HRConnect displays list of interviews. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC9 - List task list** + +**MSS** + +1. User requests to see all tasks in the HRConnect. +2. HRConnect displays list of tasks. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +**Use case: UC10 - Clear applicant list** + +**MSS** + +1. User requests to clear all applicants in the HRConnect. +2. HRConnect clears all applicants from applicant list. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The interview list is not empty. + + * 1c1. HRConnect requests for the user to clear the interview list. + * 1c2. User clears interview list.
+ Steps 1c1-1c2 are repeated until the interview list has been cleared.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC11 - Clear interview list** + +**MSS** + +1. User requests to clear all scheduled interviews in the HRConnect. +2. HRConnect clears all interviews from interview list. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ + +**Use case: UC12 - Clear task list** + +**MSS** + +1. User requests to clear all tasks from the task list. +2. HRConnect clears all tasks from task list. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC13 - Edit an applicant's particulars** + +**MSS** + +1. User requests to update a specific applicants' details. +2. HRConnect shows applicant with their updated details. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The details for some field(s) provided are invalid. + + * 1b1. HRConnect requests for valid details. + * 1b2. User enters new details for field(s).
+ Steps 1b1-1b2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1c. The given applicant has an interview. + + * 1c1. HRConnect requests for the user to delete the interview. + * 1c2. User deletes interview.
+ Steps 1c1-1c2 are repeated until the correct interview has been deleted.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC14 - Edit an interview** + +**MSS** + +1. User requests to update a specific interview. +2. HRConnect shows interview with it's updated details + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The details for some field(s) provided are invalid. + + * 1b1. HRConnect requests for valid details. + * 1b2. User enters new details for field(s).
+ Steps 1b1-1b2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC15 - Edit a task** + +**MSS** + +1. User requests to update a specific task. +2. HRConnect shows task with it's updated details + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The details for some field(s) provided are invalid. + + * 1b1. HRConnect requests for valid details. + * 1b2. User enters new details for field(s).
+ Steps 1b1-1b2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC16 - Find an applicant** + +**MSS** + +1. User requests to find a specific applicant. +2. HRConnect shows applicant with the specified details. + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The details for some field(s) provided are invalid. + + * 1b1. HRConnect requests for valid details. + * 1b2. User enters new details for field(s).
+ Steps 1b1-1b2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +**Use case: UC17 - Find a scheduled interview** + +**MSS** + +1. User requests to find a specific interview. +2. HRConnect shows interview with the specified details + + Use case ends. + +
+ +**Extensions** -1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. -2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. -3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) should be able to accomplish most of the tasks faster using commands than using the mouse. +* 1a. The type is invalid. -*{More to be added}* + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The details for some field(s) provided are invalid. + + * 1b1. HRConnect requests for valid details. + * 1b2. User enters new details for field(s).
+ Steps 1b1-1b2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +**Use case: UC18 - Find a task** + +**MSS** + +1. User requests to find a specific task. +2. HRConnect shows task with the specified details + + Use case ends. + +**Extensions** + +* 1a. The type is invalid. + + * 1a1. HRConnect requests for the correct type. + * 1a2. User enters new type.
+ Steps 1a1-1a2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +* 1b. The details for some field(s) provided are invalid. + + * 1b1. HRConnect requests for valid details. + * 1b2. User enters new details for field(s).
+ Steps 1b1-1b2 are repeated until the data entered are correct.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC19 - Export data from HRConnect** + +**MSS** + +1. User requests to export data. +2. HRConnect exports data to specified csv file. + + Use case ends. + +**Extensions** + +* 1a. The filepath to csv file is invalid. + + * 1a1. HRConnect requests for the valid filepath. + * 1a2. User enters new filepath.
+ Steps 1a1-1a2 are repeated until the filepath entered is valid.
+ Use case resumes from step 2.

+ +* 1b. The filename is missing a .csv file extension. + + * 1b1. HRConnect requests for a valid filename with the correct file extension. + * 1b2. User enters new filename.
+ Steps 1b1-1b2 are repeated until the filename entered is valid.
+ Use case resumes from step 2.

+ +
+ +**Use case: UC20 - Import data to HRConnect** + +**MSS** + +1. User requests to import data. +2. HRConnect shows all applicants imported into the HRConnect. + + Use case ends. + +**Extensions** + +* 1a. The filepath is invalid. + + * 1a1. HRConnect requests for the valid filepath. + * 1a2. User enters new filepath.
+ Steps 1a1-1a2 are repeated until the filepath entered is valid.
+ Use case resumes from step 2.

+ +* 1b. Duplicate applicant found in HRConnect + + * 1a1. HRConnect alerts user that applicant is already in HRConnect and aborts the import. + * 1b1. User deletes applicant from HRConnect. (UC4) + Use case resumes from step 1.

+ +
+ +### Non-Functional Requirements + +1. Should work on any _mainstream OS_ as long as it has Java `11` or above installed. +2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. +3. 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. ### Glossary +* **Applicant**: A person who is applying for a job position in the company. +* **Tech HR Recruiters**: A Human Resource employee that assists in the proper staffing of technical positions within an organization. * **Mainstream OS**: Windows, Linux, Unix, OS-X -* **Private contact detail**: A contact detail that is not meant to be shared with others +* **CLI**: Command Line Interface, a text interface for your computer device +* **GUI**: Graphic User Interface + + -------------------------------------------------------------------------------------------------------------------- +
## **Appendix: Instructions for manual testing** @@ -349,29 +924,118 @@ testers are expected to do more *exploratory* testing. 1. Re-launch the app by double-clicking the jar file.
Expected: The most recent window size and location is retained. -1. _{ more test cases …​ }_ +
+ +### Adding a Task + +1. Adding a Task into the task list.
+ 1. Test case: `add [t] h/Update applicant statuses d/2021-05-06 t/10:10 i/Update statuses of all applicants from last week interviews`
+ Expected: The task specified is added to the task list. Details of the added tasks shown in a message.

+ + 2. Test case: `add [t] h/Update applicant statuses d/6th May 2021 t/10:10 i/Update statuses of all applicants from last week interviews`
+ Expected: No task is added. Error details shown in the status message

+ + 3. Test case: `add [t] h/Update applicant statuses d/2021-05-06 t/10:10pm i/Update statuses of all applicants from last week interviews` + Expected: No task is added. Error details shown in the status message

+ + 4. Test case: `add [t] h/Update applicant statuses d/2021-05-06 t/10:10` + Expected: No task is added. Error details shown in the status message

+ +
+ +### Editing an Interview + +1. Editing an Interview in the interview list.
+ 1. Test case: `edit [i] 1 d/2021-05-06 t/10:30`
+ Expected: The interview specified is edited. Details of the edited interview shown in a message.

+ + 2. Test case: `edit [i] 1 d/6th May 2021 t/10:30`
+ Expected: No task is added. Error details shown in the status message

+ + 3. Test case: `edit [i] 1 d/2021-05-06 t/10:30pm`
+ Expected: No task is added. Error details shown in the status message

+ + 4. Test case: `edit [i] 1 d/2021-05-06`
+ Expected: No task is added. Error details shown in the status message

+ +
+ +### Deleting a job applicant with a scheduled interview + +1. Add an applicant to an empty HRConnect + + 1a. `add [p] n/John Doe p/01234567 e/johnd@example.com a/Pasir Ris BLK121 j/Software Engineer s/INPROGRESS` +

+ +2. Schedule an interview for John Doe + + 2a. `add [i] 1 d/2021-05-06 t/10:30` +

+ +3. Delete John Doe from HRConnect + + 3a. Test case: Do 3a1 then 3a2
+ 3a1. `delete [i] 1`
+ 3a2.`delete [p] 1`
+ Expected: John Doe removed from HRConnect. Details of deleted applicant shown in the status message. +
+ 3b. Test case:`delete [p] 1`
+ Expected: No applicant deleted from HRConnect. Error details shown in the status message

+ +### Clearing all Job Applicants + +1. Clear all applicants in HRConnect with 0 scheduled interviews in interview list. + + 1a. Test case: `clear [p]`
+ Expected: All applicants cleared from HRConnect. Message indicating successful clearing of all applicants shown in status window +

+ +2. Clear all applicants in HRConnect with 1 or more interview(s) in interview list. + + 2a. Test case: `clear [p]`
+ Expected: No applicants cleared from HRConnect. Error details shown in the status message

+ +
+ +### Finding a Job Applicant + +1. Finding all applicants in the ACCEPTED stage.

+ 1a. Test case: `find [p] g/ s/ACCEPTED`
+ Expected: All applicants in the ACCEPTED stage are listed. Details of found applicants shown in status message.

+ + 1b. Test case: `find [p] g/ ACCEPTED`
+ Expected: No applicant found. Error details shown in the status message

+ + 1c. Test case: `find [p] s/ACCEPTED` + Expected: No applicant found. Error details shown in the status message

-### Deleting a person + 1d. Test case: `find g/ s/ACCEPTED` + Expected: No applicant found. Error details shown in the status message

-1. Deleting a person while all persons are being shown - 1. Prerequisites: List all persons using the `list` command. Multiple persons in the list. +2. Finding all applicants applying for Computer System Analyst job position **and** in the ACCEPTED stage.

+ 2a. Test case: `find [p] g/ j/Computer System Analyst s/ACCEPTED`
+ Expected: All applicants in the ACCEPTED stage are listed. Details of found applicants shown in status message.

- 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. + 2b. Test case: `find [p] g/ Computer System Analyst s/ACCEPTED`
+ Expected: No applicant found. Error details shown in the status message

- 1. Test case: `delete 0`
- Expected: No person is deleted. Error details shown in the status message. Status bar remains the same. + 2c. Test case: `find [p] j/Computer System Analyst s/ACCEPTED` + Expected: No applicant found. Error details shown in the status message

- 1. Other incorrect delete commands to try: `delete`, `delete x`, `...` (where x is larger than the list size)
- Expected: Similar to previous. + 2d. Test case: `find g/ j/Computer System Analyst s/ACCEPTED` + Expected: No applicant found. Error details shown in the status message

-1. _{ more test cases …​ }_ -### Saving data +3. Finding all applicants applying for Computer Systems Analyst job position **or** in the ACCEPTED stage.

+ 3a. Test case: `find [p] g/ j/Computer Systems Analyst g/ s/ACCEPTED`
+ Expected: All applicants in the ACCEPTED stage are listed. Details of found applicants shown in status message.

-1. Dealing with missing/corrupted data files + 3b. Test case: `find [p] g/ Computer Systems Analyst g/ s/ACCEPTED`
+ Expected: No applicant found. Error details shown in the status message

- 1. _{explain how to simulate a missing/corrupted file, and the expected behavior}_ + 3c. Test case: `find [p] j/Computer Systems Analyst s/ACCEPTED` + Expected: No applicant found. Error details shown in the status message

-1. _{ more test cases …​ }_ + 3d. Test case: `find g/ j/Computer Systems Analyst g/ s/ACCEPTED` + Expected: No applicant found. Error details shown in the status message

diff --git a/docs/SettingUp.md b/docs/SettingUp.md index 275445bd551..06fe3774963 100644 --- a/docs/SettingUp.md +++ b/docs/SettingUp.md @@ -45,7 +45,7 @@ If you plan to use Intellij IDEA (highly recommended): 1. **Learn the design** - When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [AddressBook’s architecture](DeveloperGuide.md#architecture). + When you are ready to start coding, we recommend that you get some sense of the overall design by reading about [HRConnect’s architecture](DeveloperGuide.md#architecture). 1. **Do the tutorials** These tutorials will help you get acquainted with the codebase. diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 3716f3ca8a4..8ffc3ce68a3 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -3,190 +3,597 @@ layout: page title: User Guide --- -AddressBook Level 3 (AB3) is a **desktop app for managing contacts, optimized for use via a Command Line Interface** (CLI) while still having the benefits of a Graphical User Interface (GUI). If you can type fast, AB3 can get your contact management tasks done faster than traditional GUI apps. +HRConnect is a desktop application for managing the contacts of job applicants.

+It can also be used to keep track of:
+* stage progress of each applicant during the application process +* interviews with each applicant +* miscellaneous tasks that need to be completed +![combined](images/alltogether.PNG) + +
+ +

Table Of Contents

* Table of Contents {:toc} --------------------------------------------------------------------------------------------------------------------- +
+ +# Quick Start +1. Ensure you have `Java 11` or above installed in your computer. +2. Download the latest `HRConnect.jar` from here +3. Copy the file to the folder you want to use as the *home folder* for your HRConnect. +4. Double-click the file to start the app. The GUI similar to the below should appear in a few seconds. Note how the app contains some sample data.
![Ui](images/Ui.png) +5. Type your command into the command box and press `Enter` to execute it.
Some sample commands to try: + * `add [p] n/Bob Tan p/98765876 e/bot@gmail.com a/262 Serangoon Central Drive 1-125 + j/Software Developer s/INPROGRESS`: Adds a new applicant, Bob Tan, to the applicant list + * `list [p]`: Lists all applicants + * `delete [i] 1`: Deletes interview index 1 from the interview list + * `clear [i]`: Clear all interviews + * `exit`: Exits the app +6. Refer to [Features](#features) section directly below for details on each command. + +
+ +# Features + + +  +
-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: +## General Features +---------- - * **`list`** : Lists all contacts. +
- * **`add`**`n/John Doe p/98765432 e/johnd@example.com a/John street, block 123, #01-01` : Adds a contact named `John Doe` to the Address Book. +**:information_source: Notes about the command format regarding General Features:**
- * **`delete`**`3` : Deletes the 3rd contact shown in the current list. +* Extraneous parameters for commands that do not take in parameters (e.g. `help` and `exit`) will be ignored.
+ e.g. if the command specifies `help 123`, it will be interpreted as `help`. - * **`clear`** : Deletes all contacts. +
- * **`exit`** : Exits the app. +### Viewing Help: `help` +Shows a message explaining how to access the help page and the basic flags. -1. Refer to the [Features](#features) below for details of each command. +Format: `help` --------------------------------------------------------------------------------------------------------------------- +  -## Features +### Exiting the program: `exit` +Exits the program. + +Format: `exit` + +
+ +## Applicant Features +----------
-**:information_source: Notes about the command format:**
+**:information_source: Notes about the Applicant List:**
-* 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`. +* Applicant list is sorted in order in which the applicant was added into the list.
+ e.g If Alex Tan was added before Alex Wong, Alex Tan would be listed before Alex Wong. -* Items in square brackets are optional.
- e.g `n/NAME [t/TAG]` can be used as `n/John Doe t/friend` or as `n/John Doe`. +
-* Items with `…`​ after them can be used multiple times including zero times.
- e.g. `[t/TAG]…​` can be used as ` ` (i.e. 0 times), `t/friend`, `t/friend t/family` etc. +### Adding a new job applicant: `add [p]` +Adds a new job applicant to the HRConnect. -* 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. +Format: `add [p] n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS j/JOB_POSITION s/STAGE` -* If a parameter is expected only once in the command but you specified it multiple times, only the last occurrence of the parameter will be taken.
- e.g. if you specify `p/12341234 p/56785678`, only `p/56785678` will be taken. +* Duplicate applicants **cannot** be added. Applicants are considered to be duplicates if they share the **same name (case-insensitive)**.
+e.g. `Alex Tan` and `alex tan` are duplicate applicants. +* If the `NAME` input contain empty spaces, it will be trimmed and each applicant will be considered as a duplicate even if their names differ in the amount of empty spaces. Let `(space)` denote an empty space.
+e.g. `Alex(space)Tan` and `Alex(space)(space)Tan` are duplicate applicants.
+e.g. `Alex(space)Tan` and `AlexTan` are NOT duplicate applicants as `AlexTan` does not have any empty spaces. -* Extraneous parameters for commands that do not take in parameters (such as `help`, `list`, `exit` and `clear`) will be ignored.
- e.g. if the command specifies `help 123`, it will be interpreted as `help`. +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) + +
+ +Example:
+`add [p] n/John Doe p/98765432 e/johnd@example.com a/311, Clementi Ave 2, #02-25 j/Software Developer s/INPROGRESS` + +![add-applicant](images/add-applicant.png) + +  +### Editing a job applicant: `edit [p]` +Edits an existing job applicant in the HRConnect + +:exclamation: **Take note: Cannot edit an applicant if he/she has an interview scheduled.** + +Format: `edit [p] INDEX

` + +* Edits the applicant at the specified `INDEX`. `INDEX` refers to the numerical position of the **applicant in the applicant list**. `INDEX` **must** be a positive integer 1, 2, 3, …​ +* At least **one** of the optional fields must be provided. +* Existing values will be updated to the input values. If input value is same as existing value, success message will still be shown. + +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) +

+ +Example:
+`edit [p] 2 n/Amanda Tan j/Software Developer s/INPROGRESS`

+ +Before +![edit](images/before-edit-contact-3.png) + +After +![edit](images/after-edit-contact-3.png) + +
+ +### Deleting job applicant: `delete [p]` +Deletes an existing job applicant from the HRConnect. + +:exclamation: **Take note: Cannot delete an applicant if he/she has an interview scheduled.** + +Format: `delete [p] INDEX` + +* Deletes the applicant at the specified `INDEX`. `INDEX` refers to the numerical position of the **applicant in the applicant list**.`INDEX` **must** be a positive integer 1, 2, 3, …​ + +Example:
+`delete [p] 1` + +  +### Listing all job applicants: `list [p]` +Shows a list of all job applicants in the HRConnect. + +Format: `list [p]` + +  +### Clearing all job applicants: `clear [p]` +Clears all job applicants from the HRConnect. + +:exclamation: **Take note: Cannot clear applicant list if interview list is not empty.** + +Format: `clear [p]` + +* Clear command does not throw an error even if applicant list is already empty and success message will still be shown. + +
+ +### Finding job applicant(s) by search terms: `find [p]` +Finds job applicants with data containing any of the specified search terms. + +Use `g/` flags to find job applicants whose data contain **all** the search terms. + +:bulb: Tip: Use multiple `g/` flags as an **OR** command (e.g. `find [p] g/n/alex g/j/software developer g/s/INPROGRESS`) + +Format: `find [p] g/SEARCH_TERM ... ...>...` + +* `find [p]` **is required** to start with a `g/` flag. +* Finding job applicants `[p]` **only** accepts `g/`, `n/`, `p/`, `e/`, `a/`, `j/`, and `s/` flags. +* If a SEARCH_TERM without a flag (e.g. `find [p] g/alex`) is used, an error will be raised. +* SEARCH_TERMs are still required to follow the format as defined as by their respective flags. + +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) + +Examples:
+`find [p] g/s/ACCEPTED g/n/John Doe` is logically equivalent to
+`find [p] s/ACCEPTED OR n/John Doe`

+![find](images/find-applicant-OR-example.png)

+
+ +`find [p] g/j/Software Developer s/REJECTED` is logically equivalent to
+`find [p] j/Software Developer AND s/REJECTED`

+![find](images/find-applicant-AND-example.png)

+`find [p] g/j/Software Developer s/REJECTED g/n/John Doe` is logically equivalent to
+`find [p] (j/Software Developer AND s/REJECTED) OR n/John Doe`

+![find](images/find-applicant-AND-OR-example.png)

+ +
+ +## Interview Features +---------- + +
+ +**:information_source: Notes about the Interview List:**
+ +* Interview list is sorted in date time chronological order.
+ e.g An interview scheduled on 29 March 2020 is listed before an interview scheduled on 2 April 2020. +* Interview list can contain interviews with date and time before the current date and time.
+ e.g. If current date time is 4 April 2022, interview list can still contain interview with date 30 March 2022. +* Interview list can contain interviews with applicants in any stages.
+ e.g. A rejected applicant can still be scheduled for an interview.
-### Viewing help : `help` +### Adding a new interview slot for a job applicant: `add [i]` +Adds a new job interview slot to the HRConnect. -Shows a message explaning how to access the help page. +:exclamation: **Take note: Only can add an interview for an applicant that is present in applicant list.** -![help message](images/helpMessage.png) +Format: `add [i] INDEX d/DATE t/TIME` -Format: `help` +* `INDEX` refers to the numerical position of the **applicant in the applicant list**. `INDEX` **must** be a positive integer 1, 2, 3, …​
+* `TIME` refers to the start time of the interview. Duration of each interview is not fixed so a minute time gap between interviews is also allowed. +* Duplicate interviews **cannot** be added. Interviews are considered to be duplicates if they share the **same date AND time**.
+e.g. `add [i] 1 d/2021-06-25 t/17:30` and `add [i] 2 d/2021-06-25 t/17:30` are adding duplicate interviews.
+e.g. `add [i] 1 d/2021-06-25 t/17:30` and `add [i] 1 d/2021-06-30 t/12:30` are **NOT** adding duplicate interviews as the same applicant can have more than 1 interview as long as it is not the same date and time. + +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) + +Example:
+`add [i] 1 d/2021-06-25 t/17:30` + +![add-interview](images/add-interview.png) + +  +### Editing an existing interview slot: `edit [i]` +Edits an existing interview slot in the HRConnect + +Format: `edit [i] INDEX ` + +* Edits the interview at the specified `INDEX`. `INDEX` refers to the numerical position of the **interview in the interview list**. `INDEX` **must** be a positive integer 1, 2, 3, …​ +* `TIME` refers to the start time of the interview. Duration of each interview is not fixed so a minute time gap between interviews is also allowed. +* At least **one** of the optional fields must be provided. +* Existing values will be updated to the input values. If input value is same as existing value, success message will still be shown. +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) +
-### Adding a person: `add` +Example:
+`edit [i] 1 d/2021-12-30 t/10:30`

-Adds a person to the address book. +Before +![edit-interview](images/edit-interview-1.PNG) -Format: `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​` +After +![edit-interview](images/edit-interview.png) + +
+ +### Deleting an interview slot: `delete [i]` +Deletes an existing interview slot in the HRConnect. + +Format: `delete [i] INDEX` + +* Deletes the interview at the specified `INDEX`. `INDEX` refers to the numerical position of the **interview in the interview list**. `INDEX` **must** be a positive integer 1, 2, 3, …​ + +Example:
+`delete [i] 1` + +  +### Listing all scheduled interviews: `list [i]` +Shows a list of all scheduled interviews in the HRConnect. + +Format: `list [i]` + +  +### Clearing all interviews: `clear [i]` +Clears all interviews from the HRConnect. + +Format: `clear [i]` + +* Clear command does not throw an error even if interview list is already empty and success message will still be shown. + +
+ +### Finding scheduled interview slot(s) by search terms: `find [i]` +Finds interview slots with data containing any of the specified search terms. + +Use `g/` flags to find interview slot(s) with data containing **all** the search terms. + +:bulb: Hint: Use multiple `g/` flags to simulate an **OR** command (e.g. `find [i] g/n/alex g/d/2022-01-20 g/t/15:03`) + +Format: `find [i] g/SEARCH_TERM ... ...>...` + +* `find [i]` **is required** to start with a `g/` flag. +* Finding interviews `[i]` **only** accepts `g/`, `n/`, `d/`, `t/`, and `j/` flags. +* If a SEARCH_TERM without a flag (e.g. `find [i] g/alex`) is used, an error will be raised. +* SEARCH_TERMs are still required to follow the format as defined as by their respective flags. + +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) + +Examples:
+`find [i] g/n/Amanda Tan g/j/Software Developer g/t/10:10` is logically equivalent to
+`find [i] n/Amanda Tan OR j/Software Developer OR t/10:10`

+![find](images/find-interview-OR-example.PNG)

+
+ +`find [i] g/n/Amanda Tan j/Software Developer t/10:10` is logically equivalent to
+`find [i] n/Amanda Tan AND j/Software Developer AND t/10:10`

+![find](images/find-interview-AND-example.PNG)

+`find [i] g/n/Amanda Tan j/Software Developer g/t/10:10` is logically equivalent to +`find [i] (n/Amanda Tan AND g/j/Software Developer) OR t/10:10`

+![find](images/find-interview-AND-OR-example.PNG)

+ +
+ +## Task Features +---------- + +
+ +**:information_source: Notes about the Task List:**
+ +* Task list is sorted in date time chronological order.
+ e.g An task scheduled on 29 March 2020 is listed before a task scheduled on 2 April 2020. +* Task list can contain tasks with date and time before the current date and time.
+ e.g. If current date time is 4 April 2022, task list can still contain task with date 30 March 2022. -
: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` +### Adding a new task to the miscellaneous task list: `add [t]` +Adds a new task to the HRConnect. -### Listing all persons : `list` +Format: `add [t] h/HEADER d/DATE t/TIME i/INFORMATION` -Shows a list of all persons in the address book. +* `DATE` and `TIME` refers to the due date and time of the task. +* Duplicate tasks **cannot** be added. Tasks are considered to be duplicates if they share the **same header (case-insensitive) AND date AND time**.
+e.g. `add [t] h/Update interview list d/2021-06-25 t/17:30 i/Update half of the interview list` and `add [t] h/Update interview list d/2021-06-25 t/17:30 i/Update the entire interview list` are adding duplicate interviews. +* If the `HEADER` input contain empty spaces, it will be trimmed and each header will be considered as a duplicate even if their header differ in the amount of empty spaces. Let `(space)` denote an empty space.
+e.g. `Update(space)interview(space)list` and
+`Update(space)(space)interview(space)list` are duplicate headers.
+e.g. `Update(space)interview(space)list` and `Updateinterviewlist` are NOT duplicate headers as `Updateinterviewlist` does not have any empty spaces. -Format: `list` +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) -### Editing a person : `edit` +
-Edits an existing person in the address book. +Example:
+`add [t] h/Add interview slots d/2022-04-01 t/17:30 i/Add all interviews happening in the following week` -Format: `edit INDEX [n/NAME] [p/PHONE] [e/EMAIL] [a/ADDRESS] [t/TAG]…​` +![add-task](images/add-task.PNG) -* 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. +  +### Editing an existing task: `edit [t]` +Edits an existing task in the HRConnect -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. +Format: `edit [t] INDEX ` -### Locating persons by name: `find` +* `DATE` and `TIME` refers to the due date and time of the task. +* Edits the task at the specified `INDEX`. `INDEX` refers to the numerical position of the **task in the task list**. `INDEX` **must** be a positive integer 1, 2, 3, …​ +* At least **one** of the optional fields must be provided. +* Existing values will be updated to the input values. If input value is same as existing value, success message will still be shown. -Finds persons whose names contain any of the given keywords. +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) +
-Format: `find KEYWORD [MORE_KEYWORDS]` +Example:
+`edit [t] 1 d/2021-12-30 t/10:30`

-* 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` +Before +![edit-task-2](images/edit-task-2.PNG) -Examples: -* `find John` returns `john` and `John Doe` -* `find alex david` returns `Alex Yeoh`, `David Li`
- ![result for 'find alex david'](images/findAlexDavidResult.png) +After +![edit-task](images/edit-task.PNG) -### Deleting a person : `delete` +
-Deletes the specified person from the address book. +### Deleting a task: `delete [t]` +Deletes an existing task in the HRConnect. -Format: `delete INDEX` +Format: `delete [t] INDEX` -* Deletes the person at the specified `INDEX`. -* The index refers to the index number shown in the displayed person list. -* The index **must be a positive integer** 1, 2, 3, …​ +* Deletes the task at the specified `INDEX`. `INDEX` refers to the numerical position of the **task in the task list**. `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. +Example:
+`delete [t] 1` -### Clearing all entries : `clear` +  +### Listing all tasks: `list [t]` +Shows a list of all tasks in the HRConnect. -Clears all entries from the address book. +Format: `list [t]` -Format: `clear` +  +### Clearing all tasks: `clear [t]` +Clears all tasks from the HRConnect. -### Exiting the program : `exit` +Format: `clear [t]` -Exits the program. +* Clear command does not throw an error even if task list is already empty and success message will still be shown. -Format: `exit` +
-### Saving the data +### Finding task(s) by search terms: `find [t]` +Find tasks with data containing any of the specified search terms. + +Use `g/` flags to find task(s) with data containing **all** the search terms. + +:bulb: Hint: Use multiple `g/` flags to simulate an **OR** command (e.g. `find [t] g/h/update g/t/10:10`) + +Format: `find [t] g/SEARCH_TERM ... ...>...` + +* `find [t]` **is required** to start with a `g/` flag. +* Finding tasks `[t]` **only** accepts `g/`,`h/`, `d/`, `t/`, and `i/` flags. +* If a SEARCH_TERM without a flag (e.g. `find [t] g/update`) is used, an error will be raised. +* SEARCH_TERMs are still required to follow the format as defined as by their respective flags. + +:bulb: View constraints on flags and parameters [here](#flags-and-parameters-format-constraints) + +Examples:
+`find [t] g/d/2022-03-04 g/h/Update interview list g/t/10:10` is logically equivalent to
+`find [t] d/2022-03-04 OR h/Update interview list OR t/10:10`

+![find](images/find-task-OR-example.PNG)

+
+ +`find [t] g/d/2022-03-06 h/Update interview list t/09:00` is logically equivalent to
+`find [t] d/2022-03-06 AND h/Update interview list AND t/09:00`

+![find](images/find-task-AND-example.PNG)

+`find [t] g/d/2022-03-06 h/Update interview list g/t/10:10` is logically equivalent to
+`find [t] (d/2022-03-06 AND h/Update interview list) OR t/10:10`

+![find](images/find-task-AND-OR-example.PNG)

-AddressBook data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. +
+## Storage +---------- +### Saving the data +HRConnect data are saved in the hard disk automatically after any command that changes the data. There is no need to save manually. + +  ### Editing the data file +HRConnect data are saved as a JSON file `PATH_TO_JAR_FILE/data/HRConnect.json`. Advanced users are welcome to update data directly by editing that data file. -AddressBook data are saved as a JSON file `[JAR file location]/data/addressbook.json`. Advanced users are welcome to update data directly by editing that data file. +:exclamation: **Caution:** If your changes to the data file makes its format invalid, the HRConnect will discard all data and start with an empty data file at the next run.

+:exclamation: **Caution:** Manual modification of the JSON file is **NOT** a supported feature. Inconsistent data resulting from manual modification of the data file should **NOT** be considered as unexpected behaviour.
+Example: Manually editing the `NAME` of applicant in the applicant list of the data file but not on interview list will lead to inconsistent data vice versa and any unusual behaviour caused by this should **NOT** be seen as a bug. -
: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. -
+  +
+ +### Importing the data file: `import` + +Imports all **job applicants** data from a *csv* save file generated from this HRConnect. + +File structure for csv file (tab delimited): + +name | phone number | email | address | job position | stage + +Notes: +1. Filepath can be relative or absolute. +2. No duplicates are allowed to be imported into the HRConnect. + 1. Data in the HRConnect has a higher priority than data in the save file. +3. Save files needs to end with `.csv` in order for the HRConnect to recognise the save file. +4. The csv save file can have at most 1 newline at the end of file for it to be considered valid. +5. If any error is found, **none** of the data in the save file will be imported into the HRConnect. + +Format: `import FILEPATH` + +Example:
+`import C:\Users\YOUR_USERNAME\Desktop\data.csv` + +  +
+ +### Exporting to a csv data file: `export` + +Exports all **job applicants** data from the HRConnect into a *csv* save file. + +File structure for csv file (tab delimited): + +name | phone number | email | address | job position | stage + +Notes: +1. Filepath of specified CSV file can be relative or absolute. +2. Specifying the same csv file name and path will overwrite the data inside the specified csv file. +3. Csv file **must** have .csv as a file extension. +4. If any error is found while executing the command, +**none** of the data from the HRConnect will be exported into the specified csv file. + +:exclamation: **Important:** Some invalid naming conventions for the specified csv file might be allowed +on different OS. E.g. `my:Data.csv` is a valid name on MacOS but not on WindowsOS. + +Format: `export FILEPATH` + +Absolute filepath example for WindowsOS: `export C:\Users\YOUR_USERNAME\Desktop\myData.csv`
+Relative filepath example for WindowsOS: `export .\myData.csv`

+Absolute filepath example for MacOS: `export /Users/YOUR_USERNAME/Downloads/myDataFile.csv`
+Relative filepath example for MacOS: `export ./myDataFile.csv` + +:exclamation: **Important:** Some file paths might be recognised as valid/invalid depending on the OS. + +:exclamation: **Important:** Data in exported csv file will look different depending on the application used to view the +file even though the data is seperated by tabs. + +
+ +# Summary +## Flags and Parameters Format Constraints + +| Flags | Parameters | Format Constraints | +|------:|--------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| a/ | ADDRESS | Addresses can take any values, and it should not be blank | +| d/ | DATE | Date should be in the format YYYY-MM-dd | +| e/ | EMAIL | Emails should be of the format local-part@domain and adhere to the following constraints:

1. The local-part should only contain alphanumeric characters and these special characters, excluding the parentheses, (+_.-). The local-part may not start or end with any special characters.

2. This is followed by a '@' and then a domain name. The domain name is made up of domain labels separated by periods.

The domain name must:
- end with a domain label at least 2 characters long
- have each domain label start and end with alphanumeric characters
- have each domain label consist of alphanumeric characters, separated only by hyphens, if any. | +| g/ | SEARCH_TERM | Only used in the `find` command. Used in combination with the other flags on this list. | +| h/ | HEADER | Header should only contain alphanumeric characters and spaces, and it should not be blank | +| i/ | INFORMATION | Information should only contain alphanumeric characters and spaces, and it should not be blank | +| j/ | JOB_POSITION | Job should only contain alphanumeric characters and spaces, and it should not be blank | +| n/ | NAME | Names should only contain alphanumeric characters and spaces, and it should not be blank | +| p/ | PHONE_NUMBER | Phone numbers should only contain numbers, and it should be at least 3 digits long | +| s/ | STAGE | Stage should be only INPROGRESS or ACCEPTED or REJECTED (case-sensitive) | +| t/ | TIME | Time should be in the format HH:MM | + + +## Section Types + +| Type | Format | +|-----:|:--------------------------------------------------| +| [i] | Applies the current command to the interview list | +| [p] | Applies the current command to the applicant list | +| [t] | Applies the current command to the task list | + +
+ +## Command Summary + +**General Command Summary**

+ +| Action | Format | +|:------:|:------:| +| Help | `help` | +| Exit | `exit` | + +
+ +**Applicant Command Summary**

+ +| Action | Format | +|----------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Add Job Applicant | `add [p] n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS j/JOB_POSITION s/STAGE` | +| Edit Job Applicant | `edit [p] INDEX
-### Archiving data files `[coming in v2.0]` +**Interview Command Summary**

-_Details coming soon ..._ +| Action | Format | +|------------------:|:---------------------------------------------------------------------------------------------------------| +| Add Interview | `add [i] INDEX d/DATE t/TIME` (`INDEX` refers to numerical position of applicant in applicant list) | +| Edit Interview | `edit [i] INDEX ` (`INDEX` refers to numerical position of interview in interview list) | +| Delete Interview | `delete [i] INDEX` (`INDEX` refers to numerical position of interview in interview list) | +| List Interviews | `list [i]` | +| Clear Interviews | `clear [i]` | +| Find Interview(s) | `find [i] g/SEARCH_TERM ... ...>...` | --------------------------------------------------------------------------------------------------------------------- +
-## FAQ +**Task Command Summary**

-**Q**: How do I transfer my data to another Computer?
-**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous AddressBook home folder. +| Action | Format | +|-------------:|:--------------------------------------------------------------------------------------------------------------------------| +| Add Task | `add [t] h/HEADER d/DATE t/TIME i/INFORMATION` | +| Edit Task | `edit [t] INDEX ` (`INDEX` refers to numerical position of task in task list) | +| Delete Task | `delete [t] INDEX` (`INDEX` refers to numerical position of task in task list) | +| List Tasks | `list [t]` | +| Clear Tasks | `clear [t]` | +| Find Task(s) | `find [t] g/SEARCH_TERM ... ...>...` | --------------------------------------------------------------------------------------------------------------------- +
-## Command summary +**Storage Command Summary**

-Action | Format, Examples ---------|------------------ -**Add** | `add n/NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]…​`
e.g., `add n/James Ho p/22224444 e/jamesho@example.com a/123, Clementi Rd, 1234665 t/friend t/colleague` -**Clear** | `clear` -**Delete** | `delete INDEX`
e.g., `delete 3` -**Edit** | `edit INDEX [n/NAME] [p/PHONE_NUMBER] [e/EMAIL] [a/ADDRESS] [t/TAG]…​`
e.g.,`edit 2 n/James Lee e/jameslee@example.com` -**Find** | `find KEYWORD [MORE_KEYWORDS]`
e.g., `find James Jake` -**List** | `list` -**Help** | `help` +| Action | Format | +|:--------------------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| +| Import | `import FILEPATH` | +| Export | `export FILEPATH` | diff --git a/docs/_config.yml b/docs/_config.yml index 6bd245d8f4e..fd150f8f8e0 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,5 +1,5 @@ -title: "AB-3" -theme: minima +title: "HRConnect" +theme: jekyll-theme-architect header_pages: - UserGuide.md @@ -8,7 +8,7 @@ header_pages: markdown: kramdown -repository: "se-edu/addressbook-level3" +repository: "AY2122S2-CS2103T-W11-2/tp" github_icon: "images/github-icon.png" plugins: diff --git a/docs/_sass/minima/_base.scss b/docs/_sass/minima/_base.scss index 0d3f6e80ced..9e97abbce38 100644 --- a/docs/_sass/minima/_base.scss +++ b/docs/_sass/minima/_base.scss @@ -288,7 +288,7 @@ table { text-align: center; } .site-header:before { - content: "AB-3"; + content: "HRConnect"; font-size: 32px; } } diff --git a/docs/diagrams/ArchitectureSequenceDiagram.puml b/docs/diagrams/ArchitectureSequenceDiagram.puml index ef81d18c337..d5a5f0b8a2a 100644 --- a/docs/diagrams/ArchitectureSequenceDiagram.puml +++ b/docs/diagrams/ArchitectureSequenceDiagram.puml @@ -7,10 +7,10 @@ Participant ":Logic" as logic LOGIC_COLOR Participant ":Model" as model MODEL_COLOR Participant ":Storage" as storage STORAGE_COLOR -user -[USER_COLOR]> ui : "delete 1" +user -[USER_COLOR]> ui : "delete [p] 1" activate ui UI_COLOR -ui -[UI_COLOR]> logic : execute("delete 1") +ui -[UI_COLOR]> logic : execute("delete [p] 1") activate logic LOGIC_COLOR logic -[LOGIC_COLOR]> model : deletePerson(p) diff --git a/docs/diagrams/BetterModelClassDiagram.puml b/docs/diagrams/BetterModelClassDiagram.puml index 5731f9cbaa1..653471e0ece 100644 --- a/docs/diagrams/BetterModelClassDiagram.puml +++ b/docs/diagrams/BetterModelClassDiagram.puml @@ -4,18 +4,33 @@ skinparam arrowThickness 1.1 skinparam arrowColor MODEL_COLOR skinparam classBackgroundColor MODEL_COLOR +AddressBook *-right-> "1" UniqueInterviewList AddressBook *-right-> "1" UniquePersonList -AddressBook *-right-> "1" UniqueTagList -UniqueTagList -[hidden]down- UniquePersonList -UniqueTagList -[hidden]down- UniquePersonList +AddressBook *-right-> "1" UniqueTaskList +UniquePersonList -[hidden]down- UniqueInterviewList +UniquePersonList -[hidden]down- UniqueInterviewList +UniqueInterviewList -[hidden]down- UniqueTaskList -UniqueTagList *-right-> "*" Tag -UniquePersonList -right-> Person +UniquePersonList -right-> "*" Person +UniqueInterviewList -right-> "*"Interview +UniqueTaskList -down-> "*"Task -Person -up-> "*" Tag +Person *-down-> Name +Person *-down-> Phone +Person *-down-> Email +Person *-down-> Address +Person *-down-> Job +Person *-right-> Stage -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address + + +Interview *-up-> Person + +Interview *--> Date +Interview *--> Time + +Task *--> Header +Task *--> Information +Task *--> Date +Task *--> Time @enduml diff --git a/docs/diagrams/DeleteSequenceDiagram.puml b/docs/diagrams/DeleteSequenceDiagram.puml index 1dc2311b245..0cbc34f972f 100644 --- a/docs/diagrams/DeleteSequenceDiagram.puml +++ b/docs/diagrams/DeleteSequenceDiagram.puml @@ -5,7 +5,8 @@ box Logic LOGIC_COLOR_T1 participant ":LogicManager" as LogicManager LOGIC_COLOR participant ":AddressBookParser" as AddressBookParser LOGIC_COLOR participant ":DeleteCommandParser" as DeleteCommandParser LOGIC_COLOR -participant "d:DeleteCommand" as DeleteCommand LOGIC_COLOR +participant ":DeletePersonCommandParser" as DeletePersonCommandParser LOGIC_COLOR +participant "d:DeletePersonCommand" as DeletePersonCommand LOGIC_COLOR participant ":CommandResult" as CommandResult LOGIC_COLOR end box @@ -13,10 +14,10 @@ box Model MODEL_COLOR_T1 participant ":Model" as Model MODEL_COLOR end box -[-> LogicManager : execute("delete 1") +[-> LogicManager : execute("delete [p] 1") activate LogicManager -LogicManager -> AddressBookParser : parseCommand("delete 1") +LogicManager -> AddressBookParser : parseCommand("delete [p] 1") activate AddressBookParser create DeleteCommandParser @@ -26,15 +27,23 @@ activate DeleteCommandParser DeleteCommandParser --> AddressBookParser deactivate DeleteCommandParser -AddressBookParser -> DeleteCommandParser : parse("1") +AddressBookParser -> DeleteCommandParser : parse("[p] 1") activate DeleteCommandParser -create DeleteCommand -DeleteCommandParser -> DeleteCommand -activate DeleteCommand +create DeletePersonCommandParser +DeleteCommandParser -> DeletePersonCommandParser +activate DeletePersonCommandParser -DeleteCommand --> DeleteCommandParser : d -deactivate DeleteCommand +create DeletePersonCommand +DeletePersonCommandParser -> DeletePersonCommand : parse("1") +activate DeletePersonCommand + + +DeletePersonCommand -> DeletePersonCommandParser : d +deactivate DeletePersonCommand + +DeletePersonCommandParser --> DeleteCommandParser : d +deactivate DeletePersonCommandParser DeleteCommandParser --> AddressBookParser : d deactivate DeleteCommandParser @@ -45,24 +54,24 @@ destroy DeleteCommandParser AddressBookParser --> LogicManager : d deactivate AddressBookParser -LogicManager -> DeleteCommand : execute() -activate DeleteCommand +LogicManager -> DeletePersonCommand : execute(model) +activate DeletePersonCommand -DeleteCommand -> Model : deletePerson(1) +DeletePersonCommand -> Model : deletePerson(1) activate Model -Model --> DeleteCommand +Model --> DeletePersonCommand deactivate Model create CommandResult -DeleteCommand -> CommandResult +DeletePersonCommand -> CommandResult activate CommandResult -CommandResult --> DeleteCommand +CommandResult --> DeletePersonCommand deactivate CommandResult -DeleteCommand --> LogicManager : result -deactivate DeleteCommand +DeletePersonCommand --> LogicManager : result +deactivate DeletePersonCommand [<--LogicManager deactivate LogicManager diff --git a/docs/diagrams/ExportSequenceDiagram.puml b/docs/diagrams/ExportSequenceDiagram.puml new file mode 100644 index 00000000000..7e177aac5da --- /dev/null +++ b/docs/diagrams/ExportSequenceDiagram.puml @@ -0,0 +1,73 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":ExportCommandParser" as ExportCommandParser LOGIC_COLOR +participant "e:ExportCommand" as ExportCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +box File Operations STORAGE_COLOR_T1 +participant ":PrintWriter" as PrintWriter MODEL_COLOR +end box + +[-> ExportCommandParser : parse("../mydata.csv") +activate ExportCommandParser + + +ExportCommandParser -> ExportCommandParser : checkFilePath("../mydata.csv") +activate ExportCommandParser + +ExportCommandParser --> ExportCommandParser +deactivate ExportCommandParser + +create ExportCommand +ExportCommandParser -> ExportCommand +activate ExportCommand + +ExportCommand --> ExportCommandParser : e +deactivate ExportCommand + +[<-- ExportCommandParser : e +deactivate ExportCommandParser + +[<-[hidden]- ExportCommandParser +destroy ExportCommandParser + +[-> ExportCommand : execute() +activate ExportCommand + +create PrintWriter +ExportCommand -> PrintWriter +activate PrintWriter +PrintWriter --> ExportCommand +deactivate PrintWriter + +ExportCommand -> Model : getFilteredPersonList() +activate Model +Model --> ExportCommand : filteredPersonsList +deactivate Model + +loop until end of filteredPersonsList +ExportCommand -> PrintWriter : printf(person) +activate PrintWriter +PrintWriter --> ExportCommand +deactivate PrintWriter +end loop + +create CommandResult +ExportCommand -> CommandResult +activate CommandResult + +CommandResult --> ExportCommand +deactivate CommandResult + +[<-- ExportCommand : result +deactivate ExportCommand +@enduml + + diff --git a/docs/diagrams/FindCommandSequenceDiagram.puml b/docs/diagrams/FindCommandSequenceDiagram.puml new file mode 100644 index 00000000000..8bf4d98d7b1 --- /dev/null +++ b/docs/diagrams/FindCommandSequenceDiagram.puml @@ -0,0 +1,77 @@ +@startuml +!include style.puml + + +box Logic LOGIC_COLOR_T1 +participant ":FindCommandParser" as FindCommandParser LOGIC_COLOR +participant ":FindTaskCommandParser" as FindTaskCommandParser LOGIC_COLOR +participant "t:FindTaskCommand" as FindTaskCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +participant ":TaskContainsKeywordsPredicate" as TaskContainsKeywordsPredicate MODEL_COLOR +end box + +[-> FindCommandParser : parse("[t] g/i/update") +activate FindCommandParser + +FindCommandParser -> FindCommandParser : checkValidGroups(["i/update"]) +activate FindCommandParser +FindCommandParser --> FindCommandParser +deactivate FindCommandParser + +create FindTaskCommandParser +FindCommandParser -> FindTaskCommandParser +activate FindTaskCommandParser +FindTaskCommandParser --> FindCommandParser +deactivate FindTaskCommandParser + +FindCommandParser -> FindTaskCommandParser : parse(["i/update"]) +activate FindTaskCommandParser + +FindTaskCommandParser -> FindCommandParser : checkInvalidInformation(["i/update"]) +FindCommandParser --> FindTaskCommandParser + +create TaskContainsKeywordsPredicate +FindTaskCommandParser -> TaskContainsKeywordsPredicate +activate TaskContainsKeywordsPredicate +TaskContainsKeywordsPredicate --> FindTaskCommandParser +deactivate TaskContainsKeywordsPredicate + +create FindTaskCommand +FindTaskCommandParser -> FindTaskCommand +activate FindTaskCommand +FindTaskCommand --> FindTaskCommandParser : t +deactivate FindTaskCommand + +FindTaskCommandParser --> FindCommandParser : t +deactivate FindTaskCommandParser +[<-[hidden]- FindTaskCommandParser +destroy FindTaskCommandParser + +[<-- FindCommandParser : t +deactivate FindCommandParser + +[<-[hidden]- FindCommandParser +destroy FindCommandParser + +[-> FindTaskCommand : execute() +activate FindTaskCommand + +FindTaskCommand -> Model : updateFilteredTaskList() +activate Model +Model --> FindTaskCommand +deactivate Model + +create CommandResult +FindTaskCommand -> CommandResult +activate CommandResult +CommandResult --> FindTaskCommand +deactivate CommandResult + +[<-- FindTaskCommand : result +deactivate FindTaskCommand + +@enduml diff --git a/docs/diagrams/ImportCheckSequenceDiagram.puml b/docs/diagrams/ImportCheckSequenceDiagram.puml new file mode 100644 index 00000000000..7a35c811d91 --- /dev/null +++ b/docs/diagrams/ImportCheckSequenceDiagram.puml @@ -0,0 +1,47 @@ +@startuml +!include style.puml + +mainframe **sd** perform necessary checks + +box Logic LOGIC_COLOR_T1 +participant ":ImportCommandParser" as ImportCommandParser LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Person" as Person MODEL_COLOR +end box + +activate ImportCommandParser + + +loop until last entry is parsed +ImportCommandParser -> ImportCommandParser : getFields(entry) +activate ImportCommandParser +ImportCommandParser --> ImportCommandParser +deactivate ImportCommandParser + +ImportCommandParser -> ImportCommandParser : createPerson(entry) +activate ImportCommandParser + +create Person +ImportCommandParser -> Person +activate Person +Person --> ImportCommandParser +deactivate Person + + + + + + +ImportCommandParser --> ImportCommandParser +deactivate ImportCommandParser + +ImportCommandParser -> ImportCommandParser : updatePersons(person) +activate ImportCommandParser +ImportCommandParser --> ImportCommandParser +deactivate ImportCommandParser + +end + +@enduml diff --git a/docs/diagrams/ImportSequenceDiagram.puml b/docs/diagrams/ImportSequenceDiagram.puml new file mode 100644 index 00000000000..fdd26796820 --- /dev/null +++ b/docs/diagrams/ImportSequenceDiagram.puml @@ -0,0 +1,75 @@ +@startuml +!include style.puml + +box Logic LOGIC_COLOR_T1 +participant ":ImportCommandParser" as ImportCommandParser LOGIC_COLOR +participant "i:ImportCommand" as ImportCommand LOGIC_COLOR +participant ":CommandResult" as CommandResult LOGIC_COLOR +end box + +box Model MODEL_COLOR_T1 +participant ":Model" as Model MODEL_COLOR +end box + +[-> ImportCommandParser : parse("../mydata.csv") +activate ImportCommandParser + +ImportCommandParser -> ImportCommandParser : checkFilePath("../mydata.csv") +activate ImportCommandParser +ImportCommandParser --> ImportCommandParser +deactivate ImportCommandParser + +ImportCommandParser -> ImportCommandParser : readCsv("../mydata.csv") +activate ImportCommandParser + +ref over ImportCommandParser +perform necessary checks +end ref + +ImportCommandParser --> ImportCommandParser +deactivate ImportCommandParser + +create ImportCommand +ImportCommandParser -> ImportCommand +activate ImportCommand + +ImportCommand --> ImportCommandParser : i +deactivate ImportCommand + +[<-- ImportCommandParser : i +deactivate ImportCommandParser + +[<-[hidden]- ImportCommandParser : e +destroy ImportCommandParser + +[-> ImportCommand : execute() +activate ImportCommand + +loop until end of persons list +ImportCommand -> Model : hasPerson(person) +activate Model +Model --> ImportCommand +deactivate Model +end + +loop until end of persons list +ImportCommand -> Model : addPerson(person) +activate Model +Model --> ImportCommand +deactivate Model +end + + +create CommandResult +ImportCommand -> CommandResult +activate CommandResult + +CommandResult --> ImportCommand +deactivate CommandResult + +[<-- ImportCommand : result +deactivate ImportCommand + +@enduml + + diff --git a/docs/diagrams/LogicClassDiagram.puml b/docs/diagrams/LogicClassDiagram.puml index d4193173e18..4a4c5263531 100644 --- a/docs/diagrams/LogicClassDiagram.puml +++ b/docs/diagrams/LogicClassDiagram.puml @@ -8,9 +8,10 @@ package Logic { Class AddressBookParser Class XYZCommand +Class "{abstract}\nXYZCommand" as XYZAbstractCommand Class CommandResult Class "{abstract}\nCommand" as Command - +Class XYZTYPECommand Class "<>\nLogic" as Logic Class LogicManager @@ -29,16 +30,21 @@ HiddenOutside ..> Logic LogicManager .right.|> Logic LogicManager -right->"1" AddressBookParser AddressBookParser ..> XYZCommand : creates > +AddressBookParser ..> XYZTYPECommand : creates > -XYZCommand -up-|> Command -LogicManager .left.> Command : executes > +XYZAbstractCommand -up-|> Command +XYZCommand -left-|> Command +LogicManager ..> Command : executes > +XYZTYPECommand -up-|> XYZAbstractCommand LogicManager --> Model LogicManager --> Storage Storage --[hidden] Model Command .[hidden]up.> Storage Command .right.> Model -note right of XYZCommand: XYZCommand = AddCommand, \nFindCommand, etc +note right of XYZAbstractCommand: e.g. AddCommand, \nFindCommand +note right of XYZCommand: e.g. HelpCommand, \nExitCommand +note right of XYZTYPECommand: e.g. AddPersonCommand, \n FindTaskCommand Logic ..> CommandResult LogicManager .down.> CommandResult diff --git a/docs/diagrams/ModelClassDiagram.puml b/docs/diagrams/ModelClassDiagram.puml index 4439108973a..66d667faf5b 100644 --- a/docs/diagrams/ModelClassDiagram.puml +++ b/docs/diagrams/ModelClassDiagram.puml @@ -14,12 +14,12 @@ Class UserPrefs Class UniquePersonList Class Person -Class Address -Class Email -Class Name -Class Phone -Class Tag +Class UniqueInterviewList +Class Interview + +Class UniqueTaskList +Class Task } Class HiddenOutside #FFFFFF @@ -30,21 +30,18 @@ AddressBook .up.|> ReadOnlyAddressBook ModelManager .up.|> Model Model .right.> ReadOnlyUserPrefs Model .left.> ReadOnlyAddressBook -ModelManager -left-> "1" AddressBook +ModelManager --> "1" AddressBook ModelManager -right-> "1" UserPrefs UserPrefs .up.|> ReadOnlyUserPrefs AddressBook *--> "1" UniquePersonList +AddressBook *--> "1" UniqueInterviewList +AddressBook *--> "1" UniqueTaskList UniquePersonList --> "~* all" Person -Person *--> Name -Person *--> Phone -Person *--> Email -Person *--> Address -Person *--> "*" Tag - -Name -[hidden]right-> Phone -Phone -[hidden]right-> Address -Address -[hidden]right-> Email +UniqueInterviewList --> "~* all" Interview +UniqueTaskList --> "~* all" Task ModelManager -->"~* filtered" Person +ModelManager -->"~* filtered" Interview +ModelManager -->"~* filtered" Task @enduml diff --git a/docs/diagrams/ParserClasses.puml b/docs/diagrams/ParserClasses.puml index 0c7424de6e0..a2598d2c228 100644 --- a/docs/diagrams/ParserClasses.puml +++ b/docs/diagrams/ParserClasses.puml @@ -4,11 +4,11 @@ skinparam arrowThickness 1.1 skinparam arrowColor LOGIC_COLOR_T4 skinparam classBackgroundColor LOGIC_COLOR -Class "{abstract}\nCommand" as Command -Class XYZCommand package "Parser classes"{ Class "<>\nParser" as Parser +Class "{abstract}\nCommand" as Command +Class "{abstract}\nXYZCommand" as XYZAbstractCommand Class AddressBookParser Class XYZCommandParser Class CliSyntax @@ -16,23 +16,36 @@ Class ParserUtil Class ArgumentMultimap Class ArgumentTokenizer Class Prefix +Class XYZTYPECommandParser +Class XYZTYPECommand +Class XYZCommand +enum Type } Class HiddenOutside #FFFFFF HiddenOutside ..> AddressBookParser AddressBookParser .down.> XYZCommandParser: creates > - -XYZCommandParser ..> XYZCommand : creates > AddressBookParser ..> Command : returns > +XYZCommandParser ..> XYZTYPECommand : creates > XYZCommandParser .up.|> Parser XYZCommandParser ..> ArgumentMultimap XYZCommandParser ..> ArgumentTokenizer -ArgumentTokenizer .left.> ArgumentMultimap XYZCommandParser ..> CliSyntax +XYZCommandParser ..> XYZCommand : creates > +XYZCommandParser ..> XYZAbstractCommand : returns > +XYZTYPECommandParser ..> XYZTYPECommand : creates > +XYZTYPECommandParser .up.|> Parser +XYZTYPECommand -up-|> XYZAbstractCommand +XYZTYPECommandParser -up-|> Type +ArgumentTokenizer .left.> ArgumentMultimap CliSyntax ..> Prefix XYZCommandParser ..> ParserUtil ParserUtil .down.> Prefix ArgumentTokenizer .down.> Prefix +XYZAbstractCommand -right-|> Command XYZCommand -up-|> Command +XYZCommandParser ..> XYZTYPECommandParser: creates > + + @enduml diff --git a/docs/diagrams/StorageClassDiagram.puml b/docs/diagrams/StorageClassDiagram.puml index 760305e0e58..c14605e5720 100644 --- a/docs/diagrams/StorageClassDiagram.puml +++ b/docs/diagrams/StorageClassDiagram.puml @@ -19,7 +19,8 @@ Class "<>\nAddressBookStorage" as AddressBookStorage Class JsonAddressBookStorage Class JsonSerializableAddressBook Class JsonAdaptedPerson -Class JsonAdaptedTag +Class JsonAdaptedInterview +Class JsonAdaptedTask } } @@ -37,7 +38,9 @@ Storage -right-|> AddressBookStorage JsonUserPrefsStorage .up.|> UserPrefsStorage JsonAddressBookStorage .up.|> AddressBookStorage JsonAddressBookStorage ..> JsonSerializableAddressBook +JsonSerializableAddressBook --> "*" JsonAdaptedInterview +JsonAdaptedInterview --> "1" JsonAdaptedPerson JsonSerializableAddressBook --> "*" JsonAdaptedPerson -JsonAdaptedPerson --> "*" JsonAdaptedTag +JsonSerializableAddressBook --> "*" JsonAdaptedTask @enduml diff --git a/docs/diagrams/UiClassDiagram.puml b/docs/diagrams/UiClassDiagram.puml index 95473d5aa19..716396da77d 100644 --- a/docs/diagrams/UiClassDiagram.puml +++ b/docs/diagrams/UiClassDiagram.puml @@ -15,6 +15,10 @@ Class PersonListPanel Class PersonCard Class StatusBarFooter Class CommandBox +Class InterviewListPanel +Class InterviewCard +Class TaskListPanel +Class TaskCard } package Model <> { @@ -35,8 +39,12 @@ MainWindow *-down-> "1" ResultDisplay MainWindow *-down-> "1" PersonListPanel MainWindow *-down-> "1" StatusBarFooter MainWindow --> "0..1" HelpWindow +MainWindow *-down-> "1" InterviewListPanel +MainWindow *-down-> "1" TaskListPanel PersonListPanel -down-> "*" PersonCard +InterviewListPanel -down-> "*" InterviewCard +TaskListPanel -down-> "*" TaskCard MainWindow -left-|> UiPart @@ -46,12 +54,18 @@ PersonListPanel --|> UiPart PersonCard --|> UiPart StatusBarFooter --|> UiPart HelpWindow --|> UiPart +InterviewListPanel --|> UiPart +TaskListPanel --|> UiPart PersonCard ..> Model +InterviewCard ..> Model +TaskCard ..> Model UiManager -right-> Logic MainWindow -left-> Logic PersonListPanel -[hidden]left- HelpWindow +InterviewListPanel -[hidden]left- HelpWindow +TaskListPanel -[hidden]left- HelpWindow HelpWindow -[hidden]left- CommandBox CommandBox -[hidden]left- ResultDisplay ResultDisplay -[hidden]left- StatusBarFooter diff --git a/docs/images/ArchitectureSequenceDiagram.png b/docs/images/ArchitectureSequenceDiagram.png index 2f1346869d0..f9033eb8451 100644 Binary files a/docs/images/ArchitectureSequenceDiagram.png and b/docs/images/ArchitectureSequenceDiagram.png differ diff --git a/docs/images/BetterModelClassDiagram.png b/docs/images/BetterModelClassDiagram.png index 1ec62caa2a5..5e635abe7da 100644 Binary files a/docs/images/BetterModelClassDiagram.png and b/docs/images/BetterModelClassDiagram.png differ diff --git a/docs/images/DeleteSequenceDiagram.png b/docs/images/DeleteSequenceDiagram.png index fa327b39618..76ff6ddd5f2 100644 Binary files a/docs/images/DeleteSequenceDiagram.png and b/docs/images/DeleteSequenceDiagram.png differ diff --git a/docs/images/ExportSequenceDiagram.png b/docs/images/ExportSequenceDiagram.png new file mode 100644 index 00000000000..7e4c6cf1830 Binary files /dev/null and b/docs/images/ExportSequenceDiagram.png differ diff --git a/docs/images/FindCommandSequenceDiagram.png b/docs/images/FindCommandSequenceDiagram.png new file mode 100644 index 00000000000..ae1f05de951 Binary files /dev/null and b/docs/images/FindCommandSequenceDiagram.png differ diff --git a/docs/images/ImportCheckSequenceDiagram.png b/docs/images/ImportCheckSequenceDiagram.png new file mode 100644 index 00000000000..647761880e9 Binary files /dev/null and b/docs/images/ImportCheckSequenceDiagram.png differ diff --git a/docs/images/ImportSequenceDiagram.png b/docs/images/ImportSequenceDiagram.png new file mode 100644 index 00000000000..aea94429807 Binary files /dev/null and b/docs/images/ImportSequenceDiagram.png differ diff --git a/docs/images/LogicClassDiagram.png b/docs/images/LogicClassDiagram.png index 9e9ba9f79e5..e2ab294c66b 100644 Binary files a/docs/images/LogicClassDiagram.png and b/docs/images/LogicClassDiagram.png differ diff --git a/docs/images/ModelClassDiagram.png b/docs/images/ModelClassDiagram.png index 04070af60d8..e20d79f7b75 100644 Binary files a/docs/images/ModelClassDiagram.png and b/docs/images/ModelClassDiagram.png differ diff --git a/docs/images/ParserClasses.png b/docs/images/ParserClasses.png index e7b4c8880cd..f9427a59178 100644 Binary files a/docs/images/ParserClasses.png and b/docs/images/ParserClasses.png differ diff --git a/docs/images/StorageClassDiagram.png b/docs/images/StorageClassDiagram.png index 2533a5c1af0..66e260019a5 100644 Binary files a/docs/images/StorageClassDiagram.png and b/docs/images/StorageClassDiagram.png differ diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 5bd77847aa2..3f4f9339b6b 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index 785e04dbab4..3277ae6c234 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/docs/images/add-applicant.png b/docs/images/add-applicant.png new file mode 100644 index 00000000000..3ff490e2de7 Binary files /dev/null and b/docs/images/add-applicant.png differ diff --git a/docs/images/add-interview.png b/docs/images/add-interview.png new file mode 100644 index 00000000000..5ed87d3ba3d Binary files /dev/null and b/docs/images/add-interview.png differ diff --git a/docs/images/add-task.PNG b/docs/images/add-task.PNG new file mode 100644 index 00000000000..6a2c572b60a Binary files /dev/null and b/docs/images/add-task.PNG differ diff --git a/docs/images/after-edit-contact-2.png b/docs/images/after-edit-contact-2.png new file mode 100644 index 00000000000..b418039db53 Binary files /dev/null and b/docs/images/after-edit-contact-2.png differ diff --git a/docs/images/after-edit-contact-3.png b/docs/images/after-edit-contact-3.png new file mode 100644 index 00000000000..f497c95342f Binary files /dev/null and b/docs/images/after-edit-contact-3.png differ diff --git a/docs/images/after-edit-contact.png b/docs/images/after-edit-contact.png new file mode 100644 index 00000000000..64c4acb3ca0 Binary files /dev/null and b/docs/images/after-edit-contact.png differ diff --git a/docs/images/alltogether.PNG b/docs/images/alltogether.PNG new file mode 100644 index 00000000000..9700c08daaa Binary files /dev/null and b/docs/images/alltogether.PNG differ diff --git a/docs/images/before-edit-contact-2.png b/docs/images/before-edit-contact-2.png new file mode 100644 index 00000000000..56252f48427 Binary files /dev/null and b/docs/images/before-edit-contact-2.png differ diff --git a/docs/images/before-edit-contact-3.png b/docs/images/before-edit-contact-3.png new file mode 100644 index 00000000000..11dace48a68 Binary files /dev/null and b/docs/images/before-edit-contact-3.png differ diff --git a/docs/images/before-edit-contact.png b/docs/images/before-edit-contact.png new file mode 100644 index 00000000000..56252f48427 Binary files /dev/null and b/docs/images/before-edit-contact.png differ diff --git a/docs/images/brigittesantoso.png b/docs/images/brigittesantoso.png new file mode 100644 index 00000000000..fc5712a3d7a Binary files /dev/null and b/docs/images/brigittesantoso.png differ diff --git a/docs/images/edit-interview-1.PNG b/docs/images/edit-interview-1.PNG new file mode 100644 index 00000000000..82abef32154 Binary files /dev/null and b/docs/images/edit-interview-1.PNG differ diff --git a/docs/images/edit-interview.png b/docs/images/edit-interview.png new file mode 100644 index 00000000000..d9da337e19d Binary files /dev/null and b/docs/images/edit-interview.png differ diff --git a/docs/images/edit-task-2.PNG b/docs/images/edit-task-2.PNG new file mode 100644 index 00000000000..e666506966a Binary files /dev/null and b/docs/images/edit-task-2.PNG differ diff --git a/docs/images/edit-task.PNG b/docs/images/edit-task.PNG new file mode 100644 index 00000000000..f2fcbb5bfc7 Binary files /dev/null and b/docs/images/edit-task.PNG differ diff --git a/docs/images/eman-kom.png b/docs/images/eman-kom.png new file mode 100644 index 00000000000..1ce7ce16dc8 Binary files /dev/null and b/docs/images/eman-kom.png differ diff --git a/docs/images/find-applicant-AND-OR-example.png b/docs/images/find-applicant-AND-OR-example.png new file mode 100644 index 00000000000..f71800e0a11 Binary files /dev/null and b/docs/images/find-applicant-AND-OR-example.png differ diff --git a/docs/images/find-applicant-AND-example.png b/docs/images/find-applicant-AND-example.png new file mode 100644 index 00000000000..71850df12b3 Binary files /dev/null and b/docs/images/find-applicant-AND-example.png differ diff --git a/docs/images/find-applicant-OR-example.png b/docs/images/find-applicant-OR-example.png new file mode 100644 index 00000000000..f92d85942ac Binary files /dev/null and b/docs/images/find-applicant-OR-example.png differ diff --git a/docs/images/find-interview-AND-OR-example.PNG b/docs/images/find-interview-AND-OR-example.PNG new file mode 100644 index 00000000000..368f20c342c Binary files /dev/null and b/docs/images/find-interview-AND-OR-example.PNG differ diff --git a/docs/images/find-interview-AND-example.PNG b/docs/images/find-interview-AND-example.PNG new file mode 100644 index 00000000000..3316c8cc407 Binary files /dev/null and b/docs/images/find-interview-AND-example.PNG differ diff --git a/docs/images/find-interview-OR-example.PNG b/docs/images/find-interview-OR-example.PNG new file mode 100644 index 00000000000..48830607555 Binary files /dev/null and b/docs/images/find-interview-OR-example.PNG differ diff --git a/docs/images/find-interview.png b/docs/images/find-interview.png new file mode 100644 index 00000000000..2d5eed8fe89 Binary files /dev/null and b/docs/images/find-interview.png differ diff --git a/docs/images/find-task-AND-OR-example.PNG b/docs/images/find-task-AND-OR-example.PNG new file mode 100644 index 00000000000..f824b71e55f Binary files /dev/null and b/docs/images/find-task-AND-OR-example.PNG differ diff --git a/docs/images/find-task-AND-example.PNG b/docs/images/find-task-AND-example.PNG new file mode 100644 index 00000000000..dec1646282f Binary files /dev/null and b/docs/images/find-task-AND-example.PNG differ diff --git a/docs/images/find-task-OR-example.PNG b/docs/images/find-task-OR-example.PNG new file mode 100644 index 00000000000..e94b915af18 Binary files /dev/null and b/docs/images/find-task-OR-example.PNG differ diff --git a/docs/images/find.png b/docs/images/find.png new file mode 100644 index 00000000000..48622151a09 Binary files /dev/null and b/docs/images/find.png differ diff --git a/docs/images/imrajsinghsandhu.png b/docs/images/imrajsinghsandhu.png new file mode 100644 index 00000000000..895007a54f3 Binary files /dev/null and b/docs/images/imrajsinghsandhu.png differ diff --git a/docs/images/rcjj98.png b/docs/images/rcjj98.png new file mode 100644 index 00000000000..878ad88717f Binary files /dev/null and b/docs/images/rcjj98.png differ diff --git a/docs/index.md b/docs/index.md index 7601dbaad0d..0209422465f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,17 @@ --- layout: page -title: AddressBook Level-3 +title: HRConnect Level-3 --- [![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) +[![codecov](https://codecov.io/gh/AY2122S2-CS2103T-W11-2/tp/branch/master/graph/badge.svg?token=NLSR34Z1Y2)](https://codecov.io/gh/AY2122S2-CS2103T-W11-2/tp) ![Ui](images/Ui.png) -**AddressBook is a desktop application for managing your contact details.** While it has a GUI, most of the user interactions happen using a CLI (Command Line Interface). +**HRConnect is a desktop application for tech HR recruiters to manage job applicants, interviews and miscellaneous tasks.** 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 HRConnect, head over to the [_Quick Start_ section of the **User Guide**](UserGuide.html#quick-start). +* If you are interested about developing HRConnect, the [**Developer Guide**](DeveloperGuide.html) is a good place to start. **Acknowledgements** diff --git a/docs/team/brigittesantoso.md b/docs/team/brigittesantoso.md new file mode 100644 index 00000000000..1add83e0501 --- /dev/null +++ b/docs/team/brigittesantoso.md @@ -0,0 +1,53 @@ +--- +layout: page +title: Brigitte Santoso's Project Portfolio Page +--- + +## Project: HRConnect + +HRConnect is an application for managing job applicants, interviews and miscellaneous tasks.
+Given below are my contributions to the project. + +### Enhancements implemented +* **Job and Stage fields**: Added `Job` and `Stage` attributes to `Person` class + * What it does: Clearly indicate which job position an applicant is applying to and which stage progress they are currently at + * Justification: This feature improves the product significantly as the main aim of our application for HR recruiters to manage the job applicants and their stage progress easily. +* **Interview Feature**: Added a tab to showcase list of interviews along with its various commands + * What it does: Allows the user to `add`, `delete`, `edit`, `list` and `clear` interviews. + * Justification: This feature improves the product significantly as it allows HR recruiters to easily keep track of the interviews they have with each applicant. The add, delete, edit, list and clear commands help fulfill the possible changes that the user might make to the list. + * Highlights: This enhancement affects existing commands as the `Person` is an attribute in the `Interview` class thus edits have to be made with some of the `Person` commands. +* **Types**: Introduced types into code to distinguish between applicant, interview and task + * What it does: Allows the user to differentiate between which list they are updating by adding either `[p]`, `[i]` or `[t]` behind each command. + * Justification: This feature improves the product significantly as it helps to standardise command line conventions and make it less confusing and easier to differentiate between different types of commands. + * Highlights: This enhancement was difficult as it requires abstraction of code such as creating an abstract class `AddCommand` which `AddPersonCommand`, `AddInterviewCommand` and `AddTaskCommand` extends from to reduce the amount of duplicate code and ensure that OOP principles are adhered to. + +### Code Contribution +Link:
Code Contributed + +### Contributions to the UG and DG +**UG**
+* Add all Task commands to the UG with clear formatting +* Add constraint pointers to some commands + * e.g. `clearing applicant list` and `adding interview` +* Add notes for Interview and Task features + * Listed in chronological date time order +* Solve some UG bugs highlighted from PED +* Helped to format the UG by adding appropriate page breaks for PDF submission + +**DG**
+* Edited Model and Storage sections of DG along with their elaboration +* Edited UML diagrams for Model and Storage section + * `BetterModelClassDiagram`, `ModelClassDiagram` and `StorageClassDiagram` +* Helped to format the DG by adding appropriate page breaks for PDF submission + +### Contributions to team-based tasks + +* Created a few milestones and issues for team +* Ensure team completes all team-based tasks on time + +### Review/mentoring contributions + +* Reviewed and approved some team members PRs with some comments and suggestions +* Helped to solve bugs and issues in other team members features +* Bugs reported in other team products during PED
+ * Link: PED Issues diff --git a/docs/team/eman-kom.md b/docs/team/eman-kom.md new file mode 100644 index 00000000000..7c550971a80 --- /dev/null +++ b/docs/team/eman-kom.md @@ -0,0 +1,78 @@ +--- +layout: page +title: Emmanuel Mok's Project Portfolio Page +--- + +### Project: HRConnect + +This product is for tech HR recruiters who prefer to use CLI to store contacts of job applicants and their respective interviews as well as keep track of the status of each applicant through the application process. + + +Given below are my contributions to the project. + +* **New Feature:** Added more flexibility to the `find` command. + * What it does: Allows users to search of any data in their respective sections (Task/Interview/Applicant) + using the flags as defined in the app. It also simulates 'AND' and 'OR' statements through the use of + the `g/` flag. + * Justification: The previous `find` command was too simplistic in the sense that it can only search for names. With + more data fields being added to HRConnect, a stronger search function is required to enable users to effectively + look for data that they are searching for. Furthermore, the simulation of 'AND' and 'OR' commands gives users more + flexibility and control over their search criteria, leading to a better experience for the user. + * Highlights: This enhancement improves upon the original `find` command. Hence, it requires some analysis into other + design considerations to ensure that the enhancement is both intuitive and useful to the user. It was also challenging to + implement a custom `g/` flag for the `find` command as it requires custom validity checks which builds upon the + ArgumentTokenizer and ArgumentMultiMap. + +* **New Feature:** Added an `import` feature to allow job applicants data to be transferred into the address book. + * What it does: Allows users to import all job applicants data from a csv save file that was previously + exported out by the `export` command. + * Justification: This feature is complementary to the `export` command it allows previously exported data to be + reintegrated back into the address book. + * Highlights: This enhancement was challenging because the csv save file can be subjected to a lot of other changes by + the user. A lot of checks are needed to ensure that the contents of the csv save file contents adhere to the flag + formats as defined in the HRConnect app. Furthermore, there is also a check for any duplicates for both within the csv save + file and the app itself before adding all the job applicants to the app. + + +* **Code Contributed:** + * [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=eman-kom&breakdown=true) + + +* **Project Management:** + * Released v1.3 on GitHub ([v1.3 release link](https://github.com/AY2122S2-CS2103T-W11-2/tp/releases/tag/v1.3)) + * Enabled Assertions (Pull Request: [#45](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/45/commits/a0eda15e3016f5416a577dd3256d984753862229)) + * Created the GitHub organization repo + + +* **Enhancement to existing features:** + * Updated the help page in the address book (Pull Request: [#78](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/78/files)) + * Changed the application name of the address book (Pull Request: [#94](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/94/files)) + + +* **Documentation:** + * User Guide: + * Drafted the initial version of the UG (Pull request: [#53](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/53)) + * Drafted the Flags and Parameters Format Constraints & Section Types (Pull request: [#168](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/168/files)) + * Wrote the Find Feature (Task, Interview & Applicant) in the UG + * Wrote the Import Feature in the UG + + * Developer Guide: + * Added implementation details of the `find` feature + * Added implementation details of the `export` feature + * Added implementation details of the `import` feature + + +* **Team:** + * Fixed some merge conflicts + * Helped to find and fix some bugs + * Reviewed some PRs (Pull Requests: + [#43](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/43), + [#50](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/50), + [#54](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/54), + [#41](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/41)) + * Assisted team members in resolving any obstacles they face + + +* **Community:** + * Submitted 22 bugs for the other team in the PE dry run. ([PED link](https://github.com/eman-kom/ped/issues)) + diff --git a/docs/team/imrajsinghsandhu.md b/docs/team/imrajsinghsandhu.md new file mode 100644 index 00000000000..af25c0f7837 --- /dev/null +++ b/docs/team/imrajsinghsandhu.md @@ -0,0 +1,62 @@ +--- +layout: page +title: Imraj Singh's Project Portfolio Page +--- + +### Project: HRConnect + +HRConnect is a desktop address book application used for keeping track of job applicants' details during their entire application process. It is a CLI focused application, whereby users will interect with the program with commands. The GUI of this application is created with JavaFX, and the application as around 10 kLoC wrtitten in Java. + +Given below are the contributions I have made to this project. + +## Task Feature + +* What it does: Allows users to be able to add a task that has to be done +* Justification: This feature allows the user to note down any miscellaneous tasks that are to be completed + +## Code contributed + +* [RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=imrajsinghsandhu&breakdown=true) + +## Enhancements implemented + +* Implemented the add task feature +* Adhered to OOP principles to reduce the repetition of code + +## Contributions to the Developer Guide + +* Added on to the ParserClassDiagram +* Edited the DeleteSequenceDiagram to include more classes and method +* Edited the LogicClassDiagram +* Added on to the UiClassDiagram +* Changed the descriptions of the above mentioned components +* Edited some bugs found on the Developer Guide + +## Contributions to the User Guide + +* User Guide: + * Added use cases for the delete feature + * Reshuffled the orders of some of the commands in command summary table + * Changed the design of the features section + * Solved some bugs as was highighted in the PE-D + +## Team + +* Brainstormed user stories and product features with team +* Reviewed the following PRs: +[#159](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/159) +[#54](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/54) +[#41](https://github.com/AY2122S2-CS2103T-W11-2/tp/pull/41) + +## Code Review contributions + +* Reviewed and approved team members' PRs with comments and suggestions for improvments where necessary +* Helped with checkstyle edits of files + +## Leadership & Initiative + +* Helped teammates with bug fixes and understanding of tasks/code + +## Bug reports + +* Tested products of other groups and provided bug reports 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/rcjj98.md b/docs/team/rcjj98.md new file mode 100644 index 00000000000..4915e39720b --- /dev/null +++ b/docs/team/rcjj98.md @@ -0,0 +1,66 @@ +--- +layout: page +title: Ryan Chang's Project Portfolio Page +--- + +### Project: HRConnect + +HRConnect is a desktop application for managing the contacts of job applicants. It can also be used to keep track of the +progress of each applicant during the application process. + +Given below are my contributions to the project. + +# Summary of Contributions + +## Code contributed + +[RepoSense link](https://nus-cs2103-ay2122s2.github.io/tp-dashboard/?search=rcjj98&sort=groupTitle&sortWithin=title&timeframe=commit&mergegroup=&groupSelect=groupByRepos&breakdown=true&checkedFileTypes=docs~functional-code~test-code~other&since=2022-02-18&tabOpen=true&tabType=zoom&zFR=false&zA=rcjj98&zR=AY2122S2-CS2103T-W11-2%2Ftp%5Bmaster%5D&zACS=198.13128430296376&zS=2022-02-18&zFS=&zU=2022-04-08&zMG=false&zFTF=commit&zFGS=groupByRepos) + +## Enhancements implemented + +* **Edit Feature**: Added the ability to edit details of job applicants. + * What it does: Allows the user to update any current details of job applicants that were added previously. + * Justification: This feature improves the product significantly because a user can make mistakes when adding a new + job applicant and the app should provide a convenient way to rectify them. +
+ +* **Export Feature**: Added the ability to export the data from the application to a csv file. + * What it does: Allows the user to export all applicant data to a csv save file for archiving. + * Justification: This feature improves the product significantly because a user's address book can start to fill up + with contacts that are no longer important and the application should provide a convenient way to export data to a + file to allow users the convenience of making a copy before clearing the data. +
+ +* Abstract main idea from code to reduce repetition. + +* Checked and reformat code that violated java coding standards. + +## Contributions to the UG + +* Add all applicant commands to the UG with proper formatting. +* Updated commands in the command summary table to match valid command usage. +* Add warning messages for editing applicant. +* Resolved some Pe-D issues. +* Finalize bulk of the applicant commands format for the UG. + +## Contributions to the DG + +* Edited Appendix: Requirements. +* Edited Appendix: Instructions for manual testing. + +## Contributions to team-based tasks + +* Created milestone v1.2 and several issues for team repo. +* Facilitated smooth communications during team meetings using interpersonal skills. +* Handled submissions of tP final submissions. + +## Review/mentoring contributions + +* Reviewed and approved multiple PRs with tactful comments and suggestions. +* Suggested alternative solutions to some problems faced during team meetings. +* Objectively discussed which tasks were well suited for each team member to ensure efficient delegation of work. + +## Contributions beyond team-projects + +* Ensure team dynamics was well maintained throughout entire semester. +* Checking up on teammates progress intermittently during the semester to build rapport. diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index 4133aaa0151..dafba60d584 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, 0, true); + public static final Version VERSION = new Version(1, 3, 0, true); private static final Logger logger = LogsCenter.getLogger(MainApp.class); @@ -56,6 +56,7 @@ public void init() throws Exception { UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath()); UserPrefs userPrefs = initPrefs(userPrefsStorage); + AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath()); storage = new StorageManager(addressBookStorage, userPrefsStorage); @@ -155,7 +156,7 @@ 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) { diff --git a/src/main/java/seedu/address/commons/core/Messages.java b/src/main/java/seedu/address/commons/core/Messages.java index 1deb3a1e469..b2e195a14d4 100644 --- a/src/main/java/seedu/address/commons/core/Messages.java +++ b/src/main/java/seedu/address/commons/core/Messages.java @@ -7,7 +7,17 @@ public class Messages { public static final String MESSAGE_UNKNOWN_COMMAND = "Unknown command"; public static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format! \n%1$s"; - public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The person index provided is invalid"; - public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d persons listed!"; - + public static final String MESSAGE_INVALID_INTERVIEW_DISPLAYED_INDEX = "The interview index provided is invalid"; + public static final String MESSAGE_INVALID_PERSON_DISPLAYED_INDEX = "The applicant index provided is invalid"; + public static final String MESSAGE_INVALID_TASK_DISPLAYED_INDEX = "The task index provided is invalid"; + public static final String MESSAGE_PERSONS_LISTED_OVERVIEW = "%1$d applicants listed!"; + public static final String MESSAGE_INTERVIEWS_LISTED_OVERVIEW = "%1$d interviews listed!"; + public static final String MESSAGE_TASKS_LISTED_OVERVIEW = "%1$d tasks listed!"; + public static final String MESSAGE_NO_TYPE_GIVEN = "No type given. Type should be either [p] or [i] or [t]."; + public static final String MESSAGE_INVALID_TYPE_GIVEN = "Invalid type given. Type should be either [p] or [i]" + + " or [t]."; + public static final String MESSAGE_INTERVIEW_LIST_NOT_EMPTY = "Interview list is not empty. " + + "Please clear interview list first."; + public static final String MESSAGE_PERSON_HAS_INTERVIEW = "Applicant has interview. " + + "Delete interview first before editing or deleting applicant."; } diff --git a/src/main/java/seedu/address/commons/util/FileUtil.java b/src/main/java/seedu/address/commons/util/FileUtil.java index b1e2767cdd9..b537ece3039 100644 --- a/src/main/java/seedu/address/commons/util/FileUtil.java +++ b/src/main/java/seedu/address/commons/util/FileUtil.java @@ -5,6 +5,7 @@ import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; /** * Writes and reads files @@ -72,6 +73,17 @@ public static String readFromFile(Path file) throws IOException { return new String(Files.readAllBytes(file), CHARSET); } + /** + * Reads all lines from a file. + * + * @param file Input file + * @return A list of lines from the file + * @throws IOException Error occurred while reading. + */ + public static List readFromFileLines(Path file) throws IOException { + return Files.readAllLines(file); + } + /** * Writes given string to a file. * Will create the file if it does not exist yet. diff --git a/src/main/java/seedu/address/logic/Logic.java b/src/main/java/seedu/address/logic/Logic.java index 92cd8fa605a..08bad7e9090 100644 --- a/src/main/java/seedu/address/logic/Logic.java +++ b/src/main/java/seedu/address/logic/Logic.java @@ -8,7 +8,9 @@ import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.interview.Interview; import seedu.address.model.person.Person; +import seedu.address.model.tasks.Task; /** * API of the Logic component @@ -33,6 +35,12 @@ public interface Logic { /** Returns an unmodifiable view of the filtered list of persons */ ObservableList getFilteredPersonList(); + /** Returns an unmodifiable view of the filtered list of interviews */ + ObservableList getFilteredInterviewList(); + + /** Returns an unmodifiable view of the filtered list of tasks */ + ObservableList getFilteredTaskList(); + /** * Returns the user prefs' address book file path. */ @@ -47,4 +55,7 @@ public interface Logic { * Set the user prefs' GUI settings. */ void setGuiSettings(GuiSettings guiSettings); + + + } diff --git a/src/main/java/seedu/address/logic/LogicManager.java b/src/main/java/seedu/address/logic/LogicManager.java index 9d9c6d15bdc..52ea6f58a4d 100644 --- a/src/main/java/seedu/address/logic/LogicManager.java +++ b/src/main/java/seedu/address/logic/LogicManager.java @@ -14,7 +14,9 @@ import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.Model; import seedu.address.model.ReadOnlyAddressBook; +import seedu.address.model.interview.Interview; import seedu.address.model.person.Person; +import seedu.address.model.tasks.Task; import seedu.address.storage.Storage; /** @@ -64,6 +66,16 @@ public ObservableList getFilteredPersonList() { return model.getFilteredPersonList(); } + @Override + public ObservableList getFilteredInterviewList() { + return model.getFilteredInterviewList(); + } + + @Override + public ObservableList getFilteredTaskList() { + return model.getFilteredTaskList(); + } + @Override public Path getAddressBookFilePath() { return model.getAddressBookFilePath(); @@ -78,4 +90,5 @@ public GuiSettings getGuiSettings() { public void setGuiSettings(GuiSettings guiSettings) { model.setGuiSettings(guiSettings); } + } diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 71656d7c5c8..590203031e7 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -1,67 +1,15 @@ package seedu.address.logic.commands; -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; - import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; -/** - * Adds a person to the address book. - */ -public class AddCommand extends Command { +public abstract class AddCommand extends Command { public static final String COMMAND_WORD = "add"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Adds a person to the address book. " - + "Parameters: " - + PREFIX_NAME + "NAME " - + PREFIX_PHONE + "PHONE " - + PREFIX_EMAIL + "EMAIL " - + PREFIX_ADDRESS + "ADDRESS " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " " - + PREFIX_NAME + "John Doe " - + PREFIX_PHONE + "98765432 " - + PREFIX_EMAIL + "johnd@example.com " - + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " - + PREFIX_TAG + "friends " - + PREFIX_TAG + "owesMoney"; - - public static final String MESSAGE_SUCCESS = "New person added: %1$s"; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book"; - - private final Person toAdd; - - /** - * Creates an AddCommand to add the specified {@code Person} - */ - public AddCommand(Person person) { - requireNonNull(person); - toAdd = person; - } - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - - if (model.hasPerson(toAdd)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.addPerson(toAdd); - return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); - } + public abstract CommandResult execute(Model model) throws CommandException; @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof AddCommand // instanceof handles nulls - && toAdd.equals(((AddCommand) other).toAdd)); - } + public abstract boolean equals(Object other); } diff --git a/src/main/java/seedu/address/logic/commands/AddInterviewCommand.java b/src/main/java/seedu/address/logic/commands/AddInterviewCommand.java new file mode 100644 index 00000000000..70b2069a5aa --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddInterviewCommand.java @@ -0,0 +1,97 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME; +import static seedu.address.logic.parser.Type.INTERVIEW; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; +import seedu.address.model.Date; +import seedu.address.model.Model; +import seedu.address.model.Time; +import seedu.address.model.interview.Interview; +import seedu.address.model.person.Person; + +/** + * Adds an interview to the address book. + */ +public class AddInterviewCommand extends AddCommand { + + + public static final String MESSAGE_USAGE = COMMAND_WORD + " [i] : Adds an interview to the interview list " + + "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_DATE + "DATE " + + PREFIX_TIME + "TIME\n" + + "Example: " + COMMAND_WORD + " [i] 1 " + + PREFIX_DATE + "2021-05-06 " + + PREFIX_TIME + "05:29"; + + + public static final String MESSAGE_SUCCESS = "Added Interview: %1$s"; + public static final String MESSAGE_DUPLICATE_INTERVIEW = "This interview already exists in the interview list"; + + private final Index index; + private final Date date; + private final Time time; + + /** + * Creates an AddInterviewCommand to add the specified {@code Interview} + */ + public AddInterviewCommand(Index index, Date date, Time time) { + requireNonNull(index); + requireNonNull(date); + requireNonNull(time); + this.index = index; + this.date = date; + this.time = time; + } + + @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 personToInterview = lastShownList.get(index.getZeroBased()); + Interview toAdd = createInterview(personToInterview, date, time); + + if (model.hasInterview(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_INTERVIEW); + } + + model.addInterview(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd), getType()); + } + + /** + * Creates and returns a {@code Interview}. + */ + private static Interview createInterview(Person personToInterview, Date date, Time time) { + assert personToInterview != null; + + return new Interview(personToInterview, date, time); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddInterviewCommand // instanceof handles nulls + && index.equals(((AddInterviewCommand) other).index) + && date.equals(((AddInterviewCommand) other).date) + && time.equals(((AddInterviewCommand) other).time)); + } + + @Override + public Type getType() { + return INTERVIEW; + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddPersonCommand.java b/src/main/java/seedu/address/logic/commands/AddPersonCommand.java new file mode 100644 index 00000000000..e73d130830b --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddPersonCommand.java @@ -0,0 +1,74 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; +import static seedu.address.logic.parser.CliSyntax.PREFIX_JOB; +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_STAGE; +import static seedu.address.logic.parser.Type.PERSON; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; +import seedu.address.model.Model; +import seedu.address.model.person.Person; + +/** + * Adds an applicant to the address book. + */ +public class AddPersonCommand extends AddCommand { + + public static final String MESSAGE_USAGE = COMMAND_WORD + " [p] : Adds an applicant to the applicant list.\n" + + "Parameters: " + + PREFIX_NAME + "NAME " + + PREFIX_PHONE + "PHONE " + + PREFIX_EMAIL + "EMAIL " + + PREFIX_ADDRESS + "ADDRESS " + + PREFIX_JOB + "JOB " + + PREFIX_STAGE + "STAGE\n" + + "Example: " + COMMAND_WORD + " [p] " + + PREFIX_NAME + "John Doe " + + PREFIX_PHONE + "98765432 " + + PREFIX_EMAIL + "johnd@example.com " + + PREFIX_ADDRESS + "311, Clementi Ave 2, #02-25 " + + PREFIX_JOB + "Software Engineer " + + PREFIX_STAGE + "INPROGRESS"; + + public static final String MESSAGE_SUCCESS = "Added Applicant: %1$s"; + public static final String MESSAGE_DUPLICATE_PERSON = "This applicant already exists in the applicant list"; + + private final Person toAdd; + + /** + * Creates an AddCommand to add the specified {@code Person} + */ + public AddPersonCommand(Person person) { + requireNonNull(person); + toAdd = person; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasPerson(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_PERSON); + } + + model.addPerson(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd), getType()); + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddPersonCommand // instanceof handles nulls + && toAdd.equals(((AddPersonCommand) other).toAdd)); + } + + @Override + public Type getType() { + return PERSON; + } +} diff --git a/src/main/java/seedu/address/logic/commands/AddTaskCommand.java b/src/main/java/seedu/address/logic/commands/AddTaskCommand.java new file mode 100644 index 00000000000..1c7983675dc --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/AddTaskCommand.java @@ -0,0 +1,69 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_HEADER; +import static seedu.address.logic.parser.CliSyntax.PREFIX_INFORMATION; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME; +import static seedu.address.logic.parser.Type.TASK; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; +import seedu.address.model.Model; +import seedu.address.model.tasks.Task; + +/** + * Adds a task to the address book. + */ +public class AddTaskCommand extends AddCommand { + + public static final String MESSAGE_USAGE = COMMAND_WORD + " [t] : Adds a task to the list of tasks.\n" + + "Parameters: " + + PREFIX_HEADER + "HEADER " + + PREFIX_DATE + "DATE " + + PREFIX_TIME + "TIME " + + PREFIX_INFORMATION + "INFORMATION\n" + + "Example: " + COMMAND_WORD + " [t] " + + PREFIX_HEADER + "Update interview statuses " + + PREFIX_DATE + "2021-05-06 " + + PREFIX_TIME + "05:29 " + + PREFIX_INFORMATION + "Update all interview statuses to ACCEPTED"; + + public static final String MESSAGE_SUCCESS = "Added Task: %1$s"; + public static final String MESSAGE_DUPLICATE_TASK = "This task already exists in the task list"; + + public final Task toAdd; + + /** + * Constructor for AddTaskCommand. + * @param task Task to be added + */ + public AddTaskCommand(Task task) { + requireNonNull(task); + toAdd = task; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + + if (model.hasTask(toAdd)) { + throw new CommandException(MESSAGE_DUPLICATE_TASK); + } + + model.addTask(toAdd); + return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd), getType()); + } + + @Override + public Type getType() { + return TASK; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof AddTaskCommand // instanceof handles nulls + && toAdd.equals(((AddTaskCommand) other).toAdd)); + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearCommand.java b/src/main/java/seedu/address/logic/commands/ClearCommand.java index 9c86b1fa6e4..1a8cb32cc33 100644 --- a/src/main/java/seedu/address/logic/commands/ClearCommand.java +++ b/src/main/java/seedu/address/logic/commands/ClearCommand.java @@ -1,23 +1,18 @@ package seedu.address.logic.commands; -import static java.util.Objects.requireNonNull; - -import seedu.address.model.AddressBook; +import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; /** * Clears the address book. */ -public class ClearCommand extends Command { +public abstract 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_USAGE = COMMAND_WORD + " [p] to clear all applicants" + + " or [i] to clear all interviews" + " or [t] to clear all tasks"; @Override - public CommandResult execute(Model model) { - requireNonNull(model); - model.setAddressBook(new AddressBook()); - return new CommandResult(MESSAGE_SUCCESS); - } + public abstract CommandResult execute(Model model) throws CommandException; + } diff --git a/src/main/java/seedu/address/logic/commands/ClearInterviewCommand.java b/src/main/java/seedu/address/logic/commands/ClearInterviewCommand.java new file mode 100644 index 00000000000..13c0ed0a7df --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ClearInterviewCommand.java @@ -0,0 +1,24 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.Type.INTERVIEW; + +import seedu.address.logic.parser.Type; +import seedu.address.model.Model; + +public class ClearInterviewCommand extends ClearCommand { + public static final String MESSAGE_SUCCESS = "Interviews have all been cleared!"; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.resetInterviews(); + return new CommandResult(MESSAGE_SUCCESS, getType()); + } + + @Override + public Type getType() { + return INTERVIEW; + } +} + diff --git a/src/main/java/seedu/address/logic/commands/ClearPersonCommand.java b/src/main/java/seedu/address/logic/commands/ClearPersonCommand.java new file mode 100644 index 00000000000..cd7516375a7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ClearPersonCommand.java @@ -0,0 +1,29 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_INTERVIEW_LIST_NOT_EMPTY; +import static seedu.address.logic.parser.Type.PERSON; + +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; +import seedu.address.model.Model; + +public class ClearPersonCommand extends ClearCommand { + public static final String MESSAGE_SUCCESS = "Applicants have all been cleared!"; + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + if (model.getFilteredInterviewList().isEmpty()) { + model.resetPersons(); + } else { + throw new CommandException(MESSAGE_INTERVIEW_LIST_NOT_EMPTY); + } + return new CommandResult(MESSAGE_SUCCESS, getType()); + } + + @Override + public Type getType() { + return PERSON; + } +} diff --git a/src/main/java/seedu/address/logic/commands/ClearTaskCommand.java b/src/main/java/seedu/address/logic/commands/ClearTaskCommand.java new file mode 100644 index 00000000000..6042695c40f --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/ClearTaskCommand.java @@ -0,0 +1,23 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.Type.TASK; + +import seedu.address.logic.parser.Type; +import seedu.address.model.Model; + +public class ClearTaskCommand extends ClearCommand { + public static final String MESSAGE_SUCCESS = "Tasks have all been cleared!"; + + @Override + public CommandResult execute(Model model) { + requireNonNull(model); + model.resetTasks(); + return new CommandResult(MESSAGE_SUCCESS, getType()); + } + + @Override + public Type getType() { + return TASK; + } +} diff --git a/src/main/java/seedu/address/logic/commands/Command.java b/src/main/java/seedu/address/logic/commands/Command.java index 64f18992160..46d6388075d 100644 --- a/src/main/java/seedu/address/logic/commands/Command.java +++ b/src/main/java/seedu/address/logic/commands/Command.java @@ -1,6 +1,7 @@ package seedu.address.logic.commands; import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; import seedu.address.model.Model; /** @@ -17,4 +18,7 @@ public abstract class Command { */ public abstract CommandResult execute(Model model) throws CommandException; + public Type getType() { + return null; + } } diff --git a/src/main/java/seedu/address/logic/commands/CommandResult.java b/src/main/java/seedu/address/logic/commands/CommandResult.java index 92f900b7916..07c5df79cc9 100644 --- a/src/main/java/seedu/address/logic/commands/CommandResult.java +++ b/src/main/java/seedu/address/logic/commands/CommandResult.java @@ -4,6 +4,8 @@ import java.util.Objects; +import seedu.address.logic.parser.Type; + /** * Represents the result of a command execution. */ @@ -11,6 +13,8 @@ public class CommandResult { private final String feedbackToUser; + private final Type type; + /** Help information should be shown to the user. */ private final boolean showHelp; @@ -20,8 +24,9 @@ public class CommandResult { /** * Constructs a {@code CommandResult} with the specified fields. */ - public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { + public CommandResult(String feedbackToUser, Type type, boolean showHelp, boolean exit) { this.feedbackToUser = requireNonNull(feedbackToUser); + this.type = type; this.showHelp = showHelp; this.exit = exit; } @@ -30,14 +35,18 @@ public CommandResult(String feedbackToUser, boolean showHelp, boolean exit) { * Constructs a {@code CommandResult} with the specified {@code feedbackToUser}, * and other fields set to their default value. */ - public CommandResult(String feedbackToUser) { - this(feedbackToUser, false, false); + public CommandResult(String feedbackToUser, Type type) { + this(feedbackToUser, type, false, false); } public String getFeedbackToUser() { return feedbackToUser; } + public Type getType() { + return type; + } + public boolean isShowHelp() { return showHelp; } @@ -59,13 +68,14 @@ public boolean equals(Object other) { CommandResult otherCommandResult = (CommandResult) other; return feedbackToUser.equals(otherCommandResult.feedbackToUser) + && type == otherCommandResult.type && showHelp == otherCommandResult.showHelp && exit == otherCommandResult.exit; } @Override public int hashCode() { - return Objects.hash(feedbackToUser, showHelp, exit); + return Objects.hash(feedbackToUser, type, showHelp, exit); } } diff --git a/src/main/java/seedu/address/logic/commands/DeleteCommand.java b/src/main/java/seedu/address/logic/commands/DeleteCommand.java index 02fd256acba..c6123a4faee 100644 --- a/src/main/java/seedu/address/logic/commands/DeleteCommand.java +++ b/src/main/java/seedu/address/logic/commands/DeleteCommand.java @@ -1,53 +1,16 @@ package seedu.address.logic.commands; -import static java.util.Objects.requireNonNull; - -import java.util.List; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Person; -/** - * Deletes a person identified using it's displayed index from the address book. - */ -public class DeleteCommand extends Command { +public abstract class DeleteCommand extends Command { public static final String COMMAND_WORD = "delete"; - public static final String MESSAGE_USAGE = COMMAND_WORD - + ": Deletes the person identified by the index number used in the displayed person list.\n" - + "Parameters: INDEX (must be a positive integer)\n" - + "Example: " + COMMAND_WORD + " 1"; - - public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Person: %1$s"; - - private final Index targetIndex; - - public DeleteCommand(Index targetIndex) { - this.targetIndex = targetIndex; - } - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (targetIndex.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToDelete = lastShownList.get(targetIndex.getZeroBased()); - model.deletePerson(personToDelete); - return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete)); - } + public abstract CommandResult execute(Model model) throws CommandException; @Override - public boolean equals(Object other) { - return other == this // short circuit if same object - || (other instanceof DeleteCommand // instanceof handles nulls - && targetIndex.equals(((DeleteCommand) other).targetIndex)); // state check - } + public abstract boolean equals(Object other); + } diff --git a/src/main/java/seedu/address/logic/commands/DeleteInterviewCommand.java b/src/main/java/seedu/address/logic/commands/DeleteInterviewCommand.java new file mode 100644 index 00000000000..7262edd4d8f --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteInterviewCommand.java @@ -0,0 +1,60 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.Type.INTERVIEW; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; +import seedu.address.model.Model; +import seedu.address.model.interview.Interview; + +public class DeleteInterviewCommand extends DeleteCommand { + public static final String MESSAGE_USAGE = COMMAND_WORD + + " [i] : Deletes the interview identified by the index number used in the displayed interview list,\n" + + "Parameters: INDEX (must be a positive integer),\n" + + "Example: " + COMMAND_WORD + " [i] 1\n"; + + + public static final String MESSAGE_DELETE_INTERVIEW_SUCCESS = "Deleted Interview: %1$s"; + + private final Index targetIndex; + + /** + * Constructor for DeleteInterviewCommand. + * @param targetIndex + */ + public DeleteInterviewCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredInterviewList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_INTERVIEW_DISPLAYED_INDEX); + } + Interview interviewToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteInterview(interviewToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_INTERVIEW_SUCCESS, interviewToDelete), getType()); + } + + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeleteInterviewCommand // instanceof handles nulls + && targetIndex.equals(((DeleteInterviewCommand) other).targetIndex)); // state check + } + + @Override + public Type getType() { + return INTERVIEW; + } +} + diff --git a/src/main/java/seedu/address/logic/commands/DeletePersonCommand.java b/src/main/java/seedu/address/logic/commands/DeletePersonCommand.java new file mode 100644 index 00000000000..dce4d7b7cf9 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeletePersonCommand.java @@ -0,0 +1,85 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.core.Messages.MESSAGE_PERSON_HAS_INTERVIEW; +import static seedu.address.logic.parser.Type.PERSON; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; +import seedu.address.model.Model; +import seedu.address.model.interview.Interview; +import seedu.address.model.person.Person; + +public class DeletePersonCommand extends DeleteCommand { + public static final String MESSAGE_USAGE = COMMAND_WORD + + " [p] : 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 + " [p] 1\n"; + + public static final String MESSAGE_DELETE_PERSON_SUCCESS = "Deleted Applicant: %1$s"; + + private final Index targetIndex; + + /** + * Constructor for DeletePersonCommand. + * @param targetIndex + */ + public DeletePersonCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownPersonList = model.getFilteredPersonList(); + + if (targetIndex.getZeroBased() >= lastShownPersonList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); + } + + Person personToDelete = lastShownPersonList.get(targetIndex.getZeroBased()); + + if (personHasInterview(personToDelete, model)) { + throw new CommandException(MESSAGE_PERSON_HAS_INTERVIEW); + } + + model.deletePerson(personToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_PERSON_SUCCESS, personToDelete), getType()); + } + + /** + * Check if applicant that we wish to delete has interview + * + * @param person applicant + * @param model model + * @return true if applicant has interview + */ + public boolean personHasInterview(Person person, Model model) { + List lastShownInterviewList = model.getFilteredInterviewList(); + for (int i = 0; i < lastShownInterviewList.size(); i++) { + if (lastShownInterviewList.get(i).getPerson().equals(person)) { + return true; + } + } + return false; + } + + @Override + public boolean equals(Object other) { + return other == this // short circuit if same object + || (other instanceof DeletePersonCommand // instanceof handles nulls + && targetIndex.equals(((DeletePersonCommand) other).targetIndex)); // state check + } + + @Override + public Type getType() { + return PERSON; + } +} + + diff --git a/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java b/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java new file mode 100644 index 00000000000..f938448222f --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/DeleteTaskCommand.java @@ -0,0 +1,56 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.Type.TASK; + +import java.util.List; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; +import seedu.address.model.Model; +import seedu.address.model.tasks.Task; + +public class DeleteTaskCommand extends DeleteCommand { + + public static final String MESSAGE_USAGE = COMMAND_WORD + + " [t] : Deletes the task identified by the index number used in the displayed task list,\n" + + "Parameters: INDEX (must be a positive integer),\n " + + "Example: " + COMMAND_WORD + " [t] 1\n"; + + public static final String MESSAGE_DELETE_TASK_SUCCESS = "Deleted Task: %1$s"; + + private final Index targetIndex; + + /** + * Constructor for DeleteTaskCommand. + * @param targetIndex + */ + public DeleteTaskCommand(Index targetIndex) { + this.targetIndex = targetIndex; + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredTaskList(); + + if (targetIndex.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_TASK_DISPLAYED_INDEX); + } + Task taskToDelete = lastShownList.get(targetIndex.getZeroBased()); + model.deleteTask(taskToDelete); + return new CommandResult(String.format(MESSAGE_DELETE_TASK_SUCCESS, taskToDelete), getType()); + } + + @Override + public boolean equals(Object other) { + return false; + } + + @Override + public Type getType() { + return TASK; + } +} diff --git a/src/main/java/seedu/address/logic/commands/EditCommand.java b/src/main/java/seedu/address/logic/commands/EditCommand.java index 7e36114902f..5dd59d57a9e 100644 --- a/src/main/java/seedu/address/logic/commands/EditCommand.java +++ b/src/main/java/seedu/address/logic/commands/EditCommand.java @@ -1,226 +1,15 @@ package seedu.address.logic.commands; -import static java.util.Objects.requireNonNull; -import static seedu.address.logic.parser.CliSyntax.PREFIX_ADDRESS; -import static seedu.address.logic.parser.CliSyntax.PREFIX_EMAIL; -import static seedu.address.logic.parser.CliSyntax.PREFIX_NAME; -import static seedu.address.logic.parser.CliSyntax.PREFIX_PHONE; -import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; -import static seedu.address.model.Model.PREDICATE_SHOW_ALL_PERSONS; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -import seedu.address.commons.core.Messages; -import seedu.address.commons.core.index.Index; -import seedu.address.commons.util.CollectionUtil; import seedu.address.logic.commands.exceptions.CommandException; import seedu.address.model.Model; -import seedu.address.model.person.Address; -import seedu.address.model.person.Email; -import seedu.address.model.person.Name; -import seedu.address.model.person.Person; -import seedu.address.model.person.Phone; -import seedu.address.model.tag.Tag; -/** - * Edits the details of an existing person in the address book. - */ -public class EditCommand extends Command { +public abstract class EditCommand extends Command { public static final String COMMAND_WORD = "edit"; - public static final String MESSAGE_USAGE = COMMAND_WORD + ": Edits the details of the person identified " - + "by the index number used in the displayed person list. " - + "Existing values will be overwritten by the input values.\n" - + "Parameters: INDEX (must be a positive integer) " - + "[" + PREFIX_NAME + "NAME] " - + "[" + PREFIX_PHONE + "PHONE] " - + "[" + PREFIX_EMAIL + "EMAIL] " - + "[" + PREFIX_ADDRESS + "ADDRESS] " - + "[" + PREFIX_TAG + "TAG]...\n" - + "Example: " + COMMAND_WORD + " 1 " - + PREFIX_PHONE + "91234567 " - + PREFIX_EMAIL + "johndoe@example.com"; - - public static final String MESSAGE_EDIT_PERSON_SUCCESS = "Edited Person: %1$s"; - public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; - public static final String MESSAGE_DUPLICATE_PERSON = "This person already exists in the address book."; - - private final Index index; - private final EditPersonDescriptor editPersonDescriptor; - - /** - * @param index of the person in the filtered person list to edit - * @param editPersonDescriptor details to edit the person with - */ - public EditCommand(Index index, EditPersonDescriptor editPersonDescriptor) { - requireNonNull(index); - requireNonNull(editPersonDescriptor); - - this.index = index; - this.editPersonDescriptor = new EditPersonDescriptor(editPersonDescriptor); - } - @Override - public CommandResult execute(Model model) throws CommandException { - requireNonNull(model); - List lastShownList = model.getFilteredPersonList(); - - if (index.getZeroBased() >= lastShownList.size()) { - throw new CommandException(Messages.MESSAGE_INVALID_PERSON_DISPLAYED_INDEX); - } - - Person personToEdit = lastShownList.get(index.getZeroBased()); - Person editedPerson = createEditedPerson(personToEdit, editPersonDescriptor); - - if (!personToEdit.isSamePerson(editedPerson) && model.hasPerson(editedPerson)) { - throw new CommandException(MESSAGE_DUPLICATE_PERSON); - } - - model.setPerson(personToEdit, editedPerson); - model.updateFilteredPersonList(PREDICATE_SHOW_ALL_PERSONS); - return new CommandResult(String.format(MESSAGE_EDIT_PERSON_SUCCESS, editedPerson)); - } - - /** - * Creates and returns a {@code Person} with the details of {@code personToEdit} - * edited with {@code editPersonDescriptor}. - */ - private static Person createEditedPerson(Person personToEdit, EditPersonDescriptor editPersonDescriptor) { - assert personToEdit != null; - - Name updatedName = editPersonDescriptor.getName().orElse(personToEdit.getName()); - Phone updatedPhone = editPersonDescriptor.getPhone().orElse(personToEdit.getPhone()); - Email updatedEmail = editPersonDescriptor.getEmail().orElse(personToEdit.getEmail()); - Address updatedAddress = editPersonDescriptor.getAddress().orElse(personToEdit.getAddress()); - Set updatedTags = editPersonDescriptor.getTags().orElse(personToEdit.getTags()); - - return new Person(updatedName, updatedPhone, updatedEmail, updatedAddress, updatedTags); - } + public abstract CommandResult execute(Model model) throws CommandException; @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditCommand)) { - return false; - } - - // state check - EditCommand e = (EditCommand) other; - return index.equals(e.index) - && editPersonDescriptor.equals(e.editPersonDescriptor); - } - - /** - * Stores the details to edit the person with. Each non-empty field value will replace the - * corresponding field value of the person. - */ - public static class EditPersonDescriptor { - private Name name; - private Phone phone; - private Email email; - private Address address; - private Set tags; - - public EditPersonDescriptor() {} - - /** - * Copy constructor. - * A defensive copy of {@code tags} is used internally. - */ - public EditPersonDescriptor(EditPersonDescriptor toCopy) { - setName(toCopy.name); - setPhone(toCopy.phone); - setEmail(toCopy.email); - setAddress(toCopy.address); - setTags(toCopy.tags); - } - - /** - * Returns true if at least one field is edited. - */ - public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(name, phone, email, address, tags); - } - - public void setName(Name name) { - this.name = name; - } - - public Optional getName() { - return Optional.ofNullable(name); - } - - public void setPhone(Phone phone) { - this.phone = phone; - } - - public Optional getPhone() { - return Optional.ofNullable(phone); - } - - public void setEmail(Email email) { - this.email = email; - } - - public Optional getEmail() { - return Optional.ofNullable(email); - } - - public void setAddress(Address address) { - this.address = address; - } - - public Optional
getAddress() { - return Optional.ofNullable(address); - } - - /** - * Sets {@code tags} to this object's {@code tags}. - * A defensive copy of {@code tags} is used internally. - */ - public void setTags(Set tags) { - this.tags = (tags != null) ? new HashSet<>(tags) : null; - } - - /** - * Returns an unmodifiable tag set, which throws {@code UnsupportedOperationException} - * if modification is attempted. - * Returns {@code Optional#empty()} if {@code tags} is null. - */ - public Optional> getTags() { - return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); - } - - @Override - public boolean equals(Object other) { - // short circuit if same object - if (other == this) { - return true; - } - - // instanceof handles nulls - if (!(other instanceof EditPersonDescriptor)) { - return false; - } - - // state check - EditPersonDescriptor e = (EditPersonDescriptor) other; - - return getName().equals(e.getName()) - && getPhone().equals(e.getPhone()) - && getEmail().equals(e.getEmail()) - && getAddress().equals(e.getAddress()) - && getTags().equals(e.getTags()); - } - } + public abstract boolean equals(Object other); } diff --git a/src/main/java/seedu/address/logic/commands/EditInterviewCommand.java b/src/main/java/seedu/address/logic/commands/EditInterviewCommand.java new file mode 100644 index 00000000000..1ac737b10f7 --- /dev/null +++ b/src/main/java/seedu/address/logic/commands/EditInterviewCommand.java @@ -0,0 +1,172 @@ +package seedu.address.logic.commands; + +import static java.util.Objects.requireNonNull; +import static seedu.address.logic.parser.CliSyntax.PREFIX_DATE; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TIME; +import static seedu.address.logic.parser.Type.INTERVIEW; +import static seedu.address.model.Model.PREDICATE_SHOW_ALL_INTERVIEWS; + +import java.util.List; +import java.util.Optional; + +import seedu.address.commons.core.Messages; +import seedu.address.commons.core.index.Index; +import seedu.address.commons.util.CollectionUtil; +import seedu.address.logic.commands.exceptions.CommandException; +import seedu.address.logic.parser.Type; +import seedu.address.model.Date; +import seedu.address.model.Model; +import seedu.address.model.Time; +import seedu.address.model.interview.Interview; + +public class EditInterviewCommand extends EditCommand { + public static final String MESSAGE_USAGE = COMMAND_WORD + " [i] : Edits the details of the interview identified " + + "by the index number used in the displayed interview list. " + + "Existing values will be overwritten by the input values.\n" + + "Parameters: INDEX (must be a positive integer) " + + "<" + PREFIX_DATE + "DATE> " + + "<" + PREFIX_TIME + "TIME> \n" + + "Example: " + COMMAND_WORD + " [i] 1 " + + PREFIX_DATE + "2021-05-06 " + + PREFIX_TIME + "10:30"; + + public static final String MESSAGE_EDIT_INTERVIEW_SUCCESS = "Edited Interview: %1$s"; + public static final String MESSAGE_NOT_EDITED = "At least one field to edit must be provided."; + public static final String MESSAGE_DUPLICATE_INTERVIEW = "This interview already exists in the address book."; + + private final Index index; + private final EditInterviewDescriptor editInterviewDescriptor; + + /** + * @param index of the interview in the filtered interview list to edit + * @param editInterviewDescriptor details to edit the interview with + */ + public EditInterviewCommand(Index index, EditInterviewDescriptor editInterviewDescriptor) { + requireNonNull(index); + requireNonNull(editInterviewDescriptor); + + this.index = index; + this.editInterviewDescriptor = new EditInterviewDescriptor(editInterviewDescriptor); + } + + @Override + public CommandResult execute(Model model) throws CommandException { + requireNonNull(model); + List lastShownList = model.getFilteredInterviewList(); + + if (index.getZeroBased() >= lastShownList.size()) { + throw new CommandException(Messages.MESSAGE_INVALID_INTERVIEW_DISPLAYED_INDEX); + } + + Interview interviewToEdit = lastShownList.get(index.getZeroBased()); + + Interview editedInterview = createEditedInterview(interviewToEdit, editInterviewDescriptor); + + if (!interviewToEdit.isSameInterview(editedInterview) && model.hasInterview(editedInterview)) { + throw new CommandException(MESSAGE_DUPLICATE_INTERVIEW); + } + + model.setInterview(interviewToEdit, editedInterview); + model.updateFilteredInterviewList(PREDICATE_SHOW_ALL_INTERVIEWS); + return new CommandResult(String.format(MESSAGE_EDIT_INTERVIEW_SUCCESS, editedInterview), getType()); + } + + /** + * Creates and returns a {@code Interview} with the details of {@code interviewToEdit} + * edited with {@code editInterviewDescriptor}. + */ + private static Interview createEditedInterview( + Interview interviewToEdit, EditInterviewDescriptor editInterviewDescriptor) { + assert interviewToEdit != null; + + Date updatedDate = editInterviewDescriptor.getDate().orElse(interviewToEdit.getDate()); + Time updatedTime = editInterviewDescriptor.getTime().orElse(interviewToEdit.getTime()); + + return new Interview(interviewToEdit.getPerson(), updatedDate, updatedTime); + } + + @Override + public boolean equals(Object other) { + // short circuit if same object + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof EditInterviewCommand)) { + return false; + } + + // state check + EditInterviewCommand e = (EditInterviewCommand) other; + return index.equals(e.index) + && editInterviewDescriptor.equals(e.editInterviewDescriptor); + } + + @Override + public Type getType() { + return INTERVIEW; + } + /** + * Stores the details to edit the interview with. Each non-empty field value will replace the + * corresponding field value of the interview. + */ + public static class EditInterviewDescriptor { + private Date date; + private Time time; + + public EditInterviewDescriptor() {} + + /** + * Copy constructor. + * + */ + public EditInterviewDescriptor(EditInterviewDescriptor toCopy) { + setDate(toCopy.date); + setTime(toCopy.time); + } + + /** + * Returns true if at least one field is edited. + */ + public boolean isAnyFieldEdited() { + return CollectionUtil.isAnyNonNull(date, time); + } + + public void setDate(Date date) { + this.date = date; + } + + public Optional getDate() { + return Optional.ofNullable(date); + } + + public void setTime(Time time) { + this.time = time; + } + + public Optional

` (`INDEX` refers to numerical position of applicant in applicant list) | +| Delete Job Applicant | `delete [p] INDEX` (`INDEX` refers to numerical position of applicant in applicant list) | +| List Job Applicants | `list [p]` | +| Clear Job Applicants | `clear [p]` | +| Find Job Applicant(s) | `find [p] g/SEARCH_TERM ... ...>...` | + +